每一个Vue.js开发者,最熟悉的部门莫过于组件了。这是Vue.js的核心,组件系统也是它一个重要的概念。整个应用的页面也都是通过组件渲染来实现的。那么,它的内部是如何工作的呢?它是怎讲从虚拟的vnode到真实的DOM转变的呢?接下来,让我们好好来看看这其中的源码,了解其中的实现机制。
我们都了解在vue.js的内部,一个组件要真正的渲染生成DOM的过程是:创建vnode->渲染vnode->生成DOM。接下来,我们就从应用程序的入口开始,逐步来看vue.js 3.0中,组件是渲染过程。
应用程序初始化
首先我们来看vue.js 3.0 初应用的代码:
import { createApp } from 'vue'
import App from './app'
const app = createApp(App)
app.mount('#app')
我们可以看到,应用程序的入口就是:createApp这个函数。我们一起来看看这个函数的内部实现:
const createApp = ((...args) => {
const app = ensureRenderer().createApp(...args);
{
injectNativeTagCheck(app);
injectCustomElementCheck(app);
}
const { mount } = app;
app.mount = (containerOrSelector) => {
const container = normalizeContainer(containerOrSelector);
if (!container)
return;
const component = app._component;
if (!isFunction(component) && !component.render && !component.template) {
component.template = container.innerHTML;
}
// clear content before mounting
container.innerHTML = '';
const proxy = mount(container, false, container instanceof SVGElement);
if (container instanceof Element) {
container.removeAttribute('v-cloak');
container.setAttribute('data-v-app', '');
}
return proxy;
};
return app;
});
代码首先是创建了一个app对象,之后重写了mount的方法。 因此,我们可以看出createApp主要做了两件事:1.创建app对象; 2.重写mount方法。
创建app对象
创建app对象的代码是:
const app = ensureRenderer().createApp(...args);
其中,ensureRenderer()是用来创建一个渲染器对象,我们来看看内部的代码:
const rendererOptions = extend({ patchProp, forcePatchProp }, nodeOps);
let renderer;
let enabledHydration = false;
function ensureRenderer() {
return renderer || (renderer = createRenderer(rendererOptions));
}
其中,rendererOptions是渲染相关的一些配置:比如更新属性的方法,操作DOM的方法。接着会调用:createRenderer方法。
function createRenderer(options) {
return baseCreateRenderer(options);
}
接着是:baseCreateRenderer,这代码的内容非常长,具体代码的意思这里就先不细说,我们主要先把流程理清楚:
function baseCreateRenderer(options, createHydrationFns) {
...省略代码
return {
render,
hydrate,
createApp: createAppAPI(render, hydrate)
};
}
我们可以看到,通过ensureRenderer()创建的渲染器,内部会有一个createApp的方法,也就是我们代码前面用到的。它是执行createAppAPI方法返回的函数。
重写app.mount方法
其实在刚才createAppAPI函数的内部,本身就包含mount的方法:
function createAppAPI(render, hydrate) {
...
mount(rootContainer, isHydrate, isSVG) {
...
}
}
这里重写的原因是:vue.js不仅仅是为Web平台服务的,它的目标是支持跨平台渲染,而createApp函数内部的app.mount方法是一个标准的可跨平台的组件渲染流程。所以这些代码的执行逻辑都是与平台无关的。因此我们需要在外部重写这个方法,来完善Web平台下的渲染逻辑。
这个重写的mount方法:首先用normalizeContainer函数创建了一个标准话容器。
const container = normalizeContainer(containerOrSelector);
接着:
if (!isFunction(component) && !component.render && !component.template) {
component.template = container.innerHTML;
}
这代码的意思是:如组件对象没有定义render函数和template模版,则取容器的innerHTML作为组件模版内容。然后在挂载前清空容器内容:
container.innerHTML = '';
然后真正的挂载 :走的是标准组件渲染流程:
const proxy = mount(container, false, container instanceof SVGElement);
也就是从这里开始,才算真正进入组件渲染流程,渲染流程主要做了两件事:创建vnode和渲染vnode.
时间关系,这一次就先记录这么多吧,下次我们再继续看看这核心的渲染过程到底是怎样的?
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: Chqun 原文链接:https://juejin.im/post/6991434668528631816