Просмотр исходного кода

fix v-once with v-for and remove unused _t method (fix #3155)

Evan You 9 лет назад
Родитель
Сommit
cf8aeddc12

+ 1 - 0
flow/compiler.js

@@ -65,6 +65,7 @@ declare type ASTElement = {
 
   static?: boolean,
   staticRoot?: boolean,
+  staticProcessed?: boolean,
 
   text?: string,
   attrs?: Array<{ name: string, value: string }>,

+ 0 - 4
flow/component.js

@@ -87,10 +87,6 @@ declare interface Component {
     data?: Object,
     namespace?: string
   ) => VNode | void;
-  // renderText
-  _t: (
-    str?: string
-  ) => string;
   // renderStaticTree
   _m: (
     index?: number

+ 8 - 8
src/compiler/codegen.js

@@ -39,7 +39,12 @@ export function generate (
 }
 
 function genElement (el: ASTElement): string {
-  if (el.for && !el.forProcessed) {
+  if (el.staticRoot && !el.staticProcessed) {
+    // hoist static sub-trees out
+    el.staticProcessed = true
+    staticRenderFns.push(`with(this){return ${genElement(el)}}`)
+    return `_m(${staticRenderFns.length - 1})`
+  } else if (el.for && !el.forProcessed) {
     return genFor(el)
   } else if (el.if && !el.ifProcessed) {
     return genIf(el)
@@ -66,11 +71,6 @@ function genElement (el: ASTElement): string {
       })${
         children ? `,${children}` : '' // children
       })`
-      if (el.staticRoot) {
-        // hoist static sub-trees out
-        staticRenderFns.push(`with(this){return ${code}}`)
-        code = `_m(${staticRenderFns.length - 1})`
-      }
     }
     // module transforms
     for (let i = 0; i < transforms.length; i++) {
@@ -239,8 +239,8 @@ function genNode (node: ASTNode) {
 
 function genText (text: ASTText | ASTExpression): string {
   return text.type === 2
-    ? `(${text.expression})`
-    : '_t(' + JSON.stringify(text.text) + ')'
+    ? text.expression // no need for () because already wrapped in _s()
+    : JSON.stringify(text.text)
 }
 
 function genSlot (el: ASTElement): string {

+ 2 - 5
src/core/instance/render.js

@@ -5,13 +5,12 @@ import VNode, { emptyVNode } from '../vdom/vnode'
 import { normalizeChildren } from '../vdom/helpers'
 import {
   warn, bind, isObject, toObject,
-  nextTick, resolveAsset, renderString, toNumber
+  nextTick, resolveAsset, _toString, toNumber
 } from '../util/index'
 
 import {
   renderElement,
   renderElementWithChildren,
-  renderText,
   renderStatic
 } from '../vdom/create-element'
 
@@ -91,11 +90,9 @@ export function renderMixin (Vue: Class<Component>) {
   // shorthands used in render functions
   Vue.prototype._h = renderElementWithChildren
   Vue.prototype._e = renderElement
-  Vue.prototype._t = renderText
   Vue.prototype._m = renderStatic
-
   // toString for mustaches
-  Vue.prototype._s = renderString
+  Vue.prototype._s = _toString
   // number conversion
   Vue.prototype._n = toNumber
 

+ 0 - 4
src/core/vdom/create-element.js

@@ -98,10 +98,6 @@ export function renderElement (
   }
 }
 
-export function renderText (str?: string): string {
-  return str || ''
-}
-
 export function renderStatic (index?: number): Object | void {
   return this._staticTrees[index] || (
     this._staticTrees[index] = this.$options.staticRenderFns[index].call(

+ 2 - 2
src/core/vdom/patch.js

@@ -8,7 +8,7 @@
  */
 
 import VNode from './vnode'
-import { isPrimitive, renderString, warn } from '../util/index'
+import { isPrimitive, _toString, warn } from '../util/index'
 
 const emptyNode = new VNode('', {}, [])
 const hooks = ['create', 'update', 'postpatch', 'remove', 'destroy']
@@ -349,7 +349,7 @@ export function createPatchFunction (backend) {
         )
       }
     } else {
-      return renderString(vnode.text) === node.data
+      return _toString(vnode.text) === node.data
     }
   }
 

+ 1 - 1
src/shared/util.js

@@ -3,7 +3,7 @@
 /**
  * Convert a value to a string that is actually rendered.
  */
-export function renderString (val: any): string {
+export function _toString (val: any): string {
   return val == null
     ? ''
     : typeof val === 'object'

+ 14 - 0
test/unit/features/directives/once.spec.js

@@ -91,4 +91,18 @@ describe('Directive v-once', () => {
         .toBe('<span>hello</span> <div>world</div> <span>!</span>')
     }).then(done)
   })
+
+  it('should work with v-for', done => {
+    const vm = new Vue({
+      data: {
+        list: [1, 2, 3]
+      },
+      template: `<div><div v-for="i in list" v-once>{{i}}</div></div>`
+    }).$mount()
+    expect(vm.$el.textContent).toBe('123')
+    vm.list.reverse()
+    waitForUpdate(() => {
+      expect(vm.$el.textContent).toBe('123')
+    }).then(done)
+  })
 })

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

@@ -57,14 +57,14 @@ describe('codegen', () => {
   it('generate v-if directive', () => {
     assertCodegen(
       '<p v-if="show">hello</p>',
-      `with(this){return (show)?_h(_e('p'),[_t("hello")]):void 0}`
+      `with(this){return (show)?_h(_e('p'),["hello"]):void 0}`
     )
   })
 
   it('generate v-else directive', () => {
     assertCodegen(
       '<div><p v-if="show">hello</p><p v-else>world</p></div>',
-      `with(this){return _h(_e('div'),[(show)?_h(_e('p'),[_t("hello")]):_h(_e('p'),[_t("world")])])}`
+      `with(this){return _h(_e('div'),[(show)?_h(_e('p'),["hello"]):_h(_e('p'),["world"])])}`
     )
   })
 
@@ -91,15 +91,15 @@ describe('codegen', () => {
 
   it('generate template tag', () => {
     assertCodegen(
-      '<template><p>hello world</p></template>',
-      `with(this){return [_h(_e('p'),[_t("hello world")])]}`
+      '<template><p>{{hello}}</p></template>',
+      `with(this){return [_h(_e('p'),[_s(hello)])]}`
     )
   })
 
   it('generate svg tag', () => {
     assertCodegen(
       '<svg><text>hello world</text></svg>',
-      `with(this){return _h(_e('svg',void 0,'svg'),[_h(_e('text',void 0,'svg'),[_t("hello world")])])}`
+      `with(this){return _h(_e('svg',void 0,'svg'),[_h(_e('text',void 0,'svg'),["hello world"])])}`
     )
   })
 
@@ -131,14 +131,14 @@ describe('codegen', () => {
     assertCodegen(
       '<slot><div>hi</div></slot>',
       `with(this){return ($slots["default"]||[_m(0)])}`,
-      [`with(this){return _h(_e('div'),[_t("hi")])}`]
+      [`with(this){return _h(_e('div'),["hi"])}`]
     )
   })
 
   it('generate slot target', () => {
     assertCodegen(
       '<p slot="one">hello world</p>',
-      `with(this){return _h(_e('p',{slot:"one"}),[_t("hello world")])}`
+      `with(this){return _h(_e('p',{slot:"one"}),["hello world"])}`
     )
   })
 
@@ -147,40 +147,40 @@ describe('codegen', () => {
     assertCodegen(
       '<p class="class1">hello world</p>',
       'with(this){return _m(0)}',
-      [`with(this){return _h(_e('p',{staticClass:"class1"}),[_t("hello world")])}`]
+      [`with(this){return _h(_e('p',{staticClass:"class1"}),["hello world"])}`]
     )
     // dynamic
     assertCodegen(
       '<p :class="class1">hello world</p>',
-      `with(this){return _h(_e('p',{class:class1}),[_t("hello world")])}`
+      `with(this){return _h(_e('p',{class:class1}),["hello world"])}`
     )
   })
 
   it('generate style binding', () => {
     assertCodegen(
       '<p :style="error">hello world</p>',
-      `with(this){return _h(_e('p',{style:(error)}),[_t("hello world")])}`
+      `with(this){return _h(_e('p',{style:(error)}),["hello world"])}`
     )
   })
 
   it('generate transition', () => {
     assertCodegen(
       '<p transition="expand">hello world</p>',
-      `with(this){return _h(_e('p',{transition:"expand"}),[_t("hello world")])}`
+      `with(this){return _h(_e('p',{transition:"expand"}),["hello world"])}`
     )
   })
 
   it('generate dynamic transition with transition on appear', () => {
     assertCodegen(
       `<p :transition="{name:'expand',appear:true}">hello world</p>`,
-      `with(this){return _h(_e('p',{transition:{name:'expand',appear:true}}),[_t("hello world")])}`
+      `with(this){return _h(_e('p',{transition:{name:'expand',appear:true}}),["hello world"])}`
     )
   })
 
   it('generate v-show directive', () => {
     assertCodegen(
       '<p v-show="shown">hello world</p>',
-      `with(this){return _h(_e('p',{directives:[{name:"show",value:(shown),expression:"shown"}],show:true}),[_t("hello world")])}`
+      `with(this){return _h(_e('p',{directives:[{name:"show",value:(shown),expression:"shown"}],show:true}),["hello world"])}`
     )
   })
 
@@ -292,7 +292,7 @@ describe('codegen', () => {
     assertCodegen(
       '<my-component name="mycomponent1" :msg="msg" @notify="onNotify"><div>hi</div></my-component>',
       `with(this){return _h(_e('my-component',{attrs:{"msg":msg},staticAttrs:{"name":"mycomponent1"},on:{"notify":onNotify}}),function(){return [_m(0)]})}`,
-      [`with(this){return _h(_e('div'),[_t("hi")])}`]
+      [`with(this){return _h(_e('div'),["hi"])}`]
     )
   })
 
@@ -311,7 +311,7 @@ describe('codegen', () => {
     // have "inline-template'"
     assertCodegen(
       '<my-component inline-template><p>hello world</p></my-component>',
-      `with(this){return _h(_e('my-component',{inlineTemplate:{render:function(){with(this){return _m(0)}},staticRenderFns:[function(){with(this){return _h(_e('p'),[_t("hello world")])}}]}}))}`
+      `with(this){return _h(_e('my-component',{inlineTemplate:{render:function(){with(this){return _m(0)}},staticRenderFns:[function(){with(this){return _h(_e('p'),["hello world"])}}]}}))}`
     )
     // "have inline-template attrs, but not having extactly one child element
     assertCodegen(
@@ -330,7 +330,7 @@ describe('codegen', () => {
   it('not specified directives option', () => {
     assertCodegen(
       '<p v-if="show">hello world</p>',
-      `with(this){return (show)?_h(_e('p'),[_t("hello world")]):void 0}`,
+      `with(this){return (show)?_h(_e('p'),["hello world"]):void 0}`,
       { isReservedTag }
     )
   })
@@ -341,7 +341,7 @@ describe('codegen', () => {
     assertCodegen(
       '<div><p>hello world</p></div>',
       `with(this){return _m(0)}`,
-      [`with(this){return _h(_e('div'),function(){return [_h(_e('p'),function(){return [_t("hello world")]})]})}`],
+      [`with(this){return _h(_e('div'),function(){return [_h(_e('p'),function(){return ["hello world"]})]})}`],
       { directives }
     )
   })

+ 2 - 10
test/unit/modules/vdom/create-element.spec.js

@@ -3,7 +3,6 @@ import { renderState } from 'core/instance/render'
 import {
   renderElement,
   renderElementWithChildren,
-  renderText,
   renderStatic
 } from 'core/vdom/create-element'
 import { emptyVNode } from 'core/vdom/vnode'
@@ -107,11 +106,6 @@ describe('create-element', () => {
     expect('createElement cannot be called outside of component').toHaveBeenWarned()
   })
 
-  it('renderText', () => {
-    expect(renderText('hello')).toBe('hello')
-    expect(renderText()).toBe('')
-  })
-
   it('renderStatic', done => {
     const vm = new Vue({
       template: '<p>hello world</p>'
@@ -130,12 +124,11 @@ describe('create-element', () => {
 
   it('render vnode with renderElementWithChildren', () => {
     const vm = new Vue({})
-    const _t = renderText
     const _e = bind(renderElement, vm)
     const _h = bind(renderElementWithChildren, vm)
     renderState.activeInstance = vm
     const parent = _e('p', {})
-    const children = [_e('br'), _t('hello world'), _e('br')]
+    const children = [_e('br'), 'hello world', _e('br')]
     const vnode = _h(parent, children)
     expect(vnode.children[0].tag).toBe('br')
     expect(vnode.children[1].text).toBe('hello world')
@@ -151,12 +144,11 @@ describe('create-element', () => {
         }
       }
     })
-    const _t = renderText
     const _e = bind(renderElement, vm)
     const _h = bind(renderElementWithChildren, vm)
     renderState.activeInstance = vm
     const parent = _e('my-component', { props: { msg: vm.message }})
-    const children = [_e('br'), _t('hello world'), _e('br')]
+    const children = [_e('br'), 'hello world', _e('br')]
     const vnode = _h(parent, children)
     expect(vnode.componentOptions.children[0].tag).toBe('br')
     expect(vnode.componentOptions.children[1]).toBe('hello world')