Vue构造选项--options--(二)--computed、watch

213 阅读3分钟
const vm = new Vue(
options
)

继续学习Vue构造选项options

options按重要程度分为五大类

重点学习入门属性 和 进阶属性。

二、进阶属性

1、computed-计算属性

new Vue({
  data: {
    user: {}
  },
  computed: {
    displayName: {
      get() {},
      set(value) {}
    }
  },
  template: `
    <div>
      {{displayName}}
      <div>
      {{displayName}}
      <button @click="add">set</button>
      </div>
    </div>
  `,
  methods: {
    add() {}
  }
}).$mount("#app");

2、watch-侦听-撤销功能

回顾知识点1,异步是什么?

异步的特点是:我不等你反馈结果我就要往后走了。
异步示例代码

new Vue({
data:{}
 watch: {}
 template: ``
 methods:{
     undo(){
        const last = this.history.pop();
      //把当前history[]的最后一个{from: to:}值截取出来,赋值给last
      
      const old = last.from;
      //last{}里面的from值定义给old
      
      this.inUndoMode = true;
      this.n = old; 
      //把old值赋值给新的n
      // watch n 的函数会异步调用 
      this.inUndoMode = false;
     }
 }
 }).$mount("#app");

异步即代码执行的顺序问题,比如上述undo(){}里面的

      this.inUndoMode = true;
      this.n = old; 
      this.inUndoMode = false;

要先执行完undo(){},再去执行watch: {}。这就是异步。

而不是执行完

      this.inUndoMode = true;
      this.n = old;

再去执行watch: {},最后执行this.inUndoMode = false; 这样的话,因为异步原理,

      this.inUndoMode = true;
      this.n = old; 
      this.inUndoMode = false;

回撤模式的变化没有任何意义。需要对this.inUndoMode = false;也做一个异步。

撤销功能示例代码

new Vue({
  data: {
    n: 0,//展示数字
    history: [],//记录操作和撤销历史
    inUndoMode: false//默认撤销模式为false(undo是撤销的意思)
  },
  watch: {
  //实时监听n的变化,watch是完美实现历史功能的函数/方法
  
    n: function(newValue, oldValue) {
    //es6语法": function"可以省略掉。
    //newValue最新的值,oldValue之前的值
    
      console.log(`在不在撤销模式:${this.inUndoMode ? '在' : '不在'}`);
      //问号冒号表达式'a > b ? a : b;',如果'this.inUndoMode'为真,打出'在',如果为假,打出'不在'
      //学会用console.log来debugger
      
      if (!this.inUndoMode) {
      //如果当前在撤销模式,就执行下一句push()
      
        this.history.push({ from: oldValue, to: newValue });
        //用push()把n的新值和旧值记录到this.history
      }
      //省略了else(this.inUndoMode){什么也不做}
      //如果不在撤销模式,就不执行push()
    }
  },
  // 不如用 computed 来计算 displayName
  template: `
    <div>
      {{n}}
      <hr />
      <button @click="add1">+1</button>
      <button @click="add2">+2</button>
      <button @click="minus1">-1</button>
      <button @click="minus2">-2</button>
      <hr/>
      <button @click="undo">撤销</button>
      <hr/>

      {{history}}
    </div>
  `,
  methods: {
    add1() {
      this.n += 1;
    },
    add2() {
      this.n += 2;
    },
    minus1() {
      this.n -= 1;
    },
    minus2() {
      this.n -= 2;
    },
    undo() {//撤销函数
      const last = this.history.pop();
      //把当前history[]的最后一个{from: to:}值截取出来,赋值给last
      
      this.inUndoMode = true;
      console.log("ha" + this.inUndoMode);
      const old = last.from;
      //last{}里面的from值定义给old
      
      this.n = old; 
      //把old值赋值给新的n
      // watch n 的函数会异步调用
      //即在这一行立即触发watch的异步操作
      //watch所使用的也是this.$nextTick(() => {watch},0);
      
      this.$nextTick(() => {
        this.inUndoMode = false;
      },0);
      //用nextTick()把'this.inUndoMode=false'也设置为异步操作
      //这样watch的异步操作一定是优先于'this.inUndoMode = false'的异步操作的.
    }
  }
}).$mount("#app");

watch:{}和this.inUndoMode = false都使用同级别的异步操作,那么watch:{}一定比this.inUndoMode = false优先执行。

回顾知识点2pop()

pop()方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。 developer.mozilla.org/zh-CN/docs/…

回顾知识点3,问号冒号表达式

a > b ? a : b;

xiongzhongke.xyz/posts/第22课-…

3、watch-侦听-模拟computed

watch可以模拟计算属性

new Vue({
  data: {
    user: {
      email: "fangyinghang@qq.com",
      nickname: "方方",
      phone: "13812312312"
    }
  },
  watch: {
   "user.email"(){
    handler(){
        const {user: {email, nickname, phone}} = this;
    //从this里面解析出user,从user里面解析出这三个变量。
    this.displayName = nickname||email||phone
    },
    immediate: true
   },
   "user.nickname"(){
    handler(){
        const {user: {email, nickname, phone}} = this;
    this.displayName = nickname||email||phone
    },
    immediate: true
   },
   "user.phone"(){
    handler(){
        const {user: {email, nickname, phone}} = this;
    this.displayName = nickname||email||phone
    },
    immediate: true
  },
  // DRY don't repeat yourself
  // 不如用 computed 来计算 displayName
  template: `
    <div>
      {{displayName}}
      <div>
      {{displayName}}
      <button @click="add">set</button>
      </div>
    </div>
  `,
  methods: {
    add() {
      console.log("add");
      this.displayName = "圆圆";
    }
  }
}).$mount("#app");
  • 可以看出用watch模拟computed是非常低效的。
  • 因此,如果遇到既可以用watch又可以用computed做的需求,优先使用computed。
  • 他们都是在数据变化的时候去执行一个函数
  • computed主要着重于依赖之间的变化以及缓存,得出一个结果。
  • watch主要着重于

    数据变化

    的时候去执行什么函数,记录过程。而不是结果。

4、对Vue的所有监听器(比如watch)来说什么叫做数据变化?如何使用deep:true来监听所有数据变化?

回顾内存图知识

obj: {
      a: "a"
    }

obj作为一个属性,存的不是对象{ a: "a"},而是这个对象的地址。

如果执行obj.a += 'hi',obj.a变为了'ahi',也就是obj.a变了,但是obj是没有变的,因为obj的所存的{}的地址没变。

如果执行obj = {a:'a'},obj.a没变,因为a的值还是'a',但是obj变了,因为上一个{a: "a"}和下面的{a:'a'}地址不一样,下面的{a:'a'}是重新复制的一份,存在另外的地址。obj存的对象的地址变了。

简单类型变没变,看值:比如obj.a

复杂类型(对象)变没变,看地址:比如obj:{}

由于上述原理,需要引入deep:true来监听所有数据变化

浅比较

watch: {
    obj() {
      console.log("obj 变了");
    }
    //默认是一个浅比较,只有obj地址变了,才认为它变了。
  }
}).$mount("#app");

深比较引入deep:true

data: {
    obj: {
      a: "a"
    }
  },
watch: {
    obj: {
    handler(){
        console.log("obj 变了"); 
    }
    deep: true//往obj里面深处看,不但监听第一层的地址,还监听第二层-第n层的数据变化。
    //deep: false//如果不需要监听第二层-第n层,写'deep: false'
    }
  }
}).$mount("#app");

5、watch的语法

语法1

watch:{
o1:不能使用箭头函数,箭头函数里面的this是全局对象
o2:function(value, oldValue){},//两个参数(value, oldValue)是vue传给functiono3(){},//省略':function'
o4:[f1,f2],//两个函数f1 , f2,在o4变化的时候,依次执行f1 , f2
o5:'methodName',//去methods{}里面找对应的名字的函数
o6:{handler:fn, deep:true, immediate:true}
//'deep:true'表示是否往深了看
//'immediate:true'表示第一次运行的时候,就render这个函数
o7:"object.a": function(){}//字符串"object.a"作为key,function(){}为函数
}

watch文档 cn.vuejs.org/v2/api/#wat…

语法2

vm.$watch("xxx", fn, {deep:..., immediate:...})//"xxx"可以改为一个返回字符串的函数

语法2代码示例

new Vue({
    data:{
    n:0
}
    created(){
        this.$watch("n",function(){
            console.log("n 变2了")
        },{immediate:true})
        //'immediate:true'表示第一次运行的时候,就render这个函数
    },
})

面试:简述computed和watch的区别

1、computed就是计算属性的意思,watch就是监听的意思

2、各自描述

computed

  • computed是用来计算出一个值的,这个值在调用的时候不需要加括号。
  • 这个值根据依赖会自动缓存,即如果依赖不变,computed值就不会再重新计算。
new Vue({
  computed: {
    displayName: {
      get() {},
      set(value) {}
    }
  },
})

watch

  • immediate,表示在第一次渲染的时候是否要执行这个函数
  • deep,如果监听一个对象,我是否要看这个对象里面的(第二层到最后一层)属性的变化
  • 定义:如果某个属性变化了,我就去执行一个函数。

总结

1、如果一个数据依赖于其他数据,那么就使用computed设计这个数据。

2、如果需要在某个数据变化时做一些事情,使用watch来观察这个数据的变化。

关于这个问题可参见:juejin.cn/post/684490…