Skip to content

Commit 4d640ce

Browse files
committed
tweak v-if to avoid unnecessary node replacement
1 parent 014d2f8 commit 4d640ce

File tree

5 files changed

+49
-13
lines changed

5 files changed

+49
-13
lines changed

flow/component.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ declare interface Component {
8787
_s: (value: any) => string;
8888
// toNumber
8989
_n: (value: string) => number | string;
90+
// empty vnode
91+
_e: () => VNode;
9092
// resolveFilter
9193
_f: (id: string) => Function;
9294
// renderList

src/compiler/codegen/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function genIf (el: ASTElement): string {
8080
function genElse (el: ASTElement): string {
8181
return el.elseBlock
8282
? genElement(el.elseBlock)
83-
: 'void 0'
83+
: '_e()'
8484
}
8585

8686
function genFor (el: ASTElement): string {

src/core/instance/render.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ export function renderMixin (Vue: Class<Component>) {
9292
Vue.prototype._s = _toString
9393
// number conversion
9494
Vue.prototype._n = toNumber
95+
// empty vnode
96+
Vue.prototype._e = emptyVNode
9597

9698
// render static tree by index
9799
Vue.prototype._m = function renderStatic (
@@ -216,7 +218,7 @@ export function resolveSlots (
216218
// ignore single whitespace
217219
if (defaultSlot.length && !(
218220
defaultSlot.length === 1 &&
219-
defaultSlot[0].text === ' '
221+
(defaultSlot[0].text === ' ' || defaultSlot[0].isComment)
220222
)) {
221223
slots.default = defaultSlot
222224
}

test/unit/features/directives/if.spec.js

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ describe('Directive v-if', () => {
1414
template: '<div><span v-if="foo">hello</span></div>',
1515
data: { foo: false }
1616
}).$mount()
17-
expect(vm.$el.innerHTML).toBe('')
17+
expect(vm.$el.innerHTML).toBe('<!---->')
1818
})
1919

2020
it('should update if value changed', done => {
@@ -25,25 +25,25 @@ describe('Directive v-if', () => {
2525
expect(vm.$el.innerHTML).toBe('<span>hello</span>')
2626
vm.foo = false
2727
waitForUpdate(() => {
28-
expect(vm.$el.innerHTML).toBe('')
28+
expect(vm.$el.innerHTML).toBe('<!---->')
2929
vm.foo = {}
3030
}).then(() => {
3131
expect(vm.$el.innerHTML).toBe('<span>hello</span>')
3232
vm.foo = 0
3333
}).then(() => {
34-
expect(vm.$el.innerHTML).toBe('')
34+
expect(vm.$el.innerHTML).toBe('<!---->')
3535
vm.foo = []
3636
}).then(() => {
3737
expect(vm.$el.innerHTML).toBe('<span>hello</span>')
3838
vm.foo = null
3939
}).then(() => {
40-
expect(vm.$el.innerHTML).toBe('')
40+
expect(vm.$el.innerHTML).toBe('<!---->')
4141
vm.foo = '0'
4242
}).then(() => {
4343
expect(vm.$el.innerHTML).toBe('<span>hello</span>')
4444
vm.foo = undefined
4545
}).then(() => {
46-
expect(vm.$el.innerHTML).toBe('')
46+
expect(vm.$el.innerHTML).toBe('<!---->')
4747
vm.foo = 1
4848
}).then(() => {
4949
expect(vm.$el.innerHTML).toBe('<span>hello</span>')
@@ -103,16 +103,16 @@ describe('Directive v-if', () => {
103103
]
104104
}
105105
}).$mount()
106-
expect(vm.$el.innerHTML).toBe('<span>0</span><span>2</span>')
106+
expect(vm.$el.innerHTML).toBe('<span>0</span><!----><span>2</span>')
107107
vm.list[0].value = false
108108
waitForUpdate(() => {
109-
expect(vm.$el.innerHTML).toBe('<span>2</span>')
109+
expect(vm.$el.innerHTML).toBe('<!----><!----><span>2</span>')
110110
vm.list.push({ value: true })
111111
}).then(() => {
112-
expect(vm.$el.innerHTML).toBe('<span>2</span><span>3</span>')
112+
expect(vm.$el.innerHTML).toBe('<!----><!----><span>2</span><span>3</span>')
113113
vm.list.splice(1, 2)
114114
}).then(() => {
115-
expect(vm.$el.innerHTML).toBe('<span>1</span>')
115+
expect(vm.$el.innerHTML).toBe('<!----><span>1</span>')
116116
}).then(done)
117117
})
118118

@@ -173,4 +173,36 @@ describe('Directive v-if', () => {
173173
expect(vm.$el.children[0].className).toBe('inner test')
174174
}).then(done)
175175
})
176+
177+
it('should maintain stable list to avoid unnecessary patches', done => {
178+
const created = jasmine.createSpy()
179+
const destroyed = jasmine.createSpy()
180+
const vm = new Vue({
181+
data: {
182+
ok: true
183+
},
184+
// when the first div is toggled, the second div should be reused
185+
// instead of re-created/destroyed
186+
template: `
187+
<div>
188+
<div v-if="ok"></div>
189+
<div><test></test></div>
190+
</div>
191+
`,
192+
components: {
193+
test: {
194+
template: '<div></div>',
195+
created,
196+
destroyed
197+
}
198+
}
199+
}).$mount()
200+
201+
expect(created.calls.count()).toBe(1)
202+
vm.ok = false
203+
waitForUpdate(() => {
204+
expect(created.calls.count()).toBe(1)
205+
expect(destroyed).not.toHaveBeenCalled()
206+
}).then(done)
207+
})
176208
})

test/unit/modules/compiler/codegen.spec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe('codegen', () => {
5656
it('generate v-if directive', () => {
5757
assertCodegen(
5858
'<p v-if="show">hello</p>',
59-
`with(this){return (show)?_h('p',["hello"]):void 0}`
59+
`with(this){return (show)?_h('p',["hello"]):_e()}`
6060
)
6161
})
6262

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

0 commit comments

Comments
 (0)