Vue2-计算属性、监视属性(侦听属性)

:知不足而奋进,望远山而前行

更多Vue知识请点击——Vue.js

VUE2-Day2

    • 计算属性
      • 1、使用插值语法和methods拼接姓名
      • 2、使用计算属性拼接姓名
      • 3、计算属性总结
      • 4、计算属性简写
    • 监视属性(侦听属性)
      • 1、天气案例
      • 2、引出监视属性
        • 监视属性的两种写法
          • ①new Vue时传入watch配置
      • 3、深度监视
      • 4、监视的简写形式
    • 计算属性(computed)和监视属性(watch)对比

计算属性

顾名思义,计算属性就是计算出来的属性,英文名computed。
这里要和data和methods里的东西区分,data里的属性,methods里的函数,你写的都会原封不动放到vm里,但是computed里面的东西写的是对象,最后放到vm里的是这个对象的名,值是get里的返回值。

1、使用插值语法和methods拼接姓名

使用插值语法可以实现,但是如果需求复杂,里面的逻辑写一大堆,不便于观察,违背了Vue的推荐风格(简单表达式)。
使用methods不错,但是有一个问题,就是每次用到fullName都会调用一次,缓存太多

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>姓名案例-插值语法实现和methods实现title>
    
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    <div id="root">
      姓:<input type="text" v-model:value="firstName" /><br />
      
      名:<input type="text" v-model="lastName" /><br />
      
      
      全名:<span>{{firstName.slice(0,3)}}-{{lastName}}span><br />

      
      全名:<span>{{fullName()}}span><br />
      全名:<span>{{fullName()}}span><br />
    div>

    <script type="text/javascript">
      new Vue({
        el: "#root",
        data: {
          firstName: "张",
          lastName: "三",
        },
        methods: {
          fullName() {
            console.log("调用");
            return this.firstName + "-" + this.lastName;
          },
        },
      });
    script>
  body>
html>

2、使用计算属性拼接姓名

1、没有缓存问题,第一次调用之后就不会再调用,节省内存
2、计算属性里面的属性要写成对象的形式,每个对象里面都有getter和setter
3、fullName实际上就是firstName和lastName经过一番计算得到的玩意儿,直接到vm身上成为vm的属性
4、当有人读取fullName时,get调用,返回值作为fullName的值
5、set什么时候调用? fullName被修改时调用,这里边呢有个连锁反应,我手动修改vm.fullName导致firstName和lastName被修改,Vue模板重新解析,页面刷新,firstName和lastName的修改又导致get被重新执行(依赖的数据变了),返回新的fullName,计算属性计算属性,就是一直在计算,所以要想改fullName,必须改它依赖的值才行。

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>姓名案例-计算属性实现title>
    
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    <div id="root">
      姓:<input type="text" v-model:value="firstName" /><br />
      
      名:<input type="text" v-model="lastName" /><br />
      
      
      全名:<span>{{fullName()}}span><br />
      全名:<span>{{fullName()}}span><br />
      全名:<span>{{fullName()}}span>
    div>

    <script type="text/javascript">
      new Vue({
        el: "#root",
        data: {
          firstName: "张",
          lastName: "三",
        },
        computed: {
          //计算属性里面的属性要写成对象的形式,每个对象里面都有getter和setter
          //这个fullName实际上就是firstName和lastName经过一番计算得到的玩意儿,直接给到vm身上
          fullName: {
            //get有什么用?当有人读取fullName时,get调用,返回值作为fullName的值
            //get什么时候调用? 1.初次读取fullName  2.get里用到的数据发生了改变
            get() {
              //此处的this是vm
              return this.firstName + "-" + this.lastName;
            },
            //set什么时候调用? fullName被手动修改时调用
            //这里边呢有个连锁反应,我手动修改vm.fullName导致firstName和lastName被修改,Vue模板重新解析
            //页面刷新,firstName和lastName的修改又导致get被重新执行(依赖的数据变了),返回新的fullName
            //计算属性计算属性,就是一直在计算,所以要想改fullName,必须改它依赖的值
            set(value) {
              const arr = value.split("-");
              this.firstName = arr[0];
              this.lastName = arr[1];
            },
          },
        },
      });
    script>
  body>
html>

3、计算属性总结

定义:要用的属性不存在,要通过Vue中已有属性计算得来。
原理:底层借助了Objcet.defineproperty方法提供的getter和setter。
get函数什么时候执行?
(1).初次读取时会执行一次。
(2).当依赖的数据发生改变时会被再次调用。
优势:与methods实现相比,内部有缓存机制(复用),效率更高,调试方便。
备注:
1.计算属性最终会出现在vm上,直接读取使用即可,不用加括号,和methods区别一下子。
2.如果计算属性要被修改,那必须写set函数去响应修改,且set中要引起计算时依赖的数据发生改变,这很关键。

4、计算属性简写

计算属性在实际开发中更多的情况是只读取不修改的,这种只读不写的时候可以使用简写:

<!-- 准备好一个容器 -->
<div id="root">
    : <input type="text" v-model="firstName"><br><br>
    : <input type="text" v-model="lastName"><br><br>
    <!-- 拼接姓名实现"张-三"联动 -->

    <!-- 使用计算属性,只调用一次,节省内存 -->
    姓名: <span>{{fullName}}</span>
</div>

<script>
    const vm = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三'
        },
        //简写
        computed: {
            fullName() {
                console.log('fullName被读取了');
                return this.firstName + '-' + this.lastName;
            }
        }
    })
</script>

这里一定要注意,这个fullName是一个属性,不是一个方法,只不过是调用里面的getter,把返回值直接给了vm罢了,所以直接{{fullName}}就可以搞出来,不用加小括号调用!!

监视属性(侦听属性)

1、天气案例

实现点击按钮切换天气

DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>天气案例title>
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    
    <div id="root">
      
      
      <h2>今天天气很{{isHot ? '炎热': '寒冷'}}h2>
      
      <h2>今天天气很{{info}}h2>

      
      
      <button @click="changeWeather">切换天气button>
    div>

    <script type="text/javascript">
      new Vue({
        el: "#root",
        data: {
          isHot: true,
        },
        computed: {
          info() {
            return this.isHot ? "炎热" : "凉爽";
          },
        },
        methods: {
          changeWeather() {
            //注意这里的isHot要加this
            this.isHot = !this.isHot;
          },
        },
      });
    script>
  body>
html>

2、引出监视属性

1、监视属性里有个handler函数,当被监视的属性(例如isHot)被修改时调用handler函数
2、handler函数传两个参数,分别是:(修改之后的值,修改之前的值)
3、监视的属性必须存在,才能进行监视!!既可以是data里的属性(isHot),也可以是计算属性(info)

监视属性的两种写法

①new Vue时传入watch配置
<div id="root">
    <!-- 实现点击按钮切换天气 -->
    <!-- 写法1:利用插值语法和三元表达式 -->
    <h2>今天天气很{{isHot ? '炎热': '寒冷'}}</h2>

    <!-- 写法2:利用计算属性 -->
    <h2>今天天气很{{info}}</h2>
    <!--    绑定事件的时候:@xxx="yyy" yyy可以写一些简单的语句,但是最好别这么干-->
    <!--    <button @click="isHot = !isHot">切换天气</button>-->
    <button @click='change'>点击切换天气</button>
</div>
<script>
    new Vue({
        el: '#root',
        data: {
            isHot: true
        },
        methods: {
            change() {
                this.isHot = !this.isHot;
            }
        },
        computed: {
            info() {
                //注意这里的isHot要加this
                return this.isHot ? '炎热' : '寒冷';
            }
        },
        watch: {
            isHot: {
                immediate: true,   //初始化时先调用一次handler 
                //当isHot被修改时调用handler函数
                handler(newVal, oldVal) {
                    console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
                }
            },
            //watch不只可以监视data中的属性,还可以监视计算属性
            info: {
                immediate: true,   //初始化时先调用一次handler 
                //当isHot被修改时调用handler函数
                handler(newVal, oldVal) {
                    console.log('info被修改了', '改之前是' + oldVal, '改之后是' + newVal);
                }
            }
        }
    })  
</script>

②通过vm.$watch监视

const vm = new Vue({
    el: '#root',
    data: {
        isHot: true
    },
    methods: {
        change() {
            this.isHot = !this.isHot;
        }
    },
    computed: {
        info() {
            //注意这里的isHot要加this
            return this.isHot ? '炎热' : '寒冷';
        }
    }
})

	vm.$watch('isHot', {
	    immediate: true,   //初始化时先调用一次handler 
	    //当isHot被修改时调用handler函数
	    handler(newVal, oldVal) {
	        console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
	    }
	})
	
	vm.$watch('info', {
	    immediate: true,   //初始化时先调用一次handler 
	    //当isHot被修改时调用handler函数
	    handler(newVal, oldVal) {
	        console.log('info被修改了', '改之前是' + oldVal, '改之后是' + newVal);
	    }
	})

3、深度监视

(1)Vue中的watch默认不监测对象内部值的改变(一层,如果监视那么对象地址不变就不变)。
(2)配置deep:true可以监测对象内部值改变(多层)。
备注:

  • Vue自身可以监测对象内部值的改变,但Vue提供的watch默认不可以!想要可以就要手动开启deep:true
  • 使用watch时根据数据的具体结构,决定是否采用深度监视。
DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>深度监视title>
    <script type="text/javascript" src="../js/vue.js">script>
  head>
  <body>
    
    <div id="root">
      <h2>今天天气很{{info}}h2>
      <button @click="change">点击切换天气button>
      <hr />
      <h3>a的值是:{{numbers.a}}h3>
      <button @click="numbers.a++">点击让a+1button>
      <hr />
      <h3>b的值是:{{numbers.b}}h3>
      <button @click="numbers.b++">点击让b+1button>
      <h3>强制让numbers变h3>
      <button @click="numbers = {a:666, b:999}">点我改变numbersbutton>
    div>
    <script>
      new Vue({
        el: "#root",
        data: {
          isHot: true,
          numbers: {
            a: 1,
            b: 2,
          },
        },
        methods: {
          change() {
            this.isHot = !this.isHot;
          },
        },
        computed: {
          info() {
            //注意这里的isHot要加this
            return this.isHot ? "炎热" : "寒冷";
          },
        },
        watch: {
          isHot: {
            // immediate: true, //初始化时先调用一次handler
            //当isHot被修改时调用handler函数
            handler(newVal, oldVal) {
              console.log(
                "isHot被修改了",
                "改之前是" + oldVal,
                "改之后是" + newVal
              );
            },
          },

          //监视多级结构中某个属性的变化使用引号包起来(之前不加引号都是简写形式)
          "numbers.a": {
            handler() {
              console.log("a被改变了");
            },
          },

          //如果下面这么写,即使ab变了也不会执行handler,因为这么写意思是监视numbers这个属性
          //而这个属性值是一个对象,只要numbers对象的地址不变就不变,除非像上面div写的暴力方法(强制改变)
          numbers: {
            handler() {
              console.log("numbers改变了");
            },
          },

          //但是如果加个deep,就可以监视多级结构中某个属性的变化,ab变了numbers也变
          //监视多级结构中所有属性的变化
          numbers: {
            deep: true,
            handler() {
              console.log("numbers被改变了");
            },
          },
        },
      });
    script>
  body>
html>

4、监视的简写形式

如果监视只需要handler,不需要其他配置项(如immediate、deep等)时,可以进行简写。

  • 形式1:

    	watch: {
                    //正常写法
                     isHot: {
                        //immediate: true,   //初始化时先调用一次handler 
                        //当isHot被修改时调用handler函数
                        handler(newVal, oldVal) {
                            console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
                         }
                    },
                    
    				//简写
                    // 如果handler函数中没有其他的配置项(deep,immediate等),可以简写成以下形式
                    isHot(newVal, oldVal) {
                        console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
                    }
                }
    
  • 形式2:

    	//复杂形式
    	vm.$watch('isHot', {
    	    //当isHot被修改时调用handler函数
    	    handler(newVal, oldVal) {
    	        console.log('isHot被修改了', '改之前是' + oldVal, '改之后是' + newVal);
    	    }
    	})
    	//简写形式
    	vm.$watch('isHot',function (newVaule,oldVaule) {
    	        console.log(newVaule,oldVaule);
    	    })
    

计算属性(computed)和监视属性(watch)对比

首先我们要知道,这两个属性并不冲突,在开发中可以两个同时使用,这里只是对比一下他们在相同情况下的区别。

对比拼接姓名的案例


<div id="root">
    姓: <input type="text" v-model="firstName"><br><br>
    名: <input type="text" v-model="lastName"><br><br>
    
    姓名: <span>{{fullName}}span>
div>

使用计算属性:

const vm = new Vue({
    el: '#root',
    data: {
        firstName: '张',
        lastName: '三'
    },
    computed: {
        fullName() {
            console.log('fullName被读了');
            return this.firstName + '-' + this.lastName;
        }

    }
})

使用监视属性:

   const vm = new Vue({
        el: '#root',
        data: {
            firstName: '张',
            lastName: '三',
            fullName: '张-三'
        },
        watch: {
            firstName: {
                handler(newVal, oldVal) {
                    this.fullName = newVal + '-' + this.lastName;
                }
            },
            lastName: {
                handler(newVal, oldVal) {
                    this.fullName = this.firstName + '-' + newVal;
                }
            },
        }
    })

这么看好像计算属性更简单,那么我们是不是可以都使用计算属性不用监视属性了呢?这里watch有一个优点,它可以异步。
比如我如果手动改了firstName要等一秒钟再改fullName呢?
用监视属性,我们就可以这么写:

watch: {
    firstName: {
        handler(newVal, oldVal) {
            setTimeout(() => {
                this.fullName = newVal + '-' + this.lastName;
            }, 1000);
        }
    }
}

总结

1、computed能完成的功能,watch都可以完成。
2、watch能完成的功能,computed不一定能完成,例如:watch可以进行异步操作。

两个重要的原则
1、所被Vue管理的函数,最好写成普通函数,这样this的指向才是vm 或 组件实例对象
2、所有不被Vue所管理的函数(定时器的回调函数ajax的回调函数Promise的回调函数等等),最好写成箭头函数,这样this的指向才是vm 或 组件实例对象

你可能感兴趣的:(Vue2+Vue3,javascript,开发语言,前端,es6,vue.js,vue)