上面一张图,已经大概把一个简单模板引擎(这里以
EJS
为例)的原理解释得七七八八了。本文将描述一个简单的模板引擎是怎么运作的?包含实现的关键步骤、以及其背后的思想。
基本上模板引擎的套路也就这样了,但这些思想是通用的,比如你在看vue的模板编译器源码、也可以套用这些思想和方法.
const temp = new Template(` <html> <head><%= title %></head> <body> <%% 转义 %%> <%# 这里是注释 %> <%- before %> <% if (show) { %> <div>root</div> <% } %> </body> </html> `);
temp.compile(); temp.render({ show: true, title: "hello", before: "<div>xx</div>" })
|
the-super-tiny-compiler
启发,实现了一个极简的模板引擎,其实模板引擎本质上也是一个Compiler,通过上文可以了解到一个模板引擎编译有三个步骤:
解析
将模板代码解析成抽象的表示形式。复杂的编译器会有
词法解析(Lexical Analysis)
和
语法解析(Syntactic Analysis)
词法解析
, 上文我们将模板内容解析成token的过程就可以认为是‘词法解析’,它会将源代码拆分称为token数组,token是一个小单元,表示独立的‘语法片段’。
语法解析
,语法解析器接收token数组,将它们重新格式化称为抽象语法树(Abstract Syntax Tree, AST), 抽象语法树可以用于描述语法单元, 以及单元之间的关系。 语法解析阶段可以发现语法问题。
(图片来源:
https://ruslanspivak.com/lsbasi-part7
)
本文介绍的模板引擎,因为语法太简单了,所以不需要AST这个中间表示形式。直接在tokens上进行转换
转换
将上个步骤抽象的表示形式,转换成为编译器想要的。比如上文模板引擎会转换为对应语言的语句。复杂的编译器会基于AST进行‘转换’,也就是对AST进行‘增删查改’. 通常会配合Visitors模式来遍历/访问AST的节点
代码生成
将转换后的抽象表示转换为新的代码。 比如模板引擎最后一步会封装成为一个渲染函数. 复杂的编译器会将AST转换为目标代码
编译器相关的东西确实很有趣,后续有机会可以讲讲怎么编写babel插件。
Ejs源代码
the-super-tiny-compiler
Let’s Build A Simple Interpreter. Part 7: Abstract Syntax Trees