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

refactor: split render helpers into separate files

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

+ 1 - 1
src/core/instance/lifecycle.js

@@ -4,7 +4,7 @@ import Watcher from '../observer/watcher'
 import { createEmptyVNode } from '../vdom/vnode'
 import { observerState } from '../observer/index'
 import { warn, validateProp, remove, noop } from '../util/index'
-import { resolveSlots } from './render'
+import { resolveSlots } from './render-helpers/resolve-slots'
 import { updateComponentListeners } from './events'
 
 export let activeInstance: any = null

+ 39 - 0
src/core/instance/render-helpers/bind-object-props.js

@@ -0,0 +1,39 @@
+/* @flow */
+
+import config from 'core/config'
+import { isObject, warn, toObject } from 'core/util/index'
+
+/**
+ * Runtime helper for merging v-bind="object" into a VNode's data.
+ */
+export function bindObjectProps (
+  data: any,
+  tag: string,
+  value: any,
+  asProp?: boolean
+): VNodeData {
+  if (value) {
+    if (!isObject(value)) {
+      process.env.NODE_ENV !== 'production' && warn(
+        'v-bind without argument expects an Object or Array value',
+        this
+      )
+    } else {
+      if (Array.isArray(value)) {
+        value = toObject(value)
+      }
+      for (const key in value) {
+        if (key === 'class' || key === 'style') {
+          data[key] = value[key]
+        } else {
+          const type = data.attrs && data.attrs.type
+          const hash = asProp || config.mustUseProp(tag, type, key)
+            ? data.domProps || (data.domProps = {})
+            : data.attrs || (data.attrs = {})
+          hash[key] = value[key]
+        }
+      }
+    }
+  }
+  return data
+}

+ 19 - 0
src/core/instance/render-helpers/check-keycodes.js

@@ -0,0 +1,19 @@
+/* @flow */
+
+import config from 'core/config'
+
+/**
+ * Runtime helper for checking keyCodes from config.
+ */
+export function checkKeyCodes (
+  eventKeyCode: number,
+  key: string,
+  builtInAlias: number | Array<number> | void
+): boolean {
+  const keyCodes = config.keyCodes[key] || builtInAlias
+  if (Array.isArray(keyCodes)) {
+    return keyCodes.indexOf(eventKeyCode) === -1
+  } else {
+    return keyCodes !== eventKeyCode
+  }
+}

+ 32 - 0
src/core/instance/render-helpers/render-list.js

@@ -0,0 +1,32 @@
+/* @flow */
+
+import { isObject } from 'core/util/index'
+
+/**
+ * Runtime helper for rendering v-for lists.
+ */
+export function renderList (
+  val: any,
+  render: () => VNode
+): ?Array<VNode> {
+  let ret: ?Array<VNode>, i, l, keys, key
+  if (Array.isArray(val) || typeof val === 'string') {
+    ret = new Array(val.length)
+    for (i = 0, l = val.length; i < l; i++) {
+      ret[i] = render(val[i], i)
+    }
+  } else if (typeof val === 'number') {
+    ret = new Array(val)
+    for (i = 0; i < val; i++) {
+      ret[i] = render(i + 1, i)
+    }
+  } else if (isObject(val)) {
+    keys = Object.keys(val)
+    ret = new Array(keys.length)
+    for (i = 0, l = keys.length; i < l; i++) {
+      key = keys[i]
+      ret[i] = render(val[key], key, i)
+    }
+  }
+  return ret
+}

+ 34 - 0
src/core/instance/render-helpers/render-slot.js

@@ -0,0 +1,34 @@
+/* @flow */
+
+import { extend, warn } from 'core/util/index'
+
+/**
+ * Runtime helper for rendering <slot>
+ */
+export function renderSlot (
+  name: string,
+  fallback: ?Array<VNode>,
+  props: ?Object,
+  bindObject: ?Object
+): ?Array<VNode> {
+  const scopedSlotFn = this.$scopedSlots[name]
+  if (scopedSlotFn) { // scoped slot
+    props = props || {}
+    if (bindObject) {
+      extend(props, bindObject)
+    }
+    return scopedSlotFn(props) || fallback
+  } else {
+    const slotNodes = this.$slots[name]
+    // warn duplicate slot usage
+    if (slotNodes && process.env.NODE_ENV !== 'production') {
+      slotNodes._rendered && warn(
+        `Duplicate presence of slot "${name}" found in the same render tree ` +
+        `- this will likely cause render errors.`,
+        this
+      )
+      slotNodes._rendered = true
+    }
+    return slotNodes || fallback
+  }
+}

+ 60 - 0
src/core/instance/render-helpers/render-static.js

@@ -0,0 +1,60 @@
+/* @flow */
+
+import { cloneVNode, cloneVNodes } from 'core/vdom/vnode'
+
+/**
+ * Runtime helper for rendering static trees.
+ */
+export function renderStatic (
+  index: number,
+  isInFor?: boolean
+): VNode | Array<VNode> {
+  let tree = this._staticTrees[index]
+  // if has already-rendered static tree and not inside v-for,
+  // we can reuse the same tree by doing a shallow clone.
+  if (tree && !isInFor) {
+    return Array.isArray(tree)
+      ? cloneVNodes(tree)
+      : cloneVNode(tree)
+  }
+  // otherwise, render a fresh tree.
+  tree = this._staticTrees[index] =
+    this.$options.staticRenderFns[index].call(this._renderProxy)
+  markStatic(tree, `__static__${index}`, false)
+  return tree
+}
+
+/**
+ * Runtime helper for v-once.
+ * Effectively it means marking the node as static with a unique key.
+ */
+export function markOnce (
+  tree: VNode | Array<VNode>,
+  index: number,
+  key: string
+) {
+  markStatic(tree, `__once__${index}${key ? `_${key}` : ``}`, true)
+  return tree
+}
+
+function markStatic (
+  tree: VNode | Array<VNode>,
+  key: string,
+  isOnce: boolean
+) {
+  if (Array.isArray(tree)) {
+    for (let i = 0; i < tree.length; i++) {
+      if (tree[i] && typeof tree[i] !== 'string') {
+        markStaticNode(tree[i], `${key}_${i}`, isOnce)
+      }
+    }
+  } else {
+    markStaticNode(tree, key, isOnce)
+  }
+}
+
+function markStaticNode (node, key, isOnce) {
+  node.isStatic = true
+  node.key = key
+  node.isOnce = isOnce
+}

+ 10 - 0
src/core/instance/render-helpers/resolve-filter.js

@@ -0,0 +1,10 @@
+/* @flow */
+
+import { identity, resolveAsset } from 'core/util/index'
+
+/**
+ * Runtime helper for resolving filters
+ */
+export function resolveFilter (id: string): Function {
+  return resolveAsset(this.$options, 'filters', id, true) || identity
+}

+ 40 - 0
src/core/instance/render-helpers/resolve-slots.js

@@ -0,0 +1,40 @@
+/* @flow */
+
+/**
+ * Runtime helper for resolving raw children VNodes into a slot object.
+ */
+export function resolveSlots (
+  children: ?Array<VNode>,
+  context: ?Component
+): { [key: string]: Array<VNode> } {
+  const slots = {}
+  if (!children) {
+    return slots
+  }
+  const defaultSlot = []
+  let name, child
+  for (let i = 0, l = children.length; i < l; i++) {
+    child = children[i]
+    // named slots should only be respected if the vnode was rendered in the
+    // same context.
+    if ((child.context === context || child.functionalContext === context) &&
+        child.data && (name = child.data.slot)) {
+      const slot = (slots[name] || (slots[name] = []))
+      if (child.tag === 'template') {
+        slot.push.apply(slot, child.children)
+      } else {
+        slot.push(child)
+      }
+    } else {
+      defaultSlot.push(child)
+    }
+  }
+  // ignore single whitespace
+  if (defaultSlot.length && !(
+    defaultSlot.length === 1 &&
+    (defaultSlot[0].text === ' ' || defaultSlot[0].isComment)
+  )) {
+    slots.default = defaultSlot
+  }
+  return slots
+}

+ 27 - 211
src/core/instance/render.js

@@ -1,28 +1,31 @@
 /* @flow */
 
 import config from '../config'
-import VNode, {
-  cloneVNode,
-  cloneVNodes,
-  createTextVNode,
-  createEmptyVNode
-} from '../vdom/vnode'
+
 import {
   warn,
-  extend,
-  identity,
-  isObject,
-  toObject,
   nextTick,
   toNumber,
   _toString,
   looseEqual,
   looseIndexOf,
-  resolveAsset,
   formatComponentName
 } from '../util/index'
 
+import VNode, {
+  cloneVNodes,
+  createTextVNode,
+  createEmptyVNode
+} from '../vdom/vnode'
+
 import { createElement } from '../vdom/create-element'
+import { renderList } from './render-helpers/render-list'
+import { renderSlot } from './render-helpers/render-slot'
+import { resolveSlots } from './render-helpers/resolve-slots'
+import { resolveFilter } from './render-helpers/resolve-filter'
+import { checkKeyCodes } from './render-helpers/check-keycodes'
+import { bindObjectProps } from './render-helpers/bind-object-props'
+import { renderStatic, markOnce } from './render-helpers/render-static'
 
 export function initRender (vm: Component) {
   vm.$vnode = null // the placeholder node in parent tree
@@ -105,207 +108,20 @@ export function renderMixin (Vue: Class<Component>) {
     return vnode
   }
 
-  // toString for mustaches
-  Vue.prototype._s = _toString
-  // convert text to vnode
-  Vue.prototype._v = createTextVNode
-  // number conversion
+  // internal render helpers.
+  // these are exposed on the instance prototype to reduce generated render
+  // code size.
+  Vue.prototype._o = markOnce
   Vue.prototype._n = toNumber
-  // empty vnode
-  Vue.prototype._e = createEmptyVNode
-  // loose equal
+  Vue.prototype._s = _toString
+  Vue.prototype._l = renderList
+  Vue.prototype._t = renderSlot
   Vue.prototype._q = looseEqual
-  // loose indexOf
   Vue.prototype._i = looseIndexOf
-
-  // render static tree by index
-  Vue.prototype._m = function renderStatic (
-    index: number,
-    isInFor?: boolean
-  ): VNode | Array<VNode> {
-    let tree = this._staticTrees[index]
-    // if has already-rendered static tree and not inside v-for,
-    // we can reuse the same tree by doing a shallow clone.
-    if (tree && !isInFor) {
-      return Array.isArray(tree)
-        ? cloneVNodes(tree)
-        : cloneVNode(tree)
-    }
-    // otherwise, render a fresh tree.
-    tree = this._staticTrees[index] = this.$options.staticRenderFns[index].call(this._renderProxy)
-    markStatic(tree, `__static__${index}`, false)
-    return tree
-  }
-
-  // mark node as static (v-once)
-  Vue.prototype._o = function markOnce (
-    tree: VNode | Array<VNode>,
-    index: number,
-    key: string
-  ) {
-    markStatic(tree, `__once__${index}${key ? `_${key}` : ``}`, true)
-    return tree
-  }
-
-  function markStatic (tree, key, isOnce) {
-    if (Array.isArray(tree)) {
-      for (let i = 0; i < tree.length; i++) {
-        if (tree[i] && typeof tree[i] !== 'string') {
-          markStaticNode(tree[i], `${key}_${i}`, isOnce)
-        }
-      }
-    } else {
-      markStaticNode(tree, key, isOnce)
-    }
-  }
-
-  function markStaticNode (node, key, isOnce) {
-    node.isStatic = true
-    node.key = key
-    node.isOnce = isOnce
-  }
-
-  // filter resolution helper
-  Vue.prototype._f = function resolveFilter (id) {
-    return resolveAsset(this.$options, 'filters', id, true) || identity
-  }
-
-  // render v-for
-  Vue.prototype._l = function renderList (
-    val: any,
-    render: () => VNode
-  ): ?Array<VNode> {
-    let ret: ?Array<VNode>, i, l, keys, key
-    if (Array.isArray(val) || typeof val === 'string') {
-      ret = new Array(val.length)
-      for (i = 0, l = val.length; i < l; i++) {
-        ret[i] = render(val[i], i)
-      }
-    } else if (typeof val === 'number') {
-      ret = new Array(val)
-      for (i = 0; i < val; i++) {
-        ret[i] = render(i + 1, i)
-      }
-    } else if (isObject(val)) {
-      keys = Object.keys(val)
-      ret = new Array(keys.length)
-      for (i = 0, l = keys.length; i < l; i++) {
-        key = keys[i]
-        ret[i] = render(val[key], key, i)
-      }
-    }
-    return ret
-  }
-
-  // renderSlot
-  Vue.prototype._t = function (
-    name: string,
-    fallback: ?Array<VNode>,
-    props: ?Object,
-    bindObject: ?Object
-  ): ?Array<VNode> {
-    const scopedSlotFn = this.$scopedSlots[name]
-    if (scopedSlotFn) { // scoped slot
-      props = props || {}
-      if (bindObject) {
-        extend(props, bindObject)
-      }
-      return scopedSlotFn(props) || fallback
-    } else {
-      const slotNodes = this.$slots[name]
-      // warn duplicate slot usage
-      if (slotNodes && process.env.NODE_ENV !== 'production') {
-        slotNodes._rendered && warn(
-          `Duplicate presence of slot "${name}" found in the same render tree ` +
-          `- this will likely cause render errors.`,
-          this
-        )
-        slotNodes._rendered = true
-      }
-      return slotNodes || fallback
-    }
-  }
-
-  // apply v-bind object
-  Vue.prototype._b = function bindProps (
-    data: any,
-    tag: string,
-    value: any,
-    asProp?: boolean
-  ): VNodeData {
-    if (value) {
-      if (!isObject(value)) {
-        process.env.NODE_ENV !== 'production' && warn(
-          'v-bind without argument expects an Object or Array value',
-          this
-        )
-      } else {
-        if (Array.isArray(value)) {
-          value = toObject(value)
-        }
-        for (const key in value) {
-          if (key === 'class' || key === 'style') {
-            data[key] = value[key]
-          } else {
-            const type = data.attrs && data.attrs.type
-            const hash = asProp || config.mustUseProp(tag, type, key)
-              ? data.domProps || (data.domProps = {})
-              : data.attrs || (data.attrs = {})
-            hash[key] = value[key]
-          }
-        }
-      }
-    }
-    return data
-  }
-
-  // check v-on keyCodes
-  Vue.prototype._k = function checkKeyCodes (
-    eventKeyCode: number,
-    key: string,
-    builtInAlias: number | Array<number> | void
-  ): boolean {
-    const keyCodes = config.keyCodes[key] || builtInAlias
-    if (Array.isArray(keyCodes)) {
-      return keyCodes.indexOf(eventKeyCode) === -1
-    } else {
-      return keyCodes !== eventKeyCode
-    }
-  }
-}
-
-export function resolveSlots (
-  children: ?Array<VNode>,
-  context: ?Component
-): { [key: string]: Array<VNode> } {
-  const slots = {}
-  if (!children) {
-    return slots
-  }
-  const defaultSlot = []
-  let name, child
-  for (let i = 0, l = children.length; i < l; i++) {
-    child = children[i]
-    // named slots should only be respected if the vnode was rendered in the
-    // same context.
-    if ((child.context === context || child.functionalContext === context) &&
-        child.data && (name = child.data.slot)) {
-      const slot = (slots[name] || (slots[name] = []))
-      if (child.tag === 'template') {
-        slot.push.apply(slot, child.children)
-      } else {
-        slot.push(child)
-      }
-    } else {
-      defaultSlot.push(child)
-    }
-  }
-  // ignore single whitespace
-  if (defaultSlot.length && !(
-    defaultSlot.length === 1 &&
-    (defaultSlot[0].text === ' ' || defaultSlot[0].isComment)
-  )) {
-    slots.default = defaultSlot
-  }
-  return slots
+  Vue.prototype._m = renderStatic
+  Vue.prototype._f = resolveFilter
+  Vue.prototype._k = checkKeyCodes
+  Vue.prototype._b = bindObjectProps
+  Vue.prototype._v = createTextVNode
+  Vue.prototype._e = createEmptyVNode
 }

+ 1 - 1
src/core/vdom/create-component.js

@@ -3,7 +3,7 @@
 import VNode from './vnode'
 import { resolveConstructorOptions } from '../instance/init'
 import { activeInstance, callHook } from '../instance/lifecycle'
-import { resolveSlots } from '../instance/render'
+import { resolveSlots } from '../instance/render-helpers/resolve-slots'
 import { createElement } from './create-element'
 import { warn, isObject, hasOwn, hyphenate, validateProp } from '../util/index'