Vue 3 Composition API 的核心设计。
实际上,Pinia Store 和 Vue 3 的 Composable 本质上是同一种模式的两种应用场景。 它们在结构和逻辑组织上几乎是镜像对称的。
以下是它们如此相似的原因以及它们之间的细微区别:
1. 结构逻辑的对应关系
如果你对比两者的代码结构,你会发现它们都是基于“状态、派生、动作”这三个支柱:
| 组成部分 | Composable (useFileHandler) | Pinia Store (fileStore) |
|---|---|---|
| 状态 (State) | ref(), reactive() | state: () => ({ ... }) |
| 计算属性 (Getters) | computed() | getters: { ... } |
| 方法 (Actions) | function handleUpload() { ... } | actions: { handleUpload() { ... } } |
| 暴露 (Exports) | return { ... } | 自动暴露所有定义的属性 |
2. 为什么感觉像?(核心原因)
因为 Pinia 本身就是为了适配 Composition API 而设计的。
- Setup Store:Pinia 甚至支持一种
defineStore('id', () => { ... })的写法,这种写法看起来和你写的useFileHandler完全一模一样。 - 逻辑封装:两者都是为了解决 Vue 2 中
mixins的痛点,即将“关注点分离”,把散落在各处的功能代码聚合在一起。
3. Composable vs. Pinia 的本质区别
虽然写法像,但它们的生命周期和共享范围不同:
Composable (如 useFileHandler)
- 私有性:每次在组件中调用
const { ... } = useFileHandler(),通常会创建一个全新的、独立的状态实例。 - 生命周期:它的状态通常随着引用它的组件销毁而销毁。
- 用途:用于逻辑重用。就像一个“逻辑模具”,每次生产出来的东西数据是独立的,但行为(校验、上传)是一致的。
Pinia Store (如 fileStore)
- 全局性:它是单例的。无论你在 10 个组件里引用多少次,它们指向的都是同一份数据。
- 持久性:除非手动刷新页面,否则数据一直存在于内存中。
- 用途:用于数据共享。比如你在
InputField上传了文件,在Sidebar里也要看到文件列表,这时必须用 Store。
4. 在你的重构中,它们是如何协作的?
在你的 useFileHandler.js 中,这种相似性体现得淋漓尽致:
JavaScript
export function useFileHandler(isTranslateMode) {
// 1. 引入 Store(全局共享的数据)
const fileStore = useFileStore();
// 2. 定义局部逻辑(私有的行为)
const validateFiles = (files) => { ... };
// 3. 协调两者
const upload = (files) => {
if (validateFiles(files)) {
fileStore.uploadFiles(files); // 将结果交给全局 Store
}
};
return { upload };
}
总结
你觉得像,是因为 Vue 3 统一了响应式编程的模型。
- Store 是你的“数据库”和“内存银行”。
- Composable 是你的“业务逻辑层”和“技能插件”。
这种写法让你可以像搭积木一样,在不同的 Hooks 之间传递参数,而不必像 Vue 2 那样去担心 this 指向或命名冲突的问题。