computed 计算属性详解:触发时机、实战场景、Vue2 与 Vue3 对比

在 Vue 开发里,computed 是一个看起来很简单、实际上非常值得吃透的能力。

很多人会把它理解成"一个会自动更新的变量",但真正写业务时,计算属性最容易踩坑的地方其实不是"怎么写",而是:

  • 它什么时候执行
  • 什么时候重新执行
  • 为什么模板里用了它却没立刻算
  • Vue2 和 Vue3 写法上到底差在哪
  • 什么时候该用 computed,什么时候不该用

这篇文章我结合实战,把这些问题一次讲清楚。


一、computed 是什么

computed 是 Vue 提供的计算属性,它的核心作用是:

  • 基于已有响应式数据,派生出一个新的值
  • 自动缓存结果
  • 只在依赖变化时重新计算

它适合做这类事情:

  • 字符串拼接
  • 数组筛选
  • 状态派生
  • 表单展示值加工
  • 表格列名动态计算
  • 权限/角色判断后的派生状态

比如:

js 复制代码
computed: {
  fullName() {
    return this.firstName + ' ' + this.lastName
  }
}

模板里直接用:

vue 复制代码
<div>{{ fullName }}</div>

看起来像一个字段,但它本质上是一个有缓存的函数结果


二、computed 的核心特性

计算属性有三个非常重要的特性:

1. 有缓存

只要依赖没变,重复访问不会重复执行。

比如:

js 复制代码
computed: {
  total() {
    console.log('computed run')
    return this.a + this.b
  }
}

如果页面中多处用到 total,在同一轮渲染里一般只会计算一次。

2. 依赖响应式数据

它依赖的必须是响应式数据,比如 datapropsrefreactivestore 里的响应式状态等。

3. 适合派生,不适合副作用

computed 里应该是纯计算,不建议做这些事:

  • 发请求
  • 操作 DOM
  • 赋值修改其他状态
  • 写日志、副作用逻辑

三、computed 的触发时机

这是最容易搞混的部分,也是本文重点。

1. computed 不是一创建就立刻执行

很多人会误以为:

组件一初始化,computed 就全跑一遍

其实不是。

computed惰性求值的,也就是:

  • 你不用它,它不算
  • 你一旦访问它,它才算

2. 在模板中使用时,首次渲染会触发

比如:

vue 复制代码
<template>
  <div>{{ total }}</div>
</template>

这里的 total 会在首次渲染时被访问,因此会触发计算。


3. 依赖变化后,下一次访问才会重新计算

比如:

js 复制代码
computed: {
  total() {
    console.log('run')
    return this.a + this.b
  }
}

如果 a 变了:

  • Vue 会标记这个 computed "失效"
  • 但不会立刻重新算
  • 等下一次模板渲染或代码访问 total 时,再重新计算

这就是"缓存 + 惰性"的组合。


4. 在 createdmountedmethods 中访问也会触发

只要你写了:

js 复制代码
console.log(this.total)

就会触发计算。

所以如果你在 created() 里调用了某个方法,而方法里又访问了 computed,那它会在 created() 阶段被求值。


5. 多次访问通常不会重复执行

同一轮依赖没变的情况下:

js 复制代码
console.log(this.total)
console.log(this.total)

通常只会真正计算一次,第二次直接取缓存结果。


四、实战里最常见的误区

误区 1:computed 能替代 methods

很多人会问:

既然 computed 也能写逻辑,那是不是 methods 都可以不用了?

不行。

区别很简单:

  • computed 用于"依赖不变时结果稳定"的派生值
  • methods 用于"每次调用都可能不同"的函数逻辑

比如:

js 复制代码
computed: {
  fullName() {
    return this.firstName + this.lastName
  }
}

适合 computed。

但这个不适合:

js 复制代码
methods: {
  getNowTime() {
    return new Date().toLocaleString()
  }
}

因为它每次结果都可能不同,不该缓存。


误区 2:computed 里可以直接改数据

比如:

js 复制代码
computed: {
  status() {
    this.loading = false
    return this.list.length > 0
  }
}

这是不推荐的。

computed 应该保持纯净,别在里面做赋值和副作用。

否则很容易出现:

  • 逻辑混乱
  • 重新渲染循环
  • 调试困难

误区 3:computed 一定比 methods 快

不是绝对。

如果你只是简单调用一次,而且不需要缓存,那 methods 更直接。
computed 的价值在于:

  • 多次使用同一个派生结果
  • 依赖变化不频繁
  • 逻辑适合缓存

五、computed 在实战中的典型用法

场景 1:动态筛选列表

js 复制代码
computed: {
  filteredList() {
    return this.list.filter(item => item.name.includes(this.keyword))
  }
}

模板中直接绑定:

vue 复制代码
<el-table :data="filteredList" />

好处:

  • 代码简洁
  • 自动缓存
  • 搜索框变化时自动更新

场景 2:动态标题/文案

js 复制代码
computed: {
  title() {
    return this.role === 'admin' ? '管理员首页' : '普通用户首页'
  }
}

适合做权限区分、项目区分、角色区分后的派生展示。


场景 3:表单联动值

js 复制代码
computed: {
  currentAreaCode() {
    return this.useDeptId ? this.formData.deptId : this.formData.areaCode
  }
}

这类写法特别常见,能避免在方法里写一堆 if/else。


场景 4:统计值和派生状态

js 复制代码
computed: {
  totalCount() {
    return this.list.reduce((sum, item) => sum + item.count, 0)
  }
}

如果模板里要多处展示总数,computed 会比方法更合适。


六、Vue2 中 computed 的写法

Vue2 里,computed 一般写在 computed 选项中。

基础写法

js 复制代码
export default {
  data() {
    return {
      firstName: 'Tom',
      lastName: 'Lee'
    }
  },
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName
    }
  }
}

带 get / set 的写法

Vue2 还支持完整对象写法:

js 复制代码
computed: {
  fullName: {
    get() {
      return this.firstName + ' ' + this.lastName
    },
    set(value) {
      const arr = value.split(' ')
      this.firstName = arr[0]
      this.lastName = arr[1]
    }
  }
}

这种写法适合:

  • 需要双向映射
  • 需要对外暴露一个可写的派生值
  • 例如表单字段联动、弹窗输入映射

七、Vue3 中 computed 的写法

Vue3 中,computed 仍然是核心 API,但写法更灵活,尤其在组合式 API 里更常见。

1. 组合式 API 写法

js 复制代码
import { ref, computed } from 'vue'

export default {
  setup() {
    const firstName = ref('Tom')
    const lastName = ref('Lee')

    const fullName = computed(() => firstName.value + ' ' + lastName.value)

    return {
      firstName,
      lastName,
      fullName
    }
  }
}

这里要注意:

  • ref 取值要用 .value
  • computed 返回的是一个只读的响应式引用

2. 带 set 的写法

js 复制代码
const fullName = computed({
  get() {
    return firstName.value + ' ' + lastName.value
  },
  set(value) {
    const arr = value.split(' ')
    firstName.value = arr[0]
    lastName.value = arr[1]
  }
})

Vue3 里这个写法也很常用。


3. <script setup> 写法

vue 复制代码
<script setup>
import { ref, computed } from 'vue'

const firstName = ref('Tom')
const lastName = ref('Lee')

const fullName = computed(() => firstName.value + ' ' + lastName.value)
</script>

这是现在 Vue3 项目里最常见的写法之一。


八、Vue2 和 Vue3 中 computed 的对比

1. API 形式不同

Vue2:

js 复制代码
computed: {
  fullName() {
    return this.firstName + this.lastName
  }
}

Vue3:

js 复制代码
const fullName = computed(() => firstName.value + lastName.value)

2. 响应式访问方式不同

Vue2 中直接 this.xxx

Vue3 的 ref 需要 .value

js 复制代码
fullName.value

3. 逻辑组织方式不同

Vue2 更偏向"选项式写法":

  • data
  • methods
  • computed
  • watch

Vue3 更偏向"组合式组织":

  • 相关状态和逻辑可以放在一起
  • 更容易复用
  • 更适合复杂业务拆分

4. 可读性侧重点不同

Vue2 的 computed 一般集中在一个 computed 区块里,适合中小型页面。

Vue3 的 computed 更适合和业务状态写在一起,比如:

js 复制代码
const isAdmin = computed(...)
const tableData = ref([])
const canEdit = computed(...)

这样同一块业务逻辑更容易维护。


九、computed 的触发时机总结

可以简单记成一句话:

computed 只有在被访问时才会执行,依赖变化后会失效,下一次访问时重新计算。

更细一点:

  • 首次访问时执行
  • 模板首次渲染时执行
  • 方法/生命周期中访问时执行
  • 依赖变化后下一次访问时重新执行
  • 同一依赖状态下重复访问走缓存

十、什么时候用 computed,什么时候用 methods

用 computed 的情况

  • 有明确依赖
  • 结果可以缓存
  • 一个值会被多次使用
  • 逻辑是"派生计算"

例如:

  • 是否显示某列
  • 动态标题
  • 过滤后的列表
  • 合并后的展示字段

用 methods 的情况

  • 每次调用都可能返回不同结果
  • 有副作用
  • 需要手动触发
  • 不适合缓存

例如:

  • 获取当前时间
  • 发请求
  • 提交表单
  • 打开弹窗
  • 执行一次性业务动作

十一、一个实战建议:把复杂 if/else 尽量前置成 computed

比如做角色区分、项目区分、区域级别控制时,最常见的问题就是模板里写满这种逻辑:

vue 复制代码
v-if="($checkPermi(['xx']) && projectName(['xxx'])) || $checkPermi(['xxx'])"

这种写法短期能用,但长期会很难维护。

更好的方式是:

js 复制代码
computed: {
  canShowDistrict() {
    return (this.($checkPermi(['xx']) && this.projectName(['xxx'])) || this.$checkPermi(['xxx']))
  }
}

模板里就变成:

vue 复制代码
v-if="canShowDistrict"

好处非常明显:

  • 模板更干净
  • 业务语义更清楚
  • 后续改权限规则时更集中
  • 不容易漏改某个地方

十二、最后总结

computed 不是"自动执行的变量",而是:

  • 基于响应式数据的派生值
  • 只有被访问时才计算
  • 有缓存
  • 依赖变化后才失效
  • 适合做展示型、派生型逻辑

记住三句话就够了:

  • computed惰性求值
  • computed有缓存的
  • computed 适合派生值,不适合副作用

Vue2 / Vue3 的差异也很简单:

  • Vue2 更偏选项式写法
  • Vue3 更偏组合式写法
  • Vue3 里 ref 要记得 .value
相关推荐
isNotNullX2 小时前
数据大屏怎么做?数据大屏有哪四个核心环节
开发语言·前端·javascript
漫游的渔夫2 小时前
RAG 落地 3 个月,我才发现排序(Rerank)比检索更重要
前端·人工智能
衣乌安、2 小时前
Agent之ReAct
前端·ai
六月的可乐2 小时前
知识库检索入门:从普通 RAG、知识图谱 RAG 到 LLM Wiki,一篇讲清原理、区别与选型
vue.js·人工智能·openai
CodeAI2 小时前
不会 Next.js 你好意思说自己是 React 开发者?从零到上线一条龙
前端
竹林8182 小时前
Web3表单签名验证:我如何用 wagmi 和 siwe 让用户“无密码”登录
javascript
霁月的小屋2 小时前
不只是压缩:当模型蒸馏开始复制人格
前端·ai
inksci2 小时前
使用飞帆的上传组件
前端·javascript
里欧跑得慢2 小时前
微交互设计模式:提升用户体验的细节之美
前端·css·flutter·web