processAttrs
/src/compiler/parser/index.js
/**
* 处理元素上的所有属性:
* v-bind 指令变成:el.attrs 或 el.dynamicAttrs = [{ name, value, start, end, dynamic }, ...],
* 或者是必须使用 props 的属性,变成了 el.props = [{ name, value, start, end, dynamic }, ...]
* v-on 指令变成:el.events 或 el.nativeEvents = { name: [{ value, start, end, modifiers, dynamic }, ...] }
* 其它指令:el.directives = [{name, rawName, value, arg, isDynamicArg, modifier, start, end }, ...]
* 原生属性:el.attrs = [{ name, value, start, end }],或者一些必须使用 props 的属性,变成了:
* el.props = [{ name, value: true, start, end, dynamic }]
*/
function processAttrs(el) {
// list = [{ name, value, start, end }, ...]
const list = el.attrsList
let i, l, name, rawName, value, modifiers, syncGen, isDynamic
for (i = 0, l = list.length; i < l; i++) {
// 属性名
name = rawName = list[i].name
// 属性值
value = list[i].value
if (dirRE.test(name)) {
// 说明该属性是一个指令
// 元素上存在指令,将元素标记动态元素
// mark element as dynamic
el.hasBindings = true
// modifiers,在属性名上解析修饰符,比如 xx.lazy
modifiers = parseModifiers(name.replace(dirRE, ''))
// support .foo shorthand syntax for the .prop modifier
if (process.env.VBIND_PROP_SHORTHAND && propBindRE.test(name)) {
// 为 .props 修饰符支持 .foo 速记写法
(modifiers || (modifiers = {})).prop = true
name = `.` + name.slice(1).replace(modifierRE, '')
} else if (modifiers) {
// 属性中的修饰符去掉,得到一个干净的属性名
name = name.replace(modifierRE, '')
}
if (bindRE.test(name)) { // v-bind, <div :id="test"></div>
// 处理 v-bind 指令属性,最后得到 el.attrs 或者 el.dynamicAttrs = [{ name, value, start, end, dynamic }, ...]
// 属性名,比如:id
name = name.replace(bindRE, '')
// 属性值,比如:test
value = parseFilters(value)
// 是否为动态属性 <div :[id]="test"></div>
isDynamic = dynamicArgRE.test(name)
if (isDynamic) {
// 如果是动态属性,则去掉属性两侧的方括号 []
name = name.slice(1, -1)
}
// 提示,动态属性值不能为空字符串
if (
process.env.NODE_ENV !== 'production' &&
value.trim().length === 0
) {
warn(
`The value for a v-bind expression cannot be empty. Found in "v-bind:${name}"`
)
}
// 存在修饰符
if (modifiers) {
if (modifiers.prop && !isDynamic) {
name = camelize(name)
if (name === 'innerHtml') name = 'innerHTML'
}
if (modifiers.camel && !isDynamic) {
name = camelize(name)
}
// 处理 sync 修饰符
if (modifiers.sync) {
syncGen = genAssignmentCode(value, `$event`)
if (!isDynamic) {
addHandler(
el,
`update:${camelize(name)}`,
syncGen,
null,
false,
warn,
list[i]
)
if (hyphenate(name) !== camelize(name)) {
addHandler(
el,
`update:${hyphenate(name)}`,
syncGen,
null,
false,
warn,
list[i]
)
}
} else {
// handler w/ dynamic event name
addHandler(
el,
`"update:"+(${name})`,
syncGen,
null,
false,
warn,
list[i],
true // dynamic
)
}
}
}
if ((modifiers && modifiers.prop) || (
!el.component && platformMustUseProp(el.tag, el.attrsMap.type, name)
)) {
// 将属性对象添加到 el.props 数组中,表示这些属性必须通过 props 设置
// el.props = [{ name, value, start, end, dynamic }, ...]
addProp(el, name, value, list[i], isDynamic)
} else {
// 将属性添加到 el.attrs 数组或者 el.dynamicAttrs 数组
addAttr(el, name, value, list[i], isDynamic)
}
} else if (onRE.test(name)) { // v-on, 处理事件,<div @click="test"></div>