【vue源码系列2】vue组件是怎么变成真实dom的?

165 阅读3分钟

在开始看vue3源码之前,我最大的一个困惑就是,vue到底是怎么把组件变成dom的?各个模块之间是如何相互关联的?

如果你也有这样的困惑,那这篇文章将会告诉你答案。

        <div id="app">{{msg}}</div>
        <script>
          const { createApp } = Vue
          const APP = {
            data() {
              return {
                msg: 1
              }
            }
          }
          const app = createApp(APP)
          app.mount('#app')
        </script>

我们以这样一段代码为例。

今天来聊聊vue的createApp方法是怎么把vue组件变成真实dom的。

我们自己可以从需求出发,先分析一下。

createApp要做哪些事?

首先createApp是一个函数,接受一个对象参数。

其次createApp返回了一个对象,对象中有个mount方法。

根据这两点,我们可以很简单的写一个如下的createApp函数。

  function createApp(app) {
    return {
      mount(el) {
        const dom = document.querySelector(el)
        dom.innerText = app.data().msg
      }
    }
  }

但是这里有几个问题,一个是数据改变了,dom需要重新渲染。另一个是赋值操作,我们是写死的,这个应该根据语法判断。

所以我们至少要加上3个步骤。【伪代码】

  function createApp(app) {
    return {
      mount(el) {
        const dom = document.querySelector(el)
        // 1.监听数据变化
        const $proxy = new Proxy(app.data(), {
            get: () => {},
            set: () => {}
        })
        // 2.当数据变化时,更新dom
        updateComponent()
        // 3.语法分析,将值绑定到dom
        dom.innerText = app.data().msg
      }
    }
  }

理解到这里,我们再去看看vue是如何实现的。

vue是如何渲染dom的?

1.调用createApp方法,创建实例

跟我们自己写的demo类似,vue也是先创建一个实例,返回app对象。

只不过vue返回的app对象,上面定义了很多方法和属性。

image.png

2.调用mount

将id=app的div元素作为根节点,{{msg}}作为tempalte,创建虚拟节点,然后调用render函数渲染。

从这里可以看出,vue渲染dom是将dom先转化为虚拟dom,然后通过render函数渲染。

image.png

3.调用render函数【虚拟dom暂时先不看】

render方法,就是runtime的一个核心了。

image.png

第一次调用,直接走patch方法。

image.png

在patch中会根据虚拟节点的类型,做不同的处理。

此处,我们是组件,所以会调动processComponent处理组件。

因为是第一次执行,所以会走mountComponent方法。

4.调用mountComponent

在这个方法中做了几件事情,是比较重要的。

- 4.1.创建组件实例 组件的数据,方法都会挂载到实例上。

- 4.2.处理组件的数据,包括数据代理,props处理等

在挂载组件的过程中,会判断组件是否有render函数,如果没有,则调用compiler去解析template,并返回一个render函数。

image.png

compiler模块就是在这里与runtime模块做交互的。

- 4.3.添加监听器,当有数据改变时,重新渲染dom

组件挂载前,会创建一个监听器,当组件内部有数据变化时,会触发更新函数,重新渲染组件。更新的时候,就涉及到dom diff算法了,这个后面我们再说。

vue核心模块是如何工作的?

梳理了一遍源码逻辑,我们对vue的各个模块应该有了初步的了解。

从vue源码的目录结构,我们可以很清楚地看到,vue有几个核心模块。

image.png

从名字中,我们也能大概看出来,vue核心逻辑可以分为3大块。(SSR服务渲染,sfc单文件渲染暂时不管,只看核心逻辑)

image.png

根据vue渲染dom的逻辑,我们可以得出如下的渲染步骤。

  • 1.创建虚拟dom
  • 2.调用render函数挂载组件
  • 3.挂载过程需要处理组件内的数据,并创建监听器
    • 3.1当有数据变化时,通知监听器,调用更新组件方法
    • 3.2比对新旧组件
  • 4.渲染真实dom
image.png