4.1 Vue监视数据改变的原理:
Vue监视数据的过程:
1.加工data,使之成为响应式的对象,即如果data的数据被读取或修改,就发出通知,实现对对象中属性的监视;
2.vm._data = data;
Vue如何监测对象中数据的变化(加工data===》 data 变成 Observer)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模拟一个数据监控</title>
</head>
<body>
<div class="root">
</div>
<script type="text/javascript">
let data = {
name:'Sam',
age:30,
}
//创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data)
console.log(obs)
let vm = {}
vm._data = data = obs
//Observer的构造函数,获取的参数是一个对象
function Observer(obj){
// 汇总对象中所有属性,形成一个数组
const keys = Object.keys(obj);
//遍历
keys.forEach((k)=>{
//this是他的实例对象 Observer,让每一个属性都有自己的getter,setter方法
Object.defineProperty(this,k,{
get(){
return obj[k]
},
set(v) {
//利用set检测,当属性改变时调用set方法
//vue在set触发时,去重新解析模板,生成虚拟dom
console.log(`${k} is changed`)
obj[k] = v
}
})
})
}
</script>
</body>
</html>
Vue Set的使用
向响应式对象中添加一个 property,并确保这个新 property 同样是响应式的,且触发视图更新。它必须用于向响应式对象上添加新 property,因为 Vue 无法探测普通的新增 property (比如 this.myObject.newProperty = ‘hi’)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> Vue Set的使用</title>
<script type="text/javascript" src="../js/vue.js"></script>
<style>
li{
font-size: 24px;
line-height: 50px;
}
</style>
</head>
<body>
<div id="root">
<ul>
<li>name:{{name}}</li>
<li>age:{{age}}</li>
<li>gender:{{gender}}</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el:'#root',
data:{
name:'aq',
age:24,
}
})
</script>
</body>
</html>
用Vue.set给data添加一个gender属性,value为男;
参数:
{Object | Array} target
{string | number} propertyName/index
{any} value
返回值:设置的值。
执行:
Vue.set(vm._data,‘gender’,‘男’)
结果:
vue.js:634 [Vue warn]: Avoid adding reactive properties to a Vue instance or its root $data at runtime - declare it upfront in the data option.
报错提示无法给Vue实例或data添加一个响应式的属性。
这就是Vue.set的局限性,也就是说Vue.set只能给data里的对象追加属性,不能给data本身追加属性。也即Vue.set的target不能是vm或vm._data;
修改:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> Vue Set的使用</title>
<script type="text/javascript" src="../js/vue.js"></script>
<style>
li{
font-size: 24px;
line-height: 50px;
}
</style>
</head>
<body>
<div id="root">
<ul>
<li>name:{{student.name}}</li>
<li>age:{{student.age}}</li>
<li>gender:{{student.gender}}</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el:'#root',
data:{
student:{
name:'aq',
age:24,
},
}
})
</script>
</body>
</html>
执行:
Vue.set(vm.student,‘gender’,‘男’);
成功!
Vue如何监测数组的数据变化:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> Vue Set的使用</title>
<script type="text/javascript" src="../js/vue.js"></script>
<style>
li{
font-size: 24px;
line-height: 50px;
}
</style>
</head>
<body>
<div id="root">
<ul>
<li>name:{{student.name}}</li>
<li>age:{{student.age}}</li>
<li>gender:{{student.gender}}</li>
</ul>
<br>
<h2>friends</h2>
<ul>
<li v-for="f in student.friends">{{f.name}}--{{f.age}}</li>
</ul>
<br>
<h2>hobbies</h2>
<ul>
<li v-for="h in student.hobby">{{h}}</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el:'#root',
data:{
student:{
name:'aq',
age:24,
friends:[
{name:'jimmy',age:25},
{name:'joy',age:30}
],
hobby:[
'smoke','drink','eat'
]
},
}
})
</script>
</body>
</html>
注:
Vue想要修改student的hobby,是通过包装Array的常用7个方法:push,pop,shift,unshift,splice,sort,reverse;
这7个方法能对数组进行增删改查,Vue通过包装这7个方法来检测数组是否发生改变和作出响应。
vm.student.hobby[0] = ‘抽烟’
‘抽烟’
vm.student.hobby.push(‘学习’)
4
vm.student.hobby.pop()
‘学习’
vm.student.hobby.shift()
‘抽烟’
vm.student.hobby.splice(0,1,‘打台球’)
[‘drink’]
vm.student.hobby.push(‘学习’)
3
//经过Vue包装后的push与Array的原型push并不相同
vm.student.hobby.push === Array.prototype.push
false
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> Vue如何监测数组的数据变化</title>
<script type="text/javascript" src="../js/vue.js"></script>
<style>
li{
font-size: 24px;
line-height: 50px;
}
</style>
</head>
<body>
<div id="root">
<button @click="ageAddOne">age+1</button><br><br>
<button @click="addSex">Add Sex,default man</button><br><br>
<button @click="addFriend">Add Friend in the head</button><br><br>
<button @click="updataFriend">updata first Friend's name : ZhangSan</button><br><br>
<button @click="addHobby">add a hobby</button><br><br>
<button @click="updataHobby">updata the first hobby</button><br><br>
<ul>
<li>name:{{student.name}}</li>
<li>age:{{student.age}}</li>
<li>gender:{{student.gender}}</li>
</ul>
<br>
<h2>friends</h2>
<ul>
<li v-for="f in student.friends">{{f.name}}--{{f.age}}</li>
</ul>
<br>
<h2>hobbies</h2>
<ul>
<li v-for="h in student.hobby">{{h}}</li>
</ul>
</div>
<script type="text/javascript">
Vue.config.productionTip = false;
const vm = new Vue({
el:'#root',
data:{
student:{
name:'aq',
age:24,
friends:[
{name:'jimmy',age:25},
{name:'joy',age:30}
],
hobby:[
'smoke','drink','eat'
]
},
},
methods:{
ageAddOne(){
this.student.age++;
},
addSex(){
// Vue.set(this.student,'gender','man')
this.$set(this.student,'gender','man')
},
addFriend(){
this.student.friends.unshift({name:'tom',age:40})
},
updataFriend(){
// this.student.friends.splice(0,1,{name:'ZhangSan',age: 40})
this.student.friends[0].name='ZhangSan'
},
addHobby(){
this.student.hobby.push('learning')
},
updataHobby(){
this.student.hobby.splice(2,1,'singing')
}
}
})
</script>
</body>
</html>
数据监控总结:
1.Vue会监视data中所有层次的数据;
2.如何监视对象中的数据:
通过Setter实现监视,且要在new Vue时就传入要监测的数据。
(1)对象中后追加的属性,Vue默认不做响应式处理;
(2)如果需要给后追加的属性做响应式的处理,需要用到Vue.set API;
3.如何监视数组中的数据?
通过包装Array更新元素的7个方法来实现,本质是做了两件事:
(1)调用原生的对应方法对数组进行更新;
(2)重新解析模板,进而更新页面;
4.在Vue中修改数组的某个元素一定要用如下方法:
1.7个API: pop.push.shift.unshift.splice.sort.reverse;
2. Vue.set() 和 vm.$set
特别注意 Vue.set() 和 vm.$set 不能给vm 或 vm的根数据对象添加属性!!!