import {
|
warn,
|
nextTick,
|
emptyObject,
|
handleError,
|
defineReactive,
|
isArray
|
} from '../util/index'
|
|
import { createElement } from '../vdom/create-element'
|
import { installRenderHelpers } from './render-helpers/index'
|
import { resolveSlots } from './render-helpers/resolve-slots'
|
import { normalizeScopedSlots } from '../vdom/helpers/normalize-scoped-slots'
|
import VNode, { createEmptyVNode } from '../vdom/vnode'
|
|
import { isUpdatingChildComponent } from './lifecycle'
|
import type { Component } from 'types/component'
|
import { currentInstance, setCurrentInstance } from 'v3/currentInstance'
|
import { syncSetupSlots } from 'v3/apiSetup'
|
|
export function initRender(vm: Component) {
|
vm._vnode = null // the root of the child tree
|
vm._staticTrees = null // v-once cached trees
|
const options = vm.$options
|
const parentVnode = (vm.$vnode = options._parentVnode!) // the placeholder node in parent tree
|
const renderContext = parentVnode && (parentVnode.context as Component)
|
vm.$slots = resolveSlots(options._renderChildren, renderContext)
|
vm.$scopedSlots = parentVnode
|
? normalizeScopedSlots(
|
vm.$parent!,
|
parentVnode.data!.scopedSlots,
|
vm.$slots
|
)
|
: emptyObject
|
// bind the createElement fn to this instance
|
// so that we get proper render context inside it.
|
// args order: tag, data, children, normalizationType, alwaysNormalize
|
// internal version is used by render functions compiled from templates
|
// @ts-expect-error
|
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
|
// normalization is always applied for the public version, used in
|
// user-written render functions.
|
// @ts-expect-error
|
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
|
|
// $attrs & $listeners are exposed for easier HOC creation.
|
// they need to be reactive so that HOCs using them are always updated
|
const parentData = parentVnode && parentVnode.data
|
|
/* istanbul ignore else */
|
if (__DEV__) {
|
defineReactive(
|
vm,
|
'$attrs',
|
(parentData && parentData.attrs) || emptyObject,
|
() => {
|
!isUpdatingChildComponent && warn(`$attrs is readonly.`, vm)
|
},
|
true
|
)
|
defineReactive(
|
vm,
|
'$listeners',
|
options._parentListeners || emptyObject,
|
() => {
|
!isUpdatingChildComponent && warn(`$listeners is readonly.`, vm)
|
},
|
true
|
)
|
} else {
|
defineReactive(
|
vm,
|
'$attrs',
|
(parentData && parentData.attrs) || emptyObject,
|
null,
|
true
|
)
|
defineReactive(
|
vm,
|
'$listeners',
|
options._parentListeners || emptyObject,
|
null,
|
true
|
)
|
}
|
}
|
|
export let currentRenderingInstance: Component | null = null
|
|
// for testing only
|
export function setCurrentRenderingInstance(vm: Component) {
|
currentRenderingInstance = vm
|
}
|
|
export function renderMixin(Vue: typeof Component) {
|
// install runtime convenience helpers
|
installRenderHelpers(Vue.prototype)
|
|
Vue.prototype.$nextTick = function (fn: (...args: any[]) => any) {
|
return nextTick(fn, this)
|
}
|
|
Vue.prototype._render = function (): VNode {
|
const vm: Component = this
|
const { render, _parentVnode } = vm.$options
|
|
if (_parentVnode && vm._isMounted) {
|
vm.$scopedSlots = normalizeScopedSlots(
|
vm.$parent!,
|
_parentVnode.data!.scopedSlots,
|
vm.$slots,
|
vm.$scopedSlots
|
)
|
if (vm._slotsProxy) {
|
syncSetupSlots(vm._slotsProxy, vm.$scopedSlots)
|
}
|
}
|
|
// set parent vnode. this allows render functions to have access
|
// to the data on the placeholder node.
|
vm.$vnode = _parentVnode!
|
// render self
|
const prevInst = currentInstance
|
const prevRenderInst = currentRenderingInstance
|
let vnode
|
try {
|
setCurrentInstance(vm)
|
currentRenderingInstance = vm
|
vnode = render.call(vm._renderProxy, vm.$createElement)
|
} catch (e: any) {
|
handleError(e, vm, `render`)
|
// return error render result,
|
// or previous vnode to prevent render error causing blank component
|
/* istanbul ignore else */
|
if (__DEV__ && vm.$options.renderError) {
|
try {
|
vnode = vm.$options.renderError.call(
|
vm._renderProxy,
|
vm.$createElement,
|
e
|
)
|
} catch (e: any) {
|
handleError(e, vm, `renderError`)
|
vnode = vm._vnode
|
}
|
} else {
|
vnode = vm._vnode
|
}
|
} finally {
|
currentRenderingInstance = prevRenderInst
|
setCurrentInstance(prevInst)
|
}
|
// if the returned array contains only a single node, allow it
|
if (isArray(vnode) && vnode.length === 1) {
|
vnode = vnode[0]
|
}
|
// return empty vnode in case the render function errored out
|
if (!(vnode instanceof VNode)) {
|
if (__DEV__ && isArray(vnode)) {
|
warn(
|
'Multiple root nodes returned from render function. Render function ' +
|
'should return a single root node.',
|
vm
|
)
|
}
|
vnode = createEmptyVNode()
|
}
|
// set parent
|
vnode.parent = _parentVnode
|
return vnode
|
}
|
}
|