这是我参与更文挑战的第12天,活动详情查看: 更文挑战。
前言
代码生成器是模板编译的最后一步,它的作用是将 AST 转换成渲染函数中的内容,这个内容可以称为代码字符串。
<div id="el">Hello {{name}}</div>
上面的模板,最终生成的代码字符串类似这样:
`with(this){return _c("div", {attrs: {"id": "el"}}, [_v("Hello " _s(name))])}`
代码字符串中的 _c
其实是 createElement
的别名。createElement 是虚拟DOM中所提供的方法,它的作用是创建虚拟节点,有三个参数,分别是:
- 标签名
- 一个包含模板相关属性的数据对象
- 子节点列表
调用 createElement
方法,我们可以得到一个 VNode
。
通过AST生成代码字符串
生成代码字符串是一个递归的过程,从顶向下依次处理每一个 AST 节点。
代码生成器的原理
节点有不同的类型,例如元素节点
、文本节点
和注释节点
。不同类型节点的生成方式是不一样的。
元素节点
生成元素节点,其实就是生成一个 _c的函数调用字符串
function genElement(el, state){
const data = el.plain ? undefined : genData(el, state);
const children = genChildren(el, state);
code = `_c('${el.tag}'${
data ? `, ${data}` : ''
}${
children ? `,${children}`:''
})`
return code;
}
文本节点
生成文本节点很简单,我们只需要把文本放在 _v这个函数的参数中即可
function genText(text){
return `_v(${text.type === 2 ? text.expression : JSON.stringify(text.text)})`
}
为什么text需要使用JSON.stringify方法?
因为JSON.stringify可以给文本包装一层字符串,例如:
JSON.stringify('Hello') // "'Hello'"
注释节点
function genComment(comment){
return `_e(${JSON.stringify(comment.text)})`
}
总结
本文,我们学习了代码生成器的作用及其内部原理,了解了代码生成器其实就是字符串拼接的过程。通过递归 AST 来生成字符串,最先生成根节点,然后在子节点字符串生成后,将其拼接在根节点的参数中,子节点的子节点拼接在子节点的参数中,这样一层一层地拼接,直到最后拼接成完整的字符串。