Vue监测数据的原理
思路:举例 ==》 监测对象数据原理 ==》 Vue.set() ==》 监测数组数据原理 ==》 说明前面例子中的现象
Vue监测数据改变的原理
Vue底层监测data中数据改变的原理和watch中监测数据的原理是一致的,watch是Vue提供给我们用于自行监视数据变化的配置项
1. 更新时的一个问题
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>人员列表</h2>
<input type="button" value="更新马冬梅的信息" @click="updateMei"/>
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
//用computed实现
const vm = new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'马冬梅',age:19,sex:'女'},
{id:'002',name:'周冬雨',age:25,sex:'女'},
{id:'003',name:'周杰伦',age:21,sex:'男'},
{id:'004',name:'温兆伦',age:22,sex:'男'}
]
},
methods: {
updateMei() {
// 逐个字段修改值Vue可以监测到数据变化
// this.persons[0].name = '马老师';
// this.persons[0].age = 50;
// this.persons[0].sex = '男'
// 一次修改整行信息Vue并未监测到数据变化
this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'}
}
}
})
</script>
</body>
2. Vue监测对象
数据的原理
回顾数据代理
响应式:数据变了页面也会相应的更新
Vue监测对象
数据的原理:setter
name一改,setter就被调用,页面中的模板就重新解析,就会生成新的虚拟DOM,然后就是新旧虚拟DOM对比,更新页面
3. 模拟一个数据监测
<body>
<script type="text/javascript" >
let data = {
name:'尚硅谷',
address:'北京',
}
//创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
console.log(obs)
//准备一个vm实例对象
let vm = {}
vm._data = data = obs
// Observer:一个构造函数,用于创建监视的实例对象
// 这里的obj就是传入的data
function Observer(obj){
//汇总对象中所有的属性形成一个数组
const keys = Object.keys(obj)
//遍历,这里的k就是obj中的每一个key【name、address】
//加工data:把data中的每一组key、value都形成了getter、setter的写法【有了这一步就可以做响应式了】
keys.forEach((k)=>{
// 这里的this就是Observer的实例对象,也就是上面的obs
Object.defineProperty(this,k,{
get(){
// 这里的obj永远是创建实例时传入的data引用,即使后面data的引用变了,obj的指向也不会变
console.log(obj)
return obj[k]
},
set(val){
console.log(`${k}被改了,我要去解析模板,生成虚拟DOM.....我要开始忙了`)
obj[k] = val
}
})
})
}
</script>
</body>
相对于我们自己写的数据监测来说,Vue能够对多层次data进行监测,而在上面的例子里我们只监测了第一层,而且Vue对_data做了数据代理,能够更方便的修改_data中的数据
4. Vue.set()方法
实现了在data中动态添加数据的功能
读取对象里面不存在的属性 【如果是直接读取了Vue身上不存在的属性【data里没有的属性】就会报错】,不会报错,值是undefined,Vue中undefined是不会直接显示在页面上的
判断一个属性的值是否为undefined与判断true、false逻辑相同,false包含了undefined
向vm._data中通过vm._data.key = value的方式直接添加的数据并不是响应式数据,因为没有对应的setter
如果想要动态添加的数据也支持响应式,可以使用Vue.set()
Vue.set(target, key, val):
target:往谁的身上追加一个属性
key:属性名
val:属性值
注:vm.$set()功能同Vue.set()
Vue.set()的局限性:只能给data中的某一个对象【响应式对象】动态添加属性,而不能给data【根数据对象】/vm实例对象动态添加属性;除此之外,如果模板中需要展示动态增加的数据,模板中是需要事先准备好结构的,应用场景有限
官网:https://v2.cn.vuejs.org/v2/api/#Vue-set
如果要给App组件中data里的一个对象X添加属性,而App组件已经将这个对象X传给了子组件,子组件中也是可以通过vm.$set()给这个对象X中动态添加属性的。
// App组件中的data里有一个列表
data(){
return {
todoList: [{id:'1',name:'ka'},{id:'2',name:'ha'}]
}
}
// App将这列表以props的方式传给了子组件List
<List :todoList="todoList"></List>
// List组件又将列表中的每一项传给了自己的子组件Item
<Item
v-for="todo in todoList"
:key="todo.id"
:thingid="todo.id"
:thingtitle="todo.title"
:isdel="todo.isDel"
:todo="todo"
:getIsSelected="getIsSelected"
:del="del"
></Item>
// Item就可以通过$set()这个API为列表的每一项动态添加属性
// 这里的todo就是由父组件List组件传递过来的,也就是App组件中data里todoList列表的每一项
edit(todo){
// 这里的this是vc,也就是Item组件的实例对象
console.log(this)
this.$set(todo, 'isEdit', false)
}
5. Vue监测数组
数据的原理
通过索引值修改数组中的某一个元素,Vue是监测不到的,Vue监测数组数据并不是通过setter实现的
同时,数组中也没有索引对应的getter和setter
数组中能够引起数组自身改变的几个API【数组调了这几个API,原数组就变了】:
push、pop、shift、unshift、splice、sort、reverse
这几个方法称为数组的变更方法
在Vue中,对于数组数据,只有调用了上述七个能够引起数组改变的API,Vue才能够监测到变化
第1节例子的代码如下
<body>
<!-- 准备好一个容器-->
<div id="root">
<h2>人员列表</h2>
<input type="button" value="更新马冬梅的信息" @click="updateMei"/>
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
//用computed实现
const vm = new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'马冬梅',age:19,sex:'女'},
{id:'002',name:'周冬雨',age:25,sex:'女'},
{id:'003',name:'周杰伦',age:21,sex:'男'},
{id:'004',name:'温兆伦',age:22,sex:'男'}
]
},
methods: {
updateMei() {
// 逐个字段修改值Vue可以监测到数据变化
// this.persons[0].name = '马老师';
// this.persons[0].age = 50;
// this.persons[0].sex = '男'
// 一次修改整行信息Vue并未监测到数据变化
this.persons[0] = {id:'001',name:'马老师',age:50,sex:'男'}
}
}
})
</script>
</body>
直接通过索引修改数组的元素Vue是监测不到的,只能通过调用固定的七个API改变数组,Vue才能够监测到
Vue中是如何监测到我们调用了上述七个API的?
包装
Vue对数组数据进行了包装,数组数据调用的七个API已经不是Array原型对象身上的API了,所以Vue能够监测到我们调用了这几个API
Vue对数组的监测是靠包装数组身上常用的修改数组的方法【数组的变更方法】实现的
Vue包装后的API做了两件事:
- 调用Array原型对象身上对应的API
- 重新解析模板,引起页面变化
官方网站:https://v2.cn.vuejs.org/v2/guide/list.html#数组更新检测
如果data中某个数组的元素都是对象,通过数组的变更方法给该数组添加对象元素时,Vue会为每个对象创建getter和setter,保证新增加的数组内的对象元素也是响应式的
如果不想通过数组的变更方法改变数组,同时还向让Vue监测到数组元素的变化,可以通过Vue.set()/vm.$set()实现
6. 总结
Vue监视数据的原理:
1. vue会监视data中所有层次的数据。
2. 如何监测对象中的数据?
通过setter实现监视,且要在new Vue时就传入要监测的数据。
(1).对象中后追加的属性,Vue默认不做响应式处理
(2).如需给后添加的属性做响应式,请使用如下API:
Vue.set(target,propertyName/index,value) 或
vm.$set(target,propertyName/index,value)
3. 如何监测数组中的数据?
通过包裹数组更新元素的方法实现,本质就是做了两件事:
(1).调用原生对应的方法对数组进行更新。
(2).重新解析模板,进而更新页面。
4.在Vue修改数组中的某个元素一定要用如下方法:
1.使用这些API:push()、pop()、shift()、unshift()、splice()、sort()、reverse()
2.Vue.set() 或 vm.$set()
特别注意:Vue.set() 和 vm.$set() 不能给vm 或 vm的根数据对象【vm._data】 添加属性!!!
数据劫持:为data中的每一个属性增加了对应的getter和setter,属性值读取/改变时,必须先经过getter和setter,增加getter和setter的这个动作就叫做数据劫持。
标签:监测数据,Vue,name,2x,vm,set,数组,data From: https://www.cnblogs.com/wzzzj/p/18039951