Vue:computed、watch

136 阅读2分钟

computed

  • 用途 被计算出来的属性就是计算属性;

  • 缓存 1、如果依赖的属性没有变化,就不会重新计算;

2、getter/setter 默认不会做缓存,Vue 做了特殊处理;

  • 实例 1、列表展示
new Vue({
    data(){
      return{
        list:[
          create('ff','男'),
          create('yy','女'),
          create('xx','男'),
          create('zz','女')
        ],
        gender:''
      }
    },
    computed:{
      displayList(){
        //computed 的缓存
        console.log('displayList 计算了一次')
        const hash = {
          male:'男',
          female:'女'
        }
        const {list,gender}=this
        // if(gender === ''){
        //   console.log('11')
        //   return list
        // }else if(gender === 'male'){
        //   return list.filter(u=>u.gender==='男')
        // }else if(gender === 'female'){
        //   return list.filter(u=>u.gender==='女')
        // }else{
        //   throw new Error('搞错了')
        // }
  
        //用哈希表简化函数
        if(gender === ''){
          return list
        }else if(typeof gender === 'string'){
          return list.filter(u=>u.gender === hash[gender])
        }else{
          throw new Error('搞错了')
        }
      }
    },
    template:`
      <div>
          <button @click = "gender = '' ">全部</button>
          <button @click = "gender = 'male' ">男</button>
          <button @click = "gender = 'female' ">女</button>
          <ul>
              <li v-for='(u,index) in displayList' :key='index'>{{u.name}}-{{u.gender}}</li>
          </ul>
      </div>
    `,
  }).$mount('#app')
  • 缓存
let obj1 = {
  姓: "高",
  名: "圆圆",
  get 姓名() {
    console.log('将姓与名相加')
    return this.姓 + this.名;
  },
  set 姓名(xxx){
    this.姓 = xxx[0]
    this.名 = xxx.slice(1)
  },
  age: 18
};


console.log(obj1.姓名)
console.log(obj1.姓名)
console.log(obj1.姓名)

console.log('----------------') // 精髓

// 杠精说:为什么每次都要重新计算?如果计算很复杂,不就很浪费时间吗?
// 那就缓存一下吧
// 缓存是啥?就是哈希表
const cache = {} // {'高':{'圆圆': '高圆圆'}}
let obj2 = {
  姓: "高",
  名: "圆圆",
  get 姓名() {
    // 由于对象不支持 ['高','圆圆'] 数组作为 key,只能变通一下
    if(this.姓 in cache && this.名 in cache[this.姓]){
      console.log('有缓存')
      return cache[this.姓][this.名]
    }
    // 如果 cache[this.姓] 不存在,就赋值为 {}
    // 如果 cache[this.姓] 存在,就赋值为它自己(相当于什么都不做)
    cache[this.姓] = cache[this.姓] || {} // 保底值
    cache[this.姓][this.名] = this.姓 + this.名
    console.log('将姓与名相加')
    return cache[this.姓][this.名];
  },
  set 姓名(xxx){
    this.姓 = xxx[0]
    this.名 = xxx.slice(1)
  },
  age: 18
};


console.log(obj2.姓名)
console.log(obj2.姓名)
console.log(obj2.姓名)

watch

watch 是异步的

撤销demo

new Vue({
  data: {
    n: 0,
    history: [],
    inUndoMode: false
  },
  watch: {
    n: function(newValue, oldValue) {
      console.log(this.inUndoMode)
      if(!this.inUndoMode){
        this.history.push({ from: oldValue, to: newValue });
      }
    }
  },
  // 不如用 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()
      this.inUndoMode = true
      const old = last.from
      this.n = old  //watch n 的函数会异步调用,使得 this.inUndoMode 一直为 true 
      this.$nextTick(()=>{
        this.inUndoMode = false
      })
    }
  }
}).$mount("#app");

变化

  • 简单类型看值,复杂类型(对象)看地址,和 === 规则相同
new Vue({
  data:{
    n:0,
    obj:{
      a:'a'
    }
  },
  template:`
  <div>
    <button @click="n += 1">n+1</button>
    <button @click="obj.a += 'hi'">obj.a + 'hi'</button>
    <button @click="obj = {a:'a'}">obj = 新对象</button>
  </div>
  `,
  watch:{
    n(){
      console.log('n 变了:'+this.n)
    },
    obj(){
      //obj 是复杂类型(对象),obj 的值没变,但是地址变了
      console.log('obj 变了:'+this.obj)
    },
    "obj.a":function(){
      //obj.a 是对象的值(简单类型),obj.a 的值改变,obj.a 就改变
      console.log('obj.a 变了:' + this.obj.a)
    },
  }
}).$mount("#app");

语法

  • watch:{ 括号里不能用箭头函数,不然 this 不会指向 Vue 实例 }

  • 语法一

watch:{
  o2: function(value, oldValue){},
  o3(){},
  o4: [f1, f2],
  o5: 'methodName',
  o6: {handler:fn, deep:true, immediate:true}, 
  'object.a': function(){}
}
  • 语法二 vm.$watch('xxx',fn,{deep: .., immediate: ..}) 'xxx' 可以改为一个返回字符串的函数

  • immediate 1、在选项参数中指定 immediate: true 将立即以表达式的当前值触发回调:

2、注意在带有 immediate 选项时,不能在第一次回调时取消侦听给定的 property;

3、如果你仍然希望在回调内部调用一个取消侦听的函数,应该先检查其函数的可用性:

  • deep 为了发现对象内部值的变化,可以在选项参数中指定 deep: true。注意监听数组的变更不需要这么做。 1、如果obj.a 变了,那么 obj 是否也变了?

2、如果需要答案是‘变了’,那么就用 deep:true;

3、如果需要答案是‘没有变’,那么就用默认的 deep:false

computed 和 watch 的区别

  • computed 1、computed 计算出一个值,这个值在调用的时候,不需要加括号,可以当属性用;

2、根据依赖会自动缓存,如果依赖不变,computed 的值就不会再重新计算;

  • watch 1、watch 用来监听,当数据变化时,执行一个函数;

2、immediate 表示第一次渲染时是否要执行这个函数;

3、deep 表示监听一个对象时,是否要看这个对象里面的属性值的变化。

  • 着重点 computed 着重于依赖之间的变化和缓存;watch着重于在变化时执行什么东西,而不是得出一个结果。