赞
踩
在实际的应用中,我们会有一些原始数据,同时在应用中又会有一些数据是根据某些原始数据派生出来的,针对这样的一种情况,vue
定义了一个专门用来处理这种派生数据的选项:computed。
computed计算属性:根据一组已有数据派生出一组新的数据。计算属性需要写成函数(本质上是一个对象,包含了get和set方法的对象,没有set方法时可以直接写成函数)
- //计算属性:根据一组已有数据派生出一组新的数据
- computed: {
- //计算属性是一个包含了get和set方法的对象
- showUsers:{
- get(){
-
- },
- set(){
- //更改派生数据
- }
- }
- }
需求:根据单选按钮,根据数据,进行男和女数据的分类展示
- <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>
- <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>
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>
需求:根据选中的值,分类显示男和女的数据信息
- <!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>
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的值显示当前时间戳(每次点击显示的都是缓存的时间戳数据)
普通方法:按钮每次点击后显示的时间戳都会重新获取
默认情况下,计算属性函数是一个 getter
函数,如果计算属性只有 get 需求,则可以简写
- computed: {
- now() {
- return Date.now();
- }
- // 等于
- now: {
- get() {
- return Date.now();
- }
- }
- }
但是有的时候,这种派生数据既有 get
需求,也有 set
需求
需求:全选功能,当点击全选按钮时,所有复选框选中;依次点击了所有复选框后,全选按钮也被选中
- <!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>
有的时候,我们需要的派生数据是通过异步的方式处理的,这个时候,计算属性computed就不太好用了(不能处理异步)。我们可以使用另外一个选项:watch去处理异步需求。
watch没有返回值,只注重处理过程,watch中的方法可以通过参数获取新值和旧值;
watch中的方法名必须和v-model监听的值相同
(如keyWord)
当watch到值发生改变后直接处理逻辑,比如进行赋值操作,而没有返回值
需求:通过关键字搜索
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>
对于多层数据的监听,可以使用字符串+点语法
data数据中对象有多层,就需要使用'a.b.c'进行监听
- data: {
- a:{ b: { c: 1 } }
- },
- watch: {
- 'a.b.c': function() {
- //...
- }
- }
默认情况下,watch
只对当前指定的值进行一层监听,如果需要对对象进行深度监听:
如下默认只监听了a这一层,如果想监听深层的b或者c,就需要使用deep:true属性;此时a监听时不再是函数,而是对象,对象里会去执行handler()函数
- data: {
- a:{ b: { c: 1 } }
- },
- watch: {
- a: {
- handler() {
- console.log('a deep');
- },
- deep: true
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。