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

Vue3里computed的getter和setter该怎么用?常见疑问一次讲透

terry 2小时前 阅读数 19 #SEO

computed里的getter是干啥的?

computed 本质是基于响应式依赖的“智能计算器”,getter 就是定义“怎么算”的逻辑,它有两个核心特点:

自动跟踪依赖

只要 getter 里用到了 ref/reactive 包裹的响应式数据,Vue 就会自动“盯住”这些数据,一旦依赖变化,computed 的值会自动更新。

缓存优化

计算结果会被缓存,只有依赖真的变了才会重新计算,如果模板里多次用到这个 computed 值,依赖不变时不会重复执行 getter,性能更优。

举个购物车例子

<template>
  <div>数量:{{ count }},总价:{{ totalPrice }}</div>
  <button @click="count++">增加数量</button>
</template>
<script setup>
import { ref, computed } from 'vue'
const count = ref(2)   // 响应式数量
const price = ref(50)  // 响应式单价
// getter 定义“总价怎么算”
const totalPrice = computed(() => count.value * price.value)
</script>

当点击按钮让 count 增加时,totalPrice 会自动更新——因为 getter 依赖了 count,Vue 检测到依赖变化后,会重新执行 getter 计算新的总价。

为啥有时候要给computed加setter?

默认情况下,computed 生成的是只读的响应式数据,只能“被动”等依赖变化更新,但有些场景需要主动修改 computed 的值,这时候就得用 setter 定义“怎么改”的逻辑。

典型场景:数据双向转换

比如用户信息的“全名”由 firstNamelastName 拼接而成,但从接口拿到的是完整字符串,需要拆分后赋值给 firstNamelastName,这时候给 computed 加 setter 就能实现“反向推导”。

代码示例

<template>
  <div>全名:{{ fullName }}</div>
  <button @click="setFullName">设置全名</button>
</template>
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('张')
const lastName = ref('三')
// 用对象形式定义 computed,包含 get 和 set
const fullName = computed({
  get() { // 拼接 firstName 和 lastName
    return firstName.value + lastName.value
  },
  set(newFullName) { // 拆分新全名,赋值给 firstName 和 lastName
    firstName.value = newFullName[0]        // 取第一个字当姓
    lastName.value = newFullName.slice(1)  // 剩下的当名
  }
})
const setFullName = () => {
  fullName.value = '李四' // 主动赋值,触发 setter
}
</script>

点击按钮后,fullName 被赋值为 '李四',setter 会把 firstName 改成 '李'lastName 改成 '四',页面上的全名也会同步更新。

怎么给computed写setter?

语法上,给 computed 传对象(而非单个函数),对象里必须包含 get(),可选 set(val)val 是赋值时的新值)。

步骤拆解:

  1. 调用 computed() 时,传入 { get() {}, set(val) {} } 格式的对象。
  2. get() 里写“计算逻辑”,set(val) 里写“修改逻辑”(通常要更新依赖的响应式数据)。

再举个“日期格式化”的实用例子
后端存时间戳(数字),前端展示/输入是 YYYY - MM - DD 格式的字符串,用 computed 的 setter 实现双向转换:

<template>
  <input v-model="formattedDate" placeholder="输入YYYY - MM - DD" />
  <div>原始时间戳:{{ timestamp }}</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const timestamp = ref(Date.now()) // 存时间戳(响应式数字)
const formattedDate = computed({
  get() { // 时间戳 → 字符串格式
    const date = new Date(timestamp.value)
    return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`
  },
  set(val) { // 字符串 → 时间戳
    const [year, month, day] = val.split('-')
    const newDate = new Date(+year, month - 1, +day)
    timestamp.value = newDate.getTime()
  }
})
</script>

用户输入 2024 - 10 - 01 时,setter 会把字符串转成时间戳存到 timestamp;页面展示时,getter 又把时间戳转回格式化字符串——输入输出的格式逻辑被完美封装。

实际项目中哪些场景适合用带setter的computed?

表单数据的双向格式化

后端存“分”(100 分 = 1 元),前端展示“元”,用 computed 的 setter 做单位转换,减少模板里的逻辑:

<template>
  <input v-model="displayAmount" placeholder="输入金额(元)" />
  <div>实际存储(分):{{ storageAmount }}</div>
</template>
<script setup>
import { ref, computed } from 'vue'
const storageAmount = ref(0) // 存分(响应式数字)
const displayAmount = computed({
  get() { return storageAmount.value / 100 }, // 分 → 元
  set(val) { storageAmount.value = val * 100 } // 元 → 分
})
</script>

复杂状态的联动控制

比如表格“全选”功能:全选状态依赖所有子选项,点击全选框时要修改所有子选项,用 computed 把“子→全”和“全→子”的逻辑内聚:

<template>
  <input type="checkbox" v-model="isAllChecked" /> 全选
  <div v-for="(item, idx) in list" :key="idx">
    <input type="checkbox" v-model="item.checked" /> {{ item.name }}
  </div>
</template>
<script setup>
import { ref, computed } from 'vue'
const list = ref([
  { name: '选项1', checked: false },
  { name: '选项2', checked: false },
  { name: '选项3', checked: false }
])
const isAllChecked = computed({
  get() { // 子选项全选 → 全选框勾选
    return list.value.every(item => item.checked)
  },
  set(val) { // 全选框勾选 → 所有子选项勾选
    list.value.forEach(item => { item.checked = val })
  }
})
</script>

前后端数据格式适配

后端返回蛇形命名(如 user_name),前端用驼峰(如 userName),用 computed 的 setter 自动转换格式,减少手动处理:

<template>
  <div>用户名:{{ userName }}</div>
  <button @click="fetchData">模拟请求</button>
</template>
<script setup>
import { ref, computed } from 'vue'
const rawData = ref({ user_name: 'zhangsan' }) // 后端结构
const userName = computed({
  get() { return rawData.value.user_name }, // 蛇形 → 驼峰(读取)
  set(val) { rawData.value.user_name = val } // 驼峰 → 蛇形(存储)
})
const fetchData = () => {
  rawData.value = { user_name: 'lisi' } // 模拟接口更新
}
</script>

computed的getter/setter和watch有啥区别?

很多人会混淆这俩,核心区别在“声明式依赖” vs “响应式副作用”,以及使用场景:

依赖处理方式

  • computed(getter)自动跟踪依赖,只要 getter 里用了响应式数据,Vue 会自动“盯住”这些数据,依赖变化时重新计算,而且有缓存,依赖不变时直接用缓存值。
  • watch手动指定依赖,要明确写监听的数据源(如 watch(count, () => {})),每次依赖变化都会执行回调,没有缓存。

使用场景

  • computed:适合“计算一个值”(由其他数据推导而来),带 setter 后还能处理“反向推导”(如格式转换、状态联动)。
  • watch:适合“响应变化做副作用”(如发请求、操作 DOM、修改多个状态)。

代码简洁度对比(全选功能)

用 computed(逻辑内聚):

const isAllChecked = computed({
  get() { return list.every(...) },
  set(val) { list.forEach(...) }
})

用 watch(逻辑分散):

const isAllChecked = ref(false)
// 子选项变化 → 更新全选
watch(() => list.every(...), (newVal) => { isAllChecked.value = newVal })
// 全选变化 → 更新子选项
watch(isAllChecked, (newVal) => { list.forEach(...) })

用computed的getter/setter容易踩哪些坑?

setter里修改依赖,导致循环更新

setter 里修改了 getter 依赖的响应式数据,可能触发“修改→重新计算→再修改”的循环。

反例

const count = ref(1)
const double = computed({
  get() { return count.value * 2 },
  set(val) { count.value = val / 2 } // 修改count,触发getter重新计算
})
double.value = 4 // 触发setter → count变2 → getter计算double为4 → 又触发setter?

解决:复杂逻辑拆分到多个响应式数据,或用 watch 处理“副作用”,避免 setter 里直接修改依赖。

getter里有“不纯”操作,导致缓存失效

getter 必须是纯函数(相同输入返回相同输出,无副作用),如果包含“取当前时间”“随机数”“修改其他变量”等操作,会导致缓存失效或数据不一致。

反例(取当前时间):

const now = computed(() => new Date().getTime()) 
// 依赖不变(没有响应式数据),now.value永远是第一次计算的时间,不会实时更新

解决:实时数据用 ref + watchEffect,或定时更新响应式数据。

响应式丢失(返回非响应式数据)

getter 返回普通对象/数组(没被 reactive/ref 包装),后续修改不会触发更新。

反例

const rawList = ref([1,2,3])
const filteredList = computed(() => rawList.value.filter(num => num > 1)) 
// 返回普通数组,不是响应式的,后续修改无法触发更新

解决:返回 reactiveref 包装的数据,或把修改逻辑放到 setter 里。

忽略setter的参数类型,导致逻辑错误

setter 的参数是“赋值时的新值”,要确保类型匹配,比如全名例子中,若用户输入非字符串,split 会报错。

解决:setter 里加类型检查,或在模板限制输入类型(如 input type="text")。

掌握computed的getter/setter,让响应式逻辑更丝滑

Vue3 的 computed 通过 getter 和 setter,把“数据推导”和“反向修改”的逻辑优雅封装:

  • 只读场景:用 getter 足够,自动跟踪依赖+缓存,性能拉满;
  • 双向转换场景(如格式、状态联动):用带 setter 的 computed,让逻辑内聚,代码更易维护;
  • 避坑关键:getter 写纯函数、setter 防循环更新、返回值确保响应式;
  • 和 watch 配合:计算逻辑归 computed,副作用逻辑归 watch。

多写 demo 测试表单格式化、状态联动等场景,遇到问题先检查依赖跟踪和 setter 逻辑——掌握这些,响应式编程会越来越顺手,代码也会更简洁优雅~

版权声明

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

热门