Vue 计算属性原理

Vue 计算属性原理

前言

本文为想学习 Vue 计算属性的前端开发者准备,涵盖 computed 的工作原理及其在 Vue 3 中的实现机制。

1. 为什么学习计算属性原理

Vue的计算属性能够描述依赖响应式状态复杂逻辑,并且计算属性值会基于其响应式依赖被缓存,只有在computed属性的响应式依赖发生变化时才会重新计算。这种特性使得计算属性在性能优化和代码简洁性方面具有显著优势。

2. 计算属性的工作原理

计算属性通过 computed 函数创建,接受一个 getter 函数,并返回一个包含 .value 属性的响应式对象。其核心原理如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
function computed(getterOrOptions) {
  // getter 函数
  let getter;
  // setter 函数
  let setter;
  // 标准化参数
  if (isFunction(getterOrOptions)) {
    // 表面传入的是 getter 函数,不能修改计算属性的值
    getter = getterOrOptions;
    setter =
      process.env.NODE_ENV !== "production"
        ? () => {
            console.warn("Write operation failed: computed value is readonly");
          }
        : NOOP;
  } else {
    getter = getterOrOptions.get;
    setter = getterOrOptions.set;
  }
  // 数据是否脏的
  let dirty = true;
  // 计算结果
  let value;
  let computed;
  // 创建副作用函数
  const runner = effect(getter, {
    // 延时执行
    lazy: true,
    // 标记这是一个 computed effect 用于在 trigger 阶段的优先级排序
    computed: true,
    // 调度执行的实现
    scheduler: () => {
      if (!dirty) {
        dirty = true;
        // 派发通知,通知运行访问该计算属性的 activeEffect
        trigger(computed, "set" /* SET */, "value");
      }
    },
  });
  // 创建 computed 对象
  computed = {
    __v_isRef: true,
    // 暴露 effect 对象以便计算属性可以停止计算
    effect: runner,
    get value() {
      // 计算属性的 getter
      if (dirty) {
        // 只有数据为脏的时候才会重新计算
        value = runner();
        dirty = false;
      }
      // 依赖收集,收集运行访问该计算属性的 activeEffect
      track(computed, "get" /* GET */, "value");
      return value;
    },
    set value(newValue) {
      // 计算属性的 setter
      setter(newValue);
    },
  };
  return computed;
}
  • 脏检查:计算属性通过 dirty 标志来判断是否需要重新计算值。当依赖的响应式数据发生变化时,调度器会将 dirty 设置为 true
  • 依赖收集:当访问计算属性的 .value 时,会调用 track 函数,将当前的副作用函数与该计算属性关联起来。
  • 触发更新:当计算属性的依赖发生变化时,调度器会调用 trigger 函数,通知所有依赖该计算属性的副作用函数重新执行。
  • 缓存机制:计算属性只有在其依赖发生变化时才会重新计算,这通过 dirty 标志实现,从而提高性能。

3. 可写计算属性

计算属性默认是只读的,当尝试修改一个计算属性时,会在开发环境下发出警告,但在某些情况下,我们也可以同时提供getter和setter来创建可写的计算属性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<script setup>
import { ref, computed } from 'vue'

const firstName = ref('Li')
const lastName = ref('Hua')

const fullName = computed({
  // getter
  get() {
    return firstName.value + ' ' + lastName.value
  },
  // setter
  set(newValue) {
    //这里使用的是解构赋值语法
    [firstName.value, lastName.value] = newValue.split(' ')
  }
})
</script>

4. 计算属性与普通方法的区别

  • 计算属性与普通方法的主要区别在于缓存机制。普通方法每次调用都会执行函数体,而计算属性只有在其依赖发生变化时才会重新计算,这使得计算属性在性能上更优。

  • 计算属性更适合用于依赖响应式数据的复杂逻辑,而普通方法更适合用于不依赖响应式数据的简单逻辑。

5. 计算属性的实际应用

计算属性在实际开发中有广泛的应用场景,例如:

  • 表单输入的实时验证:可以使用计算属性来根据输入的值动态计算验证结果,从而实现实时反馈。
  • 复杂数据的展示:在展示复杂数据时,可以使用计算属性来处理数据的格式化和转换,简化模板逻辑。
  • 依赖于多个数据源的计算:当一个值依赖于多个响应式数据时,可以使用计算属性来集中管理这些依赖关系,提高代码的可维护性。
1
2
3
const total = computed(() => {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0)
})

6. 总结

通过学习 Vue 计算属性的工作原理,开发者可以更好地理解其内部机制,从而在实际开发中更有效地利用这一特性。计算属性不仅提高了代码的简洁性和可读性,还通过缓存机制优化了性能,是 Vue 开发中不可或缺的工具。

7. 参考资料

Licensed under CC BY-NC-SA 4.0