You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// note: this only removes the attr from the Array (attrsList) so that it// doesn't get processed by processAttrs.// By default it does NOT remove it from the map (attrsMap) because the map is// needed during codegen.exportfunctiongetAndRemoveAttr(el: ASTElement,name: string,removeFromMap?: boolean): ?string{letvalif((val=el.attrsMap[name])!=null){constlist=el.attrsListfor(leti=0,l=list.length;i<l;i++){if(list[i].name===name){list.splice(i,1)// 从attrsList删除一个属性,不会从attrsMap删除break}}}if(removeFromMap){deleteel.attrsMap[name]}return val
}
如何获取v-bind的值
以下面代码为例从源码分析vue是如何获取v-bind的值。
会从记下几个场景去分析:
常见的key属性
绑定一个普通html attribute:title
绑定class和style
绑定一个html DOM property:textContent
vBind:{key: +newDate(),title: "This is a HTML attribute v-bind",class: "{ borderRadius: isBorderRadius }"style: "{ minHeight: 100 + 'px' , maxHeight}"text-content: "hello vue v-bind"}
如果你写过vue,对v-bind这个指令一定不陌生。
下面我将从源码层面去带大家剖析一下v-bind背后的原理。
会从以下几个方面去探索:
v-bind关键源码分析
v-bind化的属性统一存储在哪里:attrsMap与attrsList
假设为p标签v-bind化了title属性,我们来分析title属性在vue中是如何被处理的。
vue在拿到这个html标签之后,处理title属性,会做以下几步:
createASTElement(... ,attrs, ...)
至于创建之后是如何处理v-bind:title这种普通的属性值的,可以在下文的v-bind:src源码分析中一探究竟。
解析HTML,解析出属性集合attrs,在start回调中返回
在start回调中创建ASTElement,
createASTElement(... ,attrs, ...)
创建后ASTElement会生成attrsList和attrsMap
attrs的数据类型定义
绑定属性获取函数 getBindingAttr 和 属性操作函数 getAndRemoveAttr
getBindingAttr及其子函数getAndRemoveAttr在处理特定场景下的v-bind十分有用,也就是”v-bind如何处理不同的绑定属性“章节很有用。
这里将其列举出来供下文
v-bind:key源码分析;v-bind:src源码分析;v-bind:class源码分析;v-bind:style源码分析;v-bind:dataset.prop源码分析
源码分析参照。如何获取v-bind的值
以下面代码为例从源码分析vue是如何获取v-bind的值。
会从记下几个场景去分析:
v-bind:key源码分析
processKey函数中用到了getBindingAttr函数,由于我们用的是v-bind,没有用
:
,所以const dynamicValue = getAndRemoveAttr(el, 'v-bind:'+'key');
,getAndRemoveAttr(el, 'v-bind:key')函数到attrsMap中判断是否存在'v-bind:key',取这个属性的值赋为val并从从attrsList删除,但是不会从attrsMap删除,最后将'v-bind:key'的值,也就是val作为dynamicValue,之后再返回解析过滤后的结果,最后将结果set为processKey中将元素的key property。然后存储在segments中,至于segments是什么,在上面的源码中可以看到。v-bind:title源码分析
title是一种“非vue特殊的”也就是普通的HTML attribute。
通过阅读源码我们看出:对于原生的属性,比如title这样的属性,vue会首先解析出name和value,然后再进行一系列的是否有modifiers的判断(modifier的部分在下文中会详细讲解),最终向更新ASTElement的attrs,从而attrsList和attrsMap也同步更新。
v-bind:class源码分析
css的class在前端开发的展现层面,是非常重要的一层。
因此vue在对于class属性也做了很多特殊的处理。
在transfromNode函数中,会通过getAndRemoveAttr得到静态class,也就是
class="foo"
;在getBindingAttr得到绑定的class,也就是v-bind:class="vBind.class"
即v-bind:class="{ borderRadius: isBorderRadius }"
,将ASTElement的classBinding赋值为我们绑定的属性供后续使用。v-bind:style源码分析
style是直接操作样式的优先级仅次于important,比class更加直观的操作样式的一个HTML attribute。
vue对这个属性也做了特殊的处理。
在transfromNode函数中,会通过getAndRemoveAttr得到静态style,也就是
style="{fontSize: '12px'}"
;在getBindingAttr得到绑定的style,也就是v-bind:style="vBind.style"
即v-bind:class={ minHeight: 100 + 'px' , maxHeight}"
,其中maxHeight是一个变量,将ASTElement的styleBinding赋值为我们绑定的属性供后续使用。v-bind:text-content.prop源码分析
textContent是DOM对象的原生属性,所以可以通过prop进行标识。
如果我们想对某个DOM prop直接通过vue进行set,可以在DOM节点上做修改。
下面我们来看源码。
通过上面的源码我们可以看出,
v-bind:text-content.prop
中的text-content首先被驼峰化为textContent(这是因为DOM property都是驼峰的格式),vue还对innerHtml错误写法做了兼容也是有心,之后再通过prop标识符,将textContent属性增加到ASTElement的props中,而这里的props本质上也是一个ASTAttr。有一个很值得思考的问题:为什么要这么做?与HTML attribute有何异同?
v-bind:title.attr,v-bind:text-content.prop
只不过vue默许不加修饰符的就是HTML attribute罢了v-bind的修饰符.camel .sync源码分析
.camel仅仅是驼峰化,很简单。
但是.sync就不是这么简单了,它会扩展成一个更新父组件绑定值的v-on侦听器。
其实刚开始看到这个.sync修饰符我是一脸懵逼的,但是仔细阅读一下组件的.sync再结合实际工作,就会发现它的强大了。
在vue中,父组件向子组件传递的props是无法被子组件直接通过
this.props.foo = newFoo
去修改的。除非我们在组件
this.$emit("updateFoo", newFoo)
,然后在父组件使用v-on做事件监听updateFoo事件。若是想要可读性更好,可以在$emit的name上改为update:foo,然后v-on:update:foo。有没有一种更加简洁的写法呢???
那就是我们这里的.sync操作符。
可以简写为:
然后在子组件通过
this.$emit("update:foo", newFoo);
去触发,注意这里的事件名必须是update:xxx的格式,因为在vue的源码中,使用.sync修饰符的属性,会自定生成一个v-on:update:xxx的监听。下面我们来看源码:
通过阅读源码我们可以看到:
对于v-bind:foo.sync的属性,vue会判断属性是否为动态属性。
若不是动态属性,首先为其增加驼峰化后的监听,然后再为其增加一个连字符的监听,例如v-bind:foo-bar.sync,首先v-on:update:fooBar,然后v-on:update:foo-bar。v-on监听是通过addHandler加上的。
若是动态属性,就不驼峰化也不连字符化了,通过
addHandler(el,
update:${name}, ...)
,老老实实监听那个动态属性的事件。一句话概括.sync:
.sync是一个语法糖,简化v-bind和v-on为v-bind.sync和this.$emit('update:xxx')。为我们提供了一种子组件快捷更新父组件数据的方式。
参考资料:
https://cn.vuejs.org/v2/api/#v-bind
https://github.com/vuejs/vue/tree/dev/src
https://cn.vuejs.org/v2/guide/components-custom-events.html#sync-%E4%BF%AE%E9%A5%B0%E7%AC%A6
The text was updated successfully, but these errors were encountered: