生成ast对象其实就是编译我们写的模板代码
就是对这段类html代码进行编译,用对象方式{...}描述模板内容,因为我们写的模板是树形结构的,所以我们生产的ast对象也是树状的
其编译的核心就是用到parseChildren这个函数
顾名思义parseChildren,编译子元素,类似这样
<div><p></p></div>
这个函数就会编译p标签。那问题来了如果是这样呢
<div><p><span></span></p></div>
p标签里面又包含了span标签,那么就会在parseChildren里面再次调用parseChildren渲染孙子节点,编译的过程其实就是递归调用parseChildren的过程,也就是编译时发现这个元素包含子元素那么就先去编译子元素,但是我们要知道当前编译到第几层了,这个函数的第三个参数就能告诉我们
ancestors是一个数组,当每次发现有子元素时就会把当前元素push到ancestors,举个例子,假设我们模板是这样的
当递归进去parseChildren六次后发现ancestors变成这样了,包含了所有父节点
需要编译模板需要模板字符串,这个字符串就放在第一个参数context里面
因为子节点会有多种类型,有普通的元素标签类型、文本类型、注释类型等,所以第二个参数mode就是区分这些类型的。 我们来看看parseChildren具体的实现,定位到源码这个位置:
一开始定义了3个变量parent、ns、nodes 前面说了ancestors是包含了所有父节点的数组,调用last
可以里看出就是获取数组最后一个值,也就是当前渲染节点的上一层节点,就是父节点
ns是渲染的类型
0代表HTML类型
1代表SVG类型
2代表MATH_ML类型
nodes初始化为一个空数组,用来存放当下面while循环生成的node 这个函数的核心就是里面的while循环,其原理就是用正则表达式切割类html的模板字符串,例如:
<div>aaa<p>bbb</p>ccc</div>
就会先正则识别出前面的div标签,然后切割成这样
aaa<p>bbb</p>ccc</div>
再渲染文本节点aaa,之后变成这样
<p>bbb</p>ccc</div>以此类推
bbb</p>ccc</div>
</p>ccc</div>
ccc</div>
</div>
直到</div>就把整个模板渲染完了
我们看看while循环里面的具体实现,源码定位到这:
首先通过isEnd判断渲染的节点是否已经结束了
然后定义了s、node
s就是渲染的模板
node就是当前一次while循环生成的子节点,可能会有多个
继续往下看,这是第一个if判断
if (mode === 0 /* DATA / || mode === 1 / RCDATA */)
首先看看mode的值代表什么
0代表普通的html标签
1代表textarea和title标签
2代表style,iframe,script,noscript标签
3代表以<![CDATA[开始的标签,xml
style,iframe,script,noscript好理解,那为什么textarea和title比较特殊呢
下面是所有的html标签
只有textarea和title是非自闭合里面不能包含子标签的(可以包含文本标签)
继续往下走
if (!context.inVPre && startsWith(s, context.options.delimiters[0]))
判断是否使用了v-pre指令并且是否已{{开头
startsWith其实内部就是调用了原生的startsWith
如果是就通过parseInterpolation处理里面的表达式
node = parseInterpolation(context, mode);
否则
else if (mode === 0 /* DATA */ && s[0] === '<')
判断第一个字符串是否等于<,是的话有下面六种情况
1:只包含’<’这个字符,报错
4:匹配到是标签开头,以parseElement处理
5:第二个字符串是‘?’,报错并当注释处理,parseBogusComment处理注释节点
其实浏览器默认也是这样处理的
6:以上都不满足则直接报错
下面看第二种情况:第二个字符为‘!’
如果以’<!--’开头则parseComment处理注释节点
如果以’
如果以’ <![CDATA[’开头则再判断类型如果不是html则parseCDATA处理xml,否则报错并且parseBogusComment处理
以上情况都不是则直接报错,并且parseBogusComment处理
第三种情况:第二个字符为‘/’
如果只有两个字符则报错
如果第三个字符是’>’则报错并且模板往后移3位,也就是前面说的切割模板
通过slice截取再复制回去,advancePositionWithMutation函数先不管,后面会详细说下
切割后直接continue进入下一次while循环
以上情况都不是则直接报错,并且parseBogusComment处理
继续往下走
如果到这一步还没生成node,就把它当作文本节点处理,处理函数parseText
如果生成了node就push到nodes,如果是多个node则循环一下再push进去
以上就是整个while循环的过程,可以看出其实就是渲染下面几种节点:
1、表达式({{}}里面的内容)处理函数parseInterpolation
2、注释节点 处理函数parseComment
3、异常节点 处理函数parseBogusComment
4、xml节点 处理函数parseCDATA
5、标签节点 处理函数parseElement
6、文本节点 处理函数parseText
下面我们逐个过下