挂载组件
// 挂载组件
const mountComponent = (
initialVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
) => {
// 1. 创建组件实例
const instance = (initialVNode.component = createComponentInstance(
initialVNode,
parentComponent,
parentSuspense
))
// inject renderer internals for keepAlive
if (isKeepAlive(initialVNode)) {
instance.ctx.renderer = internals
}
// 2. 设置组件实例
setupComponent(instance)
// setup() is async. This component relies on async logic to be resolved
// before proceeding.
// 3.设置并且运行带有副作用的渲染函数
setupRenderEffect(
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
)
}
总结:
- 创建一个组件实例
createComponentInstance
- 判断是否是
keep-alive
- 设置组件实例
- 运行
setupRenderEffect
setupComponent
function setupComponent(instance, isSSR = false) {
const { props, children, shapeFlag } = instance.vnode
// 是否是状态型组件
const isStateful = shapeFlag & 4 /* STATEFUL_COMPONENT */
// 初始化组件属性、插槽
initProps(instance, props, isStateful, isSSR)
initSlots(instance, children)
// 仅为状态型组件挂载setup信息,非状态型组件仅为纯UI展示不需要挂载状态信息
const setupResult = isStateful
? setupStatefulComponent(instance, isSSR)
: undefined
return setupResult
}
// 状态型组件生成setup结果并进行信息挂载
function setupStatefulComponent(instance, isSSR) {
// 组件options Object,也就是vnode.type
const Component = instance.type
{
if (Component.name) {
// 校验组件名是否合法
validateComponentName(Component.name, instance.appContext.config)
}
if (Component.components) {
// 批量校验组件名
const names = Object.keys(Component.components)
for (let i = 0; i < names.length; i++) {
validateComponentName(names[i], instance.appContext.config)
}
}
if (Component.directives) {
// 批量校验指令名是否合法
const names = Object.keys(Component.directives)
for (let i = 0; i < names.length; i++) {
validateDirectiveName(names[i])
}
}
}
// 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
// 1. 为组件实例创建渲染代理,同时将代理标记为raw,为的是在后续过程中不会被误转化为
// 响应式数据,渲染代理源对象是组件实例上下文,代理traps后面会详细讲解
instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)
// 2. call setup()
const { setup } = Component
if (setup) {
// 创建setup上下文并挂载到组件实例上
const setupContext = (instance.setupContext =
setup.length > 1 ? createSetupContext(instance) : null)
// 记录当前正在初始化的组件
currentInstance = instance
// 执行setup前暂停依赖收集
// PS: 执行setup期间不允许进行依赖收集,setup仅为获取我们需要为组件提供的状态信息
// 不应该在它的里面其他非必要的副作用,真正的依赖收集等有较强副作用的操作应该放到
// setup挂载之后,以免产生不可预测的问题
pauseTracking()
// 执行setup并获得安装结果信息,setup执行结果就是我们定义的响应式数据、函数、钩子等
const setupResult = callWithErrorHandling(
setup,
instance,
0 /* SETUP_FUNCTION */,
[shallowReadonly(instance.props), setupContext]
)
// setup执行完毕恢复依赖收集
resetTracking()
// 当前实例重置
currentInstance = null
// setup执行结果挂载
if (isPromise(setupResult)) {
if (isSSR) {
// return the promise so server-renderer can wait on it
// 在SSR或者suspense时setup返回promise
// suspense因为有节点fallback,而setup中是正式渲染内容,因此是一个异步resolve的过程
return setupResult.then((resolvedResult) => {
handleSetupResult(instance, resolvedResult)
})
} else {
// async setup returned Promise.
// bail here and wait for re-entry.
instance.asyncDep = setupResult
}
} else {
// 将setup的执行结果挂载到组件实例上
handleSetupResult(instance, setupResult)
}
} else {
finishComponentSetup(instance)
}
}
总结:
- 初始化组件的
props
、slots
setupStatefulComponent
方法设置组件状态并挂载- 校验组件名称是否合法
- 校验指令是否合法
- 为组件实例创建渲染代理
- 判断组件是否有
setup
方法,- 有:
- 创建
setupContext
并赋值给组件实例的setupContext - 记录当前正在初始化的组件
- 暂停
effect
的依赖收集 - 执行
setup
,并拿到返回的结果setupResult
- 放开
effect
的依赖收集 - 将setup执行结果
setupResult
进行proxy
拦截代理,并赋值给instance.setupState
- 创建
- 有: