Vue.createApp(App).mount('#app')执行过程
1.创建app入口
2.render函数
3.返回app
mount
App组件的挂载和渲染过程
patch:进行diff算法.
处理组件节点.
调用mountComponent挂载组件
初始化component,对组件的数据进行赋值和操作
调用设置和渲染的副作用函数
调用了副作用函数effect()
renderComponentRoot,对template进行渲染
把subTree子树的VNode挂载到container上,调用patch()
处理DOM元素,比如div/button/span(每个子树subtree)
如果还有子元素,进行patch之后,进行挂载
11.12.13. 处理子节点,进行挂载
来到runtime-dom>src>index.ts文件
// 创建app入口
export const createApp = ((...args) => {
// 创建app使用渲染器ensureRenderer()
// 渲染器有返回createApp属性,createApp调用之后返回app
const app = ensureRenderer().createApp(...args)
if (__DEV__) {
injectNativeTagCheck(app)
injectCompilerOptionsCheck(app)
}
// 从app取出mount
const { mount } = app
// 将app.mount函数进行重写,函数原本传入属性,而我们传入‘#app’字符串
// 重写mount函数是为了跨平台
app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
// normalizeContainer通过querySelector获取到container对象
const container = normalizeContainer(containerOrSelector)
if (!container) return
const component = app._component
if (!isFunction(component) && !component.render && !component.template) {
// __UNSAFE__
// Reason: potential execution of JS expressions in in-DOM template.
// The user must make sure the in-DOM template is trusted. If it's
// rendered by the server, the template should not contain any user data.
// 原因:在 DOM 模板中可能执行 JS 表达式。
// 用户必须确保 DOM 模板是受信任的。如果是
// 由服务器呈现,模板不应包含任何用户数据。
component.template = container.innerHTML
// 2.x compat check
if (__COMPAT__ && __DEV__) {
for (let i = 0; i < container.attributes.length; i++) {
const attr = container.attributes[i]
if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) {
compatUtils.warnDeprecation(
DeprecationTypes.GLOBAL_MOUNT_CONTAINER,
null
)
break
}
}
}
}
// clear content before mounting
// 先清空container中的原本内容
container.innerHTML = ''
const proxy = mount(container, false, container instanceof SVGElement)
if (container instanceof Element) {
container.removeAttribute('v-cloak')
container.setAttribute('data-v-app', '')
}
return proxy
}
return app
}) as CreateAppFunction<Element>
来到runtime-core>src>renderer.ts文件
// render函数传入baseCreatoRenderer()的返回值中
const render: RootRenderFunction = (vnode, container, isSVG) => {
if (vnode == null) {
if (container._vnode) {
unmount(container._vnode, null, null, true)
}
} else {
patch(container._vnode || null, vnode, container, null, null, null, isSVG)
}
flushPostFlushCbs()
container._vnode = vnode
}
const internals: RendererInternals = {
p: patch,
um: unmount,
m: move,
r: remove,
mt: mountComponent,
mc: mountChildren,
pc: patchChildren,
pbc: patchBlockChildren,
n: getNextHostNode,
o: options
}
let hydrate: ReturnType<typeof createHydrationFunctions>[0] | undefined
let hydrateNode: ReturnType<typeof createHydrationFunctions>[1] | undefined
if (createHydrationFns) {
;[hydrate, hydrateNode] = createHydrationFns(
internals as RendererInternals<Node, Element>
)
}
// baseCreatoRenderer()返回 createApp
return {
render,
hydrate,//ssr服务端错误
// createAppAPI函数使用了柯里化
createApp: createAppAPI(render, hydrate)
}
}
来到runtime-core>src>apiCreateApp.ts文件
// createApp最终返回createAppAPI() 返回app对象
export function createAppAPI<HostElement>(
render: RootRenderFunction,
hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
// 返回createApp
return function createApp(rootComponent, rootProps = null) {
if (!isFunction(rootComponent)) {
rootComponent = { ...rootComponent }
}
if (rootProps != null && !isObject(rootProps)) {
__DEV__ && warn(`root props passed to app.mount() must be an object.`)
rootProps = null
}
const context = createAppContext()
const installedPlugins = new Set()
let isMounted = false
// app对象 app.mixin.directive.comoinebt 创建一个app对象
const app: App = (context.app = {
_uid: uid++,
_component: rootComponent as ConcreteComponent,
_props: rootProps,
_container: null,
_context: context,
_instance: null,
version,
get config() {
return context.config
},
set config(v) {
if (__DEV__) {
warn(
`app.config cannot be replaced. Modify individual options instead.`
)
}
},
use(plugin: Plugin, ...options: any[]) {
if (installedPlugins.has(plugin)) {
__DEV__ && warn(`Plugin has already been applied to target app.`)
} else if (plugin && isFunction(plugin.install)) {
installedPlugins.add(plugin)
plugin.install(app, ...options)
} else if (isFunction(plugin)) {
installedPlugins.add(plugin)
plugin(app, ...options)
} else if (__DEV__) {
warn(
`A plugin must either be a function or an object with an "install" ` +
`function.`
)
}
return app
},
mixin(mixin: ComponentOptions) {
if (__FEATURE_OPTIONS_API__) {
if (!context.mixins.includes(mixin)) {
context.mixins.push(mixin)
} else if (__DEV__) {
warn(
'Mixin has already been applied to target app' +
(mixin.name ? `: ${mixin.name}` : '')
)
}
} else if (__DEV__) {
warn('Mixins are only available in builds supporting Options API')
}
return app
},
component(name: string, component?: Component): any {
if (__DEV__) {
validateComponentName(name, context.config)
}
if (!component) {
return context.components[name]
}
if (__DEV__ && context.components[name]) {
warn(`Component "${name}" has already been registered in target app.`)
}
context.components[name] = component
return app
},
directive(name: string, directive?: Directive) {
if (__DEV__) {
validateDirectiveName(name)
}
if (!directive) {
return context.directives[name] as any
}
if (__DEV__ && context.directives[name]) {
warn(`Directive "${name}" has already been registered in target app.`)
}
context.directives[name] = directive
return app
},
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
if (!isMounted) {
// #5571
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
)
// store app context on the root VNode.
// this will be set on the root instance on initial mount.
vnode.appContext = context
// HMR root reload
if (__DEV__) {
context.reload = () => {
render(cloneVNode(vnode), rootContainer, isSVG)
}
}
if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
render(vnode, rootContainer, isSVG)
}
isMounted = true
app._container = rootContainer
// for devtools and telemetry
;(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)\``
)
}
},
unmount() {
if (isMounted) {
render(null, app._container)
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
app._instance = null
devtoolsUnmountApp(app)
}
delete app._container.__vue_app__
} else if (__DEV__) {
warn(`Cannot unmount an app that is not mounted.`)
}
},
provide(key, value) {
if (__DEV__ && (key as string | symbol) in context.provides) {
warn(
`App already provides property with key "${String(key)}". ` +
`It will be overwritten with the new value.`
)
}
context.provides[key as string | symbol] = value
return app
}
})
if (__COMPAT__) {
installAppCompatProperties(app, context, render)
}
return app
}
}
来到runtime-core>src>renderer.ts文件
// 如果还有子元素,进行pathch之后,进行挂载
mountChildren(
vnode.children as VNodeArrayChildren,
el,
null,
parentComponent,
parentSuspense,
isSVG && type !== 'foreignObject',
slotScopeIds,
optimized
)
const mountChildren: MountChildrenFn = (
children,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
start = 0
) => {
// 12.对子节点进行遍历
for (let i = start; i < children.length; i++) {
const child = (children[i] = optimized
? cloneIfMounted(children[i] as VNode)
: normalizeVNode(children[i]))
// 13。遍历后进行挂载
patch(
null,
child,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
}
}
//片段只能有数组子级 因为它们要么由编译器生成,要么隐式创建 从数组。
// 拿到fragment的所有根组件,拿到子树元素,进行挂载
mountChildren(
// n2.children所有的子元素
n2.children as VNodeArrayChildren,
container,
fragmentEndAnchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
创建流程图:
大致分为13步,具体如下:
// 注意:此闭包中的函数应使用 'const xxx = () => {}'
// 样式,以防止被小写器内联。
// patch:进行diff算法,crateApp->vnode->element
const patch: PatchFn = (
n1,//旧的vnode,n1为空,进行挂载
n2,//新的vnode,n2与n1不同时,进行patch(diff算法)
container,//patch或mount之后,将vnode挂载到container上
anchor = null,
parentComponent = null,
parentSuspense = null,
isSVG = false,
slotScopeIds = null,
optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
) => {
} else if (shapeFlag & ShapeFlags.COMPONENT) {
// 处理组件节点,vnode-><HelloWorld> 按组件进行处理
processComponent(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
}
进入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
) => {
// 调用mountComponent挂载组件
mountComponent(
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
}
// 初始化component,对组件的数据进行赋值和操作
// setup组件实例,对组件的props/emits/slots/data进行初始化
setupComponent(instance)//执行完之后组件啥都有了
if (__DEV__) {
endMeasure(instance, `init`)
}
}
// setup() is async. This component relies on async logic to be resolved
// before proceeding
// 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
}
// 调用设置和渲染的副作用函数
setupRenderEffect(
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
)
副作用函数
// 副作用函数
const setupRenderEffect: SetupRenderEffectFn = (
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
) => {
// 调用了副作用函数effect()他来着reactive响应式系统,使用响应式数据时,默认进行依赖收集,进行再次执行
const update: SchedulerJob = (instance.update = () => effect.run())
update.id = instance.uid
// allowRecurse
// #1801, #2043 component render effects should allow recursive updates
//组件渲染效果应允许递归更新
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
}
// 7. renderComponentRoot,对template进行渲染
// subTree是template里面的子树,子树被<Fragment>包裹
instance.subTree = renderComponentRoot(instance)
if (__DEV__) {
endMeasure(instance, `render`)
}
if (__DEV__) {
startMeasure(instance, `hydrate`)
}
// 8. 把subTree子树的VNode挂载到container上,调用patch()
patch(
null,//因为是挂载所以是null
subTree,
container,
anchor,
instance,
parentSuspense,
isSVG
)
// 9. 处理DOM元素,比如div/button/span(每个子树subtree)
if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
进入processElement
// 如果template的根元素是div
const processElement = (
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
slotScopeIds: string[] | null,
optimized: boolean
) => {
isSVG = isSVG || (n2.type as string) === 'svg'
if (n1 == null) {
// 调用mountElement进行挂载元素
mountElement(
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
} else {
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
// 如果还有子元素,进行pathch之后,进行挂载
mountChildren(
vnode.children as VNodeArrayChildren,
el,
null,
parentComponent,
parentSuspense,
isSVG && type !== 'foreignObject',
slotScopeIds,
optimized
)
}
// 11.挂载子节点
const mountChildren: MountChildrenFn = (
children,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized,
start = 0
) => {
// 12.对子节点进行遍历
for (let i = start; i < children.length; i++) {
const child = (children[i] = optimized
? cloneIfMounted(children[i] as VNode)
: normalizeVNode(children[i]))
// 13。遍历后进行挂载
patch(
null,
child,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
}
}
最后给大家找了个流程图方便大家理解:
**如果大家觉得还不错,下方公z号👇,来跟作者一起学习吧! **
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_52691965/article/details/126043512
内容来源于网络,如有侵权,请联系作者删除!