相信组件开发现在已经成为前端开发的范式了,我们在做前端开发时,大部分是在写组件,那我们就更需要了解 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) {// 创建组件 vnodeconst vnode = createVNode(rootComponent, rootProps);if (isHydrate && hydrate) {hydrate(vnode, rootContainer);}else {// 渲染 vnoderender(vnode, rootContainer, isSVG);}app._container = rootContainer;}}
注:此处的hydrate暂时可以忽略,是SSR渲染相关。
…待完善
核心渲染流程
- 执行 mount 流程
首先执行render函数,进行 vnode创建和渲染。
组件拆箱子的流程
