尤雨溪都没告诉你的 setup() 技巧

这半年用组合式 API 用得比较深,项目里遇到不少 edge case,文档没有明确提,踩坑只能自己总结。下面这几种 setup() 写法,我们项目里是常规写法,调试方便,协作时出问题也好排查。


1. ref 解构之后变量不会响应更新

很多时候会写成:

ts 复制代码
const user = ref({ name: 'Tom', age: 25 })
const { name } = user.value

表面上看没问题,其实 name 是一个普通字符串,失去了响应能力。

正确写法应该是:

ts 复制代码
import { ref, toRefs } from 'vue'

const user = ref({ name: 'Tom', age: 25 })
const { name, age } = toRefs(user.value)

这时候 name 和 age 才是带响应追踪的 ref。


2. computed 要明确暴露依赖来源

我们经常写一些计算逻辑函数:

ts 复制代码
const count = ref(2)
function double() {
  return count.value * 2
}

这个 double() 调用没有响应能力,视图不会自动更新。改写成:

ts 复制代码
const double = computed(() => count.value * 2)

更推荐方式是直接封装成组合函数:

ts 复制代码
export function useCounter() {
  const count = ref(0)
  const double = computed(() => count.value * 2)
  const inc = () => count.value++
  return { count, double, inc }
}

setup 中这样引用:

ts 复制代码
const { count, double, inc } = useCounter()

这样处理之后响应关系更清晰,状态之间的计算逻辑也更好跟踪。


3. 有时候调试时状态挂到 window 会方便很多

某些复杂状态是组合函数里内部维护的,写完之后发现 Vue 工具看不到,也不方便控制台临时改状态。可以这样写:

ts 复制代码
if (import.meta.env.DEV) {
  window.__debugUser = { name, age, isTeen }
}

这样在控制台就能直接操作:

js 复制代码
__debugUser.age.value = 16

实际写法:

ts 复制代码
export function useUser() {
  const name = ref('Tom')
  const age = ref(25)
  const isTeen = computed(() => age.value < 18)

  if (import.meta.env.DEV) {
    window.__debugUser = { name, age, isTeen }
  }

  return { name, age, isTeen }
}

可以挂任意模块状态,只用于开发阶段,打包时不输出。


4. 不建议把 computed 放在 reactive 对象里面

很多人想把响应状态收拢成一个对象:

ts 复制代码
const state = reactive({
  count: ref(1),
  double: computed(() => state.count.value * 2)
})

这个写法是有问题的,double 不会更新。原因是 reactive 只能追踪路径,computed 是 lazy getter,不在路径里。

正确方式是:

ts 复制代码
const count = ref(1)
const double = computed(() => count.value * 2)
const state = reactive({ count, double })

或者直接保持扁平结构:

ts 复制代码
const count = ref(1)
const double = computed(() => count.value * 2)

5. setup() 内 return 不建议暴露太多字段

很多组件直接把所有 ref、computed 都 return 出去:

ts 复制代码
setup() {
  const a = ref(1)
  const b = ref(2)
  const c = ref(3)
  return { a, b, c }
}

变量一多,调试和联动关系就混乱了。

推荐用组合函数做模块化拆分,比如:

ts 复制代码
function useForm() {
  const name = ref('')
  const age = ref(18)
  const isValid = computed(() => name.value.length > 0 && age.value >= 18)
  return { name, age, isValid }
}

function usePermissions() {
  const role = ref('user')
  const isAdmin = computed(() => role.value === 'admin')
  return { role, isAdmin }
}

然后在组件中按需组合:

ts 复制代码
const { name, age, isValid } = useForm()
const { role, isAdmin } = usePermissions()

这样变量来源清晰,也更利于多人协作。


6. 模板里用的数据必须是 ref 或 computed

组合函数中你可能封装了一些 getter 函数:

ts 复制代码
function getNameLength() {
  return name.value.length
}

但这种函数你直接 return 出去,模板里是不会自动更新的。需要写成:

ts 复制代码
const nameLength = computed(() => name.value.length)

模板里用 {{ nameLength }} 才能自动刷新。


以上这些写法,都是组合式 API 常见的实际用法,并不是语法特性,而是团队在维护大型 Vue 项目时,逐步稳定下来的实践方式。建议可以整理成你们项目的内部写法规范。

如果你有别的组合式设计上的经验,也可以继续补充🙂。

📌 你可以继续看我的系列文章

相关推荐
徐小夕42 分钟前
JitWord Office预览引擎:如何用Vue3+Node.js打造丝滑的PDF/Excel/PPT嵌入方案
前端·vue.js·github
晴殇i1 小时前
揭秘JavaScript中那些“不冒泡”的DOM事件
前端·javascript·面试
孟陬1 小时前
国外技术周刊 #1:Paul Graham 重新分享最受欢迎的文章《创作者的品味》、本周被划线最多 YouTube《如何在 19 分钟内学会 AI》、为何我不
java·前端·后端
BER_c1 小时前
前端权限校验最佳实践:一个健壮的柯里化工具函数
前端·javascript
兆子龙1 小时前
别再用 useState / data 管 Tabs 的 activeKey 了:和 URL 绑定才香
前端·架构
sudo_jin1 小时前
前端包管理器演进史:为什么 npm 之后,Yarn 和 pnpm 成了新宠?
前端·npm
敲敲敲敲暴你脑袋2 小时前
写个添加注释的vscode插件
javascript·typescript·visual studio code
叁两2 小时前
用opencode打造全自动公众号写作流水线,AI 代笔太香了!
前端·人工智能·agent
golang学习记2 小时前
GitLens 十大神技:彻底改变你在 VS Code 中的 Git 工作流
前端·后端·visual studio code
SuperEugene2 小时前
后台权限与菜单渲染:基于路由和后端返回的几种实现方式
前端·javascript·vue.js