这是我参与11月更文挑战的第22天,活动详情查看:2021最后一次更文挑战
调用函数generator
生成render
函数时,tag
可直接获取,属性可使用genProps
函数进行处理并返回期望的属性结构,接下来继续完成前文未完成的工作,对子元素的处理
入口函数genChildren
const genChildren = (el) => {
// 获取子节点数
let children = el.children;
if (children && children.length) { // 存在子节点
// 遍历所有子节点,并将结果以,拼接为字符串
return `${children.map((c) => gen(c)).join(",")}`;
} else { // 不存在子节点
return false;
}
};
重点就是在gen
函数中,如何
const gen = (node) => {
if (node.type === 1) { // 标签节点
return generator(node);
} else if (node.type === 3) { // 注释节点
return genComment(node)
} else { // 文本节点
return genText(node);
}
};
若是元素节点,递归调用generator
即可。若是注释节点,直接调用genComment
函数
const genComment = (comment) => {
return `_e(${JSON.stringify(comment.text)})`;
}
最为复杂的是当其是文本节点时,因为在Vue
中文本可以是<p>hello nordon</p>
这类纯文案内容填充,也可以是<p>{{ name }}</p>
这类纯表达式内容填充,也可以是<p>hello {{ name }}</p>
这类两者的混搭使用
首先看一下如何匹配{{}}
的正则表达式
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g;
如何利用上面的正则将下面的字符串
const str = 'my is {{ name }}, age is {{ age }}';
转换为
_v("'myis"+_s(name)+",ageis"+_s(age)+"'")
上述这个过程便是genText
函数的核心:利用正则的exec
进行递归匹配
连续调用两次exec
defaultTagRE.exec(str);
第一次调用控制台输出结果:
const fitstExec = [
0: "{{ name }}"
1: " name "
groups: undefined
index: 6
input: "my is {{ name }}, age is {{ age }}"
]
可以得到fitstExec[index]
是正则匹配到第一个{
的索引位置,其之前的数据便是需要的静态文本内容,fitstExec[1]
则是需要使用_s
处理的key
,此时将fitstExec[0]
的长度和fitstExec[index]
相加并且将两者和记录
const firstEndIndex = fitstExec[0].length + fitstExec[index];
这里可以得到myis
和_s(name)
第二次调用控制台输出结果:
const secondExec = [
0: "{{ age }}"
1: " age "
groups: undefined
index: 25
input: "my is {{ name }}, age is {{ age }}"
]
可以得到firstEndIndex
至secondExec[index]
之间的内容为静态文案内容,secondExec[1]
则是需要使用_s
处理的key
可以得到,ageis
和_s(age)
将两次得到的结果以+
进行拼接得到"'myis"+_s(name)+",ageis"+_s(age)+"'"
上述的过程便是genText
函数的核心逻辑,下面通过代码将其实现
const genText = (node) => {
let text = node.text; // 记录全部的文本信息
let tokens = []; // 用于存储每一次匹配到的数据
let match = null; // 增则匹配结果
let index = null; // 第一个匹配到的 { 位置
let lastIndex = (defaultTagRE.lastIndex = 0); // 每次匹配结束,下次开始的位置
// 递归匹配,直到匹配不到 {{
while ((match = defaultTagRE.exec(text))) {
index = match.index;
if (index > lastIndex) {
// 将两次匹配之间的纯文本信息存储
tokens.push(JSON.stringify(text.slice(lastIndex, index)));
}
// 将 {{ }} 之间的内容进行处理再存储
tokens.push(`_s(${match[1].trim()})`);
// 将每次匹配结束的位置记录
lastIndex = index + match[0].length;
}
// 匹配结束之后,若是最后的位置小于文本的长度,则代表 }} 后面还有纯文本信息,需要继续存储
if (lastIndex < text.length) {
tokens.push(JSON.stringify(text.slice(lastIndex)));
}
return `_v(${tokens.join("+")})`;
}
自此通过AST
生成render
函数的简洁版本便完成了,可以返回期望的数据格式