在Vue.js中,仍然有一些属性需要特殊处理,比如class属性。这是因为Vue.js对class属性做了增强。
在Vue.js中为元素设置类名有以下几种方式:
方式一:指定class为一个字符串值
<p class="foo bar"></p>
该模板对应的vnode是:
const vnode = {
type: 'p',
props: {
class: 'foo bar'
}
}
方式二:指定class为一个对象值。
<p :class="cls"></p>
假设对象cls的内容如下:
const cls = {foo:true, bar:false}
其对应的vnode是:
const vnode = {
type: 'p',
props: {
class: {foo:true, bar:false}
}
}
方式三:class是包含上面两种情况的数组
<p :class="arr"></p>
这个数组时字符串值和对象值的组合
const arr = [
// 字符串
'foo bar',
{
baz: true
}
]
其对应的vnode是
const vnode = {
type: 'p',
props: {
class: [
'foo bar',
{ baz: true }
]
}
}
正因为class的值是多种类型的,所以必须在设置元素的class之前将值转化为统一的字符串形式,再把改字符串作为元素的class值去设置。因此需要通过封装一个normalizeClass函数,来达成需求,例如:
const vnode = {
type: 'p',
props: {
// 通过normalizeClass进行序列化
class: normalizeClass([
'foo bar',
{baz:true}
])
}
}
最后等价为
const vnode = {
type: 'p',
props: {
class:'foo bar baz'
}
}
至于normalizeClass函数的实现,后面再做详细介绍。
在获得正常化的class值后,接下来就是如果将其设置到元素上,而在浏览器中为一个元素设置class有三种方式,即setAttribute,el.className和el.classList,而其中className的性能最好,所以要调整patchProps函数,使用className来设置class属性,代码如下:
const renderer = createRenderer({
// 省略其他选项
patchPros(el,key,preValue,nextValue){
if(key==='class'){
el.className = nextValu || ''
}else if(shouldSetAsProps(el,key,nextValue)){
const type = typeof el[key]
if(type === 'boolean' && nextValue=== ''){
el[key] = true
}else{
el[key] = nextValue
}
}else{
el.setAttribute(key, nextValue)
}
}
})
其实除了class属性外,对style属性也做了增强,所以对style也是相似的处理。
通过对class的处理,可以看到vnode.props对象中定义的属性值的类型并不总是和DOM元素属性的数据结构保持一致,这取决于上层API的设计。Vue.js允许对象类型的值作为class是为了方便开发者,在底层的实现上,必须需要对值进行正常化后再使用。