Vuejs设计与实现9-编译器核心

十二、编译器核心

模板 DSL 编译器

vuejs 编译流程

  1. 模板编译器对 HTML 源码进行词法分析获得模板 AST
  2. 模板 AST 转换成 Javascript AST
  3. 根据 JS AST 生成对应 JS 代码

AST 即抽象语法书,下面展示了一个简单的 AST
即根节点下包裹一个 div 节点

// type:定义节点类型
// children:定义子节点
// tag:定义标签类型
// props:定义标签对应的属性节点
// name:指令节点特有,表示指令名称
// content:表达式节点特有,表示表达式内容

const exp = {
    
    
  type: "Root",
  children: [
    {
    
    
      type: "Element",
      tag: "div",
    },
  ],
};

对应解析函数

parse 函数:解析字符串模板为模板 AST
transform 函数:模板 AST 转换成 Javascript AST
generate 函数:JS AST 转换为 JS 代码


有限状态机

解析器 parser 的有限状态自动机

解析器会将字符串模板划分为多个 token
譬如 <p>1</p> 会被划分为 3 个 token: <p> & 1 & </p>

有限状态自动机可以理解为:在有限个状态内自动进行状态转移,下图就是 parser 的分析图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qC2d4oPW-1673567813480)(…/imgs/vue/vuejs_optimize/vp1.png)]


解析结果

使用 tokenize 函数(源码过长,此处不贴出)获取字符串模板的所有 token

下方代码表示对一段字符串模板进行解析后得到的 3 个 token

正则表达式的本质就是有限自动机

const tokens = tokenize(`<p>Vue</p>`);
// [
//   { type: 'tag', name: 'p' },        // 开始标签
//   { type: 'text', content: 'Vue' },  // 文本节点
//   { type: 'tagEnd', name: 'p' }      // 结束标签
// ]

构造 AST

引用 通用用途语言 GPL 中的 梯度下降算法 来实现 DSL
(因为 vuejs 无运算符优先级,故无需考虑)


构建过程

主要有三个元素,其中 elementStack 用来维护一个栈,栈中存放 token,之后出栈就完美的构成 AST

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IQGoL6Ka-1673567813484)(…/imgs/vue/vuejs_optimize/vp2.png)]


AST 转换

AST 的转换,指的是对 AST 进行一系列操作,将其转换为新的 AST 的过程(用于适配不同语言)

使用 DFS 来替换 AST 中的指定标签,实现 AST 转换的功能!
该替换操作可以解耦为以外部函数,每次都使用对应函数来处理替换、增删功能


转换标签节点的代码,必须要卸载退出阶段的回调函数内;
这样可以保证所有子节点处理完毕后才开始转换!


AST 需要转换成 Javascript AST

转换完成后即可使用 render 渲染该 AST


代码生成

本节实现 generate 代码,将 Javascript AST 生成对应的 JS 代码


基本原理

这是 generate 函数运作的基本流程

  1. context 表示上下文,在里面处理文本
  2. genNode 用来生成 JS,调用上下文 context

可以在 context 对象内部添加一些文本处理函数,譬如 添加缩进indent() 或者 换行newline()

function generate(node) {
    
    
  const context = {
    
    
    // 存储最终生成的渲染函数代码
    code: "",
    // 在生成代码时,通过调用 push 函数完成代码的拼接
    push(code) {
    
    
      context.code += code;
    },
  };
  // 调用 genNode 函数完成代码生成的工作,
  genNode(node, context);
  // 返回渲染函数代码
  return context.code;
}

genNode

很简单,使用 switch 匹配不同的 JavascriptAST 节点,并使用对应生成函数即可


猜你喜欢

转载自blog.csdn.net/delete_you/article/details/128668897
今日推荐