4.1 v-for更新监测
当v-for遍历的目标结构改变, Vue触发v-for的更新
情况1: 数组翻转
情况2: 数组截取
情况3: 更新值
口诀:
数组变更方法, 就会导致v-for更新, 页面更新
数组非变更方法, 返回新数组, 就不会导致v-for更新, 可采用覆盖数组或this.$set()
这些方法会触发数组改变, v-for会监测到并更新页面
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
代码演示:
<template>
<div>
<ul>
<li v-for="item in arr" :key="item">
{{item}}
</li>
</ul>
<button @click="changarr">改变数组</button>
</div>
</template>
<script>
export default {
data(){
return{
arr:[1,3,5,7,9]
}
},
methods:{
changarr:function(){
//通过索引添加数组
// this.arr[5]=10;
// - `push() 给数组末尾添加元素
//this.arr.push(7,8,9);
// - `pop()`删除掉数组末尾的元素
//this.arr.pop()
// - `shift()` 给数组第一位进行删除
//this.arr.shift()
// - `unshift()` 给数组首位开始添加元素
//this.arr.unshift(0,9,8)
// - `splice()` 删除元素,插入元素,替换元素
//说明:第一个元素表示开始插入或者开始删除的元素位置下标
//删除元素
//第二个参数表示要删除的几个元素(如果没有传,表示删除后面的所有的元素)
//this.arr.splice(1,3)
//插入元素
//第二个参数,传入0 并且后面接上要插入的元素
// this.arr.splice(1,0,3,2,1)
//替换元素
//第二个参数,表示我们替换几个元素 3表示3个元素 2,1,9表示替换的数据
// this.arr.splice(1,3,2,1,9)
// - `sort()` 排序
this.arr.sort()
// - `reverse()` 翻转
this.arr.reverse()
}
}
}
</script>
这些方法不会触发v-for更新
-
slice()
-
filter()
-
concat()
<template>
<div>
<ul>
<li v-for="(val, index) in arr" :key="index">
{{ val }}
</li>
</ul>
<button @click="revBtn">数组翻转</button>
<button @click="sliceBtn">截取前3个</button>
<button @click="updateBtn">更新第一个元素值</button>
<button @click="filterUp">过滤条件数组</button>
</div>
</template>
<script>
export default {
data(){
return {
arr: [5, 3, 9, 2, 1],
arr2:[2,5,6,7]
}
},
methods: {
revBtn(){
// 1. 数组翻转可以让v-for更新
this.arr.reverse()
},
sliceBtn(){
// 2. 数组slice方法不会造成v-for更新
// slice不会改变原始数组: 用于从已有的数组中选出选定的数组元素
//this.arr.slice(0, 3)
// 解决v-for更新 - 覆盖原始数组
let newArr = this.arr.slice(0, 3)
this.arr = newArr
},
updateBtn(){
// 3. 更新某个值
// this.arr[0] = 1000;
//concat 数组拼接
let newArr=this.arr.concat(this.arr2)
console.log(newArr)
},
filterUp(){
//filter():过滤,可以过滤数组中的元素,还可以作为过滤器使用
/*
filter会返回一个新数组,新数组中包含符合条件的所有元素,
arr.filter(function(val,index,arr))
三个参数:
val:当前数组值
index:数组的下标
arr:是数组
*/
let newarr=this.arr.filter(function(val,index,arr){
return val>3
});
console.log(newarr)
}
}
}
</script>
注意: vue 不能监测到数组里赋值的动作而更新,
如果需要请使用Vue.set() 或者this.$set(), 或者覆盖整个数组
改变原数组的方法才能让v-for更新
4.2 v-for就地更新
v-for
的默认行为会尝试原地修改元素而不是移动它们。
这种 虚拟DOM对比方式, 可以提高性能 - 但是还不够高
4.3 虚拟dom
.vue 文件中的 template 里写的标签, 都是模板,
都要被 vue 处理成虚拟 DOM 对象, 才会渲染显示到真实 DOM 页面上
-
内存中生成一样的虚拟DOM结构(==本质是个JS对象==)因为真实的DOM属性好几百个, 没办法快速的知道哪个属性改变了比如template里标签结构
<template>
<div id="box">
<p class="my_p">123</p>
</div>
</template>
对应的虚拟DOM结构
const dom = {
type: 'div',
attributes: [{id: 'box'}],
children: {
type: 'p',
attributes: [{class: 'my_p'}],
text: '123'
}}
以后vue数据更新
- 生成新的虚拟DOM结构
- 和旧的虚拟DOM结构对比
- 利用diff算法, 找不不同, 只更新变化的部分(重绘/回流)到页面 - 也叫打补丁
==好处1: 提高了更新DOM的性能(不用把页面全删除重新渲染)==
==好处2: 虚拟DOM只包含必要的属性(没有真实DOM上百个属性)==
虚拟DOM保存在内存中, 只记录dom关键信息, 配合diff算法提高DOM更新的性能
在内存中比较差异, 然后给真实DOM打补丁更新上
4.4 diff算法
vue用diff算法, 新虚拟dom, 和旧的虚拟dom比较
情况1: 根元素变了, 删除重建
旧虚拟DOM
<div id="box">
<p class="my_p">123</p>
</div>
新虚拟DOM
<ul id="box">
<li class="my_p">123</li>
</ul>
情况2: 根元素没变, 属性改变, ==元素复用==, 更新属性
旧虚拟DOM
<div id="box">
<p class="my_p">123</p>
</div>
新虚拟DOM
<div id="myBox" title="标题">
<p class="my_p">123</p>
</div>
4.5 diff算法-key
情况3: 根元素没变, 子元素没变, 元素内容改变
无key - 就地更新
v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute key
来提供一个排序提示
<ul id="myUL">
<li v-for="str in arr">
{{ str }}
<input type="text">
</li>
</ul>
<button @click="addFn">下标为1的位置新增一个</button>
export default {
data(){
return {
arr: ["老大", "新来的", "老二", "老三"]
}
},
methods: {
addFn(){
this.arr.splice(1, 0, '新来的')
}
}
};
旧 - 虚拟DOM结构 和 新 - 虚拟DOM结构 对比过程
==性能不高, 从第二个li往后都更新了==
代码演示:
<template>
<div>
<ul>
<li v-for="item in person"><input type="checkbox" name="">
{{item}}
</li>
</ul>
<button @click="addperson">增加数据</button>
</div>
</template>
<script>
export default {
data(){
return{
person:["老大","老二","老三","老四"]
}
},methods:{
addperson(){
this.person.unshift("新来的")
}
}
}
</script>
有key - 值为索引
-
还是就地更新
因为新旧虚拟DOM对比, key存在就复用此标签更新内容, 如果不存在就直接建立一个新的
<ul id="myUL">
<li v-for="(str, index) in arr" :key="index">
{{ str }}
<input type="text">
</li>
</ul>
<button @click="addFn">下标为1的位置新增一个</button>
export default {
data(){
return {
arr: ["老大", "新来的", "老二", "老三"]
}
},
methods: {
addFn(){
this.arr.splice(1, 0, '新来的')
}
}
}
key为索引-图解过程 (又就地往后更新了)
-
v-for先循环产生新的DOM结构, key是连续的, 和数据对应
-
然后比较新旧DOM结构, 找到区别, 打补丁到页面上最后补一个li, 然后从第二个往后, 都要更新内容
口诀: key的值有id用id, 没id用索引
有key - 值为id
key的值只能是唯一不重复的, 字符串或数值
v-for不会移动DOM, 而是尝试复用, 就地更新,如果需要v-for移动DOM, 你需要用特殊 attribute
key
来提供一个排序提示新DOM里数据的key存在, 去旧的虚拟DOM结构里找到key标记的标签, 复用标签
新DOM里数据的key存在, 去旧的虚拟DOM结构里没有找到key标签的标签, 创建
旧DOM结构的key, 在新的DOM结构里没有了, 则==移除key所在的标签==
<template>
<div>
<ul>
<li v-for="obj in arr" :key="obj.id">
{{ obj.name }}
<input type="text">
</li>
</ul>
<button @click="btn">下标1位置插入新来的</button>
</div>
</template>
<script>
export default {
data() {
return {
arr: [
{
name: '老大',
id: 50
},
{
name: '老二',
id: 31
},
{
name: '老三',
id: 10
}
],
};
},
methods: {
btn(){
this.arr.splice(1, 0, {
id: 19,
name: '新来的'
})
}
}
};
</script>
<style>
</style>
图解效果:
不用key也不影响功能(就地更新), 添加key可以提高更新的性能