Sfoglia il codice sorgente

tweak v-if to avoid unnecessary node replacement

Evan You 9 anni fa
parent
commit
4d640ce4d9

+ 2 - 0
flow/component.js

@@ -87,6 +87,8 @@ declare interface Component {
   _s: (value: any) => string;
   // toNumber
   _n: (value: string) => number | string;
+  // empty vnode
+  _e: () => VNode;
   // resolveFilter
   _f: (id: string) => Function;
   // renderList

+ 1 - 1
src/compiler/codegen/index.js

@@ -80,7 +80,7 @@ function genIf (el: ASTElement): string {
 function genElse (el: ASTElement): string {
   return el.elseBlock
     ? genElement(el.elseBlock)
-    : 'void 0'
+    : '_e()'
 }
 
 function genFor (el: ASTElement): string {

+ 3 - 1
src/core/instance/render.js

@@ -92,6 +92,8 @@ export function renderMixin (Vue: Class<Component>) {
   Vue.prototype._s = _toString
   // number conversion
   Vue.prototype._n = toNumber
+  // empty vnode
+  Vue.prototype._e = emptyVNode
 
   // render static tree by index
   Vue.prototype._m = function renderStatic (
@@ -216,7 +218,7 @@ export function resolveSlots (
   // ignore single whitespace
   if (defaultSlot.length && !(
     defaultSlot.length === 1 &&
-    defaultSlot[0].text === ' '
+    (defaultSlot[0].text === ' ' || defaultSlot[0].isComment)
   )) {
     slots.default = defaultSlot
   }

+ 41 - 9
test/unit/features/directives/if.spec.js

@@ -14,7 +14,7 @@ describe('Directive v-if', () => {
       template: '<div><span v-if="foo">hello</span></div>',
       data: { foo: false }
     }).$mount()
-    expect(vm.$el.innerHTML).toBe('')
+    expect(vm.$el.innerHTML).toBe('<!---->')
   })
 
   it('should update if value changed', done => {
@@ -25,25 +25,25 @@ describe('Directive v-if', () => {
     expect(vm.$el.innerHTML).toBe('<span>hello</span>')
     vm.foo = false
     waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('')
+      expect(vm.$el.innerHTML).toBe('<!---->')
       vm.foo = {}
     }).then(() => {
       expect(vm.$el.innerHTML).toBe('<span>hello</span>')
       vm.foo = 0
     }).then(() => {
-      expect(vm.$el.innerHTML).toBe('')
+      expect(vm.$el.innerHTML).toBe('<!---->')
       vm.foo = []
     }).then(() => {
       expect(vm.$el.innerHTML).toBe('<span>hello</span>')
       vm.foo = null
     }).then(() => {
-      expect(vm.$el.innerHTML).toBe('')
+      expect(vm.$el.innerHTML).toBe('<!---->')
       vm.foo = '0'
     }).then(() => {
       expect(vm.$el.innerHTML).toBe('<span>hello</span>')
       vm.foo = undefined
     }).then(() => {
-      expect(vm.$el.innerHTML).toBe('')
+      expect(vm.$el.innerHTML).toBe('<!---->')
       vm.foo = 1
     }).then(() => {
       expect(vm.$el.innerHTML).toBe('<span>hello</span>')
@@ -103,16 +103,16 @@ describe('Directive v-if', () => {
         ]
       }
     }).$mount()
-    expect(vm.$el.innerHTML).toBe('<span>0</span><span>2</span>')
+    expect(vm.$el.innerHTML).toBe('<span>0</span><!----><span>2</span>')
     vm.list[0].value = false
     waitForUpdate(() => {
-      expect(vm.$el.innerHTML).toBe('<span>2</span>')
+      expect(vm.$el.innerHTML).toBe('<!----><!----><span>2</span>')
       vm.list.push({ value: true })
     }).then(() => {
-      expect(vm.$el.innerHTML).toBe('<span>2</span><span>3</span>')
+      expect(vm.$el.innerHTML).toBe('<!----><!----><span>2</span><span>3</span>')
       vm.list.splice(1, 2)
     }).then(() => {
-      expect(vm.$el.innerHTML).toBe('<span>1</span>')
+      expect(vm.$el.innerHTML).toBe('<!----><span>1</span>')
     }).then(done)
   })
 
@@ -173,4 +173,36 @@ describe('Directive v-if', () => {
       expect(vm.$el.children[0].className).toBe('inner test')
     }).then(done)
   })
+
+  it('should maintain stable list to avoid unnecessary patches', done => {
+    const created = jasmine.createSpy()
+    const destroyed = jasmine.createSpy()
+    const vm = new Vue({
+      data: {
+        ok: true
+      },
+      // when the first div is toggled, the second div should be reused
+      // instead of re-created/destroyed
+      template: `
+        <div>
+          <div v-if="ok"></div>
+          <div><test></test></div>
+        </div>
+      `,
+      components: {
+        test: {
+          template: '<div></div>',
+          created,
+          destroyed
+        }
+      }
+    }).$mount()
+
+    expect(created.calls.count()).toBe(1)
+    vm.ok = false
+    waitForUpdate(() => {
+      expect(created.calls.count()).toBe(1)
+      expect(destroyed).not.toHaveBeenCalled()
+    }).then(done)
+  })
 })

+ 2 - 2
test/unit/modules/compiler/codegen.spec.js

@@ -56,7 +56,7 @@ describe('codegen', () => {
   it('generate v-if directive', () => {
     assertCodegen(
       '<p v-if="show">hello</p>',
-      `with(this){return (show)?_h('p',["hello"]):void 0}`
+      `with(this){return (show)?_h('p',["hello"]):_e()}`
     )
   })
 
@@ -318,7 +318,7 @@ describe('codegen', () => {
   it('not specified directives option', () => {
     assertCodegen(
       '<p v-if="show">hello world</p>',
-      `with(this){return (show)?_h('p',["hello world"]):void 0}`,
+      `with(this){return (show)?_h('p',["hello world"]):_e()}`,
       { isReservedTag }
     )
   })