vue3源码学习1-应用初始化

创建一个vue项目,我们从入口代码main.ts文件中可以看到:

// 从vue导入一个createApp方法,使用该方法创建一个app对象,并且重写app.mount方法
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.mount('#app')

从官网(https://github.com/vuejs/core)下载一份源码

在packages/runtime-dom/src/index.ts 中可以找到createApp()方法

// 使用ensureRenderer().createApp创建app对象
export const createApp = ((...args) => {
	const app = ensureRenderer().createApp(...args)
	...
	return app
}) 

// 延时创建渲染器,当用户只依赖响应式包的时候,可以通过 tree-shaking 移除核心渲染逻辑相关的代码
function ensureRenderer() {
  return (
    renderer ||
    (renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))
  )
}

在packages/runtime-core/src/renderer.ts中找到createRenderer方法

//可以发现调用的是baseCreateRenderer方法
export function createRenderer<
  HostNode = RendererNode,
  HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
  return baseCreateRenderer<HostNode, HostElement>(options)
}

//  创建一个渲染器,最后执行的是createAppAPI返回的函数。
function baseCreateRenderer(options){
	...
	const render: RootRenderFunction = (vnode, container, isSVG) => {
		// 组件渲染的核心逻辑
	}
	return {
	    render,
	    hydrate,
	    createApp: createAppAPI(render, hydrate)
	}

}

在packages/runtime-core/src/apiCreateApp.ts中找到createAppAPI方法

// 上一步中使用的函数,也就是createApp方法,接收rootComponent和rootProps两个参数, 我们在main.ts执行createApp(App)时,
// 会把App组件作为根组件传递给rootComponent,这样createApp内部就创建了一个app对象,提供了mount方法
export function createAppAPI<HostElement>(
  render: RootRenderFunction,
  hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
	return function createApp(rootComponent, rootProps = null) {
		 const app: App = (context.app = {
		    _component: rootComponent as ConcreteComponent,
			_props: rootProps,
			...	
			mount(
		        rootContainer: HostElement,
		        isHydrate?: boolean,
		        isSVG?: boolean
		    ): any {
				// 创建根组件的vnode
				const vnode = createVNode(
		            rootComponent as ConcreteComponent,
		            rootProps
	            )
				// 渲染vnode
				render(vnode, rootContainer, isSVG)
				app._container = rootContainer
				return getExposeProxy(vnode.component!) || vnode.component!.proxy
			}
			...
		return app
	}
}

app对象创建完成,接下来重写app.mount(为什么要重写,上一步中的app对象中不是已经有mount方法了吗?因为vue跨平台,之前的mount方法是可以跨平台的通用渲染流程),重写代码在packages/runtime-dom/src/index.ts的createApp方法中:

app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
	// 标准化容器,如果传入的是字符串,通过document.querySelector()转为DOM
	const container = normalizeContainer(containerOrSelector)
	if (!container) return
	const component = app._component
	if (!isFunction(component) && !component.render && !component.template) {
		// 如果组件对象没有定义render函数和template,则取容器的innerHTML作为组件模板内容
		component.template = container.innerHTML
	}
	// 挂载前清除容器
	container.innerHTML = ''
	const proxy = mount(container, false, container instanceof SVGElement)
}
posted @ 2022-08-30 13:52  菜菜123521  阅读(150)  评论(0编辑  收藏  举报