带你理解vue中的v-bind
作者:PHP开发工程师 发布时间:2023-07-02 16:52:44
目录
一、v-bind关键源码分析
1、v-bind化的属性统一存储在哪里:attrsMap与attrsList
2、解析HTML,解析出属性集合attrs,在start回调中返回
3、在start回调中创建ASTElement,createASTElement(... ,attrs, ...)
4、创建后ASTElement会生成attrsList和attrsMap
5、attrs的数据类型定义
6、绑定属性获取函数
二、如何获取v-bind的值
1、v-bind:key源码分析
2、v-bind:title源码分析
3、v-bind:class源码分析
4、、v-bind:style源码分析
5、v-bind:text-content.prop源码分析
6、v-bind的修饰符.camel .sync源码分析
一、v-bind关键源码分析
1、v-bind化的属性统一存储在哪里:attrsMap与attrsList
<p v-bind:title="vBindTitle"></p>
假设为p标签v-bind
化了title
属性,我们来分析title
属性在vue
中是如何被处理的。
vue在拿到这个html标签之后,处理title属性,会做以下几步:
解析
HTML
,解析出属性集合attrs
,在start
回调中返回在start回调中创建
ASTElement,createASTElement(... ,attrs, ...)
创建后
ASTElement
会生成attrsList
和attrsMap
至于创建之后是如何处理v-bind:title
这种普通的属性值的,可以在下文的v-bind:src源码分析中一探究竟。
2、解析HTML,解析出属性集合attrs,在start回调中返回
function handleStartTag (match) {
...
const l = match.attrs.length
const attrs = new Array(l)
for (let i = 0; i < l; i++) {
const args = match.attrs[i]
...
attrs[i] = {
name: args[1],
value: decodeAttr(value, shouldDecodeNewlines)
}
}
...
if (options.start) {
// 在这里上传到start函数
options.start(tagName, attrs, unary, match.start, match.end)
}
}
3、在start回调中创建ASTElement,createASTElement(... ,attrs, ...)
// 解析HMTL
parseHTML(template, {
...
start(tag, attrs, unary, start, end) {
let element: ASTElement = createASTElement(tag, attrs, currentParent) // 注意此处的attrs
}
})
4、创建后ASTElement会生成attrsList和attrsMap
// 创建AST元素
export function createASTElement (
tag: string,
attrs: Array<ASTAttr>, // 属性对象数组
parent: ASTElement | void // 父元素也是ASTElement
): ASTElement { // 返回的也是ASTElement
return {
type: 1,
tag,
attrsList: attrs,
attrsMap: makeAttrsMap(attrs),
rawAttrsMap: {},
parent,
children: []
}
}
5、attrs的数据类型定义
// 声明一个ASTAttr 属性抽象语法树对象 数据类型
declare type ASTAttr = {
name: string; // 属性名
value: any; // 属性值
dynamic?: boolean; // 是否是动态属性
start?: number;
end?: number
};
6、绑定属性获取函数
绑定属性获取函数getBindingAttr 和 属性操作函数 getAndRemoveAttr
getBindingAttr
及其子函数getAndRemoveAttr
在处理特定场景下的v-bind
十分有用,也就是”v-bind
如何处理不同的绑定属性“章节很有用。 这里将其列举出来供下文v-bind:key
源码分析;v-bind:src
源码分析;v-bind:class
源码分析;v-bind:style
源码分析;v-bind:dataset.prop
源码分析源码分析参照。
export function getBindingAttr (
el: ASTElement,
name: string,
getStatic?: boolean
): ?string {
const dynamicValue =
getAndRemoveAttr(el, ':' + name) ||
getAndRemoveAttr(el, 'v-bind:' + name)
if (dynamicValue != null) {
return parseFilters(dynamicValue)
} else if (getStatic !== false) {
const staticValue = getAndRemoveAttr(el, name)
if (staticValue != null) {
return JSON.stringify(staticValue)
}
}
}
// 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.
export function getAndRemoveAttr (
el: ASTElement,
name: string,
removeFromMap?: boolean
): ?string {
let val
if ((val = el.attrsMap[name]) != null) {
const list = el.attrsList
for (let i = 0, l = list.length; i < l; i++) {
if (list[i].name === name) {
list.splice(i, 1) // 从attrsList删除一个属性,不会从attrsMap删除
break
}
}
}
if (removeFromMap) {
delete el.attrsMap[name]
}
return val
}
二、如何获取v-bind的值
以下面代码为例从源码分析vue
是如何获取v-bind
的值。
会从记下几个场景去分析:
常见的
key
属性绑定一个普通
html attribute:title
绑定
class
和style
绑定一个
html DOM property:textContent
vBind:{
key: +new Date(),
title: "This is a HTML attribute v-bind",
class: "{ borderRadius: isBorderRadius }"
style: "{ minHeight: 100 + 'px' , maxHeight}"
text-content: "hello vue v-bind"
}
<div
v-bind:key="vBind.key"
v-bind:title="vBind.title"
v-bind:class="vBind.class"
v-bind:style="vBind.style"
v-bind:text-content.prop="vBind.textContent"
/>
</div>
1、v-bind:key源码分析
function processKey (el) {
const exp = getBindingAttr(el, 'key')
if(exp){
...
el.key = exp;
}
}
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
是什么,在上面的源码中可以看到。
2、v-bind:title源码分析
title
是一种“非vue
特殊的”也就是普通的HTML attribute
。
function processAttrs(el){
const list = el.attrsList;
...
if (bindRE.test(name)) { // v-bind
name = name.replace(bindRE, '')
value = parseFilters(value)
...
addAttr(el, name, value, list[i], ...)
}
}
export const bindRE = /^:|^\.|^v-bind:/
export function addAttr (el: ASTElement, name: string, value: any, range?: Range, dynamic?: boolean) {
const attrs = dynamic
? (el.dynamicAttrs || (el.dynamicAttrs = []))
: (el.attrs || (el.attrs = []))
attrs.push(rangeSetItem({ name, value, dynamic }, range))
el.plain = false
}
通过阅读源码我们看出:对于原生的属性,比如title这样的属性,vue会首先解析出name
和value
,然后再进行一系列的是否有modifiers
的判断(modifier
的部分在下文中会详细讲解),最终向更新ASTElement
的attrs
,从而attrsList
和attrsMap
也同步更新。
3、v-bind:class源码分析
css
的class
在前端开发的展现层面,是非常重要的一层。 因此vue
在对于class
属性也做了很多特殊的处理。
function transformNode (el: ASTElement, options: CompilerOptions) {
const warn = options.warn || baseWarn
const staticClass = getAndRemoveAttr(el, 'class')
if (staticClass) {
el.staticClass = JSON.stringify(staticClass)
}
const classBinding = getBindingAttr(el, 'class', false /* getStatic */)
if (classBinding) {
el.classBinding = classBinding
}
}
在transfromNode
函数中,会通过getAndRemoveAttr
得到静态class
,也就是class="foo";在getBindingAttr
得到绑定的class,也就是v-bind:class="vBind.class
"即v-bind:class="{ borderRadius: isBorderRadius
}",将ASTElement的classBinding赋值为我们绑定的属性供后续使用。
4、、v-bind:style源码分析
style是直接操作样式的优先级仅次于important
,比class更加直观的操作样式的一个HTML attribute
。 vue对这个属性也做了特殊的处理。
function transformNode (el: ASTElement, options: CompilerOptions) {
const warn = options.warn || baseWarn
const staticStyle = getAndRemoveAttr(el, 'style')
if (staticStyle) {
el.staticStyle = JSON.stringify(parseStyleText(staticStyle))
}
const styleBinding = getBindingAttr(el, 'style', false /* getStatic */)
if (styleBinding) {
el.styleBinding = styleBinding
}
}
在transfromNode
函数中,会通过getAndRemoveAttr
得到静态style,也就是style="{fontSize: '12px'
}";在getBindingAttr
得到绑定的style,也就是v-bind:style="vBind.style"即v-bind:class={ minHeight: 100 + 'px' , maxHeight
}",其中maxHeight是一个变量,将ASTElement
的styleBinding
赋值为我们绑定的属性供后续使用。
5、v-bind:text-content.prop源码分析
textContent
是DOM对象的原生属性,所以可以通过prop进行标识。 如果我们想对某个DOM prop直接通过vue进行set,可以在DOM节点上做修改。
下面我们来看源码。
function processAttrs (el) {
const list = el.attrsList
...
if (bindRE.test(name)) { // v-bind
if (modifiers) {
if (modifiers.prop && !isDynamic) {
name = camelize(name)
if (name === 'innerHtml') name = 'innerHTML'
}
}
if (modifiers && modifiers.prop) {
addProp(el, name, value, list[i], isDynamic)
}
}
}
export function addProp (el: ASTElement, name: string, value: string, range?: Range, dynamic?: boolean) {
(el.props || (el.props = [])).push(rangeSetItem({ name, value, dynamic }, range))
el.plain = false
}
props?: Array<ASTAttr>;
通过上面的源码我们可以看出,v-bind:text-content.prop
中的text-content
首先被驼峰化为textContent
(这是因为DOM property
都是驼峰的格式),vue还对innerHtml
错误写法做了兼容也是有心,之后再通过prop标识符,将textContent属性增加到ASTElement的props
中,而这里的props本质上也是一个ASTAttr。
有一个很值得思考的问题:为什么要这么做?与HTML attribute有何异同?
没有
HTML attribute
可以直接修改DOM的文本内容,所以需要单独去标识比通过js去手动更新DOM的文本节点更加快捷,省去了查询dom然后替换文本内容的步骤
在标签上即可看到我们对哪个属性进行了v-bind,非常直观
其实v-bind:title可以理解为
v-bind:title.attr,v-bind:text-content.prop
只不过vue
默许不加修饰符的就是HTMLattribute
罢了
6、v-bind的修饰符.camel .sync源码分析
.camel
仅仅是驼峰化,很简单。 但是.sync就不是这么简单了,它会扩展成一个更新父组件绑定值的v-on * 。
其实刚开始看到这个.sync修饰符我是一脸懵逼的,但是仔细阅读一下组件的.sync再结合实际工作,就会发现它的强大了。
<Parent
v-bind:foo="parent.foo"
v-on:updateFoo="parent.foo = $event"
></Parent>
在vue中,父组件向子组件传递的props
是无法被子组件直接通过this.props.foo = newFoo
去修改的。 除非我们在组件this.$emit("updateFoo", newFoo),
然后在父组件使用v-on做事件监听updateFoo
事件。若是想要可读性更好,可以在$emit
的name上改为update:foo,然后v-on:update:foo
。
有没有一种更加简洁的写法呢??? 那就是我们这里的.sync操作符。 可以简写为:
<Parent v-bind:foo.sync="parent.foo"></Parent>
然后在子组件通过this.$emit("update:foo", newFoo
);去触发,注意这里的事件名必须是update:xxx的格式,因为在vue的源码中,使用.sync修饰符的属性,会自定生成一个v-on:update:xxx
的监听。
下面我们来看源码:
if (modifiers.camel && !isDynamic) {
name = camelize(name)
}
if (modifiers.sync) {
syncGen = genAssignmentCode(value, `$event`)
if (!isDynamic) {
addHandler(el,`update:${camelize(name)}`,syncGen,null,false,warn,list[i])
// Hyphenate是连字符化函数,其中camelize是驼峰化函数
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)
}
}
通过阅读源码我们可以看到: 对于v-bind:foo.sync
的属性,vue会判断属性是否为动态属性。 若不是动态属性,首先为其增加驼峰化后的监听,然后再为其增加一个连字符的监听,例如v-bind:foo-bar.sync
,首先v-on:update:fooBar
,然后v-on:update:foo-bar
。v-on监听是通过addHandle
r加上的。 若是动态属性,就不驼峰化也不连字符化了,通过addHandler(el,update:${name}, ...),
老老实实监听那个动态属性的事件。
一句话概括.sync
: .sync
是一个语法糖,简化v-bind和v-on为v-bind.sync
和this.$emit('update:xxx'
)。为我们提供了一种子组件快捷更新父组件数据的方式。
来源:https://my.oschina.net/u/5079097/blog/5263395
猜你喜欢
- 目录项目地址运行环境运行方法数据爬取(jd.comment.py)模型训练(train.py)情感分析(sentiment.analysis
- python中eval和int的区别是什么?下面给大家介绍一下:1.eval()函数eval(<字符串>)能够以Python表达
- 前言环境搭建可查看Python人脸识别微笑检测数据集可在https://inc.ucsd.edu/mplab/wordpress/index
- 1.数据集分割通过datasets可以直接分别获取训练集和测试集。通常我们会将训练集进行分割,通过torch.utils.data.rand
- 实验1.1 列表a = [1, 2, 3, 4]for i in a: print(i)  
- 本文实例讲述了Python基于tkinter模块实现的改名小工具。分享给大家供大家参考,具体如下:#!/usr/bin/env python
- 背景:有时候我们需要在服务器上同时运行多个程式,但是却需要一个一个的打开,比较费时间,而且一旦服务器重启后,不懂程式运行的人受限于环境及代码
- 近些年随着Python语言越来越流行,越来越多的人选择Python语言作为自己的职业方向。如何在心仪公司的面试中获得好成绩,并最终成功获得o
- Win7安装mysql的具体过程,我的版本是5.5.21 以下是我的安装步骤:1、首先单击MySQL5.5.21的安装文件,出现该数据库的安
- python的版本及依赖的库的安装#版本python 3.7.1pip install pywin32==224pip install nu
- 1.首先在Xshell上通过conda创建新的虚拟环境2.此时在 /home/y210101004/.conda/envs下多了刚
- 国 * 公司安全团队开发出利用赛门铁克数据库安全与审计(SDSA)设备监视数据库的一系列客户化的政策。他们是如何在已有安全策略基础上做优
- Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:>>>
- tensorflow在1.4版本引入了keras,封装成库。现想将keras版本的GRU代码移植到TensorFlow中,看到TensorF
- 本文实例讲述了python基于urllib实现按照百度音乐分类下载mp3的方法。分享给大家供大家参考。具体实现方法如下:#!/usr/bin
- 前言对于JavaScript程序的调试,相比于alert(),使用console.log()是一种更好的方式,原因在于:alert()函数会
- 目的:是学习python 多线程的工作原理,及通过抓取400张图片这种IO密集型应用来查看多线程效率对比import requestsimp
- 迭代器迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引
- 简介在日常开发中,我们的大部分时间都会花在阅读traceback模块信息以及调试代码上。本文我们将改进traceback模块,让其中的提示信
- 前言异常值是指样本中的个别值,也称为离群点,其数值明显偏离其余的观测值。常用检测方法3σ原则和箱型图。其中,3σ原则只适用服从正态分布的数据