什么?我居然在 React 用 Pinia?

众所周知,相信大家用过 Pinia 的都知道,Pinia 定义储存采用的是 Centralized State Management(集中式状态管理)的模式,从而区分 State(状态)、Actions(操作)、Getters(计算),而这种模式非常利于数据与视图的逻辑分离:

ts 复制代码
// stores/counter.js
import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  actions: {
    increment() {
      this.count++
    },
  },
  getters: {
    doubled() {
      return this.count * 2
    },
  }
})

而视图组件则只需要 use 对应的数据集合,则可直接读取统一管理的数据:

ts 复制代码
<script setup>
import { useCounterStore } from '@/stores/counter'
const counter = useCounterStore()

counter.count++
counter.increment()
</script>

<template>
  <div>Current Count: {{ counter.count }}</div>
</template>

Pinia 凭借简洁的语法定义与逻辑架构,完美解决了 Vuex 遗留的类型推导缺失及魔法字符串问题。目前 Pinia 已成为 Vue 生态中数据管理的首选方案,足以证明了该设计模式对开发者体验的巨大提升。

React

React 的状态管理由社区支持,状态库层出不穷,从很久以前的 Redux、Mobx,到现在流行的 Zustand、Valtio、Recoil、Jotai、XState,每一个都有自己的设计哲学,作为一个前端开发,大部分我都用了个遍,说实话你问我哪个语法上好用,我是真不知道,它们每一个都可以作为生产级别的数据储存,但我都觉得它们要么太复杂、抽象度太高;要么语法是简洁了,但 State、Action 非常零散,接手项目的时候看的人脑壳痛。

大伙可能想到了,和 Pinia 类似的不就是 Valtio。作为一个轻量级的状态管理库,Valtio 在数据管理的逻辑上,使用 proxy(代理)驱动视图更新,与 Pinia 和 vue3 的逻辑非常相似,这样的状态管理方式更加精简,学习曲线也更低,也不需要 dispatch 之类的概念。

但 Valtio 定义数据的方式则是直接定义代理对象,作为快速启动中小型项目来说是很方便,但是一但数据多起来了,每个页面的 actions 跟 getters 就会非常分散。而我们只需要解决这个问题就可以了。

Valtio Define

github.com/Hairyf/valt...

首先是 API 的设计,我们就参考 Pinia 的模式,通过包装器(defineStore)作为定义数据的手段:

ts 复制代码
import { defineStore } from 'valtio-define'

const store = defineStore({
  state: () => ({ count: 0 }),
  actions: {
    increment() {
      this.count++
    },
  },
  getters: {
    doubled() {
      return this.count * 2
    },
  }
})
store.$state // { count: number }
store.$getters // { doubled: number }
store.$subscribe // Function -> 等同于 subscribe(store.$state)
store.increment // Function

使用钩子(useStore),作为视图引用的方法:

ts 复制代码
function Counter() {
  const { count, doubled } = useStore(store)
  return (
    <div>
      <button onClick={store.increment}>Increment</button>
      <div>{count} / x2:{doubled}</div>
    </div>
  )
}

这里我们与 Pinia 会有一些差异,由于 valtio 定义与使用是分离的(这其实也是 react 框架的差异),我们则遵从框架的设计理念,将 hook 分离了出来。

插件

当然少不了插件的设计,这里就拿作为使用率最高的 persist(持久化储存)来说,我们在定义插件时也要保证精简的原则:

ts 复制代码
import type { Plugin } from 'valtio-define'
import type { PersistentOptions } from 'valtio-define'
import { subscribe } from 'valtio'

export function persist(): Plugin {
  return ({ store, options }) => {
    ...
    function watch() {
      subscribe(store.$state, () => {
        ...
      })
    }
    watch()
  }
}

declare module 'valtio-define' {
  export interface StoreDefineOptions<S extends object> {
    persist?: PersistentOptions<S> | boolean
  }
}

那么在使用的时候,我们得可以通过 store.use,去加载这个插件:

ts 复制代码
const store = defineStore({
  state: () => ({ count: 0 }),
  actions: {
    increment() {
      this.count++
    },
  },
  // 通过选项或配置开启持久化
  persist: true,
})
store.use(persist())

当然,为了节省时间,也支持从全局中加载:

ts 复制代码
import valtio from 'valtio-define'

// Register the persist plugin globally
valtio.use(persist())

省流

当然啦,我怎么会忍心让你们写代码,如果你喜欢这样的方式,我已经把上面的所有功能都做成了一个项目了(valtio-define

觉得不错的点个 Star!这对我真的很有帮助,谢谢啦。

相关推荐
老虎06272 小时前
ECharts 基础与折线图
前端·echarts
小雨青年2 小时前
鸿蒙 HarmonyOS 6 | 混合开发 (01) Web 组件内核——ArkWeb 加载机制与 Cookie 管理
前端·华为·harmonyos
工边页字2 小时前
AI产品面试官超喜欢问:什么是 Embedding,它是怎么工作的 ?
前端·人工智能·后端
吃西瓜的年年3 小时前
前端面试题(vue)
前端·javascript·vue.js
wuhen_n3 小时前
结构化Prompt——让AI说“人话”
前端·vue.js·ai编程
前端小趴菜053 小时前
vue3-signature实现电子签名
前端·javascript·vue.js
玉米Yvmi3 小时前
React自定义Hook实战指南:从入门到精通,让你的代码像乐高一样灵活
前端·react.js·面试
CharlieWang3 小时前
AI + Cloudflare = 你需要的全部
前端·敏捷开发·全栈
董员外3 小时前
从零实现 AI 编程助手:LangChain.js + ReAct 循环实战
前端·javascript·后端