承接上篇文章:vue-core 3.3alpha版本源码解析1--创建应用_zhang78529的博客-CSDN博客
创建应用后需要挂载到指定的dom元素上,现在开始对挂载源码的源码进行解析
上篇文章中有叙述runtime-dom中所导出的createApp函数,对mount进行了装饰,这里不在重复叙述。
mount核心部分实现于createAppAPI所返回的方法中创建的app对象的mount方法
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
if (!isMounted) {//未挂载情况下开始挂载
if (__DEV__ && (rootContainer as any).__vue_app__) {
warn(
`There is already an app instance mounted on the host container.\n` +
` If you want to mount another app on the same host container,` +
` you need to unmount the previous app by calling \`app.unmount()\` first.`
)
}
//创建根虚拟节点
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
//根虚拟节点的appContext指向上下文
vnode.appContext = context
// HMR root reload
if (__DEV__) {//开发模式下,定义reload方法,render克隆的vnode
context.reload = () => {
render(cloneVNode(vnode), rootContainer, isSVG)
}
}
if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
//调用baseCreateRenderer中render函数进行渲染
render(vnode, rootContainer, isSVG)
}
//渲染后,状态变成已挂载
isMounted = true
//app的_container指向根节点容器
app._container = rootContainer
//根节点容器的__vue_app__属性指向app
;(rootContainer as any).__vue_app__ = app
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
app._instance = vnode.component
devtoolsInitApp(app, version)
}
return getExposeProxy(vnode.component!) || vnode.component!.proxy
} else if (__DEV__) {
warn(
`App has already been mounted.\n` +
`If you want to remount the same app, move your app creation logic ` +
`into a factory function and create fresh app instances for each ` +
`mount - e.g. \`const createMyApp = () => createApp(App)\``
)
}
},
从宏观角度看上述代码,挂载时首先创建根虚拟节点,参数rootComponent,rootProps是导出createApp函数传入的第一、二个参数,分别为根组件模板和根组件属性。然后根据根虚拟节点调用baseCreateRenderer中render函数进行渲染,渲染完成后改变isMounted状态为已挂载,app的_container属性指向容器元素。
因为render函数是基于传入vnode根虚拟节点渲染的,所以先深入解析createVNode函数所创建的vnode
export const createVNode = (
__DEV__ ? createVNodeWithArgsTransform : _createVNode
) as typeof _createVNode
createVNode是一个包装函数,根据__DEV__的值,执行不同的函数,但是
const createVNodeWithArgsTransform = (
...args: Parameters<typeof _createVNode>
): VNode => {
return _createVNode(
...(vnodeArgsTransformer
? vnodeArgsTransformer(args, currentRenderingInstance)
: args)
)
}
createVNodeWithArgsTransform本质上还是_createVNode函数的封装,只是添加了对参数的处理
因此此处核心是调用_createVNode函数
function _createVNode(
type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
props: (Data & VNodeProps) | null = null,
children: unknown = null,
patchFlag: number = 0,
dynamicProps: string[] | null = null,
isBlockNode = false
): VNode {
//不存在type时,type定义为注释
if (!type || type === NULL_DYNAMIC_COMPONENT) {
if (__DEV__ && !type) {
warn(`Invalid vnode type when creating vnode: ${type}.`)
}
type = Comment
}
//使用已存在的vnode创建新虚拟节点,这种一般发生在<component :is="vnode"/>情况下,暂时跳过
if (isVNode(type)) {
const cloned = cloneVNode(type, props, true /* mergeRef: true */)
if (children) {
normalizeChildren(cloned, children)
}
if (isBlockTreeEnabled > 0 && !isBlockNode && currentBlock) {
if (cloned.shapeFlag & ShapeFlags.COMPONENT) {
currentBlock[currentBlock.indexOf(type)] = cloned
} else {
currentBlock.push(cloned)
}
}
cloned.patchFlag |= PatchFlags.BAIL
return cloned
}
// 如果type是class组件,则将__vccOpts赋值给type
if (isClassComponent(type)) {
type = type.__vccOpts
}
// 兼容性处理,可跳过
if (__COMPAT__) {
type = convertLegacyComponent(type, currentRenderingInstance)
}
// 对class和style进行规范化处理
if (props) {
// 对响应式对象或者代理对象,进行克隆后返回,非响应式对象直接使用
props = guardReactiveProps(props)!
let { class: klass, style } = props
if (klass && !isString(klass)) {
//非字符串形式的class属性,使用normalizeClass函数处理成字符串
props.class = normalizeClass(klass)
}
if (isObject(style)) {
//对象类型的style属性,使用normalizeStyle处理
// 非数组形式的响应式对象进行Object.assign浅复制处理
if (isProxy(style) && !isArray(style)) {
style = extend({}, style)
}
props.style = normalizeStyle(style)
}
}
// 根据type的类型判断节点类型
const shapeFlag = isString(type)
? ShapeFlags.ELEMENT
: __FEATURE_SUSPENSE__ && isSuspense(type)
? ShapeFlags.SUSPENSE
: isTeleport(type)
? ShapeFlags.TELEPORT
: isObject(type)
? ShapeFlags.STATEFUL_COMPONENT
: isFunction(type)
? ShapeFlags.FUNCTIONAL_COMPONENT
: 0
if (__DEV__ && shapeFlag & ShapeFlags.STATEFUL_COMPONENT && isProxy(type)) {
type = toRaw(type)
warn(
`Vue received a Component which was made a reactive object. This can ` +
`lead to unnecessary performance overhead, and should be avoided by ` +
`marking the component with \`markRaw\` or using \`shallowRef\` ` +
`instead of \`ref\`.`,
`\nComponent that was made reactive: `,
type
)
}
return createBaseVNode(
type,
props,
children,
patchFlag,
dynamicProps,
shapeFlag,
isBlockNode,
true
)
}
对上述代码进行总结:_createVNode函数是在调用createBaseVNode函数创建真正的vnode对象前,对传入createBaseVNode的参数进行规范化统一处理。
接下来来看下真正创建vnode对象并返回的createBaseVNode函数。
function createBaseVNode(
type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
props: (Data & VNodeProps) | null = null,
children: unknown = null,
patchFlag = 0,
dynamicProps: string[] | null = null,
shapeFlag = type === Fragment ? 0 : ShapeFlags.ELEMENT,
isBlockNode = false,
needFullChildrenNormalization = false
) {
//创建vnode对象基本属性
const vnode = {
__v_isVNode: true,
__v_skip: true,
type,
props,
key: props && normalizeKey(props),
ref: props && normalizeRef(props),
scopeId: currentScopeId,
slotScopeIds: null,
children,
component: null,
suspense: null,
ssContent: null,
ssFallback: null,
dirs: null,
transition: null,
el: null,
anchor: null,
target: null,
targetAnchor: null,
staticCount: 0,
shapeFlag,
patchFlag,
dynamicProps,
dynamicChildren: null,
appContext: null,
ctx: currentRenderingInstance
} as VNode
//如果参数needFullChildrenNormalization为true 则基于vnode对象和children参数进行规范化转换
if (needFullChildrenNormalization) {
normalizeChildren(vnode, children)
// 针对suspense组件的处理,可先跳过
if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
;(type as typeof SuspenseImpl).normalize(vnode)
}
} else if (children) {//如果存在children,则根据children类型更改vnode的shapeFlag值
vnode.shapeFlag |= isString(children)
? ShapeFlags.TEXT_CHILDREN
: ShapeFlags.ARRAY_CHILDREN
}
//开发模式下,对key为NaN的情况进行警告提醒
if (__DEV__ && vnode.key !== vnode.key) {
warn(`VNode created with invalid key (NaN). VNode type:`, vnode.type)
}
// 跟踪块级节点树
if (
isBlockTreeEnabled > 0 &&
// 非块级节点和避免跟踪自身
!isBlockNode &&
// 存在父节点
currentBlock &&
// 除 HOISTED 、BAIL和HYDRATE_EVENTS三种类型patchFlag,其他都需要跟踪
// 组件类型虚拟节点也需要跟踪
(vnode.patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) &&
// the EVENTS flag is only for hydration and if it is the only flag, the
// vnode should not be considered dynamic due to handler caching.
vnode.patchFlag !== PatchFlags.HYDRATE_EVENTS
) {
currentBlock.push(vnode)
}
//兼容模式
if (__COMPAT__) {
convertLegacyVModelProps(vnode)
defineLegacyVNodeProperties(vnode)
}
//返回虚拟节点
return vnode
}
参考上述代码注释,可以了解到createBaseVNode函数,首先创建vnode对象,并赋予部分属性初始值,并对children规范化处理,最后对满足条件的块级虚拟节点推入currentBlock数组,进行持续跟踪。截止到此处,根虚拟节点实际上只进行了创建和初始化,可以推断出 根虚拟节点的其他属性赋值是在后续的render中完善的。
现在便来看下render吧
const render: RootRenderFunction = (vnode, container, isSVG) => {
if (vnode == null) {//无虚拟节点情况下,根据container上的_vnode进行卸载
if (container._vnode) {
unmount(container._vnode, null, null, true)
}
} else {//有虚拟节点情况下,执行patch
patch(container._vnode || null, vnode, container, null, null, null, isSVG)
}
//flushPreFlushCbs函数是effect副作用执行(也就是update周期)前,执行定义的回调函数 watchEffect等
flushPreFlushCbs()
//flushPostFlushCbs函数是effect副作用执行(也就是update周期)后,执行定义的回调函数 watchPostEffect等
flushPostFlushCbs()
//缓存已渲染的虚拟节点
container._vnode = vnode
}
render中在无vnode情况下卸载缓存节点元素,存在vnode时,结合新旧vnode进行patch,flushPreFlushCbs,flushPostFlushCbs主要和后续的update effect相关,当前先不叙述,后续结合响应式部分一起讲解,patch后端vnode缓存并绑定到到容器的_vnode属性上,作为旧节点数据。
接下来就是众所周知diff算法部分patch
const patch: PatchFn = (
n1,
n2,
container,
anchor = null,
parentComponent = null,
parentSuspense = null,
isSVG = false,
slotScopeIds = null,
optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
) => {
//如果n1和n2两个虚拟节点相同,直接返回
if (n1 === n2) {
return
}
// 如果新旧节点类型不同,则直接卸载旧节点
if (n1 && !isSameVNodeType(n1, n2)) {
anchor = getNextHostNode(n1)
unmount(n1, parentComponent, parentSuspense, true)
n1 = null
}
//如果是BAIL类型的静态节点标记,不执行优化模式
if (n2.patchFlag === PatchFlags.BAIL) {
optimized = false
n2.dynamicChildren = null
}
const { type, ref, shapeFlag } = n2
//结合type和shapeFlag判断需要执行的对应process函数
switch (type) {
case Text:
processText(n1, n2, container, anchor)
break
case Comment:
processCommentNode(n1, n2, container, anchor)
break
case Static:
if (n1 == null) {
mountStaticNode(n2, container, anchor, isSVG)
} else if (__DEV__) {
patchStaticNode(n1, n2, container, isSVG)
}
break
case Fragment:
processFragment(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
break
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
} else if (shapeFlag & ShapeFlags.COMPONENT) {
processComponent(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
} else if (shapeFlag & ShapeFlags.TELEPORT) {
;(type as typeof TeleportImpl).process(
n1 as TeleportVNode,
n2 as TeleportVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
internals
)
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
;(type as typeof SuspenseImpl).process(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
internals
)
} else if (__DEV__) {
warn('Invalid VNode type:', type, `(${typeof type})`)
}
}
//组件的ref属性,根vnode是不存在parentComponent的,可先跳过
if (ref != null && parentComponent) {
setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, !n2)
}
}
patch函数对照新旧虚拟节点,进行一些判断,标记,识别新节点的type和shapeFlag进行任务分发,执行不同的process函数,首次mount的是渲染根虚拟节点组件,所以对应执行的是processComponent函数
const processComponent = (
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
slotScopeIds: string[] | null,
optimized: boolean
) => {
//绑定slot插槽名称数组
n2.slotScopeIds = slotScopeIds
if (n1 == null) {//无旧虚拟节点,为首次加载或者激活keep alive组件
//keep alive组件,通过父组件激活,先跳过
if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
;(parentComponent!.ctx as KeepAliveContext).activate(
n2,
container,
anchor,
isSVG,
optimized
)
} else {
//挂载组件
mountComponent(
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
}
} else {
//同时存在新旧节点时,更新组件
updateComponent(n1, n2, optimized)
}
}
processComponent函数用来区分keep alive激活 、首次加载及更新组件的情况,并调用不同函数实现激活、加载或更新。下一步需要使用mountComponent进行首次挂载。
const mountComponent: MountComponentFn = (
initialVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
) => {
//兼容性处理,先跳过
const compatMountInstance =
__COMPAT__ && initialVNode.isCompatRoot && initialVNode.component
//存在上一步兼容实例时,直接使用
//创建组件实例并绑定为虚拟节点的component
const instance: ComponentInternalInstance =
compatMountInstance ||
(initialVNode.component = createComponentInstance(
initialVNode,
parentComponent,
parentSuspense
))
//热更新情况下的特殊处理,暂时跳过
if (__DEV__ && instance.type.__hmrId) {
registerHMR(instance)
}
//开发模式下的特殊处理,暂时跳过
if (__DEV__) {
pushWarningContext(initialVNode)
//startMeasure是基于window.performance实现的主要是配合endMeasure记录对应操作的耗时
startMeasure(instance, `mount`)
}
//如果是KeepAlive虚拟节点,则将一些函数打包成对象赋值给实例的上下文ctx的renderer属性
if (isKeepAlive(initialVNode)) {
;(instance.ctx as KeepAliveContext).renderer = internals
}
// 在非兼容模式下,执行component的setup
if (!(__COMPAT__ && compatMountInstance)) {
if (__DEV__) {
startMeasure(instance, `init`)
}
//主要针对组件的setup做处理与instance绑定,生成instance的render方法
setupComponent(instance)
if (__DEV__) {
endMeasure(instance, `init`)
}
}
//此处是针对SUSPENSE组件异步的setup处理,暂时先跳过
if (__FEATURE_SUSPENSE__ && instance.asyncDep) {
parentSuspense && parentSuspense.registerDep(instance, setupRenderEffect)
// Give it a placeholder if this is not hydration
// TODO handle self-defined fallback
if (!initialVNode.el) {
const placeholder = (instance.subTree = createVNode(Comment))
processCommentNode(null, placeholder, container!, anchor)
}
return
}
//setupComponent后,基于instance执行instance.render和执行effect等
setupRenderEffect(
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
)
if (__DEV__) {
popWarningContext()
endMeasure(instance, `mount`)
}
}
mountComponent函数首先生成组件实例instance,随后执行自定义模板的setup,初始化属性、插槽、定义instance.render等,最后调用setupRenderEffect函数,根据setupComponent的结果,执行实例的render和effect等。
接下来先深入setupComponent看看它做了什么
export function setupComponent(
instance: ComponentInternalInstance,
isSSR = false
) {
isInSSRComponentSetup = isSSR
const { props, children } = instance.vnode
//判断是否状态类型的组件,shapeFlag类型的判断赋值在_createVNode中
const isStateful = isStatefulComponent(instance)
//初始化属性
initProps(instance, props, isStateful, isSSR)
//初始化插槽
initSlots(instance, children)
//状态组件,需要执行模板定义的setup方法,并取得结果
const setupResult = isStateful
? setupStatefulComponent(instance, isSSR)
: undefined
isInSSRComponentSetup = false
return setupResult
}
function setupStatefulComponent(
instance: ComponentInternalInstance,
isSSR: boolean
) {
//Component和instance.type皆指向createApp传入的第一个模板对象
const Component = instance.type as ComponentOptions
if (__DEV__) {
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])
}
}
if (Component.compilerOptions && isRuntimeOnly()) {
warn(
`"compilerOptions" is only supported when using a build of Vue that ` +
`includes the runtime compiler. Since you are using a runtime-only ` +
`build, the options should be passed via your build tool config instead.`
)
}
}
// accessCache先设置成空对象
instance.accessCache = Object.create(null)
//将ctx上下文处理成代理对象,并设置SKIP为true,赋值给proxy
instance.proxy = markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers))
//开发模式,先跳过
if (__DEV__) {
exposePropsOnRenderContext(instance)
}
const { setup } = Component
//如果定义的模板中有setup方法,则执行setup后再执行finishComponentSetup,否则直接执行finishComponentSetup
if (setup) {
//如果setup是数组形式,则使用createSetupContext创建setupContext,否则setupContext为null
const setupContext = (instance.setupContext =
setup.length > 1 ? createSetupContext(instance) : null)
//设置全局激活组件实例为该instance
setCurrentInstance(instance)
//暂停调度器跟踪
pauseTracking()
//callWithErrorHandling是方法执行异常捕获函数,主要作用是执行第一次参数函数执行异常捕获并抛出警告
//本质是是执行setup方法,setupResult是setup方法return的值
const setupResult = callWithErrorHandling(
setup,
instance,
ErrorCodes.SETUP_FUNCTION,
[__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
)
//setup方法执行完后重新开启调度器跟踪
resetTracking()
//置空全局激活组件实例
unsetCurrentInstance()
if (isPromise(setupResult)) {//setup返回异步函数,暂时跳过
setupResult.then(unsetCurrentInstance, unsetCurrentInstance)
if (isSSR) {
// return the promise so server-renderer can wait on it
return setupResult
.then((resolvedResult: unknown) => {
handleSetupResult(instance, resolvedResult, isSSR)
})
.catch(e => {
handleError(e, instance, ErrorCodes.SETUP_FUNCTION)
})
} else if (__FEATURE_SUSPENSE__) {
// async setup returned Promise.
// bail here and wait for re-entry.
instance.asyncDep = setupResult
if (__DEV__ && !instance.suspense) {
const name = Component.name ?? 'Anonymous'
warn(
`Component <${name}>: setup function returned a promise, but no ` +
`<Suspense> boundary was found in the parent component tree. ` +
`A component with async setup() must be nested in a <Suspense> ` +
`in order to be rendered.`
)
}
} else if (__DEV__) {
warn(
`setup() returned a Promise, but the version of Vue you are using ` +
`does not support it yet.`
)
}
} else {
//非异步函数,执行handleSetupResult
handleSetupResult(instance, setupResult, isSSR)
}
} else {
finishComponentSetup(instance, isSSR)
}
}
export function handleSetupResult(
instance: ComponentInternalInstance,
setupResult: unknown,
isSSR: boolean
) {
if (isFunction(setupResult)) {
//setup中返回的是函数,则直接赋值,ssr赋值给ssrRender,非ssr赋值给render
if (__SSR__ && (instance.type as ComponentOptions).__ssrInlineRender) {
// when the function's name is `ssrRender` (compiled by SFC inline mode),
// set it as ssrRender instead.
instance.ssrRender = setupResult
} else {
instance.render = setupResult as InternalRenderFunction
}
} else if (isObject(setupResult)) {
//开发模式,先跳过
if (__DEV__ && isVNode(setupResult)) {
warn(
`setup() should not return VNodes directly - ` +
`return a render function instead.`
)
}
//开发模式,先跳过
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
instance.devtoolsRawSetupState = setupResult
}
//setup中返回的是对象,则使用proxy包装后赋值给setupState
instance.setupState = proxyRefs(setupResult)
if (__DEV__) {
exposeSetupStateOnRenderContext(instance)
}
} else if (__DEV__ && setupResult !== undefined) {
warn(
`setup() should return an object. Received: ${
setupResult === null ? 'null' : typeof setupResult
}`
)
}
finishComponentSetup(instance, isSSR)
}
export function finishComponentSetup(
instance: ComponentInternalInstance,
isSSR: boolean,
skipOptions?: boolean
) {
//Component和instance.type皆指向createApp传入的第一个模板对象
const Component = instance.type as ComponentOptions
//兼容模式处理,先跳过
if (__COMPAT__) {
convertLegacyRenderFn(instance)
if (__DEV__ && Component.compatConfig) {
validateCompatConfig(Component.compatConfig)
}
}
//setup返回为对象时,instance的render为null
if (!instance.render) {
//非ssr模式执行
if (!isSSR && compile && !Component.render) {
//非兼容模式下优先取自定义模板的template,否则取实例的template
const template =
(__COMPAT__ &&
instance.vnode.props &&
instance.vnode.props['inline-template']) ||
Component.template ||
resolveMergedOptions(instance).template
if (template) {
//开发模式下,开始解析模板计时
if (__DEV__) {
startMeasure(instance, `compile`)
}
//取全局配置和组件配置,并合并
const { isCustomElement, compilerOptions } = instance.appContext.config
const { delimiters, compilerOptions: componentCompilerOptions } =
Component
const finalCompilerOptions: CompilerOptions = extend(
extend(
{
isCustomElement,
delimiters
},
),
componentCompilerOptions
)
//兼容模式 先跳过
if (__COMPAT__) {
finalCompilerOptions.compatConfig = Object.create(globalCompatConfig)
if (Component.compatConfig) {
// @ts-expect-error types are not compatible
extend(finalCompilerOptions.compatConfig, Component.compatConfig)
}
}
//调用解析器生成render方法,并赋值给Component.render
Component.render = compile(template, finalCompilerOptions)
if (__DEV__) {
endMeasure(instance, `compile`)
}
}
}
//解析器生成render方法同时赋值给instance.render
instance.render = (Component.render || NOOP) as InternalRenderFunction
//installWithProxy函数在registerRuntimeCompiler时设置
//registerRuntimeCompiler中的installWithProxy主要作用是当instance.render的_rc为true时创建instance.ctx的代理对象赋值给instance.withProxy属性
if (installWithProxy) {
installWithProxy(instance)
}
}
// 兼容处理,先跳过
if (__FEATURE_OPTIONS_API__ && !(__COMPAT__ && skipOptions)) {
setCurrentInstance(instance)
pauseTracking()
applyOptions(instance)
resetTracking()
unsetCurrentInstance()
}
//开发模式下,无render警告
if (__DEV__ && !Component.render && instance.render === NOOP && !isSSR) {
/* istanbul ignore if */
if (!compile && Component.template) {
warn(
`Component provided template option but ` +
`runtime compilation is not supported in this build of Vue.` +
(__ESM_BUNDLER__
? ` Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".`
: __ESM_BROWSER__
? ` Use "vue.esm-browser.js" instead.`
: __GLOBAL__
? ` Use "vue.global.js" instead.`
: ``) /* should not happen */
)
} else {
warn(`Component is missing template or render function.`)
}
}
}
setupComponent执行流程setupComponent==>setupStatefulComponent==>handleSetupResult==>finishComponentSetup,参考上述注释可以发现setupComponent主要是执行模板的setup完善组件实例instance。
最后来看看setupRenderEffect
const setupRenderEffect: SetupRenderEffectFn = (
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
) => {
//组件更新函数
const componentUpdateFn = () => {
if (!instance.isMounted) {
let vnodeHook: VNodeHook | null | undefined
const { el, props } = initialVNode
const { bm, m, parent } = instance
const isAsyncWrapperVNode = isAsyncWrapper(initialVNode)
//禁用scheduler调度器递归执行
toggleRecurse(instance, false)
//执行自定义的beforeMount周期回调
if (bm) {
invokeArrayFns(bm)
}
// 执行自定义的onVnodeBeforeMount周期回调
if (
!isAsyncWrapperVNode &&
(vnodeHook = props && props.onVnodeBeforeMount)
) {
invokeVNodeHook(vnodeHook, parent, initialVNode)
}
//兼容模式 先跳过
if (
__COMPAT__ &&
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
) {
instance.emit('hook:beforeMount')
}
//开启scheduler调度器递归执行
toggleRecurse(instance, true)
if (el && hydrateNode) {//hydrate模式下的处理,暂时跳过
const hydrateSubTree = () => {
if (__DEV__) {
startMeasure(instance, `render`)
}
instance.subTree = renderComponentRoot(instance)
if (__DEV__) {
endMeasure(instance, `render`)
}
if (__DEV__) {
startMeasure(instance, `hydrate`)
}
hydrateNode!(
el as Node,
instance.subTree,
instance,
parentSuspense,
null
)
if (__DEV__) {
endMeasure(instance, `hydrate`)
}
}
if (isAsyncWrapperVNode) {
;(initialVNode.type as ComponentOptions).__asyncLoader!().then(
// note: we are moving the render call into an async callback,
// which means it won't track dependencies - but it's ok because
// a server-rendered async wrapper is already in resolved state
// and it will never need to change.
() => !instance.isUnmounted && hydrateSubTree()
)
} else {
hydrateSubTree()
}
} else {
//开发模式下,开始测量render计时
if (__DEV__) {
startMeasure(instance, `render`)
}
//
const subTree = (instance.subTree = renderComponentRoot(instance))
if (__DEV__) {
endMeasure(instance, `render`)
}
//开发模式下,开始测量子节点patch计时
if (__DEV__) {
startMeasure(instance, `patch`)
}
//根据subTree数据进行patch,
patch(
null,
subTree,
container,
anchor,
instance,
parentSuspense,
isSVG
)
if (__DEV__) {
endMeasure(instance, `patch`)
}
initialVNode.el = subTree.el
}
// 将自定义的mounted的周期回调推入effect副作用队列执行完后的队列,并按队列顺序开始执行
if (m) {
queuePostRenderEffect(m, parentSuspense)
}
// 将自定义的onVnodeMounted的周期回调推入effect副作用队列执行完后的队列,并按队列顺序开始执行
if (
!isAsyncWrapperVNode &&
(vnodeHook = props && props.onVnodeMounted)
) {
const scopedInitialVNode = initialVNode
queuePostRenderEffect(
() => invokeVNodeHook(vnodeHook!, parent, scopedInitialVNode),
parentSuspense
)
}
//兼容模式处理,暂时跳过
if (
__COMPAT__ &&
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
) {
queuePostRenderEffect(
() => instance.emit('hook:mounted'),
parentSuspense
)
}
//keep-alive组件的处理,暂时跳过
if (
initialVNode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE ||
(parent &&
isAsyncWrapper(parent.vnode) &&
parent.vnode.shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE)
) {
instance.a && queuePostRenderEffect(instance.a, parentSuspense)
if (
__COMPAT__ &&
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
) {
queuePostRenderEffect(
() => instance.emit('hook:activated'),
parentSuspense
)
}
}
//标记组件实例已挂载
instance.isMounted = true
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
devtoolsComponentAdded(instance)
}
// #2458: deference mount-only object parameters to prevent memleaks
initialVNode = container = anchor = null as any
} else {
// updateComponent
// This is triggered by mutation of component's own state (next: null)
// OR parent calling processComponent (next: VNode)
let { next, bu, u, parent, vnode } = instance
let originNext = next
let vnodeHook: VNodeHook | null | undefined
if (__DEV__) {
pushWarningContext(next || instance.vnode)
}
// Disallow component effect recursion during pre-lifecycle hooks.
toggleRecurse(instance, false)
if (next) {
next.el = vnode.el
updateComponentPreRender(instance, next, optimized)
} else {
next = vnode
}
// beforeUpdate hook
if (bu) {
invokeArrayFns(bu)
}
// onVnodeBeforeUpdate
if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
invokeVNodeHook(vnodeHook, parent, next, vnode)
}
if (
__COMPAT__ &&
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
) {
instance.emit('hook:beforeUpdate')
}
toggleRecurse(instance, true)
// render
if (__DEV__) {
startMeasure(instance, `render`)
}
const nextTree = renderComponentRoot(instance)
if (__DEV__) {
endMeasure(instance, `render`)
}
const prevTree = instance.subTree
instance.subTree = nextTree
if (__DEV__) {
startMeasure(instance, `patch`)
}
patch(
prevTree,
nextTree,
// parent may have changed if it's in a teleport
hostParentNode(prevTree.el!)!,
// anchor may have changed if it's in a fragment
getNextHostNode(prevTree),
instance,
parentSuspense,
isSVG
)
if (__DEV__) {
endMeasure(instance, `patch`)
}
next.el = nextTree.el
if (originNext === null) {
// self-triggered update. In case of HOC, update parent component
// vnode el. HOC is indicated by parent instance's subTree pointing
// to child component's vnode
updateHOCHostEl(instance, nextTree.el)
}
// updated hook
if (u) {
queuePostRenderEffect(u, parentSuspense)
}
// onVnodeUpdated
if ((vnodeHook = next.props && next.props.onVnodeUpdated)) {
queuePostRenderEffect(
() => invokeVNodeHook(vnodeHook!, parent, next!, vnode),
parentSuspense
)
}
if (
__COMPAT__ &&
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
) {
queuePostRenderEffect(
() => instance.emit('hook:updated'),
parentSuspense
)
}
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
devtoolsComponentUpdated(instance)
}
if (__DEV__) {
popWarningContext()
}
}
}
// 创建instance响应式effect副作用实例
const effect = (instance.effect = new ReactiveEffect(
componentUpdateFn,//副作用执行函数
() => queueJob(update),//此处可以理解成缓存instance的update,并在响应式trigger的时候,推入执行队列,并执行
instance.scope // 将该副作用推入到instance.scope.effects数组上
))
//update方法为调用上面effect的run函数
const update: SchedulerJob = (instance.update = () => effect.run())
//update的id绑定instance的唯一id,作用于effect执行队列的优先级排序
update.id = instance.uid
//允许effect副作用递归执行
toggleRecurse(instance, true)
//开发模式特殊处理,先跳过
if (__DEV__) {
effect.onTrack = instance.rtc
? e => invokeArrayFns(instance.rtc!, e)
: void 0
effect.onTrigger = instance.rtg
? e => invokeArrayFns(instance.rtg!, e)
: void 0
update.ownerInstance = instance
}
//先执行一次effect.run
update()
}
setupRenderEffect定义了组件更新函数,并创建ReactiveEffect实例并赋值给effect,定义update执行effect.run,最后执行到componentUpdateFn,componentUpdateFn中,首次挂载,首先执行对应生命周期的回调函数,然后根据instance.render生成subTree节点,进行patch渲染最终页面(此处patch不深入解析),完成整个mount流程。
在mount流程中,compile流程生成instance.render及instance.render生成subTree,将在下篇文章进行解析,effect部分将结合响应式部分详解