首先看一个简单的例子
<script src="../dist/vue.global.js"></script>
<div id="app">
<h1>{{title}}</h1>
</div>
<script>
const {createApp, ref} = Vue
createApp({
setup() {
const title = ref('vue3')
return {
title
}
}
})
.mount('#app')
</script>
在createApp打上断点,进入断点到了下面的路径
packages/runtime-dom/src/index.ts
const app = ensureRenderer().createApp(...args)
感觉ensureRenderer
返回一个对象,对象当中包含createApp
function ensureRenderer() {
return (
renderer ||
// 创建渲染器实例同时传入当前平台特有的节点操作
(renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))
)
}
ensureRenderer
返回renderer
,renderer
由createRenderer
返回
packages/runtime-core/src/renderer.ts
export function createRenderer<
HostNode = RendererNode,
HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
return baseCreateRenderer<HostNode, HostElement>(options)
}
function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?: typeof createHydrationFunctions
): any {
···2000多行代码
return {
render, // 把接收到的vnode转换成dom对象,并追加到宿主元素
hydrate, // SSR 服务端将一个vnode生成为html
createApp: createAppAPI(render, hydrate) // 创建app实例
}
}
最后可以确定ensureRenderer
的createApp
是baseCreateRenderer
返回,createApp
实际上是createAppAPI
的返回值
packages/runtime-core/src/apiCreateApp.ts
export function createAppAPI<HostElement>(
render: RootRenderFunction,
hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
// 用户创建的App实例是这个函数生成的
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,
version,
get config() {
},
set config(v) {
},
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 {
if (!isMounted) {
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() {
},
provide(key, value) {
}
})
return app
}
}
createAppAPI
返回一个函数createApp
,createApp
返回一个实例对象
综上所述,刚开始看到的例子 createApp
执行返回一个app实例,这个实例当中包含了各种实例的配置,mount
就是在这个实例中声明的。
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
render(vnode, rootContainer, isSVG)
},
const render: RootRenderFunction = (vnode, container, isSVG) => {
patch(container._vnode || null, vnode, container, null, null, null, isSVG)
}
第一次传入的是根组件执行processComponent
,挂载组件mountComponent
执行三步
- 获取组件实例
instance
这时是一个空的 setupComponent
初始化setupRenderEffect
建立render的副作用 更新机制
setupComponent
中执行setupStatefulComponent返回一个有状态的组件
setupStatefulComponent
中判断setup
是否存在,如果存在则执行得到setupResult
,handleSetupResult
将setupResult
添加在instance.setupState
最后执行finishComponentSetup
,这里如果没有render就会生成一个 添加到instance.render
中,
兼容vue2
setCurrentInstance(instance)
pauseTracking()
applyOptions(instance)
resetTracking()
unsetCurrentInstance()
- 创建更新函数
componetUpdateFn
- 创建
componentUpdateFn
的副作用new ReactiveEffect
update
执行update
才会去执行componentUpdateFn
如果componentUpdateFn
执行的过程中有响应式数据发生变化,则按照参数2的方式执行参数1
componetUpdateFn
中会先判断是否已经挂载,如果已经挂载说明不是第一次,初始化就是第一次
if (!instance.isMounted) {
···
} else {
···
}
执行当前组件实例的render函数获取vnode
const subTree = (instance.subTree = renderComponentRoot(instance))
patch(
null,
subTree,
container,
anchor,
instance,
parentSuspense,
isSVG
)
这次的patch是首次,将创建子元素