|
|
@ -2,76 +2,115 @@ |
|
|
|
|
|
|
|
import { resolveAsset } from 'core/util/options' |
|
|
|
import { mergeVNodeHook } from 'core/vdom/helpers' |
|
|
|
import { emptyNode } from 'core/vdom/patch' |
|
|
|
|
|
|
|
export default { |
|
|
|
create: function bindDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) { |
|
|
|
let hasInsert = false |
|
|
|
forEachDirective(oldVnode, vnode, (def, dir) => { |
|
|
|
callHook(def, dir, 'bind', vnode, oldVnode) |
|
|
|
if (def.inserted) { |
|
|
|
hasInsert = true |
|
|
|
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) { |
|
|
|
return |
|
|
|
} |
|
|
|
const isCreate = oldVnode === emptyNode |
|
|
|
const oldDirs = normalizeDirectives(oldVnode.data.directives, oldVnode.context) |
|
|
|
const newDirs = normalizeDirectives(vnode.data.directives, vnode.context) |
|
|
|
|
|
|
|
const dirsWithInsert = [] |
|
|
|
const dirsWithPostpatch = [] |
|
|
|
|
|
|
|
let key, oldDir, dir |
|
|
|
for (key in newDirs) { |
|
|
|
oldDir = oldDirs[key] |
|
|
|
dir = newDirs[key] |
|
|
|
if (!oldDir) { |
|
|
|
// new directive, bind
|
|
|
|
callHook(dir, 'bind', vnode, oldVnode) |
|
|
|
if (dir.def && dir.def.inserted) { |
|
|
|
dirsWithInsert.push(dir) |
|
|
|
} |
|
|
|
} else { |
|
|
|
// existing directive, update
|
|
|
|
dir.oldValue = oldDir.value |
|
|
|
callHook(dir, 'update', vnode, oldVnode) |
|
|
|
if (dir.def && dir.def.componentUpdated) { |
|
|
|
dirsWithPostpatch.push(dir) |
|
|
|
} |
|
|
|
}) |
|
|
|
if (hasInsert) { |
|
|
|
mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'insert', () => { |
|
|
|
applyDirectives(oldVnode, vnode, 'inserted') |
|
|
|
}, 'dir-insert') |
|
|
|
} |
|
|
|
}, |
|
|
|
update: function updateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) { |
|
|
|
applyDirectives(oldVnode, vnode, 'update') |
|
|
|
// if old vnode has directives but new vnode doesn't
|
|
|
|
// we need to teardown the directives on the old one.
|
|
|
|
if (oldVnode.data.directives && !vnode.data.directives) { |
|
|
|
applyDirectives(oldVnode, oldVnode, 'unbind') |
|
|
|
} |
|
|
|
|
|
|
|
if (dirsWithInsert.length) { |
|
|
|
const callInsert = () => { |
|
|
|
dirsWithInsert.forEach(dir => { |
|
|
|
callHook(dir, 'inserted', vnode, oldVnode) |
|
|
|
}) |
|
|
|
} |
|
|
|
if (isCreate) { |
|
|
|
mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'insert', callInsert, 'dir-insert') |
|
|
|
} else { |
|
|
|
callInsert() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (dirsWithPostpatch.length) { |
|
|
|
mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'postpatch', () => { |
|
|
|
dirsWithPostpatch.forEach(dir => { |
|
|
|
callHook(dir, 'componentUpdated', vnode, oldVnode) |
|
|
|
}) |
|
|
|
}, 'dir-postpatch') |
|
|
|
} |
|
|
|
|
|
|
|
if (!isCreate) { |
|
|
|
for (key in oldDirs) { |
|
|
|
if (!newDirs[key]) { |
|
|
|
// no longer present, unbind
|
|
|
|
callHook(oldDirs[key], 'unbind', oldVnode) |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
postpatch: function postupdateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) { |
|
|
|
applyDirectives(oldVnode, vnode, 'componentUpdated') |
|
|
|
}, |
|
|
|
destroy: function unbindDirectives (vnode: VNodeWithData) { |
|
|
|
applyDirectives(vnode, vnode, 'unbind') |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
const emptyModifiers = Object.create(null) |
|
|
|
|
|
|
|
function forEachDirective ( |
|
|
|
oldVnode: VNodeWithData, |
|
|
|
vnode: VNodeWithData, |
|
|
|
fn: Function |
|
|
|
) { |
|
|
|
const dirs = vnode.data.directives |
|
|
|
if (dirs) { |
|
|
|
for (let i = 0; i < dirs.length; i++) { |
|
|
|
const dir = dirs[i] |
|
|
|
const def = resolveAsset(vnode.context.$options, 'directives', dir.name, true) |
|
|
|
if (def) { |
|
|
|
const oldDirs = oldVnode && oldVnode.data.directives |
|
|
|
if (oldDirs) { |
|
|
|
dir.oldValue = oldDirs[i].value |
|
|
|
} |
|
|
|
if (!dir.modifiers) { |
|
|
|
dir.modifiers = emptyModifiers |
|
|
|
} |
|
|
|
fn(def, dir) |
|
|
|
} |
|
|
|
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] |
|
|
|
res[getRawDirName(dir)] = dir |
|
|
|
if (!dir.modifiers) { |
|
|
|
dir.modifiers = emptyModifiers |
|
|
|
} |
|
|
|
dir.def = resolveAsset(vm.$options, 'directives', dir.name, true) |
|
|
|
} |
|
|
|
return res |
|
|
|
} |
|
|
|
|
|
|
|
function applyDirectives ( |
|
|
|
oldVnode: VNodeWithData, |
|
|
|
vnode: VNodeWithData, |
|
|
|
hook: string |
|
|
|
) { |
|
|
|
forEachDirective(oldVnode, vnode, (def, dir) => { |
|
|
|
callHook(def, dir, hook, vnode, oldVnode) |
|
|
|
}) |
|
|
|
function getRawDirName (dir: VNodeDirective): string { |
|
|
|
return dir.rawName || ( |
|
|
|
dir.name + ( |
|
|
|
dir.modifiers |
|
|
|
? '.' + Object.keys(dir.modifiers).join('.') |
|
|
|
: '' |
|
|
|
) |
|
|
|
) |
|
|
|
} |
|
|
|
|
|
|
|
function callHook (def, dir, hook, vnode, oldVnode) { |
|
|
|
const fn = def && def[hook] |
|
|
|
function callHook (dir, hook, vnode, oldVnode) { |
|
|
|
const fn = dir.def && dir.def[hook] |
|
|
|
if (fn) { |
|
|
|
fn(vnode.elm, dir, vnode, oldVnode) |
|
|
|
} |
|
|
|