前端 vue 模板编译器详解

276 阅读4分钟

前言

在学习《vue.js 设计与实现》这本书中编译器后的总结,输出这篇文章,旨在加深自己对该技术的理解。如果你和我一样对 Vue 源码感兴趣的话,那这篇文章能帮助到你。

通过阅读这篇文章,你可以学习到:

  • 编译原理
  • 抽象语法树(AST)
  • vue 模板编译过程

编译器原理初识

在计算机编程世界中,最常使用的三种编译器:

  1. 编译器(compiler):是一种计算机程序,它会将某种编程语言(源语言)转换成另一种编程语言(目语言),将整个程序的源代码转换为计算机处理器可以执行的机器代码(0和1);
  2. 转译器(translator):将一种高级语言转换成另一个高级语言,例如将Java转换为Javascript;
  3. 解释器(interpreter):它会将高级语言转换成机器语言,然后立即运行/执行该代码。

在前端开发领域中,一般使用转译器进行编译,例如:vue 模板字符串编译成渲染函数代码;TS 转 JS;babel 编译 Javascript,ES6 转 ES5;小程序多端开发;

转译器在前端使用场景之多,足以说明它的含金量不一般,是一个难啃的知识点。那么在接下来的篇幅里,会重点去讲转译器,深度去学习转译器的编译原理。

编译器架构

完整的编译器,包括编译前端和编译后端两个部分:

  • 前端:词法分析、语法分析、语义分析
  • 后端:生成中间代码、中间代码优化、生成目标代码、目标代码优化

相信看到这里,你会产生疑惑,这里的前端和后端,并不是指前端开发工程师和后端开发工程师。而是指编译过程,前半部分做的内容与后半部分做的内容,如下图所示:

pPcbvhn.png

简述转译器编译过程:

  1. 词法分析 通过有限状态机的算法,会逐个读取字符串模板中的字符,并根据一定规则将整个字符串切割为一个一个 Token。这里的 Token 可以视作词法记号。举个栗子,假设有一段模板:

    <p>Vue<p>
    

    解析器会把这段字符串切割为三个 Token。

    - 开始标签:<p>
    - 文本节点:Vue
    - 结束标签 </p>
    

    最后通过遍历字符,生成一个 Token 数组。

    const tokens = [
        { type: 'tag', name: 'p' },
        { type: 'text', content: 'Vue'},
        { type: 'tagEnd', name: 'p'}
    ]
    
  2. 语法分析、语义分析 将 Token 数组装换成一颗抽象语法树(Abstract Syntax Tree,AST)

    抽象语法树(AST),是一个深层嵌套的对象,是源代码语法结构的一种抽象表示。之所以说是抽象,是因为忽略了逗号、括号等分隔符。之所以是树,是因为代码一般都是嵌套的关系,需要用树的父子关系来表示源码的嵌套关系。 所以抽象语法树是最适合计算机来理解代码的数据结构。

    cosnt ast = {
        "type": "Program",
        "comments": [],
        "tokens": [],
        "templateNodes": [{
            "name": "p",
            "attributes": [],
            "children": [{
                "value": "Vue"
            }],
            "value": "<p>Vue<p>"
        }]
    }
    
  3. 生成中间代码、中间代码优化 递归遍历抽象语法树,遍历到节点对相应的类型与属性做增删改查的优化,从而优化抽象语法树。

    let code = '<p>Vue<p>';
    
    let ast = parser(code);
    traverse(ast, {
        JSXOpeningElement: {
            enter: function (node, parent) {
                console.log('进入开标签', node);
            },
            exit: function (node, parent) {
                console.log('退出开标签', node);
            }
        }
    })
    
  4. 生成目标代码、目标代码优化 根据优化后的抽象语法树生成相应的节点类型和属性的渲染函数代码字符串。

    render() {
        return h('p', 'Vue')
    }
    

通过本小节,你已经了解了转移器编译原理,在下一小节将总结 Vue 编译器编译过程。

vue 编译器编译过程

vue 模板编译器三大流程:

  • 解析器
  • 转换器
  • 生成器

pPcbXkj.png

  1. 解析器(Parse) 封装 parse 函数来完成对模板字符串的词法分析和语法分析,得到模板 AST

    const template = `
        <div>
            <h1 class="parse">Vue Template</h1>
        </div>
    `
    const templateAST = parse(template)
    
  2. 转换器(Transform) 封装 transform 函数来完成模板 AST 到 Javascript AST 的转换工作

    const templateAST = parse(template)
    const jsAST = transform(templateAST)
    
  3. 生成器 封装 generate 函数将Javascript AST 生成渲染函数代码

    const templateAST = parse(template)
    const javascriptAST = transform(templateAST)
    const renderCode = generate(javascriptAST)
    

总结

  • 通过 parse 方法进行解析,得到 模板AST
  • 通过 transform 方法对 模板AST 进行转化,得到 JavaScript AST
  • 通过 generate 方法根据 JavaScript AST 生成 render 函数代码

以上就是本次的所有分享,希望您看到这里能有所收获。

才疏学浅,如有讲述不清之处,还请指正,共同进步。