第 8 课:开始引入组合式函数

这一课非常关键,因为我们开始从"组件拆分"进入到"逻辑拆分"。

前几轮你已经看到:

  • 页面头部可以拆组件
  • 卡片可以拆组件
  • 筛选栏可以拆组件
  • 表格可以拆组件
  • 弹窗也可以拆组件

但这时还会有一个问题:

就算模板都拆开了,页面级逻辑还是可能越来越多,怎么办?

这时候就该轮到组合式函数出场了。


先讲结论

你可以先记住一句非常重要的话:

组件主要负责界面结构,composable 主要负责可复用逻辑

也就是说:

  • .vue 组件更偏向"长什么样、怎么渲染"
  • useXxx.ts 更偏向"状态怎么组织、逻辑怎么流动"

这就是 Vue 3 里组合式函数最有价值的地方。


这次我们做了什么

这一轮新增了:

  • src/composables/useTasksPage.ts

同时更新了:

  • src/views/TasksView.vue
  • src/composables/__tests__/useTasksPage.spec.ts

这说明我们不是只写了一篇讲解文档,

而是真的把任务页页面级逻辑迁到了组合式函数中。


什么是组合式函数

你先不要把"组合式函数"想得太神秘。

它本质上就是:

把一组相关的响应式状态、计算属性和行为函数,打包到一个普通的 TypeScript 函数里

比如当前这个:

  • useTasksPage()

它把任务页相关的这些东西统一收口了:

  • 任务列表
  • 筛选条件
  • 筛选结果
  • 统计卡片
  • 弹窗开关
  • 创建任务逻辑
  • 页面交互处理函数

这就是典型的组合式函数形态。


为什么这一步不是"组件拆分的重复"

因为组件拆分解决的是:

模板太大、区域太多

而组合式函数解决的是:

逻辑太多、状态太散

这两个不是一个问题。

举个直观的对比:

组件拆分前

  • 模板很长
  • 逻辑也在一个文件里

组件拆分后

  • 模板被拆小了
  • 但父页面里仍可能保留一大堆页面级状态和函数

引入 composable 后

  • 页面模板清楚了
  • 页面逻辑也开始集中抽离了

所以你要理解:

组件拆分是拆界面,composable 是拆逻辑


当前 useTasksPage.ts 里收了什么

这次迁进去的内容主要有这些:

1. 页面级状态

  • tasks
  • keyword
  • statusFilter
  • priorityFilter
  • dialogVisible
  • nextTaskId

这些都属于:

整个任务页都会用到的页面级状态

所以它们非常适合放进 composable,而不是塞在 TasksView.vue 里。


2. 页面级派生数据

  • filteredTasks
  • taskStats
  • defaultAssignee

这些值都不是用户直接输入的原始数据,

而是根据已有状态算出来的。

这类东西和页面级状态关系非常紧密,

也很适合一起收进 composable。


3. 页面级行为函数

  • openCreateDialog
  • handleDialogVisibleChange
  • createTask
  • handleKeywordChange
  • handleStatusChange
  • handlePriorityChange

这些函数都在做同一件事:

驱动任务页这一个页面的整体行为

所以它们放到 useTasksPage() 里,就比散落在页面组件里更清晰。


为什么这些逻辑适合放到 composable

因为它们有一个共同点:

它们不依赖模板结构本身,而是依赖页面状态关系

比如:

  • 任务怎么筛选
  • 统计卡片怎么算
  • 创建任务后列表怎么更新
  • 弹窗开关怎么改

这些问题其实都和"HTML 长什么样"没直接关系。

它们描述的是页面行为规则。

而页面行为规则,正是组合式函数最擅长承载的东西。


现在的 TasksView.vue 为什么更清爽了

现在的 TasksView.vue 主要只做这些事情:

  1. 导入子组件
  2. 调用 useTasksPage()
  3. 把 composable 返回的状态和方法接到模板上
  4. 在页面层补一个成功提示

这说明它已经非常接近一个理想状态:

页面组件只负责组装,不负责承载大量页面逻辑

这就是组合式函数真正带来的价值。


为什么 TasksView.vue 里还保留了一个 handleCreateTask

你会发现,现在页面里仍然保留了一个小函数:

ts 复制代码
function handleCreateTask(taskDraft: TaskDraft) {
  createTask(taskDraft)
  ElMessage.success(...)
}

这一步很值得你学。

因为它体现了一个更细的分层思路:

  • useTasksPage() 负责页面业务逻辑
  • 页面组件负责页面层反馈

也就是说:

  • "把任务插入列表"是业务逻辑
  • "弹出成功提示"更偏页面交互反馈

这不是绝对规则,但这是一个很合理的边界。


为什么 composable 里还能用 Store

useTasksPage.ts 里用了:

  • useUserStore()

这对初学者很重要,因为这说明:

composable 不是只能写纯函数,它完全可以组合已有的 Store、ref、computed 和函数逻辑

组合式函数本来就是"把多个能力再组合起来"的。

所以你不要把它理解成某种特殊限制很强的东西。

它本质上就是:

更高一层的逻辑复用容器


这次为什么顺手加了 composable 测试

这次新增了:

  • src/composables/__tests__/useTasksPage.spec.ts

这是很有教学价值的一步。

因为它让你看到:

逻辑迁进 composable 后,反而更容易单独测试

比如现在就能比较直接地测:

  • 创建任务后列表是否更新
  • 创建任务后弹窗是否关闭
  • 状态筛选是否会影响过滤结果

这说明组合式函数不仅让代码更清晰,

也通常会让测试更容易写。


composable 和组件到底怎么分工

你现在可以这样记:

组件负责

  • 模板结构
  • 样式
  • 局部界面交互
  • 子组件组合

composable 负责

  • 页面级状态
  • 派生数据
  • 页面级行为函数
  • 跨多个组件共享的逻辑

一个最容易犯的错误

有些人学到 composable 之后,

会开始把所有东西都塞进 useXxx.ts。

这也不对。

你要避免两个极端:

极端 1

所有逻辑都堆在页面组件里。


极端 2

所有逻辑都强行塞进 composable,连页面层反馈都不留。

更合理的做法是:

按逻辑边界拆,不要为了用 composable 而用 composable


当前任务页为什么很适合开始引入 composable

因为现在它已经满足 3 个条件:

  1. 页面结构已经相对稳定
  2. 页面级逻辑已经明显聚成一团
  3. 这些逻辑并不依赖具体模板长相

这正是引入 composable 的好时机。

如果你在页面还没稳定时就急着抽 composable,

通常会抽得很乱。

所以顺序也很重要:

先把组件边界大致理顺,再抽页面逻辑


这次最值得你学的 4 个点

1. composable 是"逻辑容器",不是"组件替代品"

它不会替你渲染模板。

它只是帮你组织状态和行为。


2. 页面组件会越来越像"组装层"

这是好事,不是页面变弱了。

这说明职责更清楚了。


3. 页面级逻辑抽出后更容易测试

这对项目长期维护非常重要。


4. 组合式函数非常适合承载页面逻辑

尤其是:

  • 多个状态之间有明显关系
  • 有一组相关 computed
  • 有一组相关行为函数

这几种情况一出现,就可以考虑 composable。


你现在应该能回答的 10 个问题

  1. 组件拆分和 composable 拆分分别解决什么问题?
  2. useTasksPage() 当前收了哪些页面级状态?
  3. 为什么 filteredTasks 和 taskStats 很适合放进 composable?
  4. 为什么这些逻辑和模板结构没有直接关系?
  5. 为什么 TasksView.vue 现在更清爽了?
  6. 为什么页面层还保留了 handleCreateTask?
  7. 为什么 composable 里也可以使用 Store?
  8. 为什么 composable 抽出来后更容易写单元测试?
  9. 什么时候适合开始引入 composable?
  10. 为什么不能把所有逻辑都无脑塞进 composable?

这节课的动手练习

练习 1

打开 useTasksPage.ts,把里面的内容按这 3 类重新标出来:

  • 页面级状态
  • 派生数据
  • 页面级行为

目的:

训练你按逻辑角色读 composable。


练习 2

对照看:

找出:

  • 哪些逻辑已经迁走
  • 哪些逻辑还留在页面层

目的:

训练你分辨"业务逻辑"和"页面交互反馈"。


练习 3

打开 useTasksPage.spec.ts,回答:

  • 它为什么不用渲染组件,也能测试任务页逻辑?

目的:

让你开始理解"逻辑抽离后测试更容易"这件事。


这节课的复习结论

把这一课压缩成 8 句话:

  1. 组件拆分解决界面复杂度,composable 拆分解决逻辑复杂度。
  2. useTasksPage() 把任务页的页面级状态、派生数据和行为函数统一收口了。
  3. 页面逻辑不依赖模板时,通常很适合迁到 composable。
  4. TasksView.vue 现在更像页面组装层。
  5. 页面层可以保留少量交互反馈逻辑,不必把一切都塞进 composable。
  6. composable 可以组合 Store、ref、computed 和函数逻辑。
  7. composable 抽出来后,通常更容易单独测试。
  8. 先理顺组件边界,再抽页面逻辑,是更稳的节奏。
相关推荐
郑州光合科技余经理4 分钟前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
小呆呆66640 分钟前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
凤城老人1 小时前
从 PyQt6 到 Electron:给 Edge TTS 做一个“多角色配音机“的踩坑手记
javascript·typescript·electron
Dovis(誓平步青云)1 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
sunshine22 girl1 小时前
Java学习六 Java常见API-2--String-3-字符串的截取替换和其他方法
java·开发语言·学习
码艺-Alimjan2 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
sunshine22 girl2 小时前
Java学习六 Java常见API-1-Radom随机数
java·开发语言·学习
可乐鸡翅yeah_2 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒2 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技2 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端