一、computed计算属性
在实际的应用中,我们会有一些原始数据,同时在应用中又会有一些数据是根据某些原始数据派生出来的,针对这样的一种情况,vue
定义了一个专门用来处理这种派生数据的选项:computed。
computed计算属性:根据一组已有数据派生出一组新的数据。计算属性需要写成函数(本质上是一个对象,包含了get和set方法的对象,没有set方法时可以直接写成函数)
//计算属性:根据一组已有数据派生出一组新的数据
computed: {
//计算属性是一个包含了get和set方法的对象
showUsers:{
get(){
},
set(){
//更改派生数据
}
}
}
需求:根据单选按钮,根据数据,进行男和女数据的分类展示
1.1普通方法实现
1.1.1单选按钮选中效果
- 使用@click事件绑定点击事件;
- methods里编写事件过程,并筛选数据;
- 单选是否选中根据data中某个值决定this.gender = e.target.value,如, :checked="gender==='男'",并更改数据中gender的值
<body>
<div id="app">
<input type="radio" :checked="gender==''" @click="checkGender" value="" />全部
<input type="radio" :checked="gender=='男'" @click="checkGender" value="男" />男
<input type="radio" :checked="gender=='女'" @click="checkGender" value="女" />女
<hr>
<ul>
<li v-for="user in users" :key="user.id">{{user.username}} ---- {{user.gender}}</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
gender:'',// 用于存储checked的性别
users: [
{ id: 1, username: 'baogege', gender: '男' },
{ id: 2, username: 'mt', gender: '男' },
{ id: 3, username: 'haigege', gender: '男' },
{ id: 4, username: 'zMouse', gender: '男' },
{ id: 5, username: 'reci', gender: '女' },
{ id: 6, username: 'lisi', gender: '女' }
]
},
methods:{
checkGender(e){
this.gender = e.target.value;
}
}
});
</script>
</body>
1.1.2实现数据筛选,男女分类显示
<body>
<div id="app">
<input type="radio" :checked="gender==''" @click="checkGender" value="" />全部
<input type="radio" :checked="gender=='男'" @click="checkGender" value="男" />男
<input type="radio" :checked="gender=='女'" @click="checkGender" value="女" />女
<hr>
<ul>
<li v-for="user in showUsers" :key="user.id">{{user.username}} ---- {{user.gender}}</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
// 数据单独放出来,gender=''时showUsers才能有数据
let users = [
{ id: 1, username: 'baogege', gender: '男' },
{ id: 2, username: 'mt', gender: '男' },
{ id: 3, username: 'haigege', gender: '男' },
{ id: 4, username: 'zMouse', gender: '男' },
{ id: 5, username: 'reci', gender: '女' },
{ id: 6, username: 'lisi', gender: '女' }
];
let app = new Vue({
el: "#app",
data: {
gender: '',// 用于存储checked的性别
showUsers: users, //(派生数据)因为不想改变原数据,通过变量储存更改后的数据,初始化数据为users
users: users,
},
methods: {
checkGender(e) {
this.gender = e.target.value;
// 对数据实现筛选
if(this.gender==""){
this.showUsers = this.users;
}else{
this.showUsers = this.users.filter(user => this.gender == user.gender);
}
}
}
});
</script>
</body>
1.2使用computed实现
1.2.1使用v-model双绑实现radio选中效果
v-model="gender"直接和gender值进行绑定;
不再需要click事件,可以直接实现数据双绑(当v-model的值即gender的值和value的值相等时就会被选中;改变checked属性时又会反向更新data中gender的值);
<body>
<div id="app">
<input type="radio" v-model="gender" value="" />全部
<input type="radio" v-model="gender" value="男" />男
<input type="radio" v-model="gender" value="女" />女
<hr>
<ul>
<li v-for="user in users" :key="user.id">{{user.username}} ---- {{user.gender}}</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
gender:'',// 用于存储checked的性别
users: [
{ id: 1, username: 'baogege', gender: '男' },
{ id: 2, username: 'mt', gender: '男' },
{ id: 3, username: 'haigege', gender: '男' },
{ id: 4, username: 'zMouse', gender: '男' },
{ id: 5, username: 'reci', gender: '女' },
{ id: 6, username: 'lisi', gender: '女' }
]
},
});
</script>
</body>
1.2.2使用computed实现数据筛选
需求:根据选中的值,分类显示男和女的数据信息
- showUsers会监听users数据的变化,showUsers会依赖users数据的变化,users变化后,showUsers就会变化
- 如果计算属性只有get逻辑,没有set逻辑时则可以简写
- 最后showUsers的数据,相当于get()方法执行后返回的结果(类似于Object.defineProperty(obj,'x',{}}数据劫持后,obj.x执行的结果))
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>计算属性</title>
</head>
<body>
<div id="app">
<label><input type="radio" v-model="gender" value="" />全部</label>
<label><input type="radio" v-model="gender" value="男" />男</label>
<label><input type="radio" v-model="gender" value="女" />女</label>
<hr>
<ul>
<li v-for="user in showUsers" :key="user.id">{{user.username}} ---- {{user.gender}}</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
gender: '',// 用于存储checked的性别
users: [
{ id: 1, username: 'baogege', gender: '男' },
{ id: 2, username: 'mt', gender: '男' },
{ id: 3, username: 'haigege', gender: '男' },
{ id: 4, username: 'zMouse', gender: '男' },
{ id: 5, username: 'reci', gender: '女' },
{ id: 6, username: 'lisi', gender: '女' }
]
},
// 注意:computed是vue实例的一个属性,不是data中的属性
computed: {
// 会监听users数据的变化,showUsers会依赖users数据的变化,users变化后,showUsers就会变化
// showUsers: {
// get() {
// // 返回true即返回所有数据
// return this.gender === "" ? this.users : this.users.filter(user => this.gender == user.gender);
// },
// set() {
// }
// }
// 如果计算属性只有get逻辑,没有set逻辑时则可以简写
// 最后showUsers的数据,相当于get()方法执行后返回的结果(类似于Object.defineProperty(obj,'x',{}}数据劫持后,obj.x执行的结果))
showUsers(){
return this.gender === "" ? this.users : this.users.filter(user => this.gender == user.gender);
}
}
});
</script>
</body>
</html>
1.3 computed特性及缓存
普通函数也能解决以上问题,为什么还要使用computed?主要是因为computed可以缓存
- 计算属性类似
getter
和setter
,当访问某个计算属性的时候,就会调用computed
中同名的函数,函数的返回值将作为该计算属性的值 - 计算属性的值依赖计算函数中依赖的其它响应式数据
- 计算属性的值可以缓存,如果依赖的其它响应式数据没有发生变化,但多次访问该计算属性,得到结果是最近一次变化产生的值(相对于调用方法得到结果在某些时候性能要好一些)
- 计算属性不支持异步
需求:普通方法和computed分别获取时间戳
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>计算属性</title>
</head>
<body>
<div id="app">
<p>{{now}}</p>
<button @click="showDate=!showDate">showDate</button>
<!-- 此处打印的是缓存后的 now-->
<p v-if="showDate">{{now}}</p>
<hr>
<p>{{getNow()}}</p>
<button @click="showDate = !showDate">getNow</button>
<p v-if="showDate">{{getNow()}}</p>
</div>
<script src="./js/vue.js"></script>
<script>
let a = 1;
let app = new Vue({
el: "#app",
data: {
b:1,
showDate: false
},
computed: {
now() {
// 如果计算属性依赖的数据没有发生任何变化,该计算属性的get是不会调用的(即缓存)
// 这里监听的数据是其依赖的data里的showDate数据,如果showDate数据变了就会重新计算(并不是data里的任何数据变了都会computed监听)
// 如果监听的是let a =1这种数据,此处now()方法里根本没有依赖这个数据,即使a和b改变了,也不会重新计算
return Date.now();
}
},
methods:{
getNow(){
return Date.now();
}
}
});
</script>
</body>
</html>
computed缓存:按钮点击时根据showDate的值显示当前时间戳(每次点击显示的都是缓存的时间戳数据)
普通方法:按钮每次点击后显示的时间戳都会重新获取
1.4 计算属性的getter与setter方法
默认情况下,计算属性函数是一个 getter
函数,如果计算属性只有 get 需求,则可以简写
computed: {
now() {
return Date.now();
}
// 等于
now: {
get() {
return Date.now();
}
}
}
1.4.1计算属性computed的set需求
但是有的时候,这种派生数据既有 get
需求,也有 set
需求
需求:全选功能,当点击全选按钮时,所有复选框选中;依次点击了所有复选框后,全选按钮也被选中
- 如果使用v-bind:checked="checkAll"不会反向绑定;
- v-model会双向绑定,computed如果没有set()方法,但是使用v-model设置值就会报错;
- 有set()方法时会自动将当前最新的值传入,注意不能直接通过this.checkAll得到值
- 当this.users里面的数据发生改变时出发get()方法,产生新的checkAll的值;当对checkAll进行赋值时,触发set();
- 可以通过foreach或者map方法修改数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>计算属性</title>
</head>
<body>
<div id="app">
<label><input type="radio" v-model="gender" value="" />全部</label>
<label><input type="radio" v-model="gender" value="男" />男</label>
<label><input type="radio" v-model="gender" value="女" />女</label>
<hr>
<ul>
<!-- 使用v-model将复选框和数据进行双绑定 -->
<li v-for="user in showUsers" :key="user.id"><input type="checkbox" v-model="user.checked"/>{{user.username}} ---- {{user.gender}}</li>
</ul>
<!-- 使用v-model将checkAll和复选框进行双绑 -->
<input type="checkbox" v-model="checkAll"/> 全选
</div>
<script src="./js/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
gender: '',// 用于存储checked的性别
users: [
{ id: 1, username: 'baogege', gender: '男' ,checked:false},
{ id: 2, username: 'mt', gender: '男' ,checked:false},
{ id: 3, username: 'haigege', gender: '男' ,checked:false},
{ id: 4, username: 'zMouse', gender: '男' ,checked:false},
{ id: 5, username: 'reci', gender: '女' ,checked:false},
{ id: 6, username: 'lisi', gender: '女' ,checked:false}
]
},
// 注意:computed是vue实例的一个属性,不是data中的属性
computed: {
showUsers(){
return this.gender === "" ? this.users : this.users.filter(user => this.gender == user.gender);
},
// checkAll是返回的派生数据(真假check)
checkAll:{
// 当this.users里面的数据发生改变时出发get()方法,产生新的checkAll的值
get(){
// 全选函数every user.checked==true可以直接写为user.checked
return this.users.every(user=>user.checked);
},
// 如果使用v-bind:checked="checkAll"不会反向绑定
// v-model会双向绑定,computed如果没有set()方法,但是使用v-model设置值就会报错;
// 有set()方法时会自动将当前最新的值传入,注意不能通过this.checkAll得到
// 当对checkAll进行赋值时,出发set()
set(newVal){
// 设置当全选按钮点击时,同时更新data数据,将所有checked取反
// this.users.forEach(user => {
// user.checked = !user.checked;
// });
// map():接收一个函数,将原数组中的所有元素用这个函数处理后放入新数组返回。
this.users = this.users.map(user=>{
return {//...user生成新数组
...user,checked:newVal
}
});
}
}
}
});
</script>
</body>
</html>
二、watch(可实现异步)
有的时候,我们需要的派生数据是通过异步的方式处理的,这个时候,计算属性computed就不太好用了(不能处理异步)。我们可以使用另外一个选项:watch去处理异步需求。
watch没有返回值,只注重处理过程,watch中的方法可以通过参数获取新值和旧值;
watch中的方法名必须和v-model监听的值相同
(如keyWord)
当watch到值发生改变后直接处理逻辑,比如进行赋值操作,而没有返回值
- 因为watch是只有执行过程没有返回,所以需要设置showUsers接收过滤后的数据(computed是直接返回到showUsers作为结果所以不需要)
需求:通过关键字搜索
username(使用setTimeout模拟异步)
computed不能实现异步:
即使写了new Promise()依然无法实现异步处理
<body>
<div id="app">
<input type="text" v-model="keyWord"/>
<hr>
<ul>
<li v-for="user of showUsers" :key="user.id">{{user.username}}</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
keyWord:'',
users: [
{ id: 1, username: 'baogege', gender: '男'},
{ id: 2, username: 'mt', gender: '男'},
{ id: 3, username: 'haigege', gender: '男'},
{ id: 4, username: 'zMouse', gender: '男'},
{ id: 5, username: 'reci', gender: '女'},
{ id: 6, username: 'lisi', gender: '女'}
],
},
computed: {
showUsers: {
get(){
// 如果没有定时器模拟异步,可以正常显示,有异步后无法正常过滤
setTimeout(() => {
return this.keyWord==""?false:this.users.filter(user=>user.username.includes(this.keyWord));
}, 1000);
}
}
},
});
</script>
</body>
watch可以实现异步:
<body>
<div id="app">
<input type="text" v-model="keyWord"/>
<hr>
<ul>
<li v-for="user of showUsers" :key="user.id">{{user.username}}</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
let app = new Vue({
el: "#app",
data: {
keyWord:'',
users: [
{ id: 1, username: 'baogege', gender: '男'},
{ id: 2, username: 'mt', gender: '男'},
{ id: 3, username: 'haigege', gender: '男'},
{ id: 4, username: 'zMouse', gender: '男'},
{ id: 5, username: 'reci', gender: '女'},
{ id: 6, username: 'lisi', gender: '女'}
],
// 因为watch是只有执行过程没有返回,所以需要设置showUsers接收过滤后的数据(computed是直接返回到showUsers作为结果所以不需要)
showUsers:[]
},
watch: {
// 注意watch中方法名是v-model监听的值keyWord
keyWord(newVal,oldVal){
console.log(newVal,oldVal);
setTimeout(() => {
if(this.keyWord!=""){
this.showUsers = this.users.filter(user=>user.username.includes(this.keyWord));
}
}, 1000);
}
}
});
</script>
</body>
2.1 多层监听
对于多层数据的监听,可以使用字符串+点语法
data数据中对象有多层,就需要使用'a.b.c'进行监听
data: {
a:{ b: { c: 1 } }
},
watch: {
'a.b.c': function() {
//...
}
}
2.2 深度监听
默认情况下,watch
只对当前指定的值进行一层监听,如果需要对对象进行深度监听:
如下默认只监听了a这一层,如果想监听深层的b或者c,就需要使用deep:true属性;此时a监听时不再是函数,而是对象,对象里会去执行handler()函数
data: {
a:{ b: { c: 1 } }
},
watch: {
a: {
handler() {
console.log('a deep');
},
deep: true
}
}