当前位置: 首页 > Web前端 > vue.js

如何理解vue中的v-bind?_0

时间:2023-03-31 22:09:11 vue.js

如果你写过vue,你一定对v-bind命令很熟悉。下面我就带大家从源码层面分析一下v-bind背后的原理。将从以下几个方面进行探讨:v-bind关键源码分析其中v-bind属性统一存储:attrsMap和attrsList绑定属性获取函数getBindingAttr和属性操作函数getAndRemoveAttrv-bind如何处理不同的绑定属性v-bind:key源码分析v-bind:title源码分析v-bind:class源码分析v-bind:style源码分析v-bind:text-content.prop源码分析v-bindmodifier.camel.sync源码代码分析v-bind关键源码分析v-bind属性统一存储在哪里:attrsMap和attrsList

复制代码假设p标签v-bindingtitle属性,我们分析一下Vue中是如何处理title属性的。vue获取html标签后,处理title属性,会做如下步骤:解析HTML,解析出属性集attrs,在start回调中返回,在start回调中创建ASTElement,createASTElement(...,attrs,...)创建完成后,ASTElement会生成attrsList和attrsMap。至于创建后如何处理v-bind:title等公共属性值,可以在下面的v-bind:src源码分析中找到。前端训练解析HTML,解析出属性集attrs,返回函数handleStartTag(match){...constl=match.attrs.lengthconstattrs=newArray(l)for(leti=0;i,//属性对象数组parent:ASTElement|void//父元素也是ASTElement):ASTElement{//返回是还有ASTElementreturn{type:1,tag,attrsList:attrs,attrsMap:makeAttrsMap(attrs),rawAttrsMap:{},parent,children:[]}}复制代码attrs数据类型定义//声明一个ASTAttr属性提取类似于语法树对象数据类型declaretypeASTAttr={name:string;//属性名称值:任何;//属性值动态?:布尔值;//是动态属性start?:number;结束?:数字};copy代码绑定属性获取函数getBindingAttr和属性操作函数getAndRemoveAttrgetBindingAttr及其子函数getAndRemoveAttr在具体场景下处理v-bind非常有用,即“v-bind如何处理不同绑定属性”一章非常有用的和这里列出来下面的v-bind:key源码分析;v-bind:src源码分析;v-bind:class源码分析;v-bind:style源码分析;v-bind:dataset.prop源码分析源码分析参考。导出函数getBindingAttr(el:ASTElement,name:string,getStatic?:boolean):?string{constdynamicValue=getAndRemoveAttr(el,':'+name)||getAndRemoveAttr(el,'v-bind:'+name)if(dynamicValue!=null){returnparseFilters(dynamicValue)}elseif(getStatic!==false){conststaticValue=getAndRemoveAttr(el,name)if(staticValue!=null){returnJSON.stringify(staticValue)}}}复制代码//注意:这只会从数组(attrsList)中删除attr,因此它//不会被processAttrs处理。//默认情况下,它不会从地图(attrsMap)中删除它,因为地图是//在codegen.export函数中需要getAndRemoveAttr(el:ASTElement,name:string,removeFromMap?:boolean):?string{letvalif((val=el.attrsMap[name])!=null){constlist=el.attrsListfor(leti=0,l=list.length;i
复制代码v-bind:key源码分析函数processKey(el){constexp=getBindingAttr(el,'key')if(exp){...el.key=exp;}}复制代码processKey函数中使用了getBindingAttr函数。由于我们使用的是v-bind,所以没用:,所以constdynamicValue=getAndRemoveAttr(el,'v-bind:'+'key');,getAndRemoveAttr(el,'v-bind:key')函数判断是否attrsMap中有'v-bind:key',将这个属性的值赋值为val并从attrsList中删除,但不从attrsMap中删除,最后放入'v-bind:key'的值,即,val作为dynamicValue,然后返回解析过滤后的结果,最后将结果设置为processKey中元素的key属性。然后分段存储,至于分段是什么,在上面的源码中可以看到。v-bind:title源码分析title是一个“non-vuespecial”,即一个普通的HTML属性。函数processAttrs(el){常量列表=el.attrsList;...if(bindRE.test(name)){//v-bindname=name.replace(bindRE,'')value=parseFilters(value)...addAttr(el,name,value,list[i],...)}}exportconstbindRE=/^:|^.|^v-bind:/exportfunctionaddAttr(el:ASTElement,name:string,value:any,range?:Range,dynamic?:boolean){const属性=动态?(el.dynamicAttrs||(el.dynamicAttrs=[])):(el.attrs||(el.attrs=[]))attrs.push(rangeSetItem({name,value,dynamic},range))el.plain=false}复制代码通过阅读源码可以看出,对于原生的属性,比如title,vue会先解析出name和value,然后对是否有修饰符进行一系列的判断(修饰符下面会详细说明部分),最后更新ASTElement的attrs,这样attrsList和attrsMap也同步更新了。v-bind:class源码分析cssclass是前端开发中展示层面非常重要的一层。因此,Vue也对类的属性做了很多特殊的处理。functiontransformNode(el:ASTElement,options:CompilerOptions){constwarn=options.warn||baseWarnconststaticClass=getAndRemoveAttr(el,'class')if(staticClass){el.staticClass=JSON.stringify(staticClass)}constclassBinding=getBindingAttr(el,'class',false/getStatic/)if(classBinding){el.classBinding=classBinding}}复制代码在transfromNode函数中,会通过getAndRemoveAttr获取静态类,即class="foo";在getBindingAttr中获取绑定类,即v-bind:class="vBind.class",即v-bind:class="{borderRadius:isBorderRadius}",将ASTElement的classBinding赋给我们的绑定属性为后续使用。v-bind:style源码分析style是一个HTML属性,其优先级仅次于important,比class更直观。Vue对这个属性也有特殊处理。functiontransformNode(el:ASTElement,options:CompilerOptions){constwarn=options.warn||baseWarnconststaticStyle=getAndRemoveAttr(el,'style')if(staticStyle){el.staticStyle=JSON.stringify(parseStyleText(staticStyle))}conststyleBinding=getBindingAttr(el,'style',false/getStatic/)如果(styleBinding){el.styleBinding=styleBinding}}复制代码在transfromNode函数中,会通过getAndRemoveAttr获取静态样式,即style="{fontSize:'12px'}";在getBindingAttr中获取绑定样式,即v-bind:style="vBind.style"或v-bind:class={minHeight:100+'px',maxHeight}",其中maxHeight是赋值给ASTElement的styleBinding到我们绑定的属性,以供后续使用v-bind:text-content.prop源码分析textContent是DOM对象的原生属性,所以可以通过prop来识别,如果我们想要一个DOMprop可以直接通过Vue设置,在DOM节点上可以修改,下面看源码。functionprocessAttrs(el){constlist=el.attrsList...if(bindRE.test(name)){//v-bindif(modifiers){if(modifiers.prop&&!isDynamic){name=camelize(name)if(name==='innerHtml')name='innerHTML'}}if(modifiers&&modifiers.prop){addProp(el,name,value,list[i],isDynamic)}}}exportfunctionaddProp(el:ASTElement,name:string,value:string,range?:Range,dynamic?:boolean){(el.props||(el.props=[])).push(rangeSetItem({name,value,dynamic},范围))el.plain=false}props?:Array;复制代码从上面的源码我们可以看出,首先将v-bind:text-content.prop中的text-contenthumping成了textContent(这是因为DOM属性是驼峰格式),vue也做了兼容innerHtml的写法错误,然后通过prop标识符给ASTElement的props加上了textContent属性,而这里的props本质上就是一个ASTAttr。有一个问题值得思考:为什么要这样做?与HTML属性有什么异同?没有HTML属性直接修改DOM的文本内容,所以单独识别DOM的文本节点比通过js手动更新DOM的文本节点更快,省去了查询dom的步骤和替换文本内容。属性已经v-bind了,很直观。其实v-bind:title可以理解为v-bind:title.attr、v-bind:text-content.prop,但是vue默认是HTML属性不带修饰符v-bind.camel.sync源码的修饰符analysis.camel只是驼峰式的,很简单。但是.sync就没有那么简单了,它会扩展成一个更新父组件绑定值的v-on监听器。其实我第一眼看到这个.sync修饰符的时候也是一头雾水,但是仔细阅读了组件的.sync并结合实际工作,就会发现它的强大。复制代码在vue中,父组件传递给子组件的props不能直接由子组件通过this.props.foo=newFoo修改。除非我们在组件中this.$emit("updateFoo",newFoo),那么在父组件中使用v-on监听updateFoo事件。如果你想要更好的可读性,你可以将$emit的名称更改为update:foo,然后再更改为v-on:update:foo。有没有更简洁的写法???这是我们的.sync运算符。可以简写为:复制代码然后通过this.$emit("update:foo",newFoo);在要触发的子组件中,这里注意事件名称必须是update:xxx格式,因为在vue的源码中,使用.sync修饰符的属性会自动生成一个v-on:update:xxx的监听器.我们看一下源码:`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{//handlerw/dynamiceventnameaddHandler(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:更新:foo-bar。v-on监控是通过addHandler添加的。如果它是一个动态属性,它不会被驼峰或连字符。通过addHandler(el,update:${name},...),老老实实的监听那个动态属性的事件。一句话概括.sync:.sync是一种语法糖,将v-bind和v-on简化为v-bind.sync和this.$emit('update:xxx')。它为我们提供了一种子组件快速更新父组件数据的方式。