天天看点

#yyds干货盘点#——如何通俗理解AST抽象语法树(一)

       在用vue开发项目的过程中,生命周期里有mounted这一环节,mounted方法是把template会被编译成AST语法树。说AST之前,要说一说编译器,编译器的作用是把源代码编译成目标代码,源代码是什么呢? 用vue开发的话,源代码就是.vue文件,目标代码就是可被浏览器执行的.js文件。编译器的概念很多,大致上分为词法分析、语法分析(句法分析)、类型检查/推导,代码优化,代码生成...等等,编译器是大学要学的一门课。如果只讲编译器是很枯燥的,大部分人看了一部分后就看不下去了,通过vue代码能够直观的理解编译器的工作方式,vue的编译器和大部分编译器一样,大致也分为三个阶段,即:词法分析 -> 句法分析 -> 代码生成。

       在词法分析阶段vue会把字符串模板解析成一个个的令牌(token),该令牌将用于句法分析阶段,在句法分析阶段会根据令牌生成一棵AST,最后再根据该 AST生成最终的渲染函数,这样就完成了代码的生成。vue模板的解析基本分两步:

1、在mounted阶段,执行compile方法将template里的内容转化成html。

2、compile过程分三步:parse,optimize,generate。

今天先分析一下compile方法是如何来解析template模板的,也就是compile的parse阶段。

​compile 的作用是解析模板,生成渲染模板的 render:

比如说这样的模板:

<div>
   <span></span>
</div>      

经过compile之后,编译成render函数

_c('div', [_c('span')])      

而render的作用是生成如下的模板节点:

{    

    tag: "div",    

    children:[{        

        tag: "span",        

        text: undefined

    }]
}      

​parse基础解读

对模板进行解析,生成AST,意思就是通过json键值对形式,把template用树这样的数据结构表示出来。

比如上面的例子:

<div>
   <span class="list"></span>
</div>      

生成的 AST 是这样,所有模板中出现的数据,你都可以在 AST 中找到

{    

    tag: "div",      

    children:[{        

        tag: "span",        

        children: [],        

        attrsMap: {class: "list"}

    }]
}      

AST就是用数据的方式来描述事物。

parse详细分析

​parse 是 渲染三巨头的老大,其作用是把 template 字符串模板,转换成 AST,关于AST的概念,我也查了很多资料,主要是以树状结构描述语法结构,什么意思呢?

比如:

<div>
    <span>123</span>
</div>      

这是一个带有表达式的模板,转化起来相当简单。

AST={
  tag:'div', 
  type: 1,
  children:[{
    tag: 'span',
    type: 1,
    children:[{
      type:3,
      text: '123'
    }]
  }]
}      

这个很简单吧,type是描述节点的类型,它有三个可取值,分别是 1、2、3,分别代表的含义是:

1:代表当前节点类型为标签,例如:div、span、li

2:包含字面量表达式的文本节点,例如: {{info}}

3:普通文本节点或注释节点,例如:"周笔畅是男的"

parse源码分析

function parse(template) {    
    var stack = []; // 缓存模板中解析的每个节点的 ast
    var root;   // 根节点,是 ast
    var currentParent; // 当前解析的标签的父节点
    /**
    * parseHTML 处理 template 匹配标签,再传入 start,end,chars 等方法
    **/    
    parseHTML(template, {        
        start: (..被抽出,在后面)
        end: (..被抽出,在后面), // 为 起始标签 开启闭合节点
        chars: (..被抽出,在后面) // 文字节点    
    });    
    return root
}      

继续阅读