diff --git a/src/compiler/codegen/index.js b/src/compiler/codegen/index.js index 4fd183b5dab..937e037cb50 100644 --- a/src/compiler/codegen/index.js +++ b/src/compiler/codegen/index.js @@ -85,7 +85,9 @@ function genStatic (el: ASTElement): string { // v-once function genOnce (el: ASTElement): string { el.onceProcessed = true - if (el.staticInFor) { + if (el.if && !el.ifProcessed) { + return genIf(el) + } else if (el.staticInFor) { let key = '' let parent = el.parent while (parent) { @@ -107,10 +109,11 @@ function genOnce (el: ASTElement): string { } } +// v-if with v-once shuold generate code like (a)?_m(0):_m(1) function genIf (el: any): string { const exp = el.if el.ifProcessed = true // avoid recursion - return `(${exp})?${genElement(el)}:${genElse(el)}` + return `(${exp})?${el.once ? genOnce(el) : genElement(el)}:${genElse(el)}` } function genElse (el: ASTElement): string { diff --git a/src/compiler/optimizer.js b/src/compiler/optimizer.js index 991a677e137..7caecbe9903 100644 --- a/src/compiler/optimizer.js +++ b/src/compiler/optimizer.js @@ -59,7 +59,12 @@ function markStaticRoots (node: ASTNode, isInFor: boolean) { } if (node.children) { for (let i = 0, l = node.children.length; i < l; i++) { - markStaticRoots(node.children[i], isInFor || !!node.for) + const child = node.children[i] + isInFor = isInFor || !!node.for + markStaticRoots(child, isInFor) + if (child.type === 1 && child.elseBlock) { + markStaticRoots(child.elseBlock, isInFor) + } } } } diff --git a/test/unit/features/directives/once.spec.js b/test/unit/features/directives/once.spec.js index b7ada2f4584..50791edcdbe 100644 --- a/test/unit/features/directives/once.spec.js +++ b/test/unit/features/directives/once.spec.js @@ -15,10 +15,11 @@ describe('Directive v-once', () => { it('should not rerender self and child component', done => { const vm = new Vue({ - template: `
- {{ a }} - -
`, + template: ` +
+ {{ a }} + +
`, data: { a: 'hello' }, components: { item: { @@ -39,10 +40,11 @@ describe('Directive v-once', () => { it('should rerender parent but not self', done => { const vm = new Vue({ - template: `
- {{ a }} - -
`, + template: ` +
+ {{ a }} + +
`, data: { a: 'hello' }, components: { item: { @@ -63,11 +65,12 @@ describe('Directive v-once', () => { it('should not rerender static sub nodes', done => { const vm = new Vue({ - template: `
- {{ a }} - - {{ suffix }} -
`, + template: ` +
+ {{ a }} + + {{ suffix }} +
`, data: { a: 'hello', suffix: '?' @@ -92,6 +95,39 @@ describe('Directive v-once', () => { }).then(done) }) + it('should work with v-if', done => { + const vm = new Vue({ + data: { + tester: true, + yes: 'y', + no: 'n' + }, + template: ` +
+
{{ yes }}
+
{{ no }}
+
{{ yes }}
+
{{ no }}
+
{{ yes }}
+
{{ no }}
+
{{ yes }}
+
{{ no }}
+
+ ` + }).$mount() + expectTextContent(vm, 'yyyy') + vm.yes = 'yes' + waitForUpdate(() => { + expectTextContent(vm, 'yesyyesy') + vm.tester = false + }).then(() => { + expectTextContent(vm, 'nnnn') + vm.no = 'no' + }).then(() => { + expectTextContent(vm, 'nononn') + }).then(done) + }) + it('should work with v-for', done => { const vm = new Vue({ data: { @@ -140,6 +176,43 @@ describe('Directive v-once', () => { }).then(done) }) + it('should work inside v-for with v-if', done => { + const vm = new Vue({ + data: { + list: [ + { id: 0, text: 'a', tester: true, truthy: 'y' } + ] + }, + template: ` +
+
+ {{ i.truthy }} + {{ i.text }} + {{ i.truthy }} + {{ i.text }} + {{ i.truthy }} + {{ i.text }} + {{ i.truthy }} + {{ i.text }} +
+
+ ` + }).$mount() + + expectTextContent(vm, 'yyyy') + + vm.list[0].truthy = 'yy' + waitForUpdate(() => { + expectTextContent(vm, 'yyyyyy') + vm.list[0].tester = false + }).then(() => { + expectTextContent(vm, 'aaaa') + vm.list[0].text = 'nn' + }).then(() => { + expectTextContent(vm, 'annann') + }).then(done) + }) + it('should warn inside non-keyed v-for', () => { const vm = new Vue({ data: { @@ -162,3 +235,7 @@ describe('Directive v-once', () => { expect(`v-once can only be used inside v-for that is keyed.`).toHaveBeenWarned() }) }) + +function expectTextContent (vm, text) { + expect(vm.$el.textContent.replace(/\r?\n|\r|\s/g, '')).toBe(text) +}