Vue3笔记(二):计算属性、监听器、条件渲染与列表渲染

374 阅读5分钟

Vue3 + React18 + TS4 入门到实战 - 慕课网 (imooc.com) 课程笔记

05 计算属性与侦听器区别与原理

计算属性

模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护,所以过于复杂的逻辑可以移植到计算属性中进行处理。

<div id="app">{{ reverseMessage }}</div>
<script>
  let vm = Vue.createApp({
    data() {
      return {
        message: "hello",
      };
    },
    computed: {
      reverseMessage() {
        return this.message.split("").reverse().join("");
      },
    },
  }).mount("#app");
</script>

计算属性与方法比较像,如下所示:

<div id="app">
  {{ reverseMessageMethod() }}<br />
  {{ reverseMessageMethod() }}<br />
  {{ reverseMessage }}<br />
  {{ reverseMessage }}<br />
</div>
<script>
  let vm = Vue.createApp({
    data() {
      return {
        message: "hello world",
      };
    },
    methods: {
      reverseMessageMethod() {
        console.log(1);
        return this.message.split(" ").reverse().join(" ");
      },
    },
    computed: {
      reverseMessage() {
        console.log(2);
        return this.message.split(" ").reverse().join(" ");
      },
    },
  }).mount("#app");
</script>

计算属性跟方法相比,具备缓存的能力,而方法不具备缓存,所以上面代码执行完,控制台会显示两次 1 和一次 2。 计算属性虽然是对象方法,但是当做属性使用(调用时不加括号),并且计算属性是只读的。

注意:计算属性默认是只读的,一般不会直接更改计算属性,而是修改其依赖的数据从而触发计算属性的更新。如果想直接更改计算属性也是可以做到的,通过 Setter 写法实现,官方地址

总结:计算属性computed和方法methods的区别

  • 如果一个业务流程没有返回值,则用methods实现,有返回值,用computed和methods都可以实现

  • 计算属性和方法都是函数,计算属性一定有返回值,它通过对数据进行处理,返回一个结果

  • 在模板中调用时,计算属性不加(),而methods必须需要加()

  • 计算属性和方法最主要的区别是计算属性有缓存功能。 方法被调用时每次都要重复执行函数,而计算属性初次调用时执行函数,然后会缓存结果。当再次被调用时,如果依赖的响应式数据没有发生改变,则直接返回之前缓存的结果。如果依赖发生了改变,则会再次执行函数并缓存结果。

既然计算属性编写的是一个函数,而调用的时候以函数名的形式进行使用,其实实现起来也不是特别难的事情:

let computed = {
  num() {
    return 123;
  },
};
let vm = {};
for (let attr in computed) {
  Object.defineProperty(vm, attr, {
    value: computed[attr](),
  });
}

监听器

虽然计算属性在大多数情况下更合适,但有时也需要一个自定义的侦听器。侦听器的目的:侦听器用来观察和响应 Vue 实例上的数据变动,类似于临听机制+事件机制。当有一些数据需要随着其它数据变化而变化时,就可以使用侦听器。

<div id="app">{{ message }}</div>
<script>
  let vm = Vue.createApp({
    data() {
      return {
        message: "hello",
      };
    },
    watch: {
      message(newVal, oldVal) {
        console.log(newVal)
      },
    },
  }).mount("#app");
  
  setTimeout(() => {
    vm.message = "hi vue";
  }, 2000)
  
</script>

有时候,计算属性侦听器往往能实现同样的需求,那么他们有何区别呢? 看一个计算属性侦听器分别实现拼接全名的例子:

  • computed一定有返回值,而watch不需要返回值
  • 计算属性适合:多个值去影响一个值的应用;而侦听器适合:一个值去影响多个值的应用(因为对每个值都要写个函数监听,比较麻烦)
  • 计算属性在调用时需要在模板中使用,修改计算所依赖元数据;watch在调用时只需修改元数据
  • 计算属性默认深度依赖,可以监测对象值的改变,watch默认浅度观测,只监测字面量(也可设置deep为true来开启深度监测)
  • 计算属性在刚进入页面的时候,自动触发;watch默认不触发(也可设置immediate为true来触发)
  • 计算属性适合做筛选,不可异步或DOM操作;watch适合做执行异步或开销较大的操作(比如DOM操作)。

回调的触发时机

当你更改了响应式状态,它可能会同时触发 Vue 组件更新和侦听器回调。

默认情况下,用户创建的侦听器回调,都会在 Vue 组件更新之前被调用。这意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态。

如果想在侦听器回调中能访问被 Vue 更新之后的 DOM,你需要指明 flush: 'post' 选项:

export default {
  // ...
  watch: {
    key: {
      handler() {},
      flush: 'post'
    }
  }
}

为什么计算属性不能做异步而监听器可以异步?

因为计算属性必须return,而return发生在异步之前。而监听器不涉及return。

参考:

06 条件渲染与列表渲染

条件渲染

v-if 指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回 truthy 值的时候被渲染。

在 JavaScript 中,truthy(真值)指的是在布尔值上下文中,转换后的值为真的值。所有值都是真值,除非它们被定义为 falsy 假值(即除 false、0、-0、0n、“”、null、undefined 和 NaN 以外皆为真值)。

<div id="app">
  <div v-if="isShow">aaaaa</div>
  <div v-else>bbbbb</div>
</div>
<script>
  let vm = Vue.createApp({
    data() {
      return {
        isShow: 0,
      };
    },
  }).mount("#app");
</script>

v-ifv-else要连着用,中间不能插入其他标签。

列表渲染

v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名。

<div id="app">
  <div v-for="item, index in list">{{ item }}, {{ index }}</div>
  <div v-for="value, key, index in info">
    {{ value }}, {{ key }}, {{ index }}
  </div>
  <div v-for="item in num">{{ item }}</div>
  <div v-for="item in text">{{ item }}</div>
</div>
<script>
  let vm = Vue.createApp({
    data() {
      return {
        list: ["a", "b", "c"],
        info: { username: "xiaoming", age: 20 },
        num: 10,
        text: "hello",
      };
    },
  }).mount("#app");
</script>

条件渲染与列表渲染需要注意的点

  • 列表渲染需要添加 key 属性,用来跟踪列表的身份
  • v-if 和 v-for 尽量不要在一个标签内一起使用,可利用计算属性来完成筛选这类功能(因为 v-if 优先级高于 v-for,这样 v-if 拿不到 v-for 中的 item 属性)
  • template 标签的作用:形成一个整体容器并且不会被渲染。常用在v-if等标签,若使用div标签会多一个层级。