# pinia-decorator
[](https://npmjs.com/package/@qubit-ltd/pinia-decorator)
[](https://www.apache.org/licenses/LICENSE-2.0)
[](README.md)
[](https://dl.circleci.com/status-badge/redirect/gh/qubit-ltd/pinia-decorator/tree/master)
[](https://coveralls.io/github/qubit-ltd/pinia-decorator?branch=master)
[pinia-decorator] 是一个 JavaScript 库,它使用了 [JavaScript 装饰器的第三阶段提案],
简化了将 [Pinia] 存储与 [Vue 类风格组件] 集成的过程。换句话说,它提供了类似于 [vuex-class]
和 [pinia-class] 库的功能。
该库受 [vuex-class] 和 [pinia-class] 的启发,但存在一些关键区别:
1. 它是用纯 JavaScript 实现的,不需要 TypeScript 支持。
2. 它支持 [Vue 3]。
3. 它支持使用 [vue3-class-component] 实现的 JavaScript 类风格 Vue 组件,
而 [pinia-class] 主要针对使用 [vue-facing-decorator] 实现的 TypeScript 类风格 Vue 组件。
4. 它提供了 [toStore](#tostore-函数) 函数,用于将一个类转换为 Pinia 存储。
## 目录
- [pinia-decorator](#pinia-decorator)
- [目录](#目录)
- [安装](#安装)
- [使用](#使用)
- [@State 装饰器](#state-装饰器)
- [@WritableState 装饰器](#writablestate-装饰器)
- [@Getter 装饰器](#getter-装饰器)
- [@Action 装饰器](#action-装饰器)
- [@Store 装饰器](#store-装饰器)
- [@RawField 装饰器](#rawfield-装饰器)
- [toStore 函数](#tostore-函数)
- [示例](#示例)
- [贡献](#贡献)
- [许可证](#许可证)
## 安装
你可以通过 npm 或 yarn 安装 [pinia-decorator]:
```bash
npm install @qubit-ltd/pinia-decorator
```
或
```bash
yarn add @qubit-ltd/pinia-decorator
```
## 使用
[pinia-decorator] 为 [Vue 3 类风格组件] 提供了以下装饰器/函数:
- @State:用于将只读状态从 [Pinia] 存储注入到组件中。
- @WritableState:用于将可写状态从 [Pinia] 存储注入到组件中。
- @Getter:用于将 getter 从 [Pinia] 存储注入到组件中。
- @Action:用于将 action 从 [Pinia] 存储注入到组件中。
- @Store:用于将整个 [Pinia] 存储注入到组件中。
- @RawField:用于将一个类的字段标记为原始字段(非响应式字段)。
- toStore:用于将一个类转换为 [Pinia] 存储。
### @State 装饰器
`@State` 装饰器用于将只读状态从 [Pinia] 存储注入到 [Vue 类风格组件] 中。
它允许你在组件内部访问和使用 [Pinia] 存储的状态。
`@State` 装饰器的语法如下:
```js
@State(store: Object, stateName?: string)
```
- `store`(必需):使用 `Pinia` 的 `defineStore()` 函数定义的待注入的 [Pinia] 存储对象。
- `stateName`(可选):待注入的状态在 [Pinia] 存储中的名称。如果未提供,
装饰器将使用被装饰的字段名作为待注入的状态的名称。
示例:
```js
@State(useUserStore)
username; // 注入用户存储中的 'username' 状态
@State(useUserStore, 'avatar')
userAvatar; // 将用户存储中的 'avatar' 状态注入为 'userAvatar'
```
### @WritableState 装饰器
`@WritableState` 装饰器与 `@State` 类似,但它允许你将可写状态从 [Pinia] 存储注入到
[Vue 类风格组件] 中。这意味着你可以在组件内部既读取又修改 [Pinia] 存储的状态值。
`@WritableState` 装饰器的语法如下:
```javascript
@WritableState(store: Object, stateName?: string)
```
- `store`(必需):使用 `Pinia` 的 `defineStore()` 函数定义的待注入的 [Pinia] 存储对象。
- `stateName`(可选):待注入的状态在 [Pinia] 存储中的名称。如果未提供,
装饰器将使用被装饰的字段名作为待注入的状态的名称。
示例:
```js
@WritableState(useUserStore)
nickname; // 注入用户存储中的 'nickname' 状态,允许读取和修改
@WritableState(useUserStore, 'avatar')
userAvatar; // 将用户存储中的 'avatar' 状态注入为 'userAvatar',允许读取和修改
```
### @Getter 装饰器
`@Getter` 装饰器用于将 getter 从 [Pinia] 存储注入到 [Vue 类风格组件] 中。
它允许你在组件内部调用 [Pinia] 存储的 getter 函数。
`@Getter` 装饰器的语法如下:
```javascript
@Getter(store: Object, getterName?: string)
```
- `store`(必需):使用 `Pinia` 的 `defineStore()` 函数定义的待注入的 [Pinia] 存储对象。
- `getterName`(可选):待注入的 getter 在 [Pinia] 存储中的名称。如果未提供,
装饰器将使用被装饰的字段名作为待注入的 getter 的名称。
示例:
```js
@Getter(useUserStore)
isLoggedIn; // 注入用户存储中的 'isLoggedIn' getter
@Getter(useUserStore, 'fullName')
userName; // 将用户存储中的 'fullName' getter 注入为 'userName'
```
### @Action 装饰器
`@Action` 装饰器用于将 action 从 [Pinia] 存储注入到 [Vue 类风格组件] 中。
它允许你在组件内部调用存储的 action 函数。
`@Action` 装饰器的语法如下:
```javascript
@Action(store: Object, actionName?: string)
```
- `store`(必需):使用 `Pinia` 的 `defineStore()` 函数定义的待注入的 [Pinia] 存储对象。
- `actionName`(可选):待注入的 action 在 [Pinia] 存储中的名称。如果未提供,
装饰器将使用被装饰的字段名作为待注入的 action 的名称。
示例:
```js
@Action(useUserStore)
login; // 注入用户存储中的 'login' action
@Action(useUserStore, 'updateProfile')
updateUserProfile; // 将用户存储中的 'updateProfile' action 注入为 'updateUserProfile'
```
### @Store 装饰器
`@Store` 装饰器用于将整个 [Pinia] 存储注入到 Vue 类风格组件中。它允许你访问存储的所有状态、
getter 和 action。
`@Store` 装饰器的语法如下:
```javascript
@Store(store: Object)
```
- `store`(必需):使用 `Pinia` 的 `defineStore()` 函数定义的待注入的 [Pinia] 存储对象的创建函数。
示例:
```js
@Store(useUserStore)
userStore; // 将整个用户存储注入为 'userStore'
```
### @RawField 装饰器
`@RawField` 装饰器用于将一个类的字段标记为原始字段,这样该类被转换为 Pinia store 后,该
字段表示的状态不会被转换为响应式状态。
`@RawField` 装饰器的语法如下:
```javascript
@RawField
fieldName;
```
示例:
```js
class UserStore {
username = '';
@RawField
logger = createLogger('UserStore'); // 这个字段不会成为响应式的
login(username, password) {
this.logger.info(`用户 ${username} 尝试登录`);
// 登录逻辑
}
}
export default toStore('user', UserStore);
```
### toStore 函数
`toStore()` 函数用于将一个类转换为 [Pinia] 存储。它允许你定义一个类,该类的实例将作为 Pinia 存储的实例。
`toStore()` 函数的语法如下:
```javascript
toStore(storeId: string, Class: function)
```
- `storeId`(必需):Pinia 存储的ID。
- `Class`(必需):待转换为 Pinia 存储的类。
注意:
- `toStore` 函数支持类的继承。
- 类中定义的属性将成为 Pinia 存储中的状态。
- 类中定义的 getter 方法将成为 Pinia 存储中的 getter。
- 类中定义的普通方法将成为 Pinia 存储中的 action。
示例:
```js
import { toStore } from '@qubit-ltd/pinia-decorator';
class UserStore {
username = '';
token = null;
get isLoggedIn() {
return !!this.token;
}
async login(username, password) {
// 登录逻辑
this.username = username;
this.token = await api.getToken(username, password);
}
logout() {
this.username = '';
this.token = null;
}
}
export default toStore('user', UserStore);
```
## 示例
以下是如何在 Vue 组件中使用这些装饰器的简单示例:
```javascript
import { Component, toVue } from '@qubit-ltd/vue3-class-component';
import { State, WritableState, Getter, Action, Store } from '@qubit-ltd/pinia-decorator';
import { useMyStore } from './my-pinia-store-module';
@Component
export class MyComponent extends Vue {
@State(useMyStore)
myValue;
@State(useMyStore, 'message')
myMessage;
@Getter(useMyStore)
myGetter
@Getter(useMyStore, 'count')
myCountGetter
@Action(useMyStore)
fetchData;
@Action(useMyStore, 'sayMessage')
mySayMessage;
@Store(useMyStore)
store;
someOtherMessage = 'Hello World!';
callSayMessage() {
console.log('MyComponent.callSayMessage');
this.mySayMessage(this.myMessage);
}
}
export default toVue(MyComponent);
```
下面是一个使用 `toStore()` 函数的示例,注意该函数支持类的继承:
```javascript
import { toStore, RawField } from '@qubit-ltd/pinia-decorator';
import { Logger } from '@qubit-ltd/logging';
import dayjs from 'dayjs';
class BaseUserStore {
id = '';
username = '';
password = '';
nickname = '';
token = {
value: 'token-value',
expired: 1000,
};
get age() {
throw new Error('This getter will be overridden by subclass');
}
setNickname(nickname) {
this.nickname = nickname;
}
login() {
throw new Error('This function will be overridden by subclass');
}
}
class UserStore extends BaseUserStore { // 支持类的继承
avatar = '';
birthday = '';
@RawField
logger = Logger.getLogger('store.user'); // 这个字段被标记为原始字段
get age() { // 覆盖父类的 getter
return dayjs().diff(this.birthday, 'year');
}
setAvatar(avatar) {
this.avatar = avatar;
}
updatePassword(newPassword) {
this.password = newPassword;
return api.updatePassword(this.username, newPassword);
}
login() { // 覆盖父类的方法
this.logger.info('Logging in as:', this.username);
return api.login(this.username, this.password);
}
}
export default toStore('user', UserStore);
```
以上例子定义了一个名为 `user` 的 Pinia 存储,它等价于以下代码:
```javascript
import { defineStore } from 'pinia';
import { markRaw } from 'vue';
const useUserStore = defineStore('user', {
state: () => ({
id: '',
username: '',
password: '',
nickname: '',
token: {
value: 'token-value',
expired: 1000,
},
avatar: '',
birthday: '',
logger: markRaw(Logger.getLogger('store.user')),
}),
getters: {
age: (state) => dayjs().diff(state.birthday, 'year'),
},
actions: {
setNickname(nickname) {
this.nickname = nickname;
},
setAvatar(avatar) {
this.avatar = avatar;
},
updatePassword(newPassword) {
this.password = newPassword;
return api.updatePassword(this.username, newPassword);
},
login() {
this.logger.info('Logging in as:', this.username);
return api.login(this.username, this.password);
},
},
});
export default useUserStore;
```
我们可以按如下方式在 Vue 组件中使用 `user` 存储:
```vue
用户名: {{ username }}
昵称: {{ nickname }}
年龄: {{ age }}
头像:
![]()
```
有关更多详细信息,请查看以下演示项目:
- [使用 vite 的演示项目](https://github.com/haixing-hu/pinia-decorator-demo-vite)
- [使用 webpack 的演示项目](https://github.com/haixing-hu/pinia-decorator-demo-webpack)
## 贡献
如果你发现任何问题或有改进建议,请随时在 [GitHub 仓库] 中提出问题或提交请求。
我们欢迎你的贡献和反馈!
## 许可证
`pinia-decorator` 遵循 Apache 2.0 许可证分发。
请查看 [LICENSE](LICENSE) 文件以获取更多详细信息。
[pinia-decorator]: https://npmjs.com/package/@qubit-ltd/pinia-decorator
[Pinia]: https://pinia.vuejs.org/
[Vue]: https://vuejs.org/
[Vue 3]: https://vuejs.org/
[Vue 类风格组件]: https://npmjs.com/package/@qubit-ltd/vue3-class-component
[vue3-class-component]: https://npmjs.com/package/@qubit-ltd/vue3-class-component
[JavaScript 装饰器的第三阶段提案]: https://github.com/tc39/proposal-decorators
[vuex-class]: https://github.com/ktsn/vuex-class
[pinia-class]: https://github.com/jquagliatini/pinia-class
[vue-facing-decorator]: https://github.com/facing-dev/vue-facing-decorator
[GitHub 仓库]: https://github.com/qubit-ltd/pinia-decorator