持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
前言
在 patch 过程,如果是组件的话会执行 mountComponent 方法进行挂载。这篇我们来看一下它的实现。
mountComponent
mountComponent 的位置在 packages/runtime-core/src/renderer.ts 文件里。
const mountComponent: MountComponentFn = (
initialVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
) => {
// 2.x compat may pre-create the component instance before actually
// mounting
const compatMountInstance =
__COMPAT__ && initialVNode.isCompatRoot && initialVNode.component
// 组件 -> 组件实例
const instance: ComponentInternalInstance =
compatMountInstance ||
(initialVNode.component = createComponentInstance(
initialVNode,
parentComponent,
parentSuspense
))
// resolve props and slots for setup context
if (!(__COMPAT__ && compatMountInstance)) {
// 执行setup
setupComponent(instance)
}
setupRenderEffect(
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
)
}
mountComponent 里面主要调用了三个方法。
- createComponentInstance 创建组件实例,里面定义了一个组件的一些属性和方法,并进行返回
- setupComponent 组件初始化处理
- setupRenderEffect 创建 effect
setupComponent
在 setupComponent 中对组件实例做了一些初始化的工作,他的代码位置在 packages/runtime-core/src/component.ts
// packages/runtime-core/src/component.ts
export function setupComponent(
instance: ComponentInternalInstance,
isSSR = false
) {
isInSSRComponentSetup = isSSR
const { props, children } = instance.vnode
const isStateful = isStatefulComponent(instance)
initProps(instance, props, isStateful, isSSR)
initSlots(instance, children)
const setupResult = isStateful
? setupStatefulComponent(instance, isSSR)
: undefined
isInSSRComponentSetup = false
return setupResult
}
我们来看下具体做了哪些工作。
- 使用
isStatefulComponent方法,判断组件是否为有状态组件,在 isStatefulComponent 中其实是使用了我们之前讲过的 shapeFlag 来判断 如果 shapeFlag 与 ShapeFlags.STATEFUL_COMPONENT 进行位与操作是 true 时就是有状态组件 - 使用
initProps方法对组件的 prop 进行初始化 - 使用
initSlots方法对组件的插槽进行初始化。 - 执行
setupStatefulComponent方法。
我们接下来再去看看 setupStatefulComponent 的实现
function setupStatefulComponent(
instance: ComponentInternalInstance,
isSSR: boolean
) {
const Component = instance.type as ComponentOptions
// 0. create render proxy property access cache
instance.accessCache = Object.create(null)
// 1. create public instance / render proxy
// also mark it raw so it's never observed
instance.proxy = markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers))
// 2. call setup()
const { setup } = Component
if (setup) {
const setupContext = (instance.setupContext =
setup.length > 1 ? createSetupContext(instance) : null)
setCurrentInstance(instance)
pauseTracking()
const setupResult = callWithErrorHandling(
setup,
instance,
ErrorCodes.SETUP_FUNCTION,
[__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
)
resetTracking()
unsetCurrentInstance()
if (isPromise(setupResult)) {
setupResult.then(unsetCurrentInstance, unsetCurrentInstance)
} else {
handleSetupResult(instance, setupResult, isSSR)
}
} else {
finishComponentSetup(instance, isSSR)
}
}
他主要做了这些工作。
- 从 Component 中解构出 setup 方法
- 判断 setup 方法是否存在
- 如果不存在则直接调用最后的 finishComponentSetup 进行处理
- 如果存在,根据setup参数的多少决定是否初始化instance.setupContext对象,中间调用了createSetupContext方法
- 通过 callWithErrorHandling 调用 setup 方法,并返回结果
setupResult - 通过
handleSetupResult方法最结果进行处理,因为我们在写setup 的时候,返回值可能有多种类型,所以要进行分别处理 - 处理完雨后,在
handleSetupResult内部会调用finishComponentSetup方法。
小结
这篇了解了 mountComponent 里面的逻辑,知道了里面是如何处理属性、插槽、还有 setup 方法,并在最后进行调用 finishComponentSetup 方法,下一篇我们着重学习一下 finishComponentSetup的逻辑。