文章目录
基本列表
我们可以先用Vue创建一个基本的列表:
<body><divid="root"><ul><liv-for="(person,index) in persons":key="person.id">
{{person.name}} -- {{person.age}} -- {{index}}
</li></ul></div></body><script>newVue({el:'#root',data:{persons:[{id:"1",name:"Tom",age:"18"},{id:"2",name:"Linda",age:"16"},{id:"3",name:"Jack",age:"17"}]}})</script>
注意:
- 这里的key是必不可少的,他让每一个li都有一个特殊的标识。我们后面会做详细的介绍
- in前面可以接收到两个返回值,一个是列表中的元素,另一个是列表索引。
- 我们也可以利用列表索引的特殊独特性,将其作为key
- 如果接收多个返回值那个括号也可以省略。(但最好不要,有可能在一些老的脚手架中报错)
- 这个in可以替换为of
除了遍历列表之外,我们还可以遍历对象类型:
<body><divid="root"><ul><liv-for="(person,index) in persons":key="index">
{{person.name}} -- {{person.age}} -- {{index}}
</li></ul><ul><li>学校概况</li><liv-for="(inform,index) in school":key="index">
{{inform}} -- {{index}}
</li></ul></div></body><script>newVue({el:'#root',data:{persons:[{id:"1",name:"Tom",age:"18"},{id:"2",name:"Linda",age:"16"},{id:"3",name:"Jack",age:"17"}],school:{name:"NEFU",type:'211',area:"3.3万公顷",num:"3.7万人"}}})</script>
效果:
我们还可以遍历字符串:
<body><divid="root"><ul><liv-for="(person,index) in persons":key="index">
{{person.name}} -- {{person.age}} -- {{index}}
</li></ul><ul><li>学校概况</li><liv-for="(inform,index) in school":key="index">
{{inform}} -- {{index}}
</li></ul><ul><liv-for="(word,index) in str":key="index">
{{word}} -- {{index}}
</li></ul></div></body><script>newVue({el:'#root',data:{persons:[{id:"1",name:"Tom",age:"18"},{id:"2",name:"Linda",age:"16"},{id:"3",name:"Jack",age:"17"}],school:{name:"NEFU",type:'211',area:"3.3万公顷",num:"3.7万人"},str:'hello'}})</script>
效果:
还可以遍历指定次数:
<body><divid="root"><ul><liv-for="(person,index) in persons":key="index">
{{person.name}} -- {{person.age}} -- {{index}}
</li></ul><ul><li>学校概况</li><liv-for="(inform,index) in school":key="index">
{{inform}} -- {{index}}
</li></ul><ul><liv-for="(word,index) in str":key="index">
{{word}} -- {{index}}
</li></ul><ul><liv-for="(a,index) in 5":key="index">
{{a}} -- {{index}}
</li></ul></div></body><script>newVue({el:'#root',data:{persons:[{id:"1",name:"Tom",age:"18"},{id:"2",name:"Linda",age:"16"},{id:"3",name:"Jack",age:"17"}],school:{name:"NEFU",type:'211',area:"3.3万公顷",num:"3.7万人"},str:'hello'}})</script>
效果:
总结:
key的作用及原理
首先我们先粗劣的理解key的作用:给节点进行一个标识,相当于他的身份证号。
key这个属性属于Vue自用的,我们在源码中是看不到这个属性存在的。
然后我们着手来弄清楚一个问题:我们到底什么时候使用索引作为key?什么时候使用数据的编号作为key?两者有什么不同吗?
想要解决这个问题我们先要知道
key的原理
和
虚拟DOM的对比算法
。
我们给出如下案例:现在有三个人的信息,且每一个人的信息后面跟着一个input的框,点击按钮老刘被添加到列表的首位。
代码如下:
<divid="root"><!-- 遍历数组 --><h2>人员列表(遍历数组)</h2><[email protected]="add">添加一个老刘</button><ul><liv-for="(p,index) of persons":key="index">
{{p.name}}-{{p.age}}
<inputtype="text"></li></ul></div><scripttype="text/javascript">
Vue.config.productionTip =falsenewVue({el:'#root',data:{persons:[{id:'001',name:'张三',age:18},{id:'002',name:'李四',age:19},{id:'003',name:'王五',age:20}]},methods:{add(){const p ={id:'004',name:'老刘',age:40}this.persons.unshift(p)}},})</script>
在使用index作为key的情况下,如果我们将input框中添加完信息后,再去加入老刘,数据会错位:
那么这是为什么呢?我们来分析一下它的运行过程。
首先Vue会拿着我们的初始数据形成虚拟DOM。在虚拟DOM中我们是可以看到key的!(真实DOM上是没有的)
在此时我们的页面上啥都没有,只是内存里面出现了三个虚拟的DOM节点
接下来将虚拟DOM转化为真实DOM。
这里注意用户是在真实DOM里输入的东西,虚拟DOM在内存里,用户是碰不到的。用户能操作的全是真实DOM。
用户完成输入后:
此时初始化流程结束!接下来添加了新的数据老刘,导致数据开始更新,更新完成后根据新数据Vue会再次生成对应的虚拟DOM:
此时Vue会在初始的虚拟DOM和新生成的虚拟DOM之间运用虚拟DOM算法。而这个对比就要依赖
key
对比过程如下:
按照顺序,在新的虚拟DOM中取出第一个,然后它会在初始的虚拟DOM中找到同样的key的那个节点。如下图所示:
找到了之后开始对比其中的两个节点。首先比较文本节点,发现两个不一样,再比较标签节点,是一样的。如下图所示:
两个input框是一样的,两个框里面输入的内容不同,只能说明真实DOM不同,但是虚拟DOM是一样的,都是空白。我们用户输入的内容是残留在真实DOM之中的
对比结果不一样的,代表不能复用,Vue只能将对应的虚拟DOM转化成真实DOM。而对比结果一样的,代表可以复用,Vue会将对应的老的真实DOM直接拿过来用。如图:
接下来的每一项对比均是如此。
如此我们可以知道数据错乱的原因。同时我们不难发现这样效率也很低下:因为使用index作为key,在修改之后,我们的文本节点全是新生成的,没有一个被复用
接下来我们分析一下,使用数据中的序号作为key时的运行过程:
前面的过程与前文一样,我们跳过直接来到对比算法部分:
先拿新虚拟DOM中的第一个节点出来,发现在初始虚拟DOM中没有与之相同的key,于是老刘这一项全都是由虚拟DOM转化为真实DOM得来的。
进行到张三、李四、王五的时候,在key值对应的情况下,其中的文本节点,标签节点均相同,于是这些全都可以复用,直接从旧的真实DOM之中拿过来使用。
如此我们知道使用数据的序号作为key值不会出现数据错乱的情况,且执行效率也非常高(多次的节点复用)
如果我们不写key,Vue会默认把遍历时候的索引值自动作为key。也就是相当于index作为key那种情况
当然我们如上的讨论,都是建立在往前面添加数据的情况下,说的抽象一点,就是破坏了数据原有的顺序的情况下,两者的区别。
如果你是在数据末尾添加新的数据,那么两者显然都可以使用!
总结:
列表过滤
我们做一个小的案例,搜索框中输入关键词,可以将列表进行筛选。
我们可以用两种方法实现这个案例:
- 监视属性
- 计算属性
①使用监视属性
<body><!-- 准备好一个容器--><divid="root"><h2>人员列表</h2><inputtype="text"placeholder="请输入名字"v-model="keyWord"><ul><liv-for="(p,index) of filPerons":key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
</li></ul></div><scripttype="text/javascript">
Vue.config.productionTip =false//用watch实现newVue({el:'#root',data:{keyWord:'',persons:[{id:'001',name:'马冬梅',age:19,sex:'女'},{id:'002',name:'周冬雨',age:20,sex:'女'},{id:'003',name:'周杰伦',age:21,sex:'男'},{id:'004',name:'温兆伦',age:22,sex:'男'}],filPerons:[]},watch:{keyWord:{immediate:true,handler(val){this.filPerons =this.persons.filter((p)=>{return p.name.indexOf(val)!==-1})}}}})</script>
代码说明:
- persons是原本,而filPerons是要呈现的副本。
- 我们还需要了解数组中filter的使用方法 > filter() 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。> 注意: filter() 不会对空数组进行检测。> 注意: filter() 不会改变原始数组。> 语法:> array.filter(function(currentValue,index,arr), thisValue)> 里面的function要返回一个布尔值,从而决定什么元素在新的数组中>
- 配置
immediate:true
是因为初始化的时候也要调用一下handler,否则刚打开网页的时候是这样显示的: - 为了动态的获得用户的输入内容,我们使用的是双向数据绑定。在代码中我们是通过keyword去获取的。
②使用计算属性
<body><!-- 准备好一个容器--><divid="root"><h2>人员列表</h2><inputtype="text"placeholder="请输入名字"v-model="keyWord"><ul><liv-for="(p,index) of filPerons":key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
</li></ul></div><scripttype="text/javascript">
Vue.config.productionTip =false//用computed实现newVue({el:'#root',data:{keyWord:'',persons:[{id:'001',name:'马冬梅',age:19,sex:'女'},{id:'002',name:'周冬雨',age:20,sex:'女'},{id:'003',name:'周杰伦',age:21,sex:'男'},{id:'004',name:'温兆伦',age:22,sex:'男'}]},computed:{filPerons(){returnthis.persons.filter((p)=>{return p.name.indexOf(this.keyWord)!==-1})}}})</script>
当用户的输入内容发生变化时,造成了计算属性filPerons的依赖属性发生了变化,从而造成了filPerons发生变化,于是Vue会重新解析模板。
这个地方不用考虑列表初始为空的情况,因为在计算属性中get的调用有两个时机:
同时从代码量上我们也可以推测出来优先使用computed。
版权归原作者 十八岁讨厌编程 所有, 如有侵权,请联系我们删除。