回顾
前面分析到了 mountComponent 函数,该函数主要做了三件事:
-
创建组件实例 - createComponentInstance;
-
初始化组件 - setupComponent;
-
渲染组件 - setupRenderEffect。
createComponentInstance
代码在 packages/runtime-core/src/component.ts 中。组件其实就是一个对象,里面去维护了一些数据,用于渲染。这里就不多说了。
其中,有对 props 及 emits 的标准化,这个后面在介绍。
export function createComponentInstance(
vnode: VNode,
parent: ComponentInternalInstance | null,
suspense: SuspenseBoundary | null
) {
//...
const instance: ComponentInternalInstance = {
//...
// Props 及 emits 标准化配置
propsOptions: normalizePropsOptions(type, appContext),
emitsOptions: normalizeEmitsOptions(type, appContext),
//...
}
// ...
return instance
}
setupComponent
export function setupComponent(
instance: ComponentInternalInstance,
isSSR = false
) {
isInSSRComponentSetup = isSSR
// 通过 vnode 获取 props 及 children
const { props, children } = instance.vnode
// 判断是否是有状态组件
const isStateful = isStatefulComponent(instance)
// 初始化 props 及 slots
initProps(instance, props, isStateful, isSSR)
initSlots(instance, children)
// 得到 setupResult
// 如果是有状态组件通过 setupStatefulComponent 返回
// 反之 setupResult = undefined
const setupResult = isStateful ? setupStatefulComponent(instance, isSSR) : undefined
isInSSRComponentSetup = false
// 返回 setupResult
return setupResult
}
该函数返回 setupResult 对象,该对象是通过 setupStatefulComponent 函数处理返回。
此外,该函数还包括对 props 和 slots 的初始化等,这些后续在分析。
setupStatefulComponent
function setupStatefulComponent(
instance: ComponentInternalInstance,
isSSR: boolean
) {
// ...
const { setup } = Component
if (setup) {
const setupContext = (instance.setupContext =
setup.length > 1 ? createSetupContext(instance) : null)
setCurrentInstance(instance)
pauseTracking()
// 通过执行 setup 函数,获取执行结果
const setupResult = callWithErrorHandling(
setup,
instance,
ErrorCodes.SETUP_FUNCTION,
[__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
)
resetTracking()
unsetCurrentInstance()
// 处理 setup 函数返回的结果
// 如果 setup 返回的结果是 promise
if (isPromise(setupResult)) {
// ...
return setupResult
.then((resolvedResult: unknown) => {
handleSetupResult(instance, resolvedResult, isSSR)
})
.catch(e => {
handleError(e, instance, ErrorCodes.SETUP_FUNCTION)
})
// ...
} else {
// 反之
handleSetupResult(instance, setupResult, isSSR)
}
} else {
// 完成组件初始化
finishComponentSetup(instance, isSSR)
}
}
handleSetupResult
export function handleSetupResult(
instance: ComponentInternalInstance,
setupResult: unknown,
isSSR: boolean
) {
// setup 可能返回一个 render function
if (isFunction(setupResult)) {
// 如果是 node ssr,则返回一个内联渲染函数
if (__SSR__ && (instance.type as ComponentOptions).__ssrInlineRender) {
instance.ssrRender = setupResult
} else {
instance.render = setupResult as InternalRenderFunction
}
} else if (isObject(setupResult)) {
// 如果是 vnode
if (__DEV__ && isVNode(setupResult)) {
// setup 不应返回一个 vnode 应该是 render function
warn(
`setup() should not return VNodes directly - ` +
`return a render function instead.`
)
}
// 如果存在 devtools 就把 setup 返回结果赋值给 devtoolsRawSetupState
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
instance.devtoolsRawSetupState = setupResult
}
// 这步会把 setup 的返回结果在进一步通过 proxy 进行包装
instance.setupState = proxyRefs(setupResult)
// ...
} else if (__DEV__ && setupResult !== undefined) {
warn(
`setup() should return an object. Received: ${ setupResult === null ? 'null' : typeof setupResult }`
)
}
// 完成组件初始化
finishComponentSetup(instance, isSSR)
}
finishComponentSetup
export function finishComponentSetup(
instance: ComponentInternalInstance,
isSSR: boolean,
skipOptions?: boolean
) {
// 既,业务代码,如:{setup: function(props, {emit}) { ... }, props: [...]}
const Component = instance.type as ComponentOptions
// ...
if (!instance.render) {
if (!isSSR && compile && !Component.render) {
const template = (__COMPAT__ && instance.vnode.props && instance.vnode.props['inline-template']) || Component.template
if (template) {
// ...
// instance.appContext 继承父为先,获取组件自己的为后
const { isCustomElement, compilerOptions } = instance.appContext.config
// Component = instance.type
// 从这一步开始读取,业务代码中的 delimiters、compilerOptions 这两项编译配置
const { delimiters, compilerOptions: componentCompilerOptions } = Component
// 合并最终编译配置
const finalCompilerOptions: CompilerOptions = extend(
extend({ isCustomElement, delimiters }, compilerOptions), componentCompilerOptions)
// ...
// 将编译后的 render function 赋予 instance.type.render 上
Component.render = compile(template, finalCompilerOptions)
// ...
}
}
// 将 render function 挂到 组件实例上(instance)
// 如果没有 render function 使用 空函数(noop)代替
instance.render = (Component.render || NOOP) as InternalRenderFunction
// ...
}
if (__FEATURE_OPTIONS_API__ && !(__COMPAT__ && skipOptions)) {
// 兼容 2.x ...
}
// ...
}
主要做了三件事:
-
初始化编译选项;
-
对未有 render function 的组件实例,挂载 render function;
-
对 2.x 做兼容。
改函数还涉及,vue3 的编译逻辑,这个后续继续分析
至此,mountComponent 中的初始化组件操作基本完成。
ps: render function
function render(_ctx, _cache) {
with (_ctx) {
const { toDisplayString: _toDisplayString, openBlock: _openBlock, createElementBlock: _createElementBlock } = _Vue
return (_openBlock(), _createElementBlock("div", null, _toDisplayString(msg), 1 /* TEXT */))
}
}