vueカスタム命令原理
21818 ワード
vue命令の本質
命令は本質的に装飾器であり、vueによるHTML要素の拡張であり、HTML要素にカスタム機能を追加し、HTMLラベルを意味化する.vueがDOMをコンパイルすると,命令に関連付けられたJSコード,すなわち命令オブジェクトを見つけ,命令オブジェクトに関するメソッドを実行する.
カスタム命令ライフサイクル
カスタム命令には、bind、inserted、update、componentUpdated、unbindの5つのライフサイクル(フック関数とも呼ばれる)があります.
フック関数の役割の紹介 bind:一度だけ呼び出し、命令が初めて要素にバインドされたときに呼び出され、このフック関数でバインド時に一度の初期化動作を定義できます. inserted:バインド要素が親ノードに挿入されたときに呼び出されます(親ノードは存在し、documentに保存する必要はありません). update:バインド値が変化するかどうかにかかわらず、要素が存在するテンプレートの更新にバインドされたときに呼び出されます.更新前後のバインド値を比較することで、不要なテンプレート更新を無視できます. componentUpdated:バインドされた要素があるテンプレートが更新サイクルを完了したときに呼び出されます. unbind:1回のみ呼び出され、命令が要素にバインドされていないときに呼び出されます.
インプリメンテーションプロセス
ソースコード
命令は本質的に装飾器であり、vueによるHTML要素の拡張であり、HTML要素にカスタム機能を追加し、HTMLラベルを意味化する.vueがDOMをコンパイルすると,命令に関連付けられたJSコード,すなわち命令オブジェクトを見つけ,命令オブジェクトに関するメソッドを実行する.
カスタム命令ライフサイクル
カスタム命令には、bind、inserted、update、componentUpdated、unbindの5つのライフサイクル(フック関数とも呼ばれる)があります.
フック関数の役割の紹介
インプリメンテーションプロセス
ソースコード
// 2.6.10
export default {
create: updateDirectives,
update: updateDirectives,
destroy: function unbindDirectives (vnode: VNodeWithData) {
updateDirectives(vnode, emptyNode)
}
}
function updateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) {
if (oldVnode.data.directives || vnode.data.directives) {
_update(oldVnode, vnode)
}
}
function _update (oldVnode, vnode) {
const isCreate = oldVnode === emptyNode //
const isDestroy = vnode === emptyNode // , , true
const oldDirs = normalizeDirectives(oldVnode.data.directives, oldVnode.context) //
const newDirs = normalizeDirectives(vnode.data.directives, vnode.context) //
const dirsWithInsert = [] // inserted
const dirsWithPostpatch = [] // componentUpdated
let key, oldDir, dir
for (key in newDirs) {
oldDir = oldDirs[key]
dir = newDirs[key]
if (!oldDir) { // oldDir , ,
// bind
callHook(dir, 'bind', vnode, oldVnode)
// inserted , dirsWithInsert, bind inserted
if (dir.def && dir.def.inserted) {
dirsWithInsert.push(dir)
}
} else {
// oldDir ,
dir.oldValue = oldDir.value
dir.oldArg = oldDir.arg
callHook(dir, 'update', vnode, oldVnode)
// componentUpdated , dirsWithPostpatch,
// vnode vnode ( update ), componentUpdated
if (dir.def && dir.def.componentUpdated) {
dirsWithPostpatch.push(dir)
}
}
}
if (dirsWithInsert.length) {
const callInsert = () => {
for (let i = 0; i < dirsWithInsert.length; i++) {
callHook(dirsWithInsert[i], 'inserted', vnode, oldVnode)
}
}
if (isCreate) {
// , mergeVNodeHook
//
mergeVNodeHook(vnode, 'insert', callInsert)
} else {
callInsert()
}
}
if (dirsWithPostpatch.length) {
mergeVNodeHook(vnode, 'postpatch', () => {
for (let i = 0; i < dirsWithPostpatch.length; i++) {
callHook(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode)
}
})
}
//
if (!isCreate) {
// , , , unbind
for (key in oldDirs) {
if (!newDirs[key]) {
callHook(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy)
}
}
}
}
const emptyModifiers = Object.create(null)
function normalizeDirectives (
dirs: ?Array<VNodeDirective>,
vm: Component
): { [key: string]: VNodeDirective } {
const res = Object.create(null)
if (!dirs) {
return res
}
let i, dir
for (i = 0; i < dirs.length; i++) {
dir = dirs[i]
if (!dir.modifiers) {
dir.modifiers = emptyModifiers
}
res[getRawDirName(dir)] = dir
dir.def = resolveAsset(vm.$options, 'directives', dir.name, true)
}
return res
}
function getRawDirName (dir: VNodeDirective): string {
return dir.rawName || `${dir.name}.${Object.keys(dir.modifiers || {}).join('.')}`
}
function callHook (dir, hook, vnode, oldVnode, isDestroy) {
const fn = dir.def && dir.def[hook]
if (fn) {
try {
fn(vnode.elm, dir, vnode, oldVnode, isDestroy)
} catch (e) {
handleError(e, vnode.context, `directive ${dir.name} ${hook} hook`)
}
}
}