「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战」。
前言
如何搭建环境及调试源码 Vue3.0源码学习——环境搭建
Vue实例创建的过程
如何创建实例
- 还是从
todomvc开始调试(路径/packages/vue/examples/composition/todomvc),在createApp()处打上第一个断点
- 单步调试
F11进入,找到入口文件/packages/runtime-dom/src/index.ts
createApp()返回的app就是创建的实例
export const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args)
...
const { mount } = app
app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
...
}
// 最终返回
return app
}) as CreateAppFunction<Element>
实例在哪里创建
app是从ensureRenderer().createApp()中返回,在当前文件中找到ensureRenderer()
renderer一开始未定义,因此实际返回createRender()单步进入createRenderer()位置/packages/runtime-core/src/renderer.ts
- 返回的是一个
baseCreateRenderer()再次进入,这是Vue3中最大的函数,是一个渲染器 - 直接翻到这个函数的最下面大概2300多行,看到返回了一个对象,找到
createApp
return {
render,
hydrate,
createApp: createAppAPI(render, hydrate)
}
createApp执行了一个createAppAPI函数,进入位置/packages/runtime-core/src/apiCreateApp.ts
- 终于看到了
createApp的庐山真面目
export function createAppAPI<HostElement>(
render: RootRenderFunction,
hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
return function createApp(rootComponent, rootProps = null) {
...
const app: App = (context.app = {
_uid: uid++,
_component: rootComponent as ConcreteComponent,
_props: rootProps,
_container: null,
_context: context,
_instance: null,
...
use(plugin: Plugin, ...options: any[]) {
...
},
mixin(mixin: ComponentOptions) {
...
},
component(name: string, component?: Component): any {
...
},
directive(name: string, directive?: Directive) {
...
},
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
...
},
unmount() {
...
},
provide(key, value) {
...
})
...
return app
}
}
createApp().mount('#app')执行的就是app.mount()这个函数- 主要作用是将
createApp()中传入的数据和状态转换为真实dom,并追加到宿主元素#app,详细执行过程将在下一篇文章中进行讲解
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
if (!isMounted) {
// 创建虚拟dom
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
// 转化为真实dom并挂载到rootContainer
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__) {
...
}
},
往期回顾 Vue3.0源码学习——环境搭建