16.6.1 解析文本
parseText 要在模板中找出「下一个
<」和「下一个{{」,取更靠前的那个作为文本的终点——因为两者的出现顺序是未知的。
一、文本节点是怎么被识别出来的
- 状态机读取到某个字符时,如果它既不是
<也不是插值定界符{{,状态机就进入文本状态,即调用parseText处理文本内容; - 解析器会在模板中寻找下一个
<或插值定界符{{的位置索引,记为索引 I; - 从模板头部截取到索引 I 的内容,就是文本节点的内容。
二、为什么两者都要找
- 字符
<与定界符{{的出现顺序是未知的:<div>hello</div>:先遇到<;<div>{{ bar }}</div>:先遇到{{;
- 因此必须取两者中较小的一个作为文本截取的终点。
三、实现
function parseText(context) {
// endIndex 默认为整个剩余内容
let endIndex = context.source.length
// 寻找字符 < 的位置索引
const ltIndex = context.source.indexOf('<')
// 寻找定界符 {{ 的位置索引
const delimiterIndex = context.source.indexOf('{{')
// 取更靠前的那个作为新的结尾索引
if (ltIndex > -1 && ltIndex < endIndex) {
endIndex = ltIndex
}
if (delimiterIndex > -1 && delimiterIndex < endIndex) {
endIndex = delimiterIndex
}
const content = context.source.slice(0, endIndex)
context.advanceBy(content.length)
return {
type: 'Text',
content // 后续接入 decodeHtml(content),见 16.6.2
}
}
- 文本节点类型是
Text,内容存放在content中; - 返回前必须调用
advanceBy消费这段文本,否则外层while不会推进。
易错点:只找
<不找{{,插值就会被整段吞进文本节点,插值节点永远不会被解析出来。
相关
- 16.6.2 解码命名字符引用:文本内容要经过
decodeHtml解码 HTML 实体 - 16.7 解析插值与注释:
{{ }}被识别为插值后的解析过程 - 16.2 递归下降算法构造模板 AST:
parseText在状态机中的位置