UNPKG

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