相信组件开发现在已经成为前端开发的范式了,我们在做前端开发时,大部分是在写组件,那我们就更需要了解 vue里组件到底是怎么渲染的。
vue里的一个最重要的概念就是组件化,我们可以用小型的/独立的/可复用的组件构建大型复杂应用。
几乎任意类型的应用界面都可以抽象为一个组件树。
我们使用vite和pnpm搭建项目。
那么,我们就从入口文件main.ts出发,探索组件渲染的过程吧!
此处为应用程序初始化
import { createApp } from "vue"
import App from "./App.vue"
const proxy = createApp(App).mount("#app")
console.log(proxy)
可以看到入口函数:createApp函数
const createApp = ((...args) => {
// 创建 app 对象
const app = ensureRenderer().createApp(...args);
const { mount } = app
// 此处重写了 mount 函数
app.mount = (containerOrSelector) => {}
return app
})
可以看出 createApp此函数主要做了 2 件事情:创建 app 对象和重写了 app.mount方法。
- 创建 app 对象
首先它调用了ensureRenderer函数,创建了一个渲染器
// 渲染相关的一些配置,比如更新属性的方法,操作 DOM 的方法
const rendererOptions = extend({ patchProp }, nodeOps);
function ensureRenderer() {
return (renderer ||
(renderer = createRenderer(rendererOptions)));
}
function createRenderer(options) {
return baseCreateRenderer(options);
}
function baseCreateRenderer(options, createHydrationFns) {
const render = () => {}
return {
render,
// createAppAPI 利用闭包和函数柯里化的技巧
createApp: createAppAPI(render, hydrate)
}
}
- 又调用了createRenderer,此处rendererOptions包括了俩大配置对象nodeOps和patchProp
- 又调用了baseCreateRenderer,此函数返回了 createApp和render函数
- 此处的createApp是createAppApi函数进行柯里化转化返回的
- 我们再看createAppApi,在app对象上封装了一系列方法,包括mountusecomponentdirective等,供使用者直接使用
- app.mount重写
思考一下,为什么要重写这个方法?
因为Vuejs不仅仅是Web平台应用,它的目标是跨平台渲染。而createApp内部的app.mount实现了标准的可跨平台的渲染流程:
mount(rootContainer, isHydrate, isSVG) {
if (!isMounted) {
// 创建组件 vnode
const vnode = createVNode(rootComponent, rootProps);
if (isHydrate && hydrate) {
hydrate(vnode, rootContainer);
}
else {
// 渲染 vnode
render(vnode, rootContainer, isSVG);
}
app._container = rootContainer;
}
}
注:此处的hydrate暂时可以忽略,是SSR渲染相关。
…待完善
核心渲染流程
- 执行 mount 流程
首先执行render函数,进行 vnode创建和渲染。
组件拆箱子的流程