【Vue2.x 源码学习】第十三篇 - 生成 ast 语法树 - 正则说明
2022-03-11 作者: Brave
一,前言
上篇,主要介绍了 vue 数据渲染核心流程,涉及以下几个点:
本篇,生成 ast 语法树-正则说明
二,模板解析
1,模板解析的说明
上文说到,compileToFunction 是 Vue 模板编译的入口:
export function compileToFunction(template) {
// 1,将模板变成 AST 语法树
let ast = parserHTML(template);
// 2,使用 AST 生成 render 函数
let code = generate(ast);
}
compileToFunction主要做了以上两件事:
而将 html 模板编译为 ast 语法树,就是用 js 对象的树形结构来描述 HTML 语法;
这里需要对 html 模板进行解析,而解析的方式就是使用正则不断进行匹配和处理;
2,模板的解析方式
使用正则对 html 模板进行顺序解析和处理
每处理完一段,就将处理完的这部分截取掉
就这样不停的进行解析和截取,直至将整个模板全部解析完毕
abcdefg
开始标签:
abcdefg
文本:abcdefg
开始标签:
结束标签:
结束标签:
可以使用 while 循环,对模板不停截取,直至全部解析完毕
function parserHTML(html) {
while(html){
// todo...
}
}
标签还是文本?
三,正则说明
1,Vue2 相关的正则
// 标签名 a-aaa
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`;
// 命名空间标签 aa:aa-xxx
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
// 开始标签
const startTagOpen = new RegExp(`^<${qnameCapture}`); // 标签开头的正则 捕获的内容是标签名
// 结束标签
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配标签结尾的
// 匹配属性
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;
// 匹配标签结束的 >
const startTagClose = /^\s*(\/?)>/;
// 匹配 {{ }} 表达式
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g;2,匹配标签名
匹配标签名 aa-xxx
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`;
正则解析
测试匹配结果
let reg = new RegExp(ncname);
console.log(reg) // /[a-zA-Z_][\-\.0-9_a-zA-Z]*/
console.log(reg.test('a-aaa')) // true 任意小写字符 a-z,中间有-,后面可以方字符
3,命名空间标签
命名空间标签:aa:aa-xxx
const qnameCapture = `((?:${ncname}\\:)?${ncname})`;
正则解析
正则解析:
(?:${ncname}\\:)?
?: - 表示匹配但是不捕获
后面可以有一个冒号
? 可有可无
如:aa:
${ncname} 标签名
如:aa:aa-xxx 此类命名空间标签使用较少
4,匹配开始标签-开始部分
// 匹配标签名(索引1):
5,匹配结束标签
// 匹配标签名(索引1):
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`); // 匹配标签结尾的
console.log(endTag) // /^<\/((?:[a-zA-Z_][\-\.0-9_a-zA-Z]*\:)?[a-zA-Z_][\-\.0-9_a-zA-Z]*)[^>]*>/
正则解析:
^<\\/ <符号开头,后面跟一个/
${qnameCapture}[^>] 中间可以放很多但不能是>
*> 最后必须要有一个>
// 测试匹配结果:
console.log(''.match(endTag))
[
'',
'aa:aa-xxxdsadsa', // 结束标签的标签名
index: 0,
input: '',
groups: undefined
]
6,匹配属性
// 匹配属性(索引 1 为属性 key、索引 3、4、5 其中一直为属性值):aaa="xxx"、aaa='xxx'、aaa=xxx
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/;
正则解析:
^\s* n个空格开头(0 个或 n 个)
[^\s"'<>\/=]+
^\s 不是空格
^\s"'<>\/= 不是空格,不是尖脚号,不是反引号的 n 个字符
?:\s*(=)\s*
空格和空格之间可以夹一个=等号
?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)
不是空格,可能是单引号、可能是双引号、可能没有引号
// 情况 1:双引号的情况,aaa="xxx"
console.log('aaa="xxx"'.match(attribute))
[
'aaa="xxx"',
'aaa',
'=',
'xxx',
undefined,
undefined,
index: 0,
input: 'aaa="xxx"',
groups: undefined
]
// 此时,索引3是有值的(xxx),4、5是undefined
// 情况 2:单引号的情况,aaa='xxx',会匹配到下一个位置
console.log(`aaa='xxx'`.match(attribute))
[
"aaa='xxx'",
'aaa',
'=',
undefined,
'xxx',
undefined,
index: 0,
input: "aaa='xxx'",
groups: undefined
]
// 此时,会匹配到索引 4,即第二个位置
// 情况 3:没有引号的情况,aaa=xxx,第三个位置就是不带单引号的
console.log('aaa=xxx'.match(attribute))
[
'aaa=xxx',
'aaa',
'=',
undefined,
undefined,
'xxx',
index: 0,
input: 'aaa=xxx',
groups: undefined
]
// 索引3、4是undefined,5 是有值的(xxx),表示匹配到了最后一位
应用:
属性的key:[1]
属性的值:[3]||[4]||[5] 索引 3、4、5 哪个有值取哪个
7,匹配开始标签-闭合部分
// 匹配结束标签:>
const startTagClose = /^\s*(\/?)>/;
正则解析:
^\s* 空格 n 个
(\/?)> 尖角号有以下两种情况
/> 自闭合
> 没有/的闭合
8,匹配 {{ }} 表达式
// 匹配 {{ xxx }} ,匹配到 xxx
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g
四,结尾
又成功水了一篇,还剩 8 篇
本篇,主要介绍了生成 ast 语法树-正则说明部分,涉及以下几个点:
简要说明了 HTML模板的解析方式
对模板解析相关正则说明和测试
下一篇,生成 ast 语法树-代码实现
关于我们
Customize this section to tell your visitors a little bit about your publication,
writers, content, or something else entirely. Totally up to you.