初学Vue
1、错误更新数据的一种方式
看如下示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../../JS/vue.js"></script>
<title>Document</title>
</head>
<body>
<div id="root">
<h2>人员列表</h2>
<button @click="changeZhao">将赵金麦改为宋祖儿</button>
<ul>
<li v-for="(p, index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el:"#root",
data:{
persons:[
{id:"001",name:"赵金麦",age:20,sex:"女"},
{id:"002",name:"周冬雨",age:27,sex:"女"},
{id:"003",name:"周杰伦",age:37,sex:"男"},
{id:"004",name:"张译",age:37,sex:"男"},
],
},
methods:{
changeZhao(){
// 能奏效的方式:将数组中某个对象中的属性分别修改
// this.persons[0].name = "宋祖儿",
// this.persons[0].age = 21;
// this.persons[0].sex = "女",
// 不生效的方式:将数组某个对象整体修改
this.persons[0] = {id:"001",name:"宋祖儿",age:21,sex:"女"}
}
}
});
</script>
</body>
</html>
this.persons[0] = {id:"001",name:"宋祖儿",age:21,sex:"女"}
使用这种方式更新数据浏览器页面不会显示修改后的数据,但内存中的数据确实被修改了,查看vm示例可知,如下:
为什么浏览器上的数据没有改变呢,因为使用这种方式时Vue没有监视到修改。
2、Vue监视对象的原理
2.1 Vue监视对象-模拟底层代码实现-简单分析
<body>
<script>
Vue.config.productionTip = false;
let data = {
name:"小明",
address:"北京"
}
// 创建一个监视的实例对象,用于监视data中属性的变化
const obs = new Observer(data);
console.log(obs);
// 准备一个vm实例对象
let vm = {};
// 让下面三值相等
vm._data = data = obs;
function Observer(obj){
// 汇总对象中所有的属性形成一个数组
const keys = Object.keys(obj);
// 遍历
keys.forEach((k)=>{
Object.defineProperty(this,k,{ // 这里this指的是Observe的实例对象obs
// 给属性添加了get函数和set函数,使其属性值能够被获取和修改
get(){
return obj[k];
},
set(val){
console.log(`${k}被改了,重新解析模板,生成虚拟DOM,新旧DOM进行比较`)
obj[k] = val;
}
})
})
}
</script>
</body>
分析:监测的最重要部分就是每次调用set函数进行修改时,都会重新进行模板解析,生成新DOM和旧DOM进行比较,最后刷新页面,展示更新后的内容。
补充:
这只是Vue底层代码的部分实现,这里只能通过vm._data.属性="新属性值"的方式对属性值进行修改,而完整代码因实现了数据代理,而可以使用vm.属性名的方式修改属性值;此外,这里data中的数据是只有一层的,而Vue底层代码中通过实现深拷贝,可以将数据中嵌套的内容也找出来,并且添加get和set函数。
2.2 API --Vue.set()的使用
看如下代码:
<body>
<div id="root">
<h3>学校名称:{{schoolName}}</h3>
<h3>学校地址:{{address}}</h3>
<hr>
<h3>姓名:{{student.name}}</h3>
<h3>性别:{{student.sex}}</h3>
<h3>年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h3>
<h3>朋友们:</h3>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el:"#root",
data:{
schoolName:"清华",
address:"北京",
student:{
name:"tom",
age:{
rAge:40,
sAge:29
},
// 准备一个数组
friends:[
{name:"jerry",age:35},
{name:"jack",age:33}
]
},
}
});
</script>
</body>
</html>
在模板中出现了“性别”,但在data中却并没有性别这个属性和属性值,该如何添加这个属性并且设置值呢?
可不可以这么想,直接操作vm实例中的_data,创建一个属性sex,并且给它设置值呢?答案是否定的,结果和原因如下:
最影响这个结果的就是添加的属性sex中没有set函数,这个set方式是刷新页面最主要的方法,也是页面不显示结果的原因。
- Vue.set()方法
Vue的API中提供了这么一种方法,Vue.set()方法可以给data中添加属性和属性值。
语法如下:Vue.set(target,propertyName/index,value)
参数:target是目标对下个你,propertyName/index是属性名,value是属性值
例如:
因为Vue实现了数据代理,所以上面的代码还可以简写
下面再代码中实现添加性别属性的值的操作,给一个按钮绑定点击事件,点击后给性别属性值添加属性值。
<body>
<div id="root">
<h3>学校名称:{{schoolName}}</h3>
<h3>学校地址:{{address}}</h3>
<hr>
<button @click="addSexValue">给性别添加一个值</button>
<h3>姓名:{{student.name}}</h3>
<h3 v-if="student.sex">性别:{{student.sex}}</h3>
<h3>年龄:真实{{student.age.rAge}},对外{{student.age.sAge}}</h3>
<h3>朋友们:</h3>
<ul>
<li v-for="(f,index) in student.friends" :key="index">
{{f.name}}--{{f.age}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el:"#root",
data:{
schoolName:"清华",
address:"北京",
student:{
name:"tom",
age:{
rAge:40,
sAge:29
},
// 准备一个数组
friends:[
{name:"jerry",age:35},
{name:"jack",age:33}
]
},
},
methods:{
addSexValue(){
// 使用Vue对象的set方法添加
Vue.set(this.student,"sex","男");
// 还有第二种方式:使用vm实例添加
this.$set(this.student,"sex","男");
}
}
});
</script>
</body>
需要注意的是,Vue.set()方法有局限,(1)不能给data中添加属性(2)不能给vm实例上添加属性
例如:Vue.set(vm.data,"leader","帅男孩")
肯定会报错的,再例如:vue.set(vm,"leader","老男孩")
同样会报错
3、Vue检测数组的原理
简单看下如下的案例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../../JS/vue.js"></script>
<title>Document</title>
</head>
<body>
<div id="root">
<h3>学生信息</h3>
<h4>姓名:{{name}}</h4>
<h4>性别:{{sex}}</h4>
<h4>爱好:</h4>
<ul>
<li v-for="(h,index) in hobbies" :key="index">
{{h}}
</li>
</ul>
</div>
<script>
Vue.config.productionTip = false;
const vm = new Vue({
el:"#root",
data:{
name:"tom",
sex:"男",
hobbies:["游戏","电影","音乐"],
}
});
</script>
</body>
</html>
查看浏览器控制台可以发现,并没有为数组中的元素单独提供get/set方法,也就意味着不能通过数组索引来赋值从而对元素数据进行修改
不能通过数组索引来改变数组中元素的值,因为vue不承认。
正确操作数组的方式
- 第一种方式:
利用能改变原数组的API来对数组操作时才可以更改数组的值(例如:数组API中的push,pop,shift,unshifit,splice,sort,reverse方法),因为使用这类API时会让模板重新解析,从而成功修改数组。
为什么通过数组API的这期中方法可以对数组数组进行操作呢?因为Vue对数组API中的方法进行了包装,也就是说Vue中的数组API方法并不完全等同于普通数组API中的方法,证明如下:
vue中的push同样能够发挥数组API中push的作用,并且在发挥作用之后,会对模板重新进行解析,这就是与数组API中的push的区别。
- 第二种方式:
利用Vue.set()方法来进行数据修改,如下:
4、简单总结
简单总结:
1、vue会监视data中所有层次的数据
2、如何检测对象中的数据:
(1)对象中后追加的属性,Vue默认不做响应式处理
(2)如需给后添加的属性做响应式处理,可以使用下面的方式: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的根数据对象添加属性