/* @flow */
|
|
import { emptyNode } from 'core/vdom/patch'
|
import { resolveAsset, handleError } from 'core/util/index'
|
import { mergeVNodeHook } from 'core/vdom/helpers/index'
|
|
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
|
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
|
dir.oldArg = oldDir.arg
|
callHook(dir, 'update', vnode, oldVnode)
|
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(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) {
|
for (key in oldDirs) {
|
if (!newDirs[key]) {
|
// no longer present, unbind
|
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) {
|
// $flow-disable-line
|
return res
|
}
|
let i, dir
|
for (i = 0; i < dirs.length; i++) {
|
dir = dirs[i]
|
if (!dir.modifiers) {
|
// $flow-disable-line
|
dir.modifiers = emptyModifiers
|
}
|
res[getRawDirName(dir)] = dir
|
dir.def = resolveAsset(vm.$options, 'directives', dir.name, true)
|
}
|
// $flow-disable-line
|
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`)
|
}
|
}
|
}
|