鉴于篇幅的原因,本章将继承上一章的内容,继续讲解 AST
的生成。
接下来我们要讲解的就是 processElement
函数中调用的最后一个 process*
函数,它就是 processAttrs
函数,这个函数是用来处理元素描述对象的 el.attrsList
数组中剩余的所有属性的。到目前为止我们已经讲解过的属性有:
v-pre
v-for
v-if
、v-else-if
、v-else
v-once
key
ref
slot
、slot-scope
、scope
、name
is
、inline-template
以上这些属性的解析我们已经全部讲解过了,我们能够发现一些规律,比如在获取这些属性的值的时候,要么使用 getAndRemoveAttr
函数,要么就使用 getBindingAttr
函数,但是无论使用哪个函数,其共同的行为是:在获取到特定属性值的同时,还会将该属性从 el.attrsList
数组中移除。所以在调用 processAttrs
函数的时候,以上列出来的属性都已经从 el.attrsList
数组中移除了。但是 el.attrsList
数组中仍然可能存在其他属性,所以这个时候就需要使用 processAttrs
函数处理 el.attrsList
数组中剩余的属性。
在讲解 processAttrs
函数之前,我们来回顾一下现在我们掌握的知识。以如上列出的属性为例,下表中总结了特定的属性与获取该属性值的方式:
属性 | 获取属性值的方式 |
---|---|
v-pre |
getAndRemoveAttr |
v-for |
getAndRemoveAttr |
v-if 、v-else-if 、v-else |
getAndRemoveAttr |
v-once |
getAndRemoveAttr |
key |
getBindingAttr |
ref |
getBindingAttr |
name |
getBindingAttr |
slot-scope 、scope |
getAndRemoveAttr |
slot |
getBindingAttr |
is |
getBindingAttr |
inline-template |
getAndRemoveAttr |
我们发现凡是以 v-
开头的属性,在获取属性值的时候都是通过 getAndRemoveAttr
函数获取的。而对于没有 v-
开头的特性,如 key
、ref
等,在获取这些属性的值时,是通过 getBindingAttr
函数获取的,不过 slot-scope
、scope
和 inline-template
这三个属性虽然没有以 v-
开头,但仍然使用 getAndRemoveAttr
函数获取其属性值。但这并不是关键,关键的是我们要知道使用 getAndRemoveAttr
和 getBindingAttr
这两个函数获取属性值的时候到底有什么区别。
我们知道类似于 v-for
或 v-if
这类以 v-
开头的属性,在 Vue
中我们称之为指令,并且这些属性的属性值是默认情况下被当做表达式处理的,比如:
<div v-if="a && b"></div>
如上代码在执行的时候 a
和 b
都会被当做变量,并且 a && b
是具有完整意义的表达式,而非普通字符串。并且在解析阶段,如上 div
标签的元素描述对象的 el.attrsList
属性将是如下数组:
el.attrsList = [
{
name: 'v-if',
value: 'a && b'
}
]
这时,当使用 getAndRemoveAttr
函数获取 v-if
属性值时,得到的就是字符串 'a && b'
,但不要忘了这个字符串最终是要运行在 new Function()
函数中的,假设是如下代码:
new Function('a && b')
那么这句代码等价于:
function () {
a && b
}
可以看到,此时的 a && b
已经不再是普通字符串了,而是表达式。
这就意味着 slot-scope
、scope
和 inline-template
这三个属性的值,最终也将会被作为表达式处理,而非普通字符串。如下:
<div slot-scope="slotProps"></div>
如上代码是使用作用域插槽的典型例子,我们知道这里的 slotProps
确实是变量,而非字符串。
那如果使用 getBindingAttr
函数获取 slot-scope
属性的值会产生什么效果呢?由于 slot-scope
没有并非 v-bind:slot-scope
或 :slot-scope
,所以在使用 getBindingAttr
函数获取 slot-scope
属性值的时候,将会得到使用 JSON.stringify
函数处理后的结果,即:
JSON.stringify('slotProps')
这个值就是字符串 '"slotProps"'
,我们把这个字符串拿到 new Function()
中,如下:
new Function('"slotProps"')
如上这句代码等价于:
function () {
"slotProps"
}
可以发现此时函数体内只有一个字符串 "slotProps"
,而非变量。
但并不是说使用了 getBindingAttr
函数获取的属性值最终都是字符串,如果该属性是绑定的属性(使用 v-bind
或 :
),则该属性的值仍然具有 javascript
语言的能力。否则该属性的值就是一个普通的字符串。