写Vue项目时间长了以后,会发现一个很现实的问题:
组件到底应该拆多小?
拆得太少,一个页面几百甚至上千行。
拆得太多,一个简单页面打开就是十几个组件。
所以组件拆分并没有一个绝对标准,我更倾向于从"职责"来判断。
不要为了拆组件而拆组件
比如:
<template>
<button class="button">
{{ text }}
</button>
</template>如果这个按钮只在一个页面出现,而且没有复杂逻辑,那没必要为了组件化而单独创建一个文件。
反过来,如果项目里几十个地方都用同一个按钮,那它就值得抽成公共组件。
所以我一般会问三个问题:
- 会不会复用?
- 有没有独立逻辑?
- 有没有独立的业务职责?
满足其中一个或多个,就可以考虑拆。
UI组件和业务组件应该区分开
这是我比较推荐的一种方式。
比如:
components/
├── Button.vue
├── Modal.vue
├── Input.vue
└── Table.vue这些属于通用UI组件。
而:
features/
├── UserList/
├── OrderList/
└── Dashboard/属于业务功能。
这样做的好处是:
通用组件不需要知道业务。
比如Button不应该知道:
createUser()
deleteOrder()
submitForm()它只需要负责:
<Button
:loading="loading"
@click="submit"
/>Props负责输入
一个组件应该尽量通过Props接收外部数据。
比如:
interface Props {
title: string
loading?: boolean
}
const props = defineProps<Props>()调用:
<UserCard
title="用户信息"
:loading="loading"
/>这样组件的依赖关系就比较明确。
Emits负责输出
子组件不要直接修改父组件的状态。
比如:
const emit = defineEmits<{
submit: []
}>()点击时:
emit('submit')父组件:
<UserForm @submit="handleSubmit" />这种方式让数据流更清晰:
Parent
↓ Props
Child
↓ Emits
Parent一个组件什么时候算太大?
我不会简单用"超过300行就必须拆"这种规则。
更有意义的判断方式是:
这个组件里面是不是存在多个完全不同的职责?
比如一个页面同时负责:
用户搜索
用户列表
分页
用户编辑
用户删除
权限判断
弹窗这时候就应该考虑拆分。
可以变成:
UserPage.vue
├── UserSearch.vue
├── UserTable.vue
├── UserPagination.vue
└── UserEditDialog.vue页面负责组合。
子组件负责具体功能。
复杂逻辑应该挪到composable里
如果一个组件里面出现大量:
watch()
computed()
async function()而且这些逻辑和UI没有强绑定,那就可以考虑抽出来。
比如:
UserPage.vue
↓
useUsers()
↓
API这样:
const {
users,
loading,
loadUsers,
deleteUser
} = useUsers()页面主要负责UI。
业务逻辑交给composable管理。
我现在更倾向按功能组织
随着项目规模变大,我越来越不喜欢:
components/
views/
utils/
api/
hooks/下面堆几百个文件。
对于大型项目,我更倾向:
features/
├── user/
│ ├── components/
│ ├── composables/
│ ├── api.ts
│ └── types.ts
│
├── order/
│ ├── components/
│ ├── composables/
│ ├── api.ts
│ └── types.ts这样一个功能的相关代码都在相对集中的位置。
以后改用户模块,不需要在整个项目里到处翻。
组件设计没有唯一答案
Vue本身提供了很灵活的组件系统。
真正重要的不是:
"我的组件是不是足够小?"
而是:
"这个组件的职责是不是足够清晰?"
一个200行但职责单一的组件,可能比50行但职责混乱的组件更容易维护。
所以在实际开发中,我现在更看重职责边界、数据流和复用价值,而不是单纯追求组件数量。
这也是一个Vue项目从"能跑"逐渐走向"好维护"的过程中,很重要的一步。