937421b681c6b16497bb2586fbdc5441d5f9b9e9546887ad474ca09513632d542caa19e41a219994a6d99ea1c94881ddf435c442e01953ccd5de7b0355b426 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. /* @flow */
  2. import {
  3. warn,
  4. nextTick,
  5. emptyObject,
  6. handleError,
  7. defineReactive
  8. } from '../util/index'
  9. import { createElement } from '../vdom/create-element'
  10. import { installRenderHelpers } from './render-helpers/index'
  11. import { resolveSlots } from './render-helpers/resolve-slots'
  12. import { normalizeScopedSlots } from '../vdom/helpers/normalize-scoped-slots'
  13. import VNode, { createEmptyVNode } from '../vdom/vnode'
  14. import { isUpdatingChildComponent } from './lifecycle'
  15. export function initRender (vm: Component) {
  16. vm._vnode = null // the root of the child tree
  17. vm._staticTrees = null // v-once cached trees
  18. const options = vm.$options
  19. const parentVnode = vm.$vnode = options._parentVnode // the placeholder node in parent tree
  20. const renderContext = parentVnode && parentVnode.context
  21. vm.$slots = resolveSlots(options._renderChildren, renderContext)
  22. vm.$scopedSlots = emptyObject
  23. // bind the createElement fn to this instance
  24. // so that we get proper render context inside it.
  25. // args order: tag, data, children, normalizationType, alwaysNormalize
  26. // internal version is used by render functions compiled from templates
  27. vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
  28. // normalization is always applied for the public version, used in
  29. // user-written render functions.
  30. vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
  31. // $attrs & $listeners are exposed for easier HOC creation.
  32. // they need to be reactive so that HOCs using them are always updated
  33. const parentData = parentVnode && parentVnode.data
  34. /* istanbul ignore else */
  35. if (process.env.NODE_ENV !== 'production') {
  36. defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, () => {
  37. !isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
  38. }, true)
  39. defineReactive(vm, '$listeners', options._parentListeners || emptyObject, () => {
  40. !isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
  41. }, true)
  42. } else {
  43. defineReactive(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true)
  44. defineReactive(vm, '$listeners', options._parentListeners || emptyObject, null, true)
  45. }
  46. }
  47. export let currentRenderingInstance: Component | null = null
  48. // for testing only
  49. export function setCurrentRenderingInstance (vm: Component) {
  50. currentRenderingInstance = vm
  51. }
  52. export function renderMixin (Vue: Class<Component>) {
  53. // install runtime convenience helpers
  54. installRenderHelpers(Vue.prototype)
  55. Vue.prototype.$nextTick = function (fn: Function) {
  56. return nextTick(fn, this)
  57. }
  58. Vue.prototype._render = function (): VNode {
  59. const vm: Component = this
  60. const { render, _parentVnode } = vm.$options
  61. if (_parentVnode) {
  62. vm.$scopedSlots = normalizeScopedSlots(
  63. _parentVnode.data.scopedSlots,
  64. vm.$slots,
  65. vm.$scopedSlots
  66. )
  67. }
  68. // set parent vnode. this allows render functions to have access
  69. // to the data on the placeholder node.
  70. vm.$vnode = _parentVnode
  71. // render self
  72. let vnode
  73. try {
  74. // There's no need to maintain a stack because all render fns are called
  75. // separately from one another. Nested component's render fns are called
  76. // when parent component is patched.
  77. currentRenderingInstance = vm
  78. vnode = render.call(vm._renderProxy, vm.$createElement)
  79. } catch (e) {
  80. handleError(e, vm, `render`)
  81. // return error render result,
  82. // or previous vnode to prevent render error causing blank component
  83. /* istanbul ignore else */
  84. if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {
  85. try {
  86. vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
  87. } catch (e) {
  88. handleError(e, vm, `renderError`)
  89. vnode = vm._vnode
  90. }
  91. } else {
  92. vnode = vm._vnode
  93. }
  94. } finally {
  95. currentRenderingInstance = null
  96. }
  97. // if the returned array contains only a single node, allow it
  98. if (Array.isArray(vnode) && vnode.length === 1) {
  99. vnode = vnode[0]
  100. }
  101. // return empty vnode in case the render function errored out
  102. if (!(vnode instanceof VNode)) {
  103. if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
  104. warn(
  105. 'Multiple root nodes returned from render function. Render function ' +
  106. 'should return a single root node.',
  107. vm
  108. )
  109. }
  110. vnode = createEmptyVNode()
  111. }
  112. // set parent
  113. vnode.parent = _parentVnode
  114. return vnode
  115. }
  116. }