Vue.js 源码(8) —— 模板编译

414 阅读3分钟

这是我参与更文挑战的第8天,活动详情查看: 更文挑战

前言

大多数同学在使用 Vue.js 开发时,基本都还是使用的模板。

但是 Vue.js 中创建 html 并不是只有模板这一种途径,我们可以使用 render(渲染函数)来创建 html,或者使用 jsx 来创建 html。

我们经常使用的模板会被编译转换成渲染函数,渲染函数执行后,会得到一个虚拟节点树用于渲染。

下面,我们将一起学习模板转换成渲染函数的整体过程。

模板编译

将模板编译成渲染函数可以分为两个步骤,先将模板解析成 AST(Abstract Syntax Tree,抽象语法树),然后再使用 AST 生成渲染函数。

由于静态节点时不需要重新渲染的,所以在生成 AST后,还需要遍历一遍,给所有静态节点做一个标记。

模板编译分成三部分内容:

  • 将模板解析为 AST
  • 遍历 AST 标记静态节点
  • 使用 AST 生成渲染函数 这三部分,分别对应三个模块
  • 解析器
  • 优化器
  • 代码生成器
graph LR
    t[模板]
    subgraph 模板编译
        a[解析器] --> b[优化器] --> c[代码生成器]
    end
    r[渲染函数]
    t --> a
    c --> r

解析器

在解析器内部,又分了很多小解析器,包括过滤器解析器文本解析器html 解析器

在使用模板时,我们可以在其中使用过滤器,而过滤器解析器的作用就是用来解析过滤器的。

文本解析器就是用来解析文本的,它的主要作用是用来解析带变量的文本,例如下面:

Hello {{name}}

不带变量的文本是一段纯文本,不需要使用文本解析器来解析。

最后也是最重要的是HTML解析器,它是解析器中最核心的模块,它的作用就是解析模板,每当解析到HTML标签的开始位置、结束位置、文本或者注释时,都会触发钩子函数,然后将相关信息通过参数传递出来,然后生成不同的 AST 节点。

AST 和 vnode 有点类似,都是使用 JavaScript 中的对象来表示节点。

优化器

优化器的目标是遍历 AST,检测出所有静态子树(永远都不会发生变化的 DOM 节点)并给其 打标记

当 AST 中的静态子树被打上标记后,每次重新渲染时,就不需要为打上标记的静态节点创建新的虚拟节点,而是直接克隆已存在的虚拟节点。在虚拟 DOM 的更新操作中,如果发现两个节点是同一个节点,正常情况下会对这两个节点进行更新,但是如果这两个节点是静态节点,则可以直接跳过更新节点的流程

代码生成器

代码生成器是模板编译的最后一步,它的作用是将AST转换成渲染函数中的内容,这个内容可以称为 “代码字符串”

<p align="center" @click="a">hello world</p>

生成的代码字符串是:

    with(this) {
        return _c( // $createElement
            'p',
            {
                attrs: {
                    align: "center"
                },
                on: {
                    click: a
                }
            },
            [_v("hello world")]
        )
    }

这样一个代码字符串最终导出到外界使用时,会使用 new Function(...) 来创建函数,这个函数就叫作渲染函数。

总结

本文,我们简单了解了模板编译的流程。

模板编译需要经过三个过程:

  • 将模板解析成 AST
  • 遍历 AST 标记静态节点
  • 使用 AST 生成代码字符串

后面,我们会详细学习解析器是如果工作的,也就是第一个过程,如果把字符串模板解析成 AST