Vue computed和watch

68 阅读2分钟

进阶属性

image.png

image.png

computed —— 计算属性

computed

  • 不需要加括号

  • 它会根据依赖是否变化来缓存

计算属性的好处:可以让一些是根据其他属性计算而来的属性变成一个属性

new Vue({
  data: {
    user: {
      email: "fangyinghang@qq.com",
      nickname: "方方",
      phone: "13812312312"
    }
  },
  computed: {
    displayName: {
      get() {
        const user = this.user;
        return user.nickname || user.email || user.phone;
      },
      set(value) {
        console.log(value);
        this.user.nickname = value;
      }
    }
  },
// 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");

不用computed筛选男女

// 引用完整版 Vue,方便讲解
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
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("小葵", "女")
      ],
      displayUsers: []
    };
  },
  created() {
    this.displayUsers = this.users;
  },
  methods: {
    showMale() {
      this.displayUsers = this.users.filter(u => u.gender === "男");
    },
    showFemale() {
      this.displayUsers = this.users.filter(u => u.gender === "女");
    },
    showAll() {
      this.displayUsers = this.users;
    }
  },
  template: `
    <div>
      <div>
        <button @click="showAll">全部</button>
        <button @click="showMale">男</button>
        <button @click="showFemale">女</button></div>
      <ul>
        <li v-for="(u,index) in displayUsers" :key="index">{{u.name}} - {{u.gender}}</li>
      </ul>
    </div>
  `
}).$mount("#app");

用computed筛选男女

// 引用完整版 Vue,方便讲解
import Vue from "vue/dist/vue.js";
Vue.config.productionTip = false;
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做了特殊处理

watch 监听/侦听

watch

用途

  • 一旦data变化,执行一个函数

  • 撤销

new Vue({
  data: {
    n: 0,
    history: [],
    inUndoMode: false//在撤销模式
  },
  watch: {
    n: function(newValue, oldValue) { //n:function
      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;
      console.log("ha" + this.inUndoMode);
      const old = last.from;
      this.n = old; // watch n 的函数会异步调用
      this.$nextTick(() => {
        this.inUndoMode = false;
      });
    }
  }
}).$mount("#app");
  • 模拟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");

何谓变化?

  • 示例:
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");
  • obj原本是{a: 'a'},现在是obj={a:'a'},请问obj变了没有(变了),obj.a变了没有(没有)

如果把一个简单的数据类型的值变了,那就一定是变了;如果把一个对象的简单类型变了,那就只做值的对比;如果把一个对象的地址变了,Vue才会认为这个对象变了。

  • 简单的类型看值,复杂类型(对象)看地址

  • 这其实就是 === 的规则

语法

image.png

deep:true是干什么的?

  • 如果object.a变了,请问object算不算也变了

  • 如果你需要答案是【也变了】,那么就用deep:true

  • 如果你需要答案是【没有变】,那么就用deep:false

  • deep的意思是,监听object的时候是否往深了看

面试题

computed和watch的区别?

  1. computed就是计算属性的意思;watch就是监听的意思。

  2. computed是用来计算出一个值的,这个值在调用的时候不需要加括号,当属性一样用,第二根据依赖自动缓存,如果依赖不变,这个computed的值就不会重新计算。

  3. watch是用来监听的,它有两个选项:①、immediate表示是否在第一次渲染的时候执行这个函数;②、deep是我们监听一个对象,是否要看这个对象里面的属性的变化。