什么?我居然在 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!这对我真的很有帮助,谢谢啦。

相关推荐
心.c7 分钟前
大厂高频手写题
开发语言·前端·javascript
神の愛8 小时前
左连接查询数据 left join
java·服务器·前端
小码哥_常10 小时前
解锁Android嵌入式照片选择器,让你的App体验丝滑起飞
前端
郑寿昌10 小时前
IIoT本体迁移的领域扩展机制
服务器·前端·microsoft
深海鱼在掘金11 小时前
Next.js从入门到实战保姆级教程(第十一章):错误处理与加载状态
前端·typescript·next.js
深海鱼在掘金11 小时前
Next.js从入门到实战保姆级教程(第十二章):认证鉴权与中间件
前端·typescript·next.js
energy_DT11 小时前
2026年十五五油气田智能增产装备数字孪生,CIMPro孪大师赋能“流动增产工厂”三维可视化管控
前端
龙猫里的小梅啊11 小时前
CSS(四)CSS文本属性
前端·css
MXN_小南学前端11 小时前
watch详解:与computed 对比以及 Vue2 / Vue3 区别
前端·javascript·vue.js
饭小猿人12 小时前
Flutter实现底部动画弹窗有两种方式
开发语言·前端·flutter