@lytjs/store 6.4.0 → 6.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,513 @@
1
+ # @lytjs/store
2
+
3
+ > LytJS 基于 Signal 的状态管理库,支持 Option Store 和 Setup Store 模式。
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@lytjs/store.svg)](https://www.npmjs.com/package/@lytjs/store)
6
+ [![license](https://img.shields.io/npm/l/@lytjs/store.svg)](https://gitee.com/lytjs/lytjs/blob/main/LICENSE)
7
+
8
+ ## 简介
9
+
10
+ `@lytjs/store` 是 LytJS 框架的官方状态管理库,灵感来源于 Pinia,专为 LytJS 的响应式 Signal 系统设计。它提供了简洁而强大的状态管理方案,支持两种定义风格:Option Store 和 Setup Store。
11
+
12
+ ### 核心特性
13
+
14
+ - **双模式定义**:支持 Option Store 和 Setup Store 两种定义方式
15
+ - **完全类型安全**:完整的 TypeScript 类型推导
16
+ - **极小的打包体积**:零外部依赖
17
+ - **响应式集成**:深度集成 LytJS Signal 响应式系统
18
+ - **插件系统**:支持自定义插件扩展
19
+ - **DevTools 支持**:与 @lytjs/devtools 无缝集成
20
+ - **自动订阅**:组件中自动订阅,卸载时自动取消
21
+
22
+ ## 安装
23
+
24
+ ```bash
25
+ npm install @lytjs/store
26
+ ```
27
+
28
+ 或使用 pnpm:
29
+
30
+ ```bash
31
+ pnpm add @lytjs/store
32
+ ```
33
+
34
+ ## 依赖关系
35
+
36
+ `@lytjs/store` 依赖以下 LytJS 核心包:
37
+
38
+ - `@lytjs/reactivity` - 响应式系统
39
+ - `@lytjs/component` - 组件系统
40
+ - `@lytjs/common-is` - 工具函数
41
+ - `@lytjs/common-object` - 对象工具函数
42
+
43
+ ## 快速开始
44
+
45
+ ### 初始化 Pinia
46
+
47
+ ```typescript
48
+ import { createPinia } from '@lytjs/store';
49
+
50
+ const pinia = createPinia();
51
+
52
+ export { pinia };
53
+ ```
54
+
55
+ ### 在应用中使用
56
+
57
+ ```typescript
58
+ import { mount } from '@lytjs/vdom';
59
+ import App from './App';
60
+ import { pinia } from './stores';
61
+
62
+ const app = mount(document.getElementById('app'), App, {
63
+ plugins: [pinia],
64
+ });
65
+ ```
66
+
67
+ ## Option Store 模式
68
+
69
+ 使用选项式 API 定义 Store,适合简单到中等复杂度的状态管理。
70
+
71
+ ### 定义 Store
72
+
73
+ ```typescript
74
+ import { defineStore } from '@lytjs/store';
75
+
76
+ export const useUserStore = defineStore('user', {
77
+ state: () => ({
78
+ id: '',
79
+ name: '',
80
+ email: '',
81
+ avatar: '',
82
+ isLoggedIn: false,
83
+ permissions: [],
84
+ }),
85
+
86
+ getters: {
87
+ displayName: (state) => state.name || state.email || '匿名用户',
88
+ isAdmin: (state) => state.permissions.includes('admin'),
89
+ hasPermission: (state) => (permission: string) => state.permissions.includes(permission),
90
+ },
91
+
92
+ actions: {
93
+ async login(credentials: { email: string; password: string }) {
94
+ const response = await api.login(credentials);
95
+ this.id = response.user.id;
96
+ this.name = response.user.name;
97
+ this.email = response.user.email;
98
+ this.avatar = response.user.avatar;
99
+ this.isLoggedIn = true;
100
+ this.permissions = response.user.permissions;
101
+ },
102
+
103
+ logout() {
104
+ this.id = '';
105
+ this.name = '';
106
+ this.email = '';
107
+ this.avatar = '';
108
+ this.isLoggedIn = false;
109
+ this.permissions = [];
110
+ },
111
+
112
+ updateProfile(data: Partial<{ name: string; avatar: string }>) {
113
+ if (data.name) this.name = data.name;
114
+ if (data.avatar) this.avatar = data.avatar;
115
+ },
116
+ },
117
+ });
118
+ ```
119
+
120
+ ### 使用 Store
121
+
122
+ ```typescript
123
+ import { useUserStore } from './stores/user';
124
+
125
+ export function UserProfile() {
126
+ const userStore = useUserStore();
127
+
128
+ return () => (
129
+ <div class="user-profile">
130
+ <img src={userStore.avatar} alt={userStore.name} />
131
+ <h1>{userStore.displayName}</h1>
132
+ {userStore.isAdmin && <span class="badge">管理员</span>}
133
+ <button onClick={() => userStore.logout()}>退出登录</button>
134
+ </div>
135
+ );
136
+ }
137
+ ```
138
+
139
+ ## Setup Store 模式
140
+
141
+ 使用组合式函数定义 Store,适合复杂的状态逻辑和复用。
142
+
143
+ ### 定义 Store
144
+
145
+ ```typescript
146
+ import { defineStore } from '@lytjs/store';
147
+ import { ref, computed } from '@lytjs/reactivity';
148
+
149
+ export const useCartStore = defineStore('cart', () => {
150
+ const items = ref([]);
151
+ const couponCode = ref('');
152
+
153
+ const itemCount = computed(() => items.value.length);
154
+
155
+ const totalPrice = computed(() =>
156
+ items.value.reduce((sum, item) => sum + item.price * item.quantity, 0),
157
+ );
158
+
159
+ const discountedPrice = computed(() => {
160
+ if (couponCode.value === 'SAVE10') {
161
+ return totalPrice.value * 0.9;
162
+ }
163
+ if (couponCode.value === 'SAVE20') {
164
+ return totalPrice.value * 0.8;
165
+ }
166
+ return totalPrice.value;
167
+ });
168
+
169
+ function addItem(product: { id: string; name: string; price: number }) {
170
+ const existing = items.value.find((item) => item.id === product.id);
171
+ if (existing) {
172
+ existing.quantity++;
173
+ } else {
174
+ items.value.push({ ...product, quantity: 1 });
175
+ }
176
+ }
177
+
178
+ function removeItem(productId: string) {
179
+ const index = items.value.findIndex((item) => item.id === productId);
180
+ if (index !== -1) {
181
+ items.value.splice(index, 1);
182
+ }
183
+ }
184
+
185
+ function updateQuantity(productId: string, quantity: number) {
186
+ const item = items.value.find((item) => item.id === productId);
187
+ if (item) {
188
+ if (quantity <= 0) {
189
+ removeItem(productId);
190
+ } else {
191
+ item.quantity = quantity;
192
+ }
193
+ }
194
+ }
195
+
196
+ function applyCoupon(code: string) {
197
+ couponCode.value = code;
198
+ }
199
+
200
+ function clearCart() {
201
+ items.value = [];
202
+ couponCode.value = '';
203
+ }
204
+
205
+ async function checkout() {
206
+ const order = await api.createOrder({
207
+ items: items.value,
208
+ total: discountedPrice.value,
209
+ });
210
+ clearCart();
211
+ return order;
212
+ }
213
+
214
+ return {
215
+ items,
216
+ couponCode,
217
+ itemCount,
218
+ totalPrice,
219
+ discountedPrice,
220
+ addItem,
221
+ removeItem,
222
+ updateQuantity,
223
+ applyCoupon,
224
+ clearCart,
225
+ checkout,
226
+ };
227
+ });
228
+ ```
229
+
230
+ ### 使用 Store
231
+
232
+ ```typescript
233
+ import { useCartStore } from './stores/cart';
234
+
235
+ export function ShoppingCart() {
236
+ const cartStore = useCartStore();
237
+
238
+ return () => (
239
+ <div class="cart">
240
+ <h2>购物车 ({cartStore.itemCount})</h2>
241
+
242
+ <div class="items">
243
+ {cartStore.items.map(item => (
244
+ <div key={item.id} class="cart-item">
245
+ <span>{item.name}</span>
246
+ <span>¥{item.price}</span>
247
+ <input
248
+ type="number"
249
+ value={item.quantity}
250
+ onInput={(e) => cartStore.updateQuantity(item.id, +e.target.value)}
251
+ />
252
+ <button onClick={() => cartStore.removeItem(item.id)}>删除</button>
253
+ </div>
254
+ ))}
255
+ </div>
256
+
257
+ <div class="coupon">
258
+ <input
259
+ placeholder="输入优惠码"
260
+ value={cartStore.couponCode}
261
+ onInput={(e) => cartStore.applyCoupon(e.target.value)}
262
+ />
263
+ </div>
264
+
265
+ <div class="totals">
266
+ <p>原价: ¥{cartStore.totalPrice.toFixed(2)}</p>
267
+ <p>折后价: ¥{cartStore.discountedPrice.toFixed(2)}</p>
268
+ </div>
269
+
270
+ <button onClick={() => cartStore.checkout()}>结算</button>
271
+ </div>
272
+ );
273
+ }
274
+ ```
275
+
276
+ ## 主要 API
277
+
278
+ ### `defineStore(id, options)`
279
+
280
+ 定义 Option Store。
281
+
282
+ ```typescript
283
+ import { defineStore } from '@lytjs/store';
284
+
285
+ const useStore = defineStore('storeId', {
286
+ state: () => ({ count: 0 }),
287
+ getters: {
288
+ /* ... */
289
+ },
290
+ actions: {
291
+ /* ... */
292
+ },
293
+ });
294
+ ```
295
+
296
+ ### `defineStoresetupFn)`
297
+
298
+ 定义 Setup Store。
299
+
300
+ ```typescript
301
+ import { defineStore } from '@lytjs/store';
302
+
303
+ const useStore = defineStore('storeId', () => {
304
+ const count = ref(0);
305
+ const doubled = computed(() => count.value * 2);
306
+
307
+ function increment() {
308
+ count.value++;
309
+ }
310
+
311
+ return { count, doubled, increment };
312
+ });
313
+ ```
314
+
315
+ ### `createPinia()`
316
+
317
+ 创建 Pinia 实例。
318
+
319
+ ```typescript
320
+ import { createPinia } from '@lytjs/store';
321
+
322
+ const pinia = createPinia({
323
+ plugins: [logPlugin],
324
+ depGroups: {},
325
+ });
326
+ ```
327
+
328
+ ### `storeToRefs(store)`
329
+
330
+ 将 Store 状态转换为响应式引用。
331
+
332
+ ```typescript
333
+ import { storeToRefs } from '@lytjs/store';
334
+
335
+ const store = useStore();
336
+ const { name, isActive } = storeToRefs(store);
337
+ ```
338
+
339
+ ### `getActivePinia()`
340
+
341
+ 获取当前活跃的 Pinia 实例。
342
+
343
+ ```typescript
344
+ import { getActivePinia } from '@lytjs/store';
345
+
346
+ const pinia = getActivePinia();
347
+ ```
348
+
349
+ ### `setActivePinia(pinia)`
350
+
351
+ 设置当前活跃的 Pinia 实例。
352
+
353
+ ```typescript
354
+ import { setActivePinia } from '@lytjs/store';
355
+
356
+ setActivePinia(pinia);
357
+ ```
358
+
359
+ ### `clearStoreCache()`
360
+
361
+ 清除 Store 缓存。
362
+
363
+ ```typescript
364
+ import { clearStoreCache } from '@lytjs/store';
365
+
366
+ clearStoreCache();
367
+ ```
368
+
369
+ ## 插件系统
370
+
371
+ ### 创建插件
372
+
373
+ ```typescript
374
+ import { defineStore } from '@lytjs/store';
375
+ import type { PiniaPlugin } from '@lytjs/store';
376
+
377
+ const persistPlugin: PiniaPlugin = ({ store, options }) => {
378
+ const savedState = localStorage.getItem(store.$id);
379
+ if (savedState) {
380
+ store.$patch(JSON.parse(savedState));
381
+ }
382
+
383
+ store.$subscribe((mutation, state) => {
384
+ localStorage.setItem(store.$id, JSON.stringify(state));
385
+ });
386
+ };
387
+ ```
388
+
389
+ ### 使用插件
390
+
391
+ ```typescript
392
+ import { createPinia } from '@lytjs/store';
393
+
394
+ const pinia = createPinia({
395
+ plugins: [persistPlugin],
396
+ });
397
+ ```
398
+
399
+ ## Store 实例方法
400
+
401
+ ```typescript
402
+ const store = useStore();
403
+
404
+ // 读取状态
405
+ store.$state.count;
406
+
407
+ // 更新状态
408
+ store.$patch({ count: 10 });
409
+ store.$patch((state) => {
410
+ state.count++;
411
+ });
412
+
413
+ // 重置状态
414
+ store.$reset();
415
+
416
+ // 替换状态
417
+ store.$state = { count: 0 };
418
+
419
+ // 订阅变化
420
+ store.$subscribe((mutation, state) => {
421
+ console.log('状态变化:', mutation.type);
422
+ });
423
+
424
+ // 订阅动作
425
+ store.$onAction((context) => {
426
+ console.log('动作执行:', context.name);
427
+ });
428
+
429
+ // 持久化
430
+ store.$persist();
431
+ ```
432
+
433
+ ## TypeScript 类型
434
+
435
+ ```typescript
436
+ interface Store<G = any, S = any, A = any> {
437
+ $id: string;
438
+ $state: S;
439
+ $ getters: G;
440
+ $actions: A;
441
+ $patch: (partial: Partial<S> | ((state: S) => void)) => void;
442
+ $reset: () => void;
443
+ $subscribe: (callback: SubscriptionCallback<S>) => () => void;
444
+ $onAction: (callback: ActionCallback<S, A>) => () => void;
445
+ $persist: () => void;
446
+ }
447
+ ```
448
+
449
+ ## 最佳实践
450
+
451
+ ### Store 命名
452
+
453
+ ```typescript
454
+ // ✅ 推荐:使用 use 前缀
455
+ const useUserStore = defineStore('user', {
456
+ /* ... */
457
+ });
458
+ const useCartStore = defineStore('cart', {
459
+ /* ... */
460
+ });
461
+
462
+ // ❌ 避免:不使用 use 前缀
463
+ const useUser = defineStore('user', {
464
+ /* ... */
465
+ });
466
+ ```
467
+
468
+ ### 状态结构
469
+
470
+ ```typescript
471
+ // ✅ 推荐:使用扁平状态结构
472
+ state: () => ({
473
+ user: { id: '', name: '', email: '' },
474
+ preferences: { theme: 'light', language: 'zh-CN' },
475
+ });
476
+
477
+ // ❌ 避免:过深的嵌套
478
+ state: () => ({
479
+ app: {
480
+ user: {
481
+ profile: {
482
+ name: '',
483
+ },
484
+ },
485
+ },
486
+ });
487
+ ```
488
+
489
+ ### 动作命名
490
+
491
+ ```typescript
492
+ // ✅ 推荐:使用动词前缀
493
+ actions: {
494
+ fetchUser() { /* 获取用户 */ },
495
+ updateUser() { /* 更新用户 */ },
496
+ deleteUser() { /* 删除用户 */ }
497
+ }
498
+ ```
499
+
500
+ ## 浏览器兼容性
501
+
502
+ `@lytjs/store` 支持所有现代浏览器。如果需要支持旧版浏览器,可能需要引入 polyfill。
503
+
504
+ ## 许可证
505
+
506
+ MIT License - [查看许可证](https://gitee.com/lytjs/lytjs/blob/main/LICENSE)
507
+
508
+ ## 贡献指南
509
+
510
+ 欢迎提交 Issue 和 Pull Request!
511
+
512
+ - [Gitee 仓库](https://gitee.com/lytjs/lytjs)
513
+ - [问题反馈](https://gitee.com/lytjs/lytjs/issues)