16.7 解析插值与注释
插值
{{ }}与注释<!-- -->的解析思路一模一样:消费开始定界符 → 找到结束定界符 → 截取中间内容 → 消费内容与结束定界符。
一、文本插值
- 文本插值是 Vue.js 模板中渲染动态数据的常用方法;
- 默认以字符串
{{开头、以}}结尾; - 解析时只需把开始定界符与结束定界符之间的内容提取出来,作为 JavaScript 表达式即可。
function parseInterpolation(context) {
// 消费开始定界符
context.advanceBy('{{'.length)
// 找到结束定界符的位置索引
const closeIndex = context.source.indexOf('}}')
if (closeIndex < 0) {
console.error('插值缺少结束定界符')
}
// 截取定界符之间的内容作为插值表达式
const content = context.source.slice(0, closeIndex)
context.advanceBy(content.length) // 消费表达式
context.advanceBy('}}'.length) // 消费结束定界符
return {
type: 'Interpolation',
content: {
type: 'Expression',
// 表达式内容同样需要 HTML 解码
content: decodeHtml(content)
}
}
}
- 插值节点的类型是
Interpolation,它的content是一个类型为Expression的表达式节点; - 表达式内容仍要经过
decodeHtml,这样用户写在插值里的 HTML 实体才能与浏览器行为一致。
二、注释
- 解析注释的思路与解析插值非常相似:
<!--是开始定界符,-->是结束定界符。
function parseComment(context) {
// 消费注释的开始部分
context.advanceBy('<!--'.length)
// 找到注释结束部分的位置索引
const closeIndex = context.source.indexOf('-->')
// 截取注释节点的内容
const content = context.source.slice(0, closeIndex)
context.advanceBy(content.length) // 消费内容
context.advanceBy('-->'.length) // 消费结束部分
return {
type: 'Comment',
content
}
}
- 注释节点的 type 是
Comment,内容直接存放在content中,不做decodeHtml。
三、二者对照
| 项 | 插值 | 注释 |
|---|---|---|
| 开始定界符 | {{ | <!-- |
| 结束定界符 | }} | --> |
| 节点 type | Interpolation(内嵌 Expression 子节点) | Comment |
| 内容是否解码实体 | 是(decodeHtml) | 否 |
测试:
const ast = parse(`<div>{{ bar }}</div>`)
const astC = parse(`<div><!-- comments content --></div>`)
console.log(ast, astC)
易错点:
{{只在 DATA / RCDATA 模式下才会被识别为插值;在 RAWTEXT / CDATA 模式下它只是普通文本。
相关
- 16.6.2 解码命名字符引用:插值表达式内容调用的
decodeHtml - 16.6.1 解析文本:文本为什么要在
{{处截断 - 16.2 递归下降算法构造模板 AST:插值与注释在状态机中的分支