2
0

render.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /* @flow */
  2. import config from '../config'
  3. import VNode, { emptyVNode } from '../vdom/vnode'
  4. import { normalizeChildren } from '../vdom/helpers'
  5. import {
  6. warn, bind, remove, isObject, toObject,
  7. nextTick, resolveAsset, renderString
  8. } from '../util/index'
  9. import {
  10. renderElement,
  11. renderElementWithChildren,
  12. renderText,
  13. renderStatic
  14. } from '../vdom/create-element'
  15. export const renderState: {
  16. activeInstance: Component | null
  17. } = {
  18. activeInstance: null
  19. }
  20. export function initRender (vm: Component) {
  21. vm._vnode = null
  22. vm._staticTrees = null
  23. vm.$slots = {}
  24. // bind the public createElement fn to this instance
  25. // so that we get proper render context inside it.
  26. vm.$createElement = bind(function (
  27. tag?: string | Class<Component> | Function | Object,
  28. data?: VNodeData,
  29. children?: VNodeChildren,
  30. namespace?: string
  31. ) {
  32. return this._h(this._e(tag, data, namespace), children)
  33. }, vm)
  34. if (vm.$options.el) {
  35. vm.$mount(vm.$options.el)
  36. }
  37. }
  38. export function renderMixin (Vue: Class<Component>) {
  39. Vue.prototype.$nextTick = function (fn: Function) {
  40. nextTick(fn, this)
  41. }
  42. Vue.prototype._render = function (): VNode {
  43. const vm: Component = this
  44. const prev = renderState.activeInstance
  45. renderState.activeInstance = vm
  46. if (!vm._isMounted) {
  47. // render static sub-trees for once on initial render
  48. renderStaticTrees(vm)
  49. }
  50. const { render, _renderChildren, _parentVnode } = vm.$options
  51. // resolve slots. becaues slots are rendered in parent scope,
  52. // we set the activeInstance to parent.
  53. if (_renderChildren) {
  54. resolveSlots(vm, _renderChildren)
  55. }
  56. // render self
  57. let vnode = render.call(vm._renderProxy)
  58. // return empty vnode in case the render function errored out
  59. if (!(vnode instanceof VNode)) {
  60. vnode = emptyVNode
  61. }
  62. // set parent
  63. vnode.parent = _parentVnode
  64. // restore render state
  65. renderState.activeInstance = prev
  66. return vnode
  67. }
  68. // shorthands used in render functions
  69. Vue.prototype._h = renderElementWithChildren
  70. Vue.prototype._e = renderElement
  71. Vue.prototype._t = renderText
  72. Vue.prototype._m = renderStatic
  73. // toString for mustaches
  74. Vue.prototype._s = renderString
  75. // filter resolution helper
  76. const identity = _ => _
  77. Vue.prototype._f = function (id) {
  78. return resolveAsset(this.$options, 'filters', id, true) || identity
  79. }
  80. // render v-for
  81. Vue.prototype._l = function (
  82. val: any,
  83. render: () => VNode
  84. ): ?Array<VNode> {
  85. let ret: ?Array<VNode>, i, l, keys, key
  86. if (Array.isArray(val)) {
  87. ret = new Array(val.length)
  88. for (i = 0, l = val.length; i < l; i++) {
  89. ret[i] = render(val[i], i, i)
  90. }
  91. } else if (typeof val === 'number') {
  92. ret = new Array(val)
  93. for (i = 0; i < val; i++) {
  94. ret[i] = render(i + 1, i, i)
  95. }
  96. } else if (isObject(val)) {
  97. keys = Object.keys(val)
  98. ret = new Array(keys.length)
  99. for (i = 0, l = keys.length; i < l; i++) {
  100. key = keys[i]
  101. ret[i] = render(val[key], i, key)
  102. }
  103. }
  104. return ret
  105. }
  106. // register ref
  107. Vue.prototype._r = function (
  108. key: string,
  109. ref: Vue | Element,
  110. vFor: boolean,
  111. isRemoval: boolean
  112. ) {
  113. const vm: Component = this
  114. const refs = vm.$refs
  115. if (isRemoval) {
  116. if (Array.isArray(refs[key])) {
  117. remove(refs[key], ref)
  118. } else {
  119. refs[key] = undefined
  120. }
  121. } else {
  122. if (vFor) {
  123. if (Array.isArray(refs[key])) {
  124. refs[key].push(ref)
  125. } else {
  126. refs[key] = [ref]
  127. }
  128. } else {
  129. refs[key] = ref
  130. }
  131. }
  132. }
  133. // apply v-bind object
  134. Vue.prototype._b = function (vnode: VNodeWithData, value: any) {
  135. if (value) {
  136. if (!isObject(value)) {
  137. process.env.NODE_ENV !== 'production' && warn(
  138. 'v-bind without argument expects an Object or Array value',
  139. this
  140. )
  141. } else {
  142. if (Array.isArray(value)) {
  143. value = toObject(value)
  144. }
  145. const data = vnode.data
  146. for (const key in value) {
  147. const hash = config.mustUseProp(key)
  148. ? data.props || (data.props = {})
  149. : data.attrs || (data.attrs = {})
  150. hash[key] = value[key]
  151. }
  152. }
  153. }
  154. }
  155. }
  156. function renderStaticTrees (vm: Component) {
  157. const staticRenderFns = vm.$options.staticRenderFns
  158. if (staticRenderFns) {
  159. const trees = vm._staticTrees = new Array(staticRenderFns.length)
  160. for (let i = 0; i < staticRenderFns.length; i++) {
  161. trees[i] = staticRenderFns[i].call(vm._renderProxy)
  162. }
  163. }
  164. }
  165. function resolveSlots (
  166. vm: Component,
  167. renderChildren: Array<any> | () => Array<any> | string
  168. ) {
  169. if (renderChildren) {
  170. const children = normalizeChildren(renderChildren)
  171. const slots = {}
  172. const defaultSlot = []
  173. let name, child
  174. for (let i = 0, l = children.length; i < l; i++) {
  175. child = children[i]
  176. if ((name = child.data && child.data.slot)) {
  177. const slot = (slots[name] || (slots[name] = []))
  178. if (child.tag === 'template') {
  179. slot.push.apply(slot, child.children)
  180. } else {
  181. slot.push(child)
  182. }
  183. } else {
  184. defaultSlot.push(child)
  185. }
  186. }
  187. if (defaultSlot.length && !(
  188. defaultSlot.length === 1 &&
  189. defaultSlot[0].text === ' '
  190. )) {
  191. slots['default'] = defaultSlot
  192. }
  193. vm.$slots = slots
  194. }
  195. }