mirrorstate
v1.1.1
Published
This is a global state management system that adopts a divide and conquer approach
Readme
MirrorState
一个轻量级、框架无关的响应式状态管理库,支持 React 和 Vue,让你以统一的 API 管理应用状态。
✨ 特性
- 🚀 框架无关 - 同一套 API 同时支持 React 和 Vue
- 📦 轻量小巧 - 核心代码仅 2KB,零依赖
- 🎯 按需更新 - 只有订阅了状态变化的组件才会重新渲染
- 🔧 中间件机制 - 支持日志、持久化、时间旅行等扩展
- 💪 TypeScript 支持 - 完整的类型推导
- 🧹 自动清理 - 组件卸载时自动清理订阅
- 🎨 灵活使用 - 既可用于响应式 UI 状态,也可用于非响应式数据
📦 安装
npm install mirrorstate
# 或
yarn add mirrorstate
# 或
pnpm add mirrorstate🚀 快速开始
React 中使用
import { createStore } from "mirrorstate"
import { useId, useEffect } from "react"
// 创建自定义 Hook
const useCounter = () => {
const componentId = useId()
const [count, setCount] = useState(0)
const [text, setText] = useState("hello")
const store = createStore({
componentId,
storeName: "counter",
setMethod: {
count: (v) => v ? setCount : count,
text: (v) => v ? setText : text
}
})
useEffect(() => {
return () => store.cleanup()
}, [])
return store
}
// 在组件中使用
function Counter() {
const { count, text } = useCounter()
return (
<div>
<p>{count()}</p>
<button onClick={() => count(v => v + 1)}>+1</button>
<button onClick={() => count(0)}>重置</button>
<p>{text()}</p>
<input value={text()} onChange={(e) => text(e.target.value)} />
</div>
)
}Vue 中使用
<script setup>
import { createStore } from "mirrorstate"
import { ref, onUnmounted } from "vue"
// 创建自定义 Hook
const useCounter = () => {
const componentId = Symbol('counter')
const count = ref(0)
const text = ref("hello")
const setCount = (value) => { count.value = value }
const setText = (value) => { text.value = value }
const store = createStore({
componentId,
storeName: "counter",
setMethod: {
count: (v) => v ? setCount : count.value,
text: (v) => v ? setText : text.value
}
})
onUnmounted(() => {
store.cleanup()
})
return store
}
// 在组件中使用
const { count, text } = useCounter()
</script>
<template>
<div>
<p>{{ count() }}</p>
<button @click="count(v => v + 1)">+1</button>
<button @click="count(0)">重置</button>
<p>{{ text() }}</p>
<input :value="text()" @input="text($event.target.value)" />
</div>
</template>📖 核心概念
createStore 配置
interface CreateStoreOptions<T> {
// 必填:组件唯一标识,用于状态隔离
componentId: string | symbol
// 必填:仓库名称
storeName: string
// 必填:状态定义
setMethod: {
[K in keyof T]: (v?: any) => any
}
// 可选:中间件数组
middlewares?: Middleware[]
}setMethod 的设计哲学
setMethod 采用函数式设计,通过参数判断是读还是写:
setMethod: {
// 当不传参时返回当前值(读)
// 当传参时返回 setter 函数(写)
count: (v) => v ? setCount : count,
// 支持函数式更新
count: (v) => v ? setCount : count
// count(v => v + 1) 会自动处理
}中间件
中间件让你能够在状态变更前后执行自定义逻辑:
import type { Middleware } from "mirrorstate"
// 日志中间件
const logger: Middleware = async (ctx, next) => {
console.log(`[${ctx.storeName}] ${ctx.key}:`, ctx.value)
await next()
console.log(`[${ctx.storeName}] ${ctx.key} 更新完成`)
}
// 持久化中间件
const persist: Middleware = async (ctx, next) => {
await next()
localStorage.setItem(ctx.storeName, JSON.stringify(ctx.store))
}
// 使用中间件
const store = createStore({
componentId: useId(),
storeName: "user",
middlewares: [logger, persist],
setMethod: {
name: (v) => v ? setName : name
}
})🎯 高级用法
1. 全局路由状态(非响应式)
// store/route.ts
import { createStore } from "mirrorstate"
export const useRouteStore = () => {
// 使用固定 ID,确保全局唯一
const componentId = "global-route-store"
// 普通变量,不触发视图更新
let currentRoute = '/'
let isLogin = false
let permissions = new Set<string>()
const setCurrentRoute = (route: string) => { currentRoute = route }
const setIsLogin = (status: boolean) => { isLogin = status }
const setPermissions = (perms: string[]) => { permissions = new Set(perms) }
const store = createStore({
componentId,
storeName: "route",
setMethod: {
currentRoute: (v) => v ? setCurrentRoute : currentRoute,
isLogin: (v) => v ? setIsLogin : isLogin,
permissions: (v) => v ? setPermissions : permissions,
hasPermission: (perm: string) => () => permissions.has(perm)
}
})
return store
}
// 在路由守卫中使用
router.beforeEach((to, from, next) => {
const { isLogin, hasPermission } = useRouteStore()
if (to.meta.requiresAuth && !isLogin()) {
next('/login')
return
}
if (to.meta.permission && !hasPermission(to.meta.permission)) {
next('/403')
return
}
next()
})2. 批量更新
const { batch } = useUser()
// 一次更新多个状态
batch({
name: '张三',
age: 25,
email: '[email protected]'
})3. 组件间通信
// Component A
function Sender() {
const { count } = useCounter()
return <button onClick={() => count(100)}>发送</button>
}
// Component B(自动更新)
function Receiver() {
const { count } = useCounter() // 使用同一个 storeName
return <div>{count()}</div> // 当 A 更新时自动重新渲染
}🛠 API 参考
createStore(options)
创建状态仓库。
Store 实例方法
| 方法 | 描述 |
|------|------|
| state() | 获取状态值 |
| state(value) | 直接设置状态 |
| state(fn) | 函数式更新 |
| batch(object) | 批量更新多个状态 |
| cleanup() | 清理订阅和状态 |
中间件上下文 (Context)
interface Context {
storeName: string // 仓库名称
key: string // 更新的键名
store: any // 整个仓库对象
value: any // 新值
subscribeStore: Map<string, Set<Function>> // 订阅者信息
}⚡ 性能优化
- 按需订阅:只有组件实际使用的状态才会建立订阅关系
- 精准更新:状态变化只通知真正订阅的组件
- 自动清理:组件卸载时自动取消所有订阅
- 非响应式支持:对于不需要触发视图更新的数据(如路由状态),使用普通变量存储
📝 TypeScript 支持
interface UserState {
name: string
age: number
email: string
}
const useUser = createStore<UserState>({
componentId: useId(),
storeName: "user",
setMethod: {
name: (v) => v ? setName : name,
age: (v) => v ? setAge : age,
email: (v) => v ? setEmail : email
}
})
// 自动推导类型
const { name, age } = useUser()
name() // string
age(18) // number🤝 贡献指南
欢迎贡献代码或提出建议!
📄 许可证
MIT © 2026
