|
|
@@ -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)
|
|
|
}
|