好家伙,书接上回
在上一篇Vue源码学习(二):
现在我们继续,将模板解析的转换为ast语法树
1.前情提要
代码已开源https://github.com/Fattiger4399/analytic-vue.git手动调试一遍,
胜过我解释给你听一万遍
function start(tag, attrs) { //开始标签
console.log(tag, attrs, '开始的标签')
}
function charts(text) { //获取文本
console.log(text, '文本')
}
function end(tag) { //结束的标签
console.log(tag, '结束标签')
}
在这里,我们知道start,charts,end分别可以拿到
我们的`开始标签`,`文本`,`结束标签`
效果如下:(仔细看,这也是我们实验要用到的例子)
随后我们开始改造这几个方法
2.代码详解
2.1.ast树节点的结构
确定我们ast树节点的结构:
let root; //根元素
let createParent //当前元素的父亲
let stack = []
function createASTElement(tag, attrs) {
return {
tag,
attrs,
children: [],
type: 1,
parent: null
}
}
节点元素分别为
- tag:标签名
- attrs:标签属性
- children:子元素(数组)
- type:类型(后面会用到,目前"1"代表标签"3"代表文本)
- parent:父元素
2.2.start()方法
function start(tag, attrs) { //开始标签
let element = createASTElement(tag, attrs) //生成一个开始标签元素
//查看root根元素是否为空
//若是,将该元素作为根
//非原则
if (!root) {
root = element
}
createParent = element
stack.push(element)
console.log(tag, attrs, '开始的标签')
}
此处,生成一个开始标签元素,判断root是否为空,若为空,则将该元素作为根元素
随后将该元素作为父元素.
2.3.charts()方法
function charts(text) { //获取文本
console.log(text, '文本')
// text = text.replace(/a/g,'')
if(text){
createParent.children.push({
type:3,
text
})
}
// console.log(stack,'stack')
}
这个好理解,将"文本内容"作为父元素的孩子
2.4.end()方法
function end(tag) { //结束的标签
let element = stack.pop()
createParent = stack[stack.length - 1]
if (createParent) { //元素闭合
element.parent = createParent.tag
createParent.children.push(element)
}
console.log(tag, '结束标签')
}
此处,我们先将栈stack最新的元素弹出栈(作为当前元素,我们要对他进行操作),
随后获取栈的前一个元素作为父元素,
当前元素的父元素属性指向父元素的标签属性
随后将该元素推入父元素的children中,
emmmm,我还是说人话吧
假设现在stack=['div','h1']
然后pop了,createParent = 'h1'
'h1'.parent =>'div'
'div'.children =>'h1'
(多看几遍就理解了,其实非常简单)
来看看最终实现的ast语法树长什么样子
(父子关系和谐)
搞定啦!
3.完整代码
const attribute =
/^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
//属性 例如: {id=app}
const ncname = `[a-zA-Z_][\\-\\.0-9_a-zA-Z]*`; //标签名称
const qnameCapture = `((?:${ncname}\\:)?${ncname})` //
const startTagOpen = new RegExp(`^<${qnameCapture}`) //标签开头
const startTagClose = /^\s*(\/?)>/ //匹配结束标签 的 >
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`) //结束标签 例如