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
vm._staticTrees = null
const options = vm.$options
const parentVnode = (vm.$vnode = options._parentVnode!)
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
vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
const parentData = parentVnode && parentVnode.data
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
export function setCurrentRenderingInstance(vm: Component) {
currentRenderingInstance = vm
}
export function renderMixin(Vue: typeof Component) {
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)
}
}
vm.$vnode = _parentVnode!
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`)
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 (isArray(vnode) && vnode.length === 1) {
vnode = vnode[0]
}
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()
}
vnode.parent = _parentVnode
return vnode
}
}