Vue3 setup 里 computed 的 get 和 set 咋用?看这篇就够啦!
Vue3 setup 中,computed 为啥要有 get 和 set?
得先从 computed 的基础逻辑 说起,在 Vue3 的 setup 语法里,普通 computed 长这样:
import { ref, computed } from 'vue'
const count = ref(1)
const double = computed(() => count.value * 2)
这时候 double 是 只读 的——只能用 double.value 读取计算结果,要是尝试赋值(double.value = 10),Vue 会直接报错“计算属性是只读的”。
那 get 和 set 是干啥的?简单说:
get负责“读”的时候怎么计算结果(比如把多个响应式数据拼起来);set负责“写”的时候怎么处理新值(比如把新值拆分成多个响应式数据的更新)。
举个生活例子:做用户“全名”功能,姓(lastName)和名(firstName)是两个独立的输入框,全名(fullName)要自动拼接显示;但用户也能直接在“全名”输入框改内容,这时候得把新输入的内容拆成“姓”和“名”,再更新对应的输入框,这时候 computed 就得同时有 get)和 set)。
咋写带 get 和 set 的 computed?举个能跑的例子!
直接上代码(用 <script setup> 语法),看明白每一步干啥:
import { ref, computed } from 'vue'
// 定义“姓”和“名”的响应式数据
const lastName = ref('张')
const firstName = ref('三')
// 定义带 get 和 set 的 computed
const fullName = computed({
// get:读取 fullName 时,返回“姓 + 名”
get() {
return lastName.value + firstName.value
},
// set:给 fullName 赋值时,拆分新值到“姓”和“名”
set(newValue) {
// 假设输入格式是“姓+名”(李四”拆成姓“李”、名“四”)
const [newLast, newFirst] = newValue.split('')
lastName.value = newLast
firstName.value = newFirst
}
})
// 测试逻辑
console.log(fullName.value) // 初始输出:张三
// 给 fullName 赋值,触发 set
fullName.value = '李四'
console.log(lastName.value) // 输出:李
console.log(firstName.value) // 输出:四
解释下关键逻辑:
get里,把lastName和firstName拼起来,所以读fullName.value时能拿到拼接结果;set里,接收新值newValue(李四”),拆分后更新lastName和firstName——这一步必须修改响应式数据,否则 computed 下次读取时不会更新(后面会讲坑)。
实际开发中,哪些场景必须用带 get/set 的 computed?
光看例子不够,得结合真实需求理解,这 3 类场景最常见:
场景 1:表单的“双向计算”需求
比如做日期选择组件,后端存的是时间戳(timestamp),但前端要显示格式化字符串(2024-10-01”),这时候:
get负责把时间戳转成字符串显示;set负责把用户输入的字符串转成时间戳,再存回响应式数据。
代码逻辑:
import { ref, computed } from 'vue'
import { formatDate, parseDate } from './date-utils' // 假设的工具函数
const timestamp = ref(Date.now())
const dateStr = computed({
get() {
return formatDate(timestamp.value) // 时间戳 → 字符串
},
set(val) {
timestamp.value = parseDate(val) // 字符串 → 时间戳
}
})
场景 2:购物车的“数量-总价”联动
商品单价(price)固定,数量(num)和总价(total)要双向联动:
- 用户改数量,总价自动变(
get负责num * price); - 用户改总价(比如促销调整),数量自动变(
set负责total / price)。
代码逻辑:
const price = ref(100) // 单价
const num = ref(2) // 数量
const total = computed({
get() {
return num.value * price.value
},
set(newTotal) {
num.value = newTotal / price.value
}
})
场景 3:主题切换的“双状态”同步
比如用 isDarkMode(布尔值,true 代表深色)控制主题,但 UI 上要显示“dark/light”字符串(displayMode),这时候:
get负责把布尔值转成字符串(isDarkMode ? 'dark' : 'light');set负责把字符串转成布尔值(val === 'dark')。
代码逻辑:
const isDarkMode = ref(false)
const displayMode = computed({
get() {
return isDarkMode.value ? 'dark' : 'light'
},
set(val) {
isDarkMode.value = val === 'dark'
}
})
只用 get 的 computed 和带 set 的,区别到底是啥?
核心区别就两点:
能否“赋值”
- 只有
get的 computed:只读,赋值会报错(double.value = 10会触发“计算属性是只读的”警告); - 带
set的 computed:可写,赋值时会执行set里的逻辑。
setter 必须“更新依赖”
带 set 的 computed,set 里必须修改用来计算 get 的响应式数据,否则,computed 下次读取时不会更新。
举个反面教材(错误示范):
const badFullName = computed({
get() { return lastName.value + firstName.value },
set(newValue) {
console.log('赋值了,但没改依赖') // 只打日志,没更新 lastName/firstName
}
})
badFullName.value = '王五'
console.log(lastName.value, firstName.value) // 还是原来的“张”“三”
console.log(badFullName.value) // 还是“张三”(因为依赖没变化)
写带 get/set 的 computed 时,容易踩哪些坑?怎么避?
踩过这些坑,才懂啥叫“纸上得来终觉浅”……
坑 1:setter 里没更新依赖,导致 computed 不更新
比如前面的反面教材,set 里只打日志没改 lastName/firstName。解决方法:set 必须修改参与 get 计算的响应式数据。
坑 2:setter 里出现“循环赋值”
const loopFullName = computed({
get() { return lastName.value + firstName.value },
set(newValue) {
loopFullName.value = newValue // 直接赋值给自己,无限循环!
}
})
一赋值就会栈溢出报错。解决方法:永远只修改依赖的响应式数据(lastName/firstName),别碰 computed 自身。
坑 3:TypeScript 下类型不匹配
如果用 TS,get 的返回类型和 set 的参数类型必须一致。get 返回 string,set 的参数也得是 string,否则 TS 会报错。
坑 4:<script setup> 里误用 this
在 <script setup> 语法中,不能用 this,get/set 里要直接访问 ref 变量(lastName.value),而不是 this.lastName,如果是选项式 API(非 setup),才用 this.lastName。
再给个复杂例子:结合组件通信咋玩?
实际开发中,组件间传值也常用 computed 的 get/set,比如父组件控制“是否禁用”,子组件用按钮文字(“启用/禁用”)显示状态,同时点击按钮要切换状态。
父组件:
<template>
<Child :isDisabled="isDisabled" @update:isDisabled="isDisabled = $event" />
</template>
<script setup>
import { ref } from 'vue'
import Child from './Child.vue'
const isDisabled = ref(true)
</script>
子组件:
<template>
<button @click="buttonText = buttonText === '禁用' ? '启用' : '禁用'">
{{ buttonText }}
</button>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps(['isDisabled'])
const emit = defineEmits(['update:isDisabled'])
const buttonText = computed({
get() {
// 根据父组件传的 isDisabled,返回按钮文字
return props.isDisabled ? '启用' : '禁用'
},
set(newText) {
// 点击后,把文字转成布尔值,emit 给父组件
const newState = newText === '启用'
emit('update:isDisabled', newState)
}
})
</script>
这里子组件的 buttonText,get 负责根据父组件的 isDisabled 显示文字,set 负责点击后把文字转成状态并通知父组件,既实现了“计算显示”,又实现了“反向修改”,是 get/set 的典型实战场景。
总结一下核心知识点
- get 的作用:计算属性“读”的时候,怎么整合依赖数据;
- set 的作用:计算属性“写”的时候,怎么拆分新值、更新依赖数据;
- 场景:表单双向计算、购物车联动、主题切换、组件通信等需要“双向交互”的场景;
- 避坑:set 必须更新依赖、不能循环赋值、TS 类型要一致、setup 不用 this。
把这些逻辑吃透,再遇到“计算属性需要赋值”的需求,就知道咋下手啦~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网

