0.1.0 • Published 2 years ago

puppet-vue3 v0.1.0

Weekly downloads
-
License
MIT
Repository
-
Last release
2 years ago

puppet-vue3

一款轻量级的 Vue3 状态管理库,基于 Typescript + Vue3 响应式系统实现。

安装

  1. 安装依赖包
npm i -S puppet-vue3
  1. 在tsconfig.json中配置如下属性:
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"useDefineForClassFields": false,

如何创建一个 Store?

Puppet 是基于来实现的 Store,你可以在项目中的任何地方创建一个类,作为你的 Store,至于你想让这个 Store 是单例的还是多例的(一般是单例的),由你自己来决定。

如何创建一个 State?

你可以通过 @State() 装饰器来定义你的 State,被装饰的对象为 Store 中的成员变量,你可以通过@State()装饰器在一个 Store 中定义多个 State。当然,我们推荐您通过 private 访问修饰符将 State 设为私有的,再定义其 get 访问器,让外界通过 get 访问器访问你的 State。

export class ExampleStore {
    @State()
    private nameState = 'Zhangsan';

    @State()
    private dateOfBirthState = {
        year: 1999,
        month: 1,
        day: 10
    };

    get name(): string {
        return this.nameState;
    }

    get dateOfBirth(): DateOfBirth {
        return this.dateOfBirthState;
    }
}

通过这种方式得到的 State 是双向数据流的,你可以使用 Puppet 中的 @Freezer() 装饰器,得到一个单向数据流的 State!

@Freezer()
get dateOfBirth(): DateOfBirth {
    return this.dateOfBirthState;
}

对于基本类型的 State,因为没有 set 访问器,本身就是单向的,无需用该装饰器装饰。

get name(): string {
    return this.nameState;
}

如何使用 State?

如果你的项目没有依赖注入工具的话,推荐你建一个或多个 ts 文件,来保存你的 state 实例。

export const exampleStore = new ExampleStore();
export const exampleStore2 = new ExampleStore2();
export const exampleStore3 = new ExampleStore3();

在 vue 文件中,如果你的 State 是对象类型,如上面 ExampleStore 中的 dateOfBirth,Puppet 允许你对第一层解构来使用,而不失响应性。当然你不可以将这个 State 再赋其他类型的值。如:原 State 是对象类型,为其赋基本类型的值、原 State 是非数组类型的对象,将其赋数组类型的对象。

<template>
    <ul>
        <li>Date Of Birth</li>
        <li>Year: {{ dateOfBirth.year }}</li>
        <li>Month: {{ dateOfBirth.month }}</li>
        <li>Day: {{ dateOfBirth.day }}</li>
    </ul>
</template>

<script lang="ts" setup>
import { exampleStore } from '@/stores';
const { dateOfBirth } = exampleStore;
</script>

如果你的 state 是基本类型,如上面 ExampleStore 中的 name,则不可以解构,你需要通过 exampleStore.name 来使用,否则会失去响应性。

<div>Name:{{ exampleStore.name }}</div>

如何更改 State?

在 Puppet 中没有像 Vuex 中 mutation、action 的概念,你可以在 Store 中定义普通的方法来改变该 Store 中的 State。

setName(newName: string): void {
    this.nameState = newName;
}

setDateOfBirth(newDateOfBirth: DateOfBirth): void {
    this.dateOfBirthState = newDateOfBirth;
}

setYearOfBirth(newYear: number): void {
    this.dateOfBirthState.year = newYear;
}

如何双向绑定 State?

如果你需要的场景是输入的值一旦改变,立即更新 State,则建议: 基本类型的 State:提供 set 访问器,直接在 vue 文件中双向绑定。 对象类型的 State:提供没有被@Freezer()装饰器装饰的 get 访问器,双向绑定 get 访问器返回的 State。

<ul>
    <li>Name: <input v-model="exampleStore.name" /></li>
    <li>Date Of Birth</li>
    <li>年:<input v-model="changeableDateOfBirth.year" /></li>
    <li>月:<input v-model="changeableDateOfBirth.month" /></li>
    <li>日:<input v-model="changeableDateOfBirth.day" /></li>
</ul>

set name(newName: string) {
    this.nameState = newName;
}

get name(): string {
    return this.nameState;
}

get changeableDateOfBirth(): DateOfBirth {
    return this.dateOfBirthState;
}

如果你需要的场景是输入值改变,在某一动作后,再更新 State,如提交表单,则建议使用 Puppet 提供的 @Shadow() 装饰器,通过该装饰器创建 State 或 State 的一部分的响应式副本,再在页面进行双向绑定。该装饰器有两个重载:

export function Shadow(shadowKey: string | symbol): PropertyDecorator;
export function Shadow<R extends Record<string | symbol, any>>(
    partialState: (stateHolder: any) => R,
    shadowKey: R extends any[] ? number : keyof R
): PropertyDecorator;

第一个重载的参数为,当前 Store 中你需要的 State 的 key。 第二个重载的参数为,partialState:当前 Store 中你需要的 State 中的对象的父对象; shadowKey:当前 Store 中你需要的 State 中的对象的 key。 使用方式如下:

@Shadow('name')
nameShadow: PuppetShadow<string>;

@Shadow((store: ExampleStore) => store.dateOfBirthState, 'month')
monthOfBirthStateShadow: PuppetShadow<number>;

返回的 PuppetShadow 类型对象是一个元组,第一个元素为,你需要的 State 或 State 的一部分的对象副本,并且是响应式的,如果这个副本是基本类型,则 Puppet 会通过 Vue3 的 ref 函数去创建并包装这个副本。 第二个元素为提交这个副本的函数,调用此函数后,Puppet 会将你更改后的副本覆盖到你的 State 中去。第三个元素为重置这个副本的函数,调用此函数后,将当前的副本重置到初始值或上一次提交的值。

<ul class="puppet-ul">
    <li>Year Of Birth:<input v-model="dateOfBirthShadow.year" /></li>
    <li><button @click="commitDateOfBirthShadow">Commit</button></li>
    <li><button @click="resetDateOfBirthShadow">Reset</button></li>
    <li>Month Of Birth:<input v-model="monthOfBirthShadow" /></li>
    <li><button @click="commitMonthOfBirthShadow">Commit</button></li>
    <li><button @click="resetMonthOfBirthShadow">Reset</button></li>
</ul>

const [dateOfBirthShadow, commitDateOfBirthShadow, resetDateOfBirthShadow] = exampleStore.dateOfBirthShadow;
const [monthOfBirthShadow, commitMonthOfBirthShadow, resetMonthOfBirthShadow] = exampleStore.monthOfBirthShadow;