Explorar el Código

revert directive bind invocation timing, add inserted hook for directives

Evan You hace 9 años
padre
commit
ada90e5e37
Se han modificado 2 ficheros con 47 adiciones y 12 borrados
  1. 34 10
      src/core/vdom/modules/directives.js
  2. 13 2
      test/unit/features/options/directives.spec.js

+ 34 - 10
src/core/vdom/modules/directives.js

@@ -5,9 +5,18 @@ import { mergeVNodeHook } from 'core/vdom/helpers'
 
 export default {
   create: function bindDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) {
-    mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'insert', () => {
-      applyDirectives(oldVnode, vnode, 'bind')
+    let hasInsert = false
+    forEachDirective(oldVnode, vnode, (def, dir) => {
+      callHook(def, dir, 'bind', vnode, oldVnode)
+      if (def.inserted) {
+        hasInsert = true
+      }
     })
+    if (hasInsert) {
+      mergeVNodeHook(vnode.data.hook || (vnode.data.hook = {}), 'insert', () => {
+        applyDirectives(oldVnode, vnode, 'inserted')
+      })
+    }
   },
   update: function updateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) {
     applyDirectives(oldVnode, vnode, 'update')
@@ -27,28 +36,43 @@ export default {
 
 const emptyModifiers = Object.create(null)
 
-function applyDirectives (
+function forEachDirective (
   oldVnode: VNodeWithData,
   vnode: VNodeWithData,
-  hook: string
+  fn: Function
 ) {
   const dirs = vnode.data.directives
   if (dirs) {
-    const oldDirs = oldVnode.data.directives
-    const isUpdate = hook === 'update' || hook === 'componentUpdated'
     for (let i = 0; i < dirs.length; i++) {
       const dir = dirs[i]
       const def = resolveAsset(vnode.context.$options, 'directives', dir.name, true)
-      const fn = def && def[hook]
-      if (fn) {
-        if (isUpdate && oldDirs) {
+      if (def) {
+        const oldDirs = oldVnode && oldVnode.data.directives
+        if (oldDirs) {
           dir.oldValue = oldDirs[i].value
         }
         if (!dir.modifiers) {
           dir.modifiers = emptyModifiers
         }
-        fn(vnode.elm, dir, vnode, oldVnode)
+        fn(def, dir)
       }
     }
   }
 }
+
+function applyDirectives (
+  oldVnode: VNodeWithData,
+  vnode: VNodeWithData,
+  hook: string
+) {
+  forEachDirective(oldVnode, vnode, (def, dir) => {
+    callHook(def, dir, hook, vnode, oldVnode)
+  })
+}
+
+function callHook (def, dir, hook, vnode, oldVnode) {
+  const fn = def && def[hook]
+  if (fn) {
+    fn(vnode.elm, dir, vnode, oldVnode)
+  }
+}

+ 13 - 2
test/unit/features/options/directives.spec.js

@@ -3,6 +3,7 @@ import Vue from 'vue'
 describe('Options directives', () => {
   it('basic usage', done => {
     const bindSpy = jasmine.createSpy('bind')
+    const insertedSpy = jasmine.createSpy('inserted')
     const updateSpy = jasmine.createSpy('update')
     const componentUpdatedSpy = jasmine.createSpy('componentUpdated')
     const unbindSpy = jasmine.createSpy('unbind')
@@ -14,7 +15,7 @@ describe('Options directives', () => {
     }
 
     const vm = new Vue({
-      template: '<div v-if="ok" v-test:arg.hello="a">{{ msg }}</div>',
+      template: '<div class="hi"><div v-if="ok" v-test:arg.hello="a">{{ msg }}</div></div>',
       data: {
         msg: 'hi',
         a: 'foo',
@@ -28,11 +29,20 @@ describe('Options directives', () => {
             expect(binding.value).toBe('foo')
             expect(binding.expression).toBe('a')
             expect(binding.oldValue).toBeUndefined()
+            expect(el.parentNode).toBeNull()
+          },
+          inserted (el, binding, vnode) {
+            insertedSpy()
+            assertContext(el, binding, vnode)
+            expect(binding.value).toBe('foo')
+            expect(binding.expression).toBe('a')
+            expect(binding.oldValue).toBeUndefined()
+            expect(el.parentNode.className).toBe('hi')
           },
           update (el, binding, vnode, oldVnode) {
             updateSpy()
             assertContext(el, binding, vnode)
-            expect(el).toBe(vm.$el)
+            expect(el).toBe(vm.$el.children[0])
             expect(oldVnode).not.toBe(vnode)
             expect(binding.expression).toBe('a')
             if (binding.value !== binding.oldValue) {
@@ -54,6 +64,7 @@ describe('Options directives', () => {
 
     vm.$mount()
     expect(bindSpy).toHaveBeenCalled()
+    expect(insertedSpy).toHaveBeenCalled()
     expect(updateSpy).not.toHaveBeenCalled()
     expect(componentUpdatedSpy).not.toHaveBeenCalled()
     expect(unbindSpy).not.toHaveBeenCalled()