Code前端首页关于Code前端联系我们

Vue 3 的 computed properties 该怎么用?这篇问答全讲透

terry 2小时前 阅读数 21 #SEO
文章标签 Vue3;计算属性

Vue 3 里的 computed 属性到底能解决啥问题?

写 Vue 组件时,你是不是遇到过模板里逻辑越堆越乱?比如拼接姓名、计算购物车总价,直接把逻辑塞模板里,代码又丑又难维护。computed properties(计算属性)就是来“救场”的——它能把复杂逻辑封装成“响应式变量”,让模板和逻辑分离,还能自动缓存结果提升性能。

举个直观例子:

<template>
  <div>
    <!-- 直接写逻辑,又丑又难维护 -->
    {{ firstName + ' ' + lastName }} 
    <!-- 用 computed 清爽多了 -->
    {{ fullName }}
  </div>
</template>
<script>
export default {
  data() {
    return { firstName: 'John', lastName: 'Doe' }
  },
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName
    }
  }
}
</script>

fullName 是计算属性,它依赖 firstNamelastName,这俩数据不变时,fullName 不会重复计算;一旦其中一个变化,Vue 自动更新 fullName,既让模板干净,又省了重复计算的性能开销。

computed 和 methods 有啥不一样?为啥不用 methods 代替?

很多新手会疑惑:“用 methods 写个函数返回拼接后的名字,效果不是一样吗?” 还真不一样,核心区别是“缓存”

  • methods 里的函数,每次模板渲染都会重新执行,比如在 v-for 循环里调用 methods 函数,列表越长、渲染越频繁,性能浪费越严重;
  • computed 的值会被 缓存,只有依赖的响应式数据变化时,才会重新计算,只要依赖不变,不管模板渲染多少次,computed 结果直接拿缓存,不用重复执行逻辑。

举个性能对比例子:假设显示用户等级(等级由积分计算,积分变化少但页面频繁刷新),用 computed 只在积分变化时计算一次;用 methods 每次刷新都要重新算,完全没必要。

再看代码区别:

<!-- methods 写法 -->
<template>{{ getFullName() }}</template>
<script>
export default {
  methods: {
    getFullName() {
      return this.firstName + ' ' + this.lastName
    }
  }
}
</script>
<!-- computed 写法 -->
<template>{{ fullName }}</template>
<script>
export default {
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName
    }
  }
}
</script>

需要“复用+缓存”的衍生值,用 computed;需要“触发动作(比如事件处理、异步请求)”,用 methods

Vue 3 里咋定义 computed 属性?选项式和组合式 API 有区别吗?

Vue 3 支持选项式 API(和 Vue 2 类似)和组合式 API(更灵活),定义 computed 的方式不同,得按需选。

选项式 API 里的 computed

直接在组件选项写 computed 对象,每个属性是函数(getter):

<template>
  <div>购物车总价:{{ totalPrice }}</div>
</template>
<script>
export default {
  data() {
    return {
      products: [
        { name: '手机', price: 3999, quantity: 1 },
        { name: '耳机', price: 999, quantity: 2 }
      ]
    }
  },
  computed: {
    totalPrice() {
      return this.products.reduce((sum, item) => {
        return sum + item.price * item.quantity
      }, 0)
    }
  }
}
</script>

totalPrice 依赖 products,商品价格或数量变化时,总价自动更新。

组合式 API 里的 computed

组合式 API 要从 vue 导入 computed 函数,配合 ref/reactive 使用,写法更灵活:

基本用法(只读,只有 getter)

<template>
  <div>过滤后列表:{{ filteredList }}</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const searchKeyword = ref('')
const originalList = ref(['Vue', 'React', 'Angular', 'Svelte'])
// 定义 computed:根据关键词过滤列表
const filteredList = computed(() => {
  return originalList.value.filter(item => 
    item.includes(searchKeyword.value)
  )
})
</script>

filteredList 依赖 searchKeywordoriginalList,任一变化,过滤结果自动更新。

computed 的缓存机制具体咋工作?能关掉缓存吗?

缓存是 computed 的核心优势,但很多人搞不清“何时触发缓存、何时重新计算”。

原理:computed 会自动跟踪“响应式依赖”fullName 依赖 firstNamelastName,Vue 计算 fullName 时,会记录这两个依赖,之后只有依赖变化,fullName 才重新计算;依赖不变,不管模板渲染多少次,都直接用缓存结果。

能关掉缓存吗?不行computed 设计初衷是“基于依赖的缓存计算”,不需要缓存就用 methods,强行绕开缓存,既违背设计逻辑,又影响性能。

computed 能设置 setter 吗?啥场景需要?

默认 computed 只有 getter(返回计算值),但也能定义 setter 实现“双向计算”,比如通过修改 fullName,同步更新 firstNamelastName

看例子:

<template>
  <div>
    <input v-model="fullName" placeholder="输入全名" />
    <p>First Name: {{ firstName }}</p>
    <p>Last Name: {{ lastName }}</p>
  </div>
</template>
<script>
export default {
  data() {
    return { firstName: 'John', lastName: 'Doe' }
  },
  computed: {
    fullName: {
      // getter:拼接名字
      get() {
        return this.firstName + ' ' + this.lastName
      },
      // setter:拆分名字到 firstName 和 lastName
      set(newValue) {
        const [first, last] = newValue.split(' ')
        this.firstName = first
        this.lastName = last || ''
      }
    }
  }
}
</script>

用户修改 fullName 时,setter 拆分字符串并更新 firstNamelastName,这种场景常见于“单个输入框控制多个字段”的表单设计,让逻辑更内聚。

组合式 API 里的 computed 能写 setter 吗?咋写?

组合式 API 也支持给 computed 加 setter,把 computed 参数改成对象,包含 getset 方法:

<template>
  <div>
    <input v-model="fullName" placeholder="输入全名" />
    <p>First Name: {{ firstName }}</p>
    <p>Last Name: {{ lastName }}</p>
  </div>
</template>
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
// 定义带 setter 的 computed
const fullName = computed({
  get() {
    return firstName.value + ' ' + lastName.value
  },
  set(newValue) {
    const [first, last] = newValue.split(' ')
    firstName.value = first
    lastName.value = last || ''
  }
})
</script>

和选项式 API 逻辑一致,只是语法更函数式,适合组合式 API 的“逻辑聚合”风格。

啥时候必须用 computed?啥时候坚决不能用?

不少同学滥用 computed 导致逻辑混乱或性能问题,得明确适用场景禁忌场景

必须用 computed 的场景:

  1. 模板需要“衍生值”:拼接字符串、计算列表总数、过滤/排序后的列表,这些值由其他响应式数据推导而来,用 computed 让模板更简洁;
  2. 逻辑需要复用+缓存:多个地方要用同一计算结果(比如用户等级、购物车总价),用 computed 缓存,避免重复计算;
  3. 依赖多个响应式数据:一个值依赖多个 data/props,且依赖变化时需自动更新,computed 自动跟踪依赖,比手动写 watch 简洁。

坚决不能用 computed 的场景:

  1. 需要执行“副作用”:发请求、修改 DOM、操作 localStorage 等“动作类”逻辑,computed 是“计算值”的,不是“执行操作”的,该用 methodswatch
  2. 异步操作computed 的 getter 必须同步返回值,若等接口返回再计算,会直接报错(异步无法及时返回),这时用 async 函数配合 watch
  3. 不需要缓存的场景:每次调用需重新执行逻辑(比如随机数、当前时间戳),用 methods 更合适,computed 缓存会导致结果不更新。

computed 和 watch 咋选?它们不是一回事?

很多人分不清 computedwatch,其实设计目标完全不同:

特性 computed watch
核心作用 推导(从已有数据生成新值) 监听变化,执行副作用(动作)
依赖关系 自动跟踪依赖的响应式数据 需手动指定要监听的数据源
返回值 必须返回一个值(给模板或其他逻辑用) 无返回值,专注“做事情”
适用场景 复杂衍生值、依赖多个数据的计算 数据变化后发请求、修改 DOM、埋点等

举个对比:

  • 表单验证实时提示(输入时判断是否合法)→ 用 computed,需返回“是否合法”的值;
  • 输入完成后自动提交表单(输入变化后发请求)→ 用 watch,需执行“发请求”的动作。

依赖的响应式数据变了,computed 咋知道要更新?

简单说下 Vue 响应式原理(人话版):

Vue 里的响应式数据(如 dataref/reactive 包装的数据)会被“劫持”(通过 ProxyObject.defineProperty)。computed 执行 getter 时,Vue 会记录“这个 computed 依赖了哪些响应式数据”,之后,只要被依赖的数据变化,Vue 就标记 computed 为“脏值”,下次访问时重新计算。

fullName 依赖 firstNamefirstName 赋值变化时,Vue 触发依赖更新,fullName 下次被访问(模板渲染、脚本调用)时重新计算。

用 computed 容易踩哪些坑?咋避?

就算懂基本用法,实际开发也容易掉坑,这几个典型错误要警惕:

坑 1:在 computed 里做异步操作

比如想在 computed 里等接口返回后计算值:

// 错误!computed 不能返回 Promise
computed: {
  async userInfo() {
    const res = await fetch('/api/user')
    return res.data
  }
}

computed 要求 getter 同步返回值,异步操作无法及时返回,会导致页面渲染出 [object Promise]undefined

解决:需异步计算时,改用 watch 监听触发条件,结合 ref 存结果。

坑 2:在 computed 里修改依赖数据

比如想在计算 fullName 时修改 firstName

computed: {
  fullName() {
    // 错误!computed 里修改依赖会导致循环更新
    this.firstName = '强行修改' 
    return this.firstName + ' ' + this.lastName
  }
}

computed 职责是“计算”,不是“修改数据”,修改依赖会触发 computed 重新计算,可能导致无限循环(计算→改数据→触发计算→改数据…)。

解决:修改数据放到 methodswatch 里,computed 只负责返回计算值。

坑 3:依赖了非响应式数据

比如用普通变量当依赖:

<script>
export default {
  data() { return { firstName: 'John' } },
  computed: {
    fullName() {
      // 错误!name 是普通变量,不是响应式的
      let name = 'Doe' 
      return this.firstName + ' ' + name
    }
  }
}
</script>

Vue 只能跟踪响应式数据datarefreactive 包装的)的变化,若 computed 依赖普通变量、函数参数等非响应式数据,数据变化时 computed 不会更新。

解决:确保所有依赖是响应式的(用 datarefreactive 包裹)。

computed 是 Vue 响应式的“智能计算器”

一句话总结 computed 价值:用声明式方式,把“依赖→计算→缓存→自动更新”封装成响应式变量,它让复杂逻辑从模板解放,通过缓存省性能,让数据依赖关系清晰可维护。

记住关键结论:

  • methods 选谁?看需不需要缓存;
  • watch 选谁?看是“算值”还是“做事”;
  • 选项式和组合式 API 写法不同,原理一致;
  • 别在 computed 里搞异步、改数据、依赖非响应式变量。

把这些搞明白,Vue 3 的 computed 就能用得顺手,让组件逻辑简洁又高效~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

热门