8.4 class的处理
Vue.js 对
class做了增强:值可以是字符串、对象或数组,因此必须先把值归一化为统一字符串,再用性能最优的方式设置到元素上。
一、三种书写方式
| 方式 | 写法 | 归一化结果 |
|---|---|---|
| 字符串 | <div class="foo bar baz"></div> | 'foo bar baz' |
| 对象 | <div :class="{ foo: true, bar: true, baz: false }"></div> | 'foo bar'(值为 true 的键才生效) |
| 数组 | <div :class="['foo', { bar: true, baz: true }]"></div> | 'foo bar baz' |
对象的键是类名、值是布尔;数组的元素本身又可能是字符串或对象,需要递归处理。
二、归一化:normalizeClass
在设置元素的 class 之前,把值归一化成统一的字符串形式:
const isObject = val => val !== null && typeof val === 'object'
function normalizeClass(value) {
let res = ''
if (typeof value === 'string') {
res = value
} else if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
const normalized = normalizeClass(value[i]) // 递归:数组里可以再嵌字符串 / 对象
if (normalized) {
res += normalized + ' '
}
}
} else if (isObject(value)) {
for (const name in value) {
if (value[name]) {
res += name + ' '
}
}
}
return res.trim()
}
三、用哪种 API 设置 class
浏览器中为元素设置 class 有三种方式:setAttribute、el.className、el.classList。
| 方式 | 说明 |
|---|---|
setAttribute('class', v) | 值会被字符串化,还要走 Attribute 流程 |
el.className = v | 直接写 DOM Properties,实测设置 1000 次 class 性能最优 |
el.classList | 适合增删单个类,整体覆盖不如 className 直接 |
因此 patchProps 里为 class 单独开一个分支:
patchProps(el, key, prevValue, nextValue) {
if (key === 'class') {
// 对 class 进行特殊处理:用 className 代替 setAttribute
el.className = nextValue || ''
} else if (shouldSetAsProps(el, key, nextValue)) {
// ...DOM Properties 分支
} else {
el.setAttribute(key, nextValue)
}
}
归一化值的过程是有代价的:如果需要进行大量的归一化操作,会消耗更多性能。因此编译器会尽量在编译期把静态
class处理好,让运行时少做归一化。style同样被 Vue.js 做了增强,也需要类似处理(patchProps里再加一个分支)。
相关
- 8.3 正确地设置元素属性:
patchProps的整体结构 - 8.5 卸载操作:属性设置告一段落,接下来处理「删除」