Vue-computed和watch

1,487 阅读2分钟

进阶属性

  • computed:计算属性。不需要加括号,它会根据依赖是否变化来进行缓存
  • watch: 监听。一旦data变化,就会执行的函数。需要了解options.watch用法,this.$watch用法和deep, immediate的含义
  • directives: 指令。内置指令v-if,v-for,v-bind,v-on, 还有自定义指令,例如v-foucs。指令的目的是减少重复的DOM操作
  • mixin: 混入。重复三次之后的出路,需要了解 混入vs全局混入。选项会自动合并,混入的目的就是减少重复的构造选项
  • extends: 继承。需要了解Vue.extend,因为觉得用了mixin还是重复,于是自己写了一个View,它继承vue, 然后可以预先定义其他的构造选项。继承的目的就是减少重复的构造选项,使用了ES 6extends
  • provide/inject: 跨代通信。爷爷想和孙子通信,祖宗想和所有后代通信,一般情况下可以使用全局变量,但是Vue不想使用全局变量,使用了局部的全局变量。

computed-计算属性

定义

被计算出来的属性就是计算属性

场景

当需要对data的数据进行显示计算时

实例

let id = 0;
const createUser = (name, gender) => {
  id += 1;
  return { id: id, name: name, gender: gender };
};
new Vue({
  data() {
    return {
      users: [
        createUser("方方", "男"),
        createUser("圆圆", "女"),
        createUser("小新", "男"),
        createUser("小葵", "女")
      ],
      gender: ""
    };
  },
  computed: {
    displayUsers() {
      const hash = {
        male: "男",
        female: "女"
      };
      const { users, gender } = this;
      if (gender === "") {
        return users;
      } else if (typeof gender === "string") {
        return users.filter(u => u.gender === hash[gender]);
      } else {
        throw new Error("gender 的值是意外的值");
      }
    }
  },
  methods: {
    setGender(string) {
      this.gender = string;
    }
  },

  template: `
    <div>
      <div>
      <button @click="setGender('') ">全部</button>
      <button @click="setGender('male')">男</button>
      <button @click="setGender('female')">女</button></div>
      <ul>
        <li v-for="(u,index) in displayUsers" :key="index">{{u.name}} - {{u.gender}}</li>
      </ul>
      
    </div>
  `
}).$mount("#app");

缓存

如果依赖的属性没有变化,就不会重新继续。getter/setter默认不会做缓存,Vue做了处理,有所改动,就可以缓存,节省资源,避免重复渲染。

计算属性缓存 vs 方法

计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。(不接受新的参数,只是基于data里面的数据进行计算) 方法是只要其他数据发生变化,触发重新渲染时,调用方法将总会再次执行函数,性能开销比较大。(可以接受参数)

watch-监听

定义

当数据变化时,执行一个函数

场景

监听到数据的变化,去指定响应的操作, watch是异步操作

变化

  • 简单的数据类型改变时,查看值
  • 复杂的数据类型,查看地址

查看下面的代码,我们可以发现,obj原来是{a:'a'}, 现在改为obj={a:'a'}, 例如:{a:'a'}{a:'a'} 是两个对象不同地址, 即{a:'a'} === {a:'a'}的值为 false ,即obj会发生变化,因为地址发生了变化,obj.a不会发生变化,因为值没有发生变化。

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 变了");
    },
    obj() {
      console.log("obj 变了");
    },
    "obj.a": function() {
      console.log("obj.a 变了");
    }
  }
}).$mount("#app");

Deep:true-监听

根据上面的例子,如果改变obj.a='a'obj.a='b',obj本身并不会发生变化,如果想改变属性的时候,也改变对象,就需要用到deep:true

  • deep: true,当obj.a变了,obj也变化
  • dee[:false,当obj.a变了,obj不变化

handler和immediate属性

watch 的一个特点是,最初绑定的时候是不会执行的,要等到 firstName 改变时才执行监听计算。那我们想要一开始就让他最初绑定的时候就执行改怎么办呢?我们需要修改一下我们的 watch 写法,使用immediate属性,修改过后的 watch

在下面的代码中,我们给firstName绑定了一个handler方法,之前我们写的 watch 方法其实默认写的就是这个handlerVue.js会去处理这个逻辑,最终编译出来其实就是这个handler 代码如下

  • immediate: true, 立即执行handler方法
  • immediate: false, 默认设置,并不会立即执行handler方法
<div>
      <p>FullName: {{fullName}}</p>
      <p>FirstName: <input type="text" v-model="firstName"></p>
</div>

new Vue({
  el: '#root',
  data: {
    firstName: 'Dawei',
    lastName: 'Lou',
    fullName: ''
  },
  watch: {
    firstName(newName, oldName) {
      this.fullName = newName + ' ' + this.lastName;
    },
    // 代表在wacth里声明了firstName这个方法之后立即先去执行handler方法
    immediate: true
  } 
})

语法

语法1:

watch {
    o1:()=>{} //不推荐使用这种语法,需要搞懂this指向,箭头函数的this指向外部的全局对象
    o2:function(value,oldvalue){},
    o3(){},
    o4:[f1, f2],
    o5:'methoName',
    o6:{handler:fn, deep: true, immediate:true},
    'object.a':function(){}
}

语法2: 其中的'xxx'可以改为一个返回字符串的函数

vm.$watch('xxx', fn, {deep:..., immediate:..})

实例

使用watch模拟computed

new Vue({
  data: {
    user: {
      email: "fangfang@qq.com",
      nickname: "方方",
      phone: "13812312312"
    },
    displayName: ""
  },
  watch: {
    "user.email": {
      handler: "changed",
      immediate: true // 第一次渲染是也触发 watch
    },
    "user.nickname": {
      handler: "changed",
      immediate: true // 第一次渲染是也触发 watch
    },
    "user.phone": {
      handler: "changed",
      immediate: true // 第一次渲染是也触发 watch
    }
  },
  // 不如用 computed 来计算 displayName
  template: `
    <div>
       {{displayName}}
       <button @click="user.nickname=undefined">remove nickname</button>
    </div>
  `,
  methods: {
    changed() {
      console.log(arguments);
      const user = this.user;
      this.displayName = user.nickname || user.email || user.phone;
    }
  }
}).$mount("#app");

computed VS watch

computed-计算属性

computed看上去是方法,但是实际上是计算属性,它会根据你所依赖的数据动态显示新的计算结果。计算结果会被缓存,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时才会重新调用对应的getter来计算

应用场景:适用于重新计算比较费时不用重复数据计算的环境。所有 gettersetterthis 上下文自动地绑定为 Vue 实例。如果一个数据依赖于其他数据,那么把这个数据设计为computed

watch-监听

watche 更像是一个 data 的数据监听回调,当依赖的 data的数据变化,执行回调,在方法中会传入 newValoldVal。可以提供输入值无效,提供中间值 特场景。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性。如果你需要在某个数据变化时做一些事情,使用watch

总结

  • 如果一个数据依赖于其他数据,那么把这个数据设计为computed

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

以上资料来源

Vue 中文文档

Vue watch语法

Vue入门笔记体系(四)computed和watch

面试题: Vue中的 computed 和 watch的区别

更多资源来源于 ©饥人谷