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

new render mechanism (#2857)

* updated compiler with new render functions

* separated createElement into renderSelf & renderElement
supported getters for text node & static root

* adapted new element creator into render call

* improved \$createElement

* fixed by flow check

* fixed ssr bugs for $createElement
Jinjiang 10 лет назад
Родитель
Сommit
20fb79a1ea
5 измененных файлов с 84 добавлено и 18 удалено
  1. 13 3
      flow/component.js
  2. 5 5
      src/compiler/codegen.js
  3. 13 3
      src/core/instance/render.js
  4. 49 7
      src/core/vdom/create-element.js
  5. 4 0
      src/core/vdom/vnode.js

+ 13 - 3
flow/component.js

@@ -1,5 +1,6 @@
 import type { Config } from '../src/core/config'
 import type VNode from '../src/core/vdom/vnode'
+import type { ComponentDef } from '../src/core/vdom/create-element'
 import type Watcher from '../src/core/observer/watcher'
 
 declare interface Component {
@@ -69,12 +70,21 @@ declare interface Component {
   // rendering
   _render: () => VNode;
   __patch__: (a: Element | VNode | void, b: VNode) => Element;
-  __h__: (
+  __r__: (
+    vnode?: VNode | ComponentDef,
+    children?: VNodeChildren
+  ) => VNode;
+  __s__: (
     tag?: string | Component | Object,
     data?: Object,
-    children?: VNodeChildren,
     namespace?: string
-  ) => VNode;
+  ) => VNode | ComponentDef | void;
+  __t__: (
+    str?: string
+  ) => string;
+  __m__: (
+    index?: number
+  ) => Object | void;
   __toString__: (value: any) => string;
   __resolveFilter__: (id: string) => Function;
   __renderList__: (

+ 5 - 5
src/compiler/codegen.js

@@ -31,7 +31,7 @@ export function generate (
   warn = options.warn || baseWarn
   platformDirectives = options.directives || {}
   isPlatformReservedTag = options.isReservedTag || (() => false)
-  const code = ast ? genElement(ast) : '__h__("div")'
+  const code = ast ? genElement(ast) : '__r__(__s__("div"))'
   staticRenderFns = prevStaticRenderFns
   return {
     render: `with (this) { return ${code}}`,
@@ -59,11 +59,11 @@ function genElement (el: ASTElement): string {
       ? 'undefined'
       : genChildren(el, !isPlatformReservedTag(el.tag) /* asThunk */)
     const namespace = el.ns ? `,'${el.ns}'` : ''
-    const code = `__h__('${el.tag}', ${genData(el)}, ${children}${namespace})`
+    const code = `__r__(__s__('${el.tag}', ${genData(el)}${namespace}), ${children})`
     if (el.staticRoot) {
       // hoist static sub-trees out
       staticRenderFns.push(`with(this){return ${code}}`)
-      return `_staticTrees[${staticRenderFns.length - 1}]`
+      return `__m__(${staticRenderFns.length - 1})`
     } else {
       return code
     }
@@ -227,7 +227,7 @@ function genNode (node) {
 function genText (text: ASTText): string {
   return text.expression
     ? `(${text.expression})`
-    : JSON.stringify(text.text)
+    : '__t__(' + JSON.stringify(text.text) + ')'
 }
 
 function genRender (el: ASTElement): string {
@@ -240,7 +240,7 @@ function genSlot (el: ASTElement): string {
 }
 
 function genComponent (el: ASTElement): string {
-  return `__h__(${el.component}, ${genData(el)}, ${genChildren(el, true)})`
+  return `__r__(__s__(${el.component}, ${genData(el)}), ${genChildren(el, true)})`
 }
 
 function genProps (props: Array<{ name: string, value: string }>): string {

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

@@ -1,6 +1,6 @@
 /* @flow */
 
-import createElement from '../vdom/create-element'
+import { renderElement, renderSelf, renderText, renderStatic } from '../vdom/create-element'
 import { emptyVNode } from '../vdom/vnode'
 import { normalizeChildren } from '../vdom/helpers'
 import { bind, remove, isObject, renderString } from 'shared/util'
@@ -18,7 +18,14 @@ export function initRender (vm: Component) {
   vm.$slots = {}
   // bind the public createElement fn to this instance
   // so that we get proper render context inside it.
-  vm.$createElement = bind(createElement, vm)
+  vm.$createElement = bind(function (
+    tag?: string | Class<Component> | Function | Object,
+    data?: VNodeData,
+    children?: VNodeChildren,
+    namespace?: string
+  ) {
+    return this.__r__(this.__s__(tag, data, namespace), children)
+  }, vm)
   if (vm.$options.el) {
     vm.$mount(vm.$options.el)
   }
@@ -53,7 +60,10 @@ export function renderMixin (Vue: Class<Component>) {
   }
 
   // shorthands used in render functions
-  Vue.prototype.__h__ = createElement
+  Vue.prototype.__r__ = renderElement
+  Vue.prototype.__s__ = renderSelf
+  Vue.prototype.__t__ = renderText
+  Vue.prototype.__m__ = renderStatic
 
   // toString for mustaches
   Vue.prototype.__toString__ = renderString

+ 49 - 7
src/core/vdom/create-element.js

@@ -7,12 +7,27 @@ import { normalizeChildren } from './helpers'
 import { renderState } from '../instance/render'
 import { warn, resolveAsset } from '../util/index'
 
-export default function createElement (
+export function renderElement (
+  vnode?: VNode | ComponentDef,
+  children?: VNodeChildren
+): VNode | void {
+  if (vnode instanceof ComponentDef) {
+    return createComponent(
+      vnode.Ctor, vnode.data, vnode.parent,
+      vnode.context, children
+    )
+  }
+  if (vnode instanceof VNode) {
+    vnode.setChildren(normalizeChildren(children))
+  }
+  return vnode
+}
+
+export function renderSelf (
   tag?: string | Class<Component> | Function | Object,
   data?: VNodeData,
-  children?: VNodeChildren,
   namespace?: string
-): VNode | void {
+): VNode | ComponentDef | void {
   const context: Component = this
   const parent: Component | null = renderState.activeInstance
   if (!parent) {
@@ -30,11 +45,11 @@ export default function createElement (
     let Ctor
     if (config.isReservedTag(tag)) {
       return new VNode(
-        tag, data, normalizeChildren(children),
+        tag, data, undefined,
         undefined, undefined, namespace, context
       )
     } else if ((Ctor = resolveAsset(context.$options, 'components', tag))) {
-      return createComponent(Ctor, data, parent, context, children)
+      return new ComponentDef(Ctor, data, parent, context)
     } else {
       if (process.env.NODE_ENV !== 'production') {
         if (!namespace && config.isUnknownElement(tag)) {
@@ -46,11 +61,38 @@ export default function createElement (
         }
       }
       return new VNode(
-        tag, data, normalizeChildren(children),
+        tag, data, undefined,
         undefined, undefined, namespace, context
       )
     }
   } else {
-    return createComponent(tag, data, parent, context, children)
+    return new ComponentDef(tag, data, parent, context)
+  }
+}
+
+export function renderText (str?: string): string {
+  return str || ''
+}
+
+export function renderStatic (index?: number): Object | void {
+  return this._staticTrees[index]
+}
+
+export class ComponentDef {
+  Ctor: Class<Component> | Function | Object | void;
+  data: VNodeData | void;
+  parent: Component;
+  context: Component;
+
+  constructor (
+    Ctor?: Class<Component> | Function | Object,
+    data?: VNodeData,
+    parent: Component,
+    context: Component
+  ) {
+    this.Ctor = Ctor
+    this.data = data
+    this.parent = parent
+    this.context = context
   }
 }

+ 4 - 0
src/core/vdom/vnode.js

@@ -33,6 +33,10 @@ export default class VNode {
     this.componentOptions = componentOptions
     this.child = undefined
   }
+
+  setChildren (children?: Array<VNode>) {
+    this.children = children
+  }
 }
 
 export const emptyVNode = new VNode(undefined, undefined, undefined, '')