vue版本:3.2.33
const { createApp } = Vue;
createApp({
data() {
return {
msg: 'hello vue'
}
},
}).mount('#app')
createApp
都干了啥呢?首先我们通过上一章节:如何调试源码?发现他进入到了 runtime-dom
目录下面的 index.ts
中的 createApp
这个方法中。代码片段如下:
这里插一句:还提供了 createSSRApp
方法,主要是提供给SSR渲染用的
export const createApp = ((...args) => {
// 创建应用app
const app = ensureRenderer().createApp(...args)
// 先把刚才创建的app应用中的mount方法取出来,然后重写app.mount方法的时候会用得到
const { mount } = app
app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
// 规范化容器处理
const container = normalizeContainer(containerOrSelector)
if (!container) return
// 取出根节点
const component = app._component
// 根节点不是函数并且没有render并且没有template,就把刚才规范化容器的innerHTML塞给根节点的template
if (!isFunction(component) && !component.render && !component.template) {
component.template = container.innerHTML
}
// app mount之前把容器的innerHTML清空,保持干净整洁
container.innerHTML = ''
// 然后把刚才取出来的mount捡起来继续执行以下, 就是createAppAPI里面的那个mount
const proxy = mount(container, false, container instanceof SVGElement)
return proxy
}
return app
}) as CreateAppFunction<Element>
通过上面代码发现createApp
通过ensureRenderer().createApp(...args)
创建一个app,然后重写app.mount
方法,最终把这个app对象返回出去。
ensureRenderer()
干了啥?const rendererOptions = /*#__PURE__*/ extend({ patchProp }, nodeOps)
function ensureRenderer() {
return (
renderer ||
(renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))
)
}
export function createRenderer<
HostNode = RendererNode,
HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
return baseCreateRenderer<HostNode, HostElement>(options)
}
rendererOptions
由 下面两部分合并组成。patchProp
: 处理 props、Attribute、class、style、event事件这几个东西nodeOps
: 处理 DOM 节点操作nodeOps对象包含:insert、remove、createElement、createText、createComment、setText、setElementText、parentNode、nextSibling、querySelector、setScopeId、cloneNode、insertStaticContent。
createRenderer
这个方法,并且把合并后的rendererOptions
当作参数传递进去。createRenderer
这个方法又调用了 baseCreateRenderer
的方法,baseCreateRenderer
这个方法内容就比较多了,大概有接近2000行吧。如下:
function baseCreateRenderer(options,createHydrationFns) {
// 核心diff过程
const patch = () => {}
// 渲染挂载流程
const render = (vnode, container, isSVG) => {}
return {
render,
hydrate, // 服务端渲染用的
createApp: createAppAPI(render, hydrate)
}
}
从代码代码可以看出baseCreateRenderer
最终返回了render
、hydrate
、createApp
。
后面我们针对baseCreateRenderer
中那近2000行代码到时候单独来好好聊聊,大家点个关注不迷路,好找回家路
。
createApp
是通过createAppAPI(render, hydrate)
这个方法实现的。createAppAPI
代码如下:
export function createAppAPI<HostElement>( render: RootRenderFunction,
hydrate?: RootHydrateFunction ): CreateAppFunction<HostElement> {
// 这里的createApp才是真的创建app,里面的一些方法都是我们比较眼熟的吧
return function createApp(rootComponent, rootProps = null) {
if (!isFunction(rootComponent)) {
rootComponent = { ...rootComponent }
}
// 创建APP默认配置
const context = createAppContext()
const installedPlugins = new Set()
let isMounted = false
// 然后这里根据createAppContext()创建的app默认配置一顿各种加工,最后把加工好的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) {},
use(plugin: Plugin, ...options: any[]) {},
mixin(mixin: ComponentOptions) {},
component(name: string, component?: Component): any {},
directive(name: string, directive?: Directive) {},
mount(){},
unmount() {},
provide(key, value) {
return app
}
})
return app
}
}
通过上面代码,我们发现createAppAPI
主要做以下几件事情:
createApp > ensureRenderer > createRenderer > baseCreateRenderer > createAppApi > mount
baseCreateRenderer
这个方法,就是那个2000多行代码的方法。核心diff过程createAppApi
这个方法
createApp
方法里面重写 mount
方法后面我们接着来聊聊 mount
的过程。