在上篇文章中 ,阐述了对字符串模板的解析,但是核心的start/end/chars函数并没有解析。接下来的2~3篇文章将分析并总结以上函数。
本文将分析start与end函数
start
在解析open标签的时候执行
参数
1. tag:正在解析的标签
2. attrs: 对象数组,如
,在解析前面3. unary,布尔值,表示当前解析的标签是否为自闭和标签如
等
4. start,字符串开始解析的位置
5. end,字符串结束解析的位置
主要逻辑
1. 寻找命名空间,一般元素是没有命名空间的,但是与svg有关的标签会有;如果父元素有命名空间,就使用父元素的命名空间
2. 创建ast节点。createASTElement(tag, attrs, currentParent),currentParent为当前标签的父标签,其实就是内部栈的此元素的上一个元素
3. 创建el.rawAttrsMap属性,将attrs由对象数组转变成对象,便于属性查找
4. 对动态绑定的属性名称进行校验,不合法的会警告
5. 对style/script标签警告
6. preTransforms函数处理
7. 对v-pre指令进行检查
8. 检查是否为pre标签
9. 如果有v-pre指令就只调用 processRawAttrs函数
10. 如果没有v-pre指令,且元素没有处理过就调用processFor,processIf,processOnce
11. 指定根元素,即,第一个解析的元素
12. 如果是非自闭和标签,将currentParent赋值为当前正在解析的元素,并将元素入栈
13. 如果是自闭和标签就调用closeElement函数
注:closeElement函数将单独提出来讲;这里的stack非parseHTML函数内部的stack
v-pre与processRawAttrs
官方文档对v-pre的说明:跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。
刚开始遇到v-pre的时候会标记el.pre为true,然后将全局变量inVPre为true,这样,在随后的解析中,此元素的子节点在编译的时候,inVPre都为true跳过了v-for,v-if,v-once的编译。同时,该元素的所有子元素el.plain也为true。
然后在clolseElement函数中,通过el.pre的值来变更inVpre变量的值。(后面会讲到closeElement)
processFor
官方文档给出了多种v-for的写法。这里其实都是用正则表达式去匹配的。
详见正则分析学习系列文章: https://juejin.cn/post/6887952192368541703
processIf
元素的v-if/v-else-if/v-else都会调用这个函数,处理比较简单,见下方图片注释。
processOnce
为el.once赋值为true
end
参数
1. tag:当前正在 解析的闭合标签
2. start: 字符串开始的位置
3. end:字符串结束的位置
逻辑
1. 元素出栈,即,为当前正在解析的元素
2. 调整栈长度
2. 获取当前解析元素的父元素
4. closeElement
注:这里的stack非parseHTML函数内部的stack
closeElement
colseElement函数,是最终要的函数。在start函数中,仅仅是初步建立了ast元素,处理了一v-for,v-if,v-else-if,v-else,v-once。
主要逻辑
1. 如果函数没有processed标记,就调用processElement,再次处理ast
2. 对根元素的v-if指令进行处理
3. 对元素slotScope属性进行处理,为父元素添加scopedSlots属性,值为当前解析的ast
4. 构建父子关系,即,将currentParent.children数组添加当前元素;将当前元素的parent属性指向currentParent
5. 将当前元素子ast节点中含有scopedSlots属性的节点全部过滤掉
6. postTransforms处理
对v-if/v-else-if/v-else的处理
1. 对根元素的v-if处理
如果根元素有v-if,其子元素有v-else-if或v-else,那就为根元素添加ifConditions属性,气质为带有v-else-if或v-else的子元素数组
2. 对其他元素的v-else/v-else-if处理
- findPrevElement函数通过parent.children数组,找到同级的带有v-if指令的元素
2)为找到元素添加ifConditions属性
对作用域插槽的处理
2. 但是,为了不提提前渲染作用域插槽,就不能在父元素的children中存在带有slotScope属性的元素,因此使用filter过滤
processElement
processKey
为ast节点添加key属性,同时对transition-group组件的子节点的key值进行检测
processRef
processSlotConent/processSlotOutlet
processComponent
对于is属性的处理,会为ast节点添加component属性
processAttrs
主要对v-bind,v-on,v-model,及其他自定义指令进行处理。处v-bind外,之前的文章都分析过了就不在多说了。
最后
1. 在模板编译的时候,最核心的是利用栈结构来保存解析的标签/ast节点,并以此来构建一颗ast树。
2. 对于作用域插槽注意的是在父ast节点添加scopedSlots对象,其值为带有slotScope属性的子ast节点,另外为了防止在父组件编译的时候就编译出作用域插槽的vnode,需要把带有slotScope属性的子ast节点从其父节点的children数组中过滤掉
3. v-pre指令的处理,利用了栈结构,巧妙地对带有v-pre元素的子元素做了处理。
4. 重头戏processAttrs,对各种指令的处理。
下一篇将分析一下chars/comment函数