Vue 尚硅谷学习笔记五Vue监视数据原理、列表渲染、key的原理、数组过滤和排序
0. 作者有话说
本章包含了部分Vue底层原理知识,比如key的原理,Vue如何监视数据,掌握了这些知识可以帮助我们更好的理解Vue。
1. 数组 列表显示
v-for
指令
- 用于展示列表数据
- 语法:
v-for="(item, index) in xxx" :key="yyy"
- 可遍历:数组、对象、字符串(用的很少)、指定次数(用的很少)
- 数组:
(item, index)
- 对象:
(value, key)
- 字符串:
(char, index)
- 数字:
(number, index)
2. key的原理
2.1 虚拟DOM中key
的作用
key是虚拟DOM对象的标识,当数据发生变化时,Vue会根据新数据生成新的虚拟DOM, 随后Vue进行新虚拟DOM与旧虚拟DOM的差异比较,比较规则如下:
2.2 对比规则
- 旧虚拟DOM中找到了与新虚拟DOM相同的
key
:
- 若虚拟DOM中内容没变, 直接使用之前的真实DOM
- 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
- 旧虚拟DOM中未找到与新虚拟DOM相同的
key
创建新的真实DOM,随后渲染到到页面。
2.3 用index
作为key
可能会引发的问题
- 若对数据进行:逆序添加、逆序删除等破坏顺序操作: 会产生没有必要的真实DOM更新
==>
界面效果没问题, 但效率低 - 如果结构中还包含输入类的DOM: 会产生错误DOM更新
==>
界面有问题
2.4 开发中如何选择key
- 最好使用每条数据的唯一标识作为
key
, 比如id、手机号、身份证号、学号等唯一值。 - 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用
index
作为key
是没有问题的。
3. 数组过滤与排序
3.1 数组过滤
-
filter()
返回一个新的数组,包含了过滤出来的数据 -
实现方法:watch与计算属性
-
注意:
'abc'.indexOf('a')
输出为 0 ,'abc'.indexOf('')
输出也为 0,说明所有字符串都包含空字符
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>人员列表</h2>
<input type="text" placeholder="请输入名字" v-model="keyWord">
<ul>
<li v-for="(p,index) of filPerons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
//用watch实现
//#region
/* new Vue({
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方法,使页面初始有数据
handler(val){
this.filPerons = this.persons.filter((p)=>{
return p.name.indexOf(val) !== -1
})
}
}
}
}) */
//#endregion
//用computed实现
new Vue({
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() {
return this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
}
}
})
</script>
3.2 列表排序
computed:{
filPerons(){
const arr = this.persons.filter((p) => {
return p.name.indexOf(this.keyWord) !== -1
})
//判断一下是否需要排序
if(this.sortType){
arr.sort((p1,p2) => {
return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age
})
}
return arr
}
}
4. Vue监视数据的原理
Vue实例化对象挂载到根元素后,生成全局Vue对象实例,Vue对象在实例化过程中会传入配置对象(options),options中包括data、methods、computed、watch等等。至于Vue是如何对data中数据的变化进行监测的,原理大致如下:
4.1 普通数据的监视原理
1. data对象加工成_data对象,并存入Vue实例
Vue首先对options中的data对象进行加工,生成_data对象,并存入全局Vue实例中,_data对象并不是单纯对data对象进行简单复制:
- 怎么加工:递归为每一个非对象类型的数据添加响应式的get、set方法(reactive getter、reactive setter)(数据劫持)
- reactive getter和reactive setter怎么添加?(会用到数据代理)
- 怎么存入:
vm._data = data = obs
let data = {
key1:value1,
key2:value2,
}
// 创建一个观察者实例对象,用于监测data的数据变化
const obs = new Observer(data)
function Observer(obj){
// 汇总obj中所有属性,形成一个数组keys
const keys = Object.keys(obj)
// 遍历keys数组(此处为简化示例,未体现属性是一个对象或一个数组进行递归的情况)
keys.forEach((key)=>{
Object.defineProperty(this, key,{
get(){
return obj[key]
},
set(value){
obj[key] = value
}
})
})
}
2. Vue全局实例对象代理_data对象,实现对_data中属性的直接操作
加工生成_data对象,并存入Vue实例对象后,Vue实例对象会通过数据代理的方式,对_data对象进行代理。
为每个对象设置getter和setter,从而实现通过this.key1 = new_value1
以及 const value = this.key1
的形式,直接对_data中的key1进行修改和读取, 即 vm.name = vm._data.name
。
所谓数据代理,就是通过一个对象代理对另一个对象中属性的操作【读/写】,至于数据代理的实现形式,当然就是使用原生JS的Object.defineProperty()
方法,具体的代理过程可以查看 【Vue 尚硅谷学习笔记(二)】数据代理与事件处理
3. 数据变化监测效果
每个具有reactive setter的数据发生变化时,都会调用这个reactive setter,而这个reactive setter被调用时,会触发重新解析模板、生成新的虚拟DOM,、新旧虚拟DOM对比,更新内容映射到真实DOM、重新渲染这一套流程。
4.2 动态新增可被监测的属性 Vue.set()或 vm.$set()
项目开发过程中,可能会出现新增属性的需求,而新增的属性也必须是响应式的,因此需要用到 Vue.set(targetObject,attributeName,attributeValue)
方法或vm.$set(targetObject,attributeName,attributeValue)
进行响应式属性的动态添加,所谓响应式属性也就是这个属性会被添加到_data对象中,并且具有相应的reactive getter和reactive setter,最后被存入到Vue对象实例里。
需要注意,由于Vue本身有规定,不能把一个响应式的属性直接添加到Vue实例身上,而data对象中的所有属性最终都会通过数据代理添加到Vue实例身上,因此Vue.set() 方法和 this.$set() 方法的targetObject这个参数不能是Vue实例本身,也不能是data对象,只能是data对象中的二级属性对象。
即
Vue.set()
和vm.$set()
不能给vm
或vm
的根数据对象 添加属性
4.3 Vue监测数组数据的变化
-
Vue不会为数组元素添加响应式的getter和setter,所以通过下标更改数组数据是无法被Vue所监测到的。
-
Vue 重写了数组中的一系列改变数组内部数据的方法(先调用原生,再更新界面),因此针对数组,只有通过调用
push
、pop
、shift
、unshift
、splice
、reverse
、sort
这7个改变原数组本身的API,才会引起Vue的响应。
this.persons[index] = newP; //并没有改变persons本身,数组内部发生了变化,但是没有调用变异方法,vue不会更新界面
- 相比之下,也有非变更方法,例如
filter()
、concat()
和slice()
。它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以用新数组替换旧数组。 slice()
与splice()
前者不改变原数组,后者改变原数组。splice()
方法通过移除或者替换已存在的元素和/或添加新元素就地改变一个数组的内容。slice()
方法返回一个新的数组对象,这一对象是一个由start
和end
决定的原数组的浅拷贝(包括start
,不包括end
),其中start
和end
代表了数组元素的索引。原始数组不会被改变。
let fpersons = persons.filter(
p => p.name.includes(searchName)
)
<body>
<div id="demo">
<h2>测试:v-for遍历数组</h2>
<ul>
<li v-for="(p, index) in persons" :key="index">
{{index}}---{{p.name}}---{{p.age}}
<button @click="deleteP(index)">删除</button>
<button @click="updateP(index, {name:'Cat', age: 20})">更新</button>
</li>
</ul>
<h2>测试:v-for遍历对象</h2>
<ul>
<li v-for="(value, key) in persons[1]" :key="key">
{{value}}---{{key}}
</li>
</ul>
</div>
<script src="../js/vue.js"></script>
<script>
// Vue本身只是监视了persons的改变,没有监视数组内部数据的改变
// Vue 重写了数组中的一系列改变数组内部数据的方法(先调用原生,再更新界面)
new Vue({
el: '#demo',
data: {
persons: [{
name: 'Tom',
age: 18
},
{
name: 'Jack',
age: 19
},
{
name: 'Marry',
age: 16
},
{
name: 'Rose',
age: 12
},
]
},
methods: {
deleteP(index) {
// 删除persons中指定idnex的p(有数据绑定)
this.persons.splice(index, 1);
},
updateP(index, newP) {
// this.persons[index] = newP; //数据(数组内部)变了,界面没有变化(没有数据绑定)
//并没有改变persons本身,数组内部发生了变化,但是没有调用变异方法,vue不会更新界面
// this.persons = [] //界面有变化,改变了persons
this.persons.splice(index, 1, newP);
}
}
})
</script>
</body>
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhfhbbjj
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01