Vue.js 3.0源码解读:组件的实现-组件渲染(1)__Vue.js__源码
发布于 1 个月前 作者 banyungong 134 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

每一个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

回到顶部