Vue2 - computed 和 method 的原理区别

目录

1,简单对比

  1. computed 当做属性使用,method 当做方法使用。
  2. computed 可以提供 getter 和 setter 来赋值。
  3. computed 无法接收多个参数。
  4. computed 有缓存。

2,原理的不同

1,method 的处理

处理比较简单,只需要遍历 methods 配置的每个属性,将其对应的函数通过 bind 绑定到当前实例。之后复制其引用到组件实例即可(为了通过 this 可访问)。

js 复制代码
// 伪代码
function Vue(options) {
  // ...
  // 其他代码
  // ...
  Object.entries(options.methods).forEach(([methodName, fn]) => {
    this[methodName] = fn.bind(this);
  });
}

new Vue(vnode.componentOptions);

2,computed 的处理

在触发组件的生命周期函数 beforeCreate后,会做一系列的事情,其中包括对 computed 的处理:

  1. 遍历 computed 配置中的所有属性,对每个属性都创建一个 Watcher 对象,并传入一个函数。该函数本质上就是 computed 的 getter 。之后 getter 触发时就会收集依赖。

数据响应式中 Watcher 原理参考。

简单来说,为了监听响应式数据的变化来触发更新,Vue 会将使用了响应式数据的函数 (模板对应 render 函数)通过 Watcher 记录下来(收集依赖)。之后响应式数据发生变化,就会通知对应的 Watcher 来执行对应的函数触发更新。

  1. Watcher 创建好之后,vue 会使用代理模式,将计算属性挂载到组件实例上(为了通过 this 访问)。

  2. 不同于渲染函数 render 的 Watcher,为计算属性创建的 Watcher 不会立即执行。因为要考虑该计算属性是否被使用(渲染函数或其他方法中),没使用就不会执行。所以在创建 Watcher 时配置了一个 lazy 属性,lazy === true 时 Watcher 不会立即执行。

实现缓存

  1. 受到 lazy 的影响,Watcher 内部还会配置2个属性:value 和 dirty

    • value ,保存 Watcher 运行时的结果,一开始(Watcher 还没有执行时)为 undefined。
    • dirty ,标记当前 value 是否过期,一开始为 true。
  2. 当读取计算属性时,会先检查 dirty,

    • 为 true 时则运行之前传入 Watcher 的函数(也就是计算属性的 getter),将计算依赖得到的值保存在 Watcher 的 value 中,同时设置 dirty = false。
    • 为 false,则直接返回 Watcher.value,也就是缓存的值。

触发更新

  1. 为了实现更新,在收集依赖时,被依赖的数据不仅会收集计算属性的 Watcher,还会收集组件的 Watcher。

  2. 当计算属性变化时,会先触发计算属性的 Watcher,但只会将 dirty = true ,其他不做处理。之后触发组件的 Watcher 重新渲染。render 函数(或模板)又读取 了计算属性,因为dirty === true,所以会重新运行 getter 计算。

  3. 设置计算属性时比较简单,直接运行 setter 即可。

3,触发更新时的问题

注意到上面触发更新 时,如果计算属性的依赖发生变化,但计算属性没有在模板(或render函数)中使用时(其他方法或watch 场景同理),虽然会触发计算属性的 Watcher,但不会触发同时收集到的 组件的 Watcher。

举例:

html 复制代码
<template>
  <div>
    <h1 v-if="showName">{{ fullName }}</h1>
    <button @click="handleClick">隐藏 fullName</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      firstName: '渣渣',
      lastName: '辉',
      showName: true
    }
  },
  computed: {
    fullName: {
      get() {
        return this.firstName + ' ' + this.lastName
      },
      set(newValue) {
        ;[this.firstName, this.lastName] = newValue.split(' ')
      }
    }
  },
  methods: {
    handleClick() {
      this.firstName = '123' // 计算属性依赖发生变化
      this.showName = !this.showName
    }
  },
  updated() {
    console.log(this)
  }
}
</script>

showName === false 所以不渲染 h1,所以render 函数不会读取计算属性 fullName。

所以只会执行:

计算属性的依赖:

firstName: **计算属性的 Watcher** ,组件的 Watcher(对应render函数 )

lastName: **计算属性的 Watcher** ,组件的 Watcher(对应render函数)

showName: **组件的 Watcher**


以上。

相关推荐
不停喝水2 分钟前
【前端转全栈java速通课】 项目实战④7-11节 操作数据库-登录-注册-修改密码-注销-mubatis-plus简化crud-
java·前端·数据库
一水鉴天1 小时前
统一对象拓扑(UOT)设计全链定稿:从三件事分离到三继承 之1 20261010(豆包“查看聊天内容”继续)
前端·css
栖凤1 小时前
Java 的 try-catch 在 Agent 里失效了,我用了 5 个模式才兜住
java·前端·python
粥里有勺糖1 小时前
视野修炼第136期 | 前端小恐龙"Deno"被收购
前端·github·ai编程
修炼的dance1 小时前
页面塞了13.88 MB字体,导出的PDF只有71 KB
前端
qq_369173631 小时前
如何将 AI 生成的 HTML 网页发布成在线链接?不用自己搭建服务器
前端·人工智能·html·效率工具·html 发布
风花一世月1 小时前
⚡ 我把一座含氢能源园区搬进了浏览器(五类能流实时算守恒,在线直接玩)
前端·人工智能
羲云2 小时前
Claude 越界事件之后:Agent 权限设计为什么不能只靠提示词
前端
行者全栈架构师2 小时前
【鸿蒙心迹】鸿蒙网络请求架构实战——@ohos.net.http 到 Axios 封装、拦截器与统一错误处理(HarmonyOS 7.x)
前端·算法·架构
全栈Agent 小李2 小时前
【无标题】
前端·后端·agent·ai编程·全栈·cursor·mcp