electron-state-sync 1.1.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.
Files changed (97) hide show
  1. package/CLAUDE.md +254 -0
  2. package/LICENSE +21 -0
  3. package/README.md +753 -0
  4. package/README.zh-CN.md +743 -0
  5. package/bun.lock +542 -0
  6. package/bunfig.toml +7 -0
  7. package/dist/jotai.cjs +106 -0
  8. package/dist/jotai.d.cts +48 -0
  9. package/dist/jotai.d.cts.map +1 -0
  10. package/dist/jotai.d.ts +48 -0
  11. package/dist/jotai.d.ts.map +1 -0
  12. package/dist/jotai.js +105 -0
  13. package/dist/jotai.js.map +1 -0
  14. package/dist/main.cjs +177 -0
  15. package/dist/main.d.cts +26 -0
  16. package/dist/main.d.cts.map +1 -0
  17. package/dist/main.d.ts +26 -0
  18. package/dist/main.d.ts.map +1 -0
  19. package/dist/main.js +177 -0
  20. package/dist/main.js.map +1 -0
  21. package/dist/preact.cjs +46 -0
  22. package/dist/preact.d.cts +11 -0
  23. package/dist/preact.d.cts.map +1 -0
  24. package/dist/preact.d.ts +11 -0
  25. package/dist/preact.d.ts.map +1 -0
  26. package/dist/preact.js +46 -0
  27. package/dist/preact.js.map +1 -0
  28. package/dist/preload.cjs +51 -0
  29. package/dist/preload.d.cts +20 -0
  30. package/dist/preload.d.cts.map +1 -0
  31. package/dist/preload.d.ts +20 -0
  32. package/dist/preload.d.ts.map +1 -0
  33. package/dist/preload.js +51 -0
  34. package/dist/preload.js.map +1 -0
  35. package/dist/react-query.cjs +113 -0
  36. package/dist/react-query.d.cts +58 -0
  37. package/dist/react-query.d.cts.map +1 -0
  38. package/dist/react-query.d.ts +58 -0
  39. package/dist/react-query.d.ts.map +1 -0
  40. package/dist/react-query.js +112 -0
  41. package/dist/react-query.js.map +1 -0
  42. package/dist/react.cjs +46 -0
  43. package/dist/react.d.cts +11 -0
  44. package/dist/react.d.cts.map +1 -0
  45. package/dist/react.d.ts +11 -0
  46. package/dist/react.d.ts.map +1 -0
  47. package/dist/react.js +46 -0
  48. package/dist/react.js.map +1 -0
  49. package/dist/redux.cjs +148 -0
  50. package/dist/redux.d.cts +80 -0
  51. package/dist/redux.d.cts.map +1 -0
  52. package/dist/redux.d.ts +80 -0
  53. package/dist/redux.d.ts.map +1 -0
  54. package/dist/redux.js +146 -0
  55. package/dist/redux.js.map +1 -0
  56. package/dist/renderer/index.cjs +7 -0
  57. package/dist/renderer/index.d.cts +23 -0
  58. package/dist/renderer/index.d.cts.map +1 -0
  59. package/dist/renderer/index.d.ts +23 -0
  60. package/dist/renderer/index.d.ts.map +1 -0
  61. package/dist/renderer/index.js +3 -0
  62. package/dist/renderer-C7zF3UQm.js +57 -0
  63. package/dist/renderer-C7zF3UQm.js.map +1 -0
  64. package/dist/renderer-D3YziJ_U.cjs +86 -0
  65. package/dist/solid.cjs +74 -0
  66. package/dist/solid.d.cts +13 -0
  67. package/dist/solid.d.cts.map +1 -0
  68. package/dist/solid.d.ts +13 -0
  69. package/dist/solid.d.ts.map +1 -0
  70. package/dist/solid.js +74 -0
  71. package/dist/solid.js.map +1 -0
  72. package/dist/svelte.cjs +63 -0
  73. package/dist/svelte.d.cts +14 -0
  74. package/dist/svelte.d.cts.map +1 -0
  75. package/dist/svelte.d.ts +14 -0
  76. package/dist/svelte.d.ts.map +1 -0
  77. package/dist/svelte.js +63 -0
  78. package/dist/svelte.js.map +1 -0
  79. package/dist/types-7wPPX0ty.d.ts +37 -0
  80. package/dist/types-7wPPX0ty.d.ts.map +1 -0
  81. package/dist/types-C18dHgLI.d.cts +37 -0
  82. package/dist/types-C18dHgLI.d.cts.map +1 -0
  83. package/dist/vue.cjs +69 -0
  84. package/dist/vue.d.cts +15 -0
  85. package/dist/vue.d.cts.map +1 -0
  86. package/dist/vue.d.ts +15 -0
  87. package/dist/vue.d.ts.map +1 -0
  88. package/dist/vue.js +70 -0
  89. package/dist/vue.js.map +1 -0
  90. package/dist/zustand.cjs +193 -0
  91. package/dist/zustand.d.cts +61 -0
  92. package/dist/zustand.d.cts.map +1 -0
  93. package/dist/zustand.d.ts +61 -0
  94. package/dist/zustand.d.ts.map +1 -0
  95. package/dist/zustand.js +191 -0
  96. package/dist/zustand.js.map +1 -0
  97. package/package.json +162 -0
@@ -0,0 +1,743 @@
1
+ # Electron State Sync
2
+
3
+ [![npm version](https://badge.fury.io/js/electron-state-sync.svg)](https://www.npmjs.com/package/electron-state-sync) [![npm downloads](https://img.shields.io/npm/dm/electron-state-sync)](https://www.npmjs.com/package/electron-state-sync) [![License](https://img.shields.io/npm/l/electron-state-sync)](LICENSE) [![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue)](https://www.typescriptlang.org/) [![Electron](https://img.shields.io/badge/Electron-18%2B-brightgreen)](https://electronjs.org/) [![CI](https://github.com/abramdev/electron-state-sync/actions/workflows/ci.yml/badge.svg)](https://github.com/abramdev/electron-state-sync/actions/workflows/ci.yml)
4
+
5
+ 🌐 [English](./README.md) | [中文](./README.zh-CN.md)
6
+
7
+ 一个轻量级的 Electron 状态同步库,实现主进程与渲染端之间的数据无缝共享。支持 React、Vue、Svelte、SolidJS、Zustand、TanStack Query、Jotai 和 Redux Toolkit,具备自动多窗口同步功能。
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ npm install electron-state-sync
13
+ ```
14
+
15
+ ## 特性
16
+
17
+ - 📦 **轻量构建**:主进程 6.3KB,渲染端 1.5-2.2KB
18
+ - 🧩 **多框架支持**:React / Vue / Svelte / Solid
19
+ - 🔄 **状态管理**:Zustand / TanStack Query / Jotai / Redux Toolkit
20
+ - 🔒 **写入控制**:支持只读与可写模式
21
+ - ✅ **写入校验**:主进程校验渲染端写入并返回标准错误码
22
+ - 🔌 **自定义桥接**:支持自定义 **SyncStateBridge** 对接
23
+
24
+ ## 使用方法
25
+
26
+ ### 主进程
27
+
28
+ #### 快速配置
29
+
30
+ ```ts
31
+ // main.ts
32
+ import { state } from "electron-state-sync/main";
33
+
34
+ const counter = state({
35
+ name: "counter",
36
+ initialValue: 0,
37
+ });
38
+
39
+ counter.set(10);
40
+ const value = counter.get();
41
+ ```
42
+
43
+ #### 高级配置
44
+
45
+ ```ts
46
+ // main.ts
47
+ import { state } from "electron-state-sync/main";
48
+
49
+ const counter = state({
50
+ baseChannel: "state",
51
+ name: "counter",
52
+ initialValue: 0,
53
+ allowRendererSet: true,
54
+ resolveRendererValue: (value) => {
55
+ if (typeof value !== "number") {
56
+ throw new Error("counter 只接受 number");
57
+ }
58
+ return value;
59
+ },
60
+ });
61
+
62
+ counter.set(100);
63
+ const current = counter.get();
64
+ ```
65
+
66
+ #### 多窗口同步
67
+
68
+ 状态变更时所有窗口自动接收更新:
69
+
70
+ ```ts
71
+ // main.ts
72
+ import { state } from "electron-state-sync/main";
73
+
74
+ const theme = state({
75
+ name: "theme",
76
+ initialValue: "light",
77
+ });
78
+
79
+ // 所有使用此状态的窗口都会收到更新
80
+ theme.set("dark"); // 广播到所有订阅的窗口
81
+ ```
82
+
83
+ #### 停止同步
84
+
85
+ 调用 `dispose()` 停止同步并清理 IPC 处理器:
86
+
87
+ ```ts
88
+ // main.ts
89
+ import { state } from "electron-state-sync/main";
90
+
91
+ const counter = state({
92
+ name: "counter",
93
+ initialValue: 0,
94
+ });
95
+
96
+ counter.set(10); // 同步并广播
97
+ counter.get(); // 返回 10
98
+
99
+ // 停止同步 - 移除 IPC 处理器并清除订阅者
100
+ counter.dispose();
101
+ ```
102
+
103
+ 调用 `dispose()` 后:
104
+ - `get`/`set`/`subscribe`/`unsubscribe` 的 IPC 处理器被移除
105
+ - 所有订阅者被清除
106
+ - 渲染端调用会静默失败
107
+
108
+ 每个窗口订阅状态变更并自动接收更新:
109
+
110
+ ```ts
111
+ // renderer process
112
+ import { useSyncState } from "electron-state-sync/react";
113
+
114
+ const [theme] = useSyncState("light", {
115
+ name: "theme",
116
+ });
117
+ // 当任一窗口调用 theme.set(),所有窗口自动更新
118
+ ```
119
+
120
+ ### Preload
121
+
122
+ ```ts
123
+ // preload.ts
124
+ import { exposeSyncState } from "electron-state-sync/preload";
125
+
126
+ exposeSyncState();
127
+ ```
128
+
129
+ ### 通用接口
130
+
131
+ 浏览器端会暴露 **window.syncState**,包含 **get** / **set** / **subscribe**:
132
+
133
+ ```ts
134
+ // renderer process
135
+ const bridge = window.syncState;
136
+ if (!bridge) {
137
+ throw new Error("syncState 未注入");
138
+ }
139
+
140
+ const value = await bridge.get<number>({
141
+ baseChannel: "state",
142
+ name: "counter",
143
+ });
144
+
145
+ await bridge.set(
146
+ {
147
+ baseChannel: "state",
148
+ name: "counter",
149
+ },
150
+ value + 1
151
+ );
152
+
153
+ const unsubscribe = bridge.subscribe<number>(
154
+ {
155
+ baseChannel: "state",
156
+ name: "counter",
157
+ },
158
+ (nextValue) => {
159
+ console.log(nextValue);
160
+ }
161
+ );
162
+ ```
163
+
164
+ ### 自定义桥接
165
+
166
+ 可以实现 **SyncStateBridge** 自定义对接,再注入到 Hook:
167
+
168
+ ```ts
169
+ // renderer process
170
+ import type { SyncStateBridge } from "electron-state-sync/renderer";
171
+
172
+ const customBridge: SyncStateBridge = {
173
+ get: async (options) => window.syncState!.get(options),
174
+ set: async (options, value) => window.syncState!.set(options, value),
175
+ subscribe: (options, listener) => window.syncState!.subscribe(options, listener),
176
+ };
177
+ ```
178
+
179
+ ### Vue 端
180
+
181
+ **useSyncState** 仅依赖通用接口,可以直接复用或自定义桥接实现。
182
+
183
+ #### 最简化使用
184
+
185
+ ```ts
186
+ import { useSyncState } from "electron-state-sync/vue";
187
+
188
+ const counter = useSyncState(0, {
189
+ name: "counter",
190
+ });
191
+ // counter.isSynced - Ref<boolean>
192
+ ```
193
+
194
+ #### 使用全局配置
195
+
196
+ ```ts
197
+ import { initSyncState, useSyncState } from "electron-state-sync/vue";
198
+
199
+ // 在应用初始化时设置全局配置
200
+ initSyncState({
201
+ baseChannel: "myapp",
202
+ });
203
+
204
+ // 所有 Hook 自动使用全局配置
205
+ const counter = useSyncState(0, {
206
+ name: "counter",
207
+ });
208
+
209
+ const user = useSyncState({ name: "" }, {
210
+ name: "user",
211
+ });
212
+
213
+ // 覆盖全局配置
214
+ const theme = useSyncState("light", {
215
+ baseChannel: "settings",
216
+ name: "theme",
217
+ });
218
+ ```
219
+
220
+ #### 自定义桥接
221
+
222
+ ```ts
223
+ import { useSyncState } from "electron-state-sync/vue";
224
+
225
+ const counter = useSyncState(0, {
226
+ name: "counter",
227
+ bridge: customBridge,
228
+ deep: false,
229
+ });
230
+ ```
231
+
232
+ ### React 端
233
+
234
+ #### 最简化使用
235
+
236
+ ```ts
237
+ import { useSyncState } from "electron-state-sync/react";
238
+
239
+ function App() {
240
+ const [counter, setCounter, isSynced] = useSyncState(0, {
241
+ name: "counter",
242
+ });
243
+
244
+ return <div onClick={() => setCounter(5)}>{counter}</div>;
245
+ }
246
+ ```
247
+
248
+ #### 使用全局配置
249
+
250
+ ```ts
251
+ import { initSyncState, useSyncState } from "electron-state-sync/react";
252
+
253
+ // 在应用初始化时设置全局配置
254
+ initSyncState({
255
+ baseChannel: "myapp",
256
+ });
257
+
258
+ // 所有 Hook 自动使用全局配置
259
+ const [counter, setCounter] = useSyncState(0, {
260
+ name: "counter",
261
+ });
262
+
263
+ const [user, setUser] = useSyncState({ name: "" }, {
264
+ name: "user",
265
+ });
266
+
267
+ // 覆盖全局配置
268
+ const [theme, setTheme] = useSyncState("light", {
269
+ baseChannel: "settings",
270
+ name: "theme",
271
+ });
272
+ ```
273
+
274
+ #### 自定义桥接
275
+
276
+ ```ts
277
+ import { useSyncState } from "electron-state-sync/react";
278
+
279
+ const [counter, setCounter] = useSyncState(0, {
280
+ name: "counter",
281
+ bridge: customBridge,
282
+ });
283
+ ```
284
+
285
+ ### Svelte 端
286
+
287
+ #### 最简化使用
288
+
289
+ ```ts
290
+ import { useSyncState } from "electron-state-sync/svelte";
291
+
292
+ const counter = useSyncState(0, {
293
+ name: "counter",
294
+ });
295
+ // counter.isSynced - Readable<boolean>
296
+ ```
297
+
298
+ #### 使用全局配置
299
+
300
+ ```ts
301
+ import { initSyncState, useSyncState } from "electron-state-sync/svelte";
302
+
303
+ // 在应用初始化时设置全局配置
304
+ initSyncState({
305
+ baseChannel: "myapp",
306
+ });
307
+
308
+ // 所有 Store 自动使用全局配置
309
+ const counter = useSyncState(0, {
310
+ name: "counter",
311
+ });
312
+
313
+ const user = useSyncState({ name: "" }, {
314
+ name: "user",
315
+ });
316
+
317
+ // 覆盖全局配置
318
+ const theme = useSyncState("light", {
319
+ baseChannel: "settings",
320
+ name: "theme",
321
+ });
322
+ ```
323
+
324
+ #### 自定义桥接
325
+
326
+ ```ts
327
+ import { useSyncState } from "electron-state-sync/svelte";
328
+
329
+ const counter = useSyncState(0, {
330
+ name: "counter",
331
+ bridge: customBridge,
332
+ });
333
+ ```
334
+
335
+ ```svelte
336
+ <script lang="ts">
337
+ import { counter } from "./stores";
338
+ </script>
339
+
340
+ <div>{$counter}</div>
341
+ ```
342
+
343
+ ### SolidJS 端
344
+
345
+ #### 最简化使用
346
+
347
+ ```ts
348
+ import { useSyncState } from "electron-state-sync/solid";
349
+
350
+ const [counter, setCounter] = useSyncState(0, {
351
+ name: "counter",
352
+ });
353
+ ```
354
+
355
+ #### 使用全局配置
356
+
357
+ ```ts
358
+ import { initSyncState, useSyncState } from "electron-state-sync/solid";
359
+
360
+ // 在应用初始化时设置全局配置
361
+ initSyncState({
362
+ baseChannel: "myapp",
363
+ });
364
+
365
+ // 所有 Hook 自动使用全局配置
366
+ const [counter, setCounter] = useSyncState(0, {
367
+ name: "counter",
368
+ });
369
+
370
+ const [user, setUser] = useSyncState({ name: "" }, {
371
+ name: "user",
372
+ });
373
+
374
+ // 覆盖全局配置
375
+ const [theme, setTheme] = useSyncState("light", {
376
+ baseChannel: "settings",
377
+ name: "theme",
378
+ });
379
+ ```
380
+
381
+ #### 自定义桥接
382
+
383
+ ```ts
384
+ import { useSyncState } from "electron-state-sync/solid";
385
+
386
+ const [counter, setCounter] = useSyncState(0, {
387
+ name: "counter",
388
+ bridge: customBridge,
389
+ });
390
+ ```
391
+
392
+ ### Zustand
393
+
394
+ #### 最简化使用
395
+
396
+ ```ts
397
+ import { create } from "zustand";
398
+ import { syncStateMiddleware } from "electron-state-sync/zustand";
399
+
400
+ const useStore = create(
401
+ syncStateMiddleware({ name: "counter" })((set) => ({
402
+ count: 0,
403
+ increment: () => set((state) => ({ count: state.count + 1 })),
404
+ }))
405
+ );
406
+
407
+ // 在组件中使用
408
+ const count = useStore((state) => state.count);
409
+ ```
410
+
411
+ #### 使用全局配置
412
+
413
+ ```ts
414
+ import { initSyncState } from "electron-state-sync/zustand";
415
+ import { create } from "zustand";
416
+ import { syncStateMiddleware } from "electron-state-sync/zustand";
417
+
418
+ initSyncState({
419
+ baseChannel: "myapp",
420
+ });
421
+
422
+ const useStore = create(
423
+ syncStateMiddleware({ name: "counter" })((set) => ({
424
+ count: 0,
425
+ increment: () => set((state) => ({ count: state.count + 1 })),
426
+ }))
427
+ );
428
+ ```
429
+
430
+ #### 自定义桥接
431
+
432
+ ```ts
433
+ import { create } from "zustand";
434
+ import { syncStateMiddleware } from "electron-state-sync/zustand";
435
+
436
+ const useStore = create(
437
+ syncStateMiddleware({
438
+ name: "counter",
439
+ bridge: customBridge,
440
+ })((set) => ({
441
+ count: 0,
442
+ increment: () => set((state) => ({ count: state.count + 1 })),
443
+ }))
444
+ );
445
+ ```
446
+
447
+ ### TanStack Query (React Query)
448
+
449
+ #### 最简化使用
450
+
451
+ ```ts
452
+ import { useSyncState } from "electron-state-sync/react-query";
453
+
454
+ function App() {
455
+ const { data: count, isSynced, update } = useSyncState(0, {
456
+ name: "counter",
457
+ });
458
+
459
+ return <div onClick={() => update(5)}>{count}</div>;
460
+ }
461
+ ```
462
+
463
+ #### 使用全局配置
464
+
465
+ ```ts
466
+ import { initSyncState, useSyncState } from "electron-state-sync/react-query";
467
+
468
+ initSyncState({
469
+ baseChannel: "myapp",
470
+ });
471
+
472
+ function App() {
473
+ const { data: count, isSynced, update } = useSyncState(0, {
474
+ name: "counter",
475
+ });
476
+
477
+ return <div onClick={() => update(5)}>{count}</div>;
478
+ }
479
+ ```
480
+
481
+ #### 自定义桥接
482
+
483
+ ```ts
484
+ import { useSyncState } from "electron-state-sync/react-query";
485
+
486
+ function App() {
487
+ const { data: count, isSynced, update } = useSyncState(0, {
488
+ name: "counter",
489
+ bridge: customBridge,
490
+ });
491
+
492
+ return <div onClick={() => update(5)}>{count}</div>;
493
+ }
494
+ ```
495
+
496
+ ### Jotai
497
+
498
+ #### 最简化使用
499
+
500
+ ```ts
501
+ import { atom, useAtom } from "jotai";
502
+ import { syncStateAtom } from "electron-state-sync/jotai";
503
+
504
+ const countAtom = syncStateAtom(0, { name: "counter" });
505
+
506
+ function App() {
507
+ const [count, setCount] = useAtom(countAtom);
508
+ return <div onClick={() => setCount(5)}>{count}</div>;
509
+ }
510
+ ```
511
+
512
+ #### 使用全局配置
513
+
514
+ ```ts
515
+ import { initSyncState } from "electron-state-sync/jotai";
516
+ import { atom, useAtom } from "jotai";
517
+ import { syncStateAtom } from "electron-state-sync/jotai";
518
+
519
+ initSyncState({
520
+ baseChannel: "myapp",
521
+ });
522
+
523
+ const countAtom = syncStateAtom(0, { name: "counter" });
524
+
525
+ function App() {
526
+ const [count, setCount] = useAtom(countAtom);
527
+ return <div onClick={() => setCount(5)}>{count}</div>;
528
+ }
529
+ ```
530
+
531
+ #### 自定义桥接
532
+
533
+ ```ts
534
+ import { atom, useAtom } from "jotai";
535
+ import { syncStateAtom } from "electron-state-sync/jotai";
536
+
537
+ const countAtom = syncStateAtom(0, {
538
+ name: "counter",
539
+ bridge: customBridge,
540
+ });
541
+
542
+ function App() {
543
+ const [count, setCount] = useAtom(countAtom);
544
+ return <div onClick={() => setCount(5)}>{count}</div>;
545
+ }
546
+ ```
547
+
548
+ ### Redux Toolkit
549
+
550
+ #### 最简化使用
551
+
552
+ ```ts
553
+ import { configureStore, createSlice } from "@reduxjs/toolkit";
554
+ import { syncStateMiddleware } from "electron-state-sync/redux";
555
+ import { Provider, useDispatch, useSelector } from "react-redux";
556
+
557
+ const counterSlice = createSlice({
558
+ name: "counter",
559
+ initialState: { value: 0 },
560
+ reducers: {
561
+ setValue: (state, action) => {
562
+ state.value = action.payload;
563
+ },
564
+ },
565
+ });
566
+
567
+ const store = configureStore({
568
+ reducer: {
569
+ counter: counterSlice.reducer,
570
+ },
571
+ middleware: (getDefaultMiddleware) =>
572
+ getDefaultMiddleware().concat(
573
+ syncStateMiddleware({
574
+ name: "counter",
575
+ selector: (state) => state.counter.value,
576
+ actionType: "counter/setValue",
577
+ })
578
+ ),
579
+ });
580
+
581
+ function App() {
582
+ const count = useSelector((state) => state.counter.value);
583
+ const dispatch = useDispatch();
584
+ return <div onClick={() => dispatch(counterSlice.actions.setValue(5))}>{count}</div>;
585
+ }
586
+ ```
587
+
588
+ #### 使用全局配置
589
+
590
+ ```ts
591
+ import { initSyncState } from "electron-state-sync/redux";
592
+ import { configureStore, createSlice } from "@reduxjs/toolkit";
593
+ import { syncStateMiddleware } from "electron-state-sync/redux";
594
+
595
+ initSyncState({
596
+ baseChannel: "myapp",
597
+ });
598
+
599
+ const counterSlice = createSlice({
600
+ name: "counter",
601
+ initialState: { value: 0 },
602
+ reducers: {
603
+ setValue: (state, action) => {
604
+ state.value = action.payload;
605
+ },
606
+ },
607
+ });
608
+
609
+ const store = configureStore({
610
+ reducer: {
611
+ counter: counterSlice.reducer,
612
+ },
613
+ middleware: (getDefaultMiddleware) =>
614
+ getDefaultMiddleware().concat(
615
+ syncStateMiddleware({
616
+ name: "counter",
617
+ selector: (state) => state.counter.value,
618
+ actionType: "counter/setValue",
619
+ })
620
+ ),
621
+ });
622
+ ```
623
+
624
+ #### 自定义桥接
625
+
626
+ ```ts
627
+ import { configureStore, createSlice } from "@reduxjs/toolkit";
628
+ import { syncStateMiddleware } from "electron-state-sync/redux";
629
+
630
+ const counterSlice = createSlice({
631
+ name: "counter",
632
+ initialState: { value: 0 },
633
+ reducers: {
634
+ setValue: (state, action) => {
635
+ state.value = action.payload;
636
+ },
637
+ },
638
+ });
639
+
640
+ const store = configureStore({
641
+ reducer: {
642
+ counter: counterSlice.reducer,
643
+ },
644
+ middleware: (getDefaultMiddleware) =>
645
+ getDefaultMiddleware().concat(
646
+ syncStateMiddleware({
647
+ name: "counter",
648
+ selector: (state) => state.counter.value,
649
+ actionType: "counter/setValue",
650
+ bridge: customBridge,
651
+ })
652
+ ),
653
+ });
654
+ ```
655
+
656
+ ### IPC 通道命名
657
+
658
+ 通道格式为 **${baseChannel}:${name}:get|set|subscribe|unsubscribe|update**。
659
+
660
+ ### 写入权限与校验
661
+
662
+ - **allowRendererSet: false** 时渲染端写入会抛错,但仍可订阅更新。
663
+ - **resolveRendererValue** 用于校验或转换渲染端写入值,抛错会拒绝写入。
664
+ - 主进程始终作为权威来源,所有变更都会广播给订阅者。
665
+
666
+ ### 初始值同步
667
+
668
+ - 渲染端 **initialValue** 仅用于首屏占位,最终以主进程值为准。
669
+ - 渲染端会先订阅主进程更新,再调用 **get** 拉取当前值,可能会触发一次覆盖更新。
670
+ - 若主进程初始值与渲染端一致,通常不会感知到闪动。
671
+ - 渲染端可读取 **isSynced** 判断是否已完成首次同步。
672
+ - React/Solid Hook 的第三个返回值为 **isSynced**。
673
+ - Vue 返回的 Ref 挂载 **isSynced** 字段。
674
+ - Svelte Store 挂载 **isSynced** Store。
675
+
676
+ ### 错误码
677
+
678
+ - 只读写入:**SyncStateError** 的 **code** 为 **RENDERER_READONLY**。
679
+ - 写入校验失败:**SyncStateError** 的 **code** 为 **RENDERER_INVALID_VALUE**。
680
+
681
+ ### 对象深度监听
682
+
683
+ **仅限 Vue**:深度监听仅在 Vue 集成中支持。
684
+
685
+ 当值是对象时启用深度监听(仅 Vue):
686
+
687
+ ```ts
688
+ // Vue 示例
689
+ const profile = useSyncState(
690
+ { name: "Alice" },
691
+ {
692
+ name: "profile",
693
+ deep: true, // 仅在 Vue 中可用
694
+ }
695
+ );
696
+ ```
697
+
698
+ **注意**:
699
+ - Vue 集成会在同步前将响应式 Proxy 转为原始值,确保 IPC 可序列化。
700
+ - React、Svelte 和 SolidJS 集成不支持深度监听。在这些框架中,如需监听对象内部变化,请创建新的对象引用以触发更新。
701
+
702
+ ## 包体积
703
+
704
+ 各框架包体积(ESM / CJS):
705
+
706
+ | 包 | ESM | CJS | gzip |
707
+ |---------|-----|-----|------|
708
+ | Main | 6.44 kB | 6.51 kB | 1.95 kB |
709
+ | Preload | 1.49 kB | 1.54 kB | 0.49 kB |
710
+ | Zustand | 5.88 kB | 6.06 kB | 1.43 kB |
711
+ | Redux | 4.37 kB | 4.54 kB | 1.34 kB |
712
+ | React Query | 3.34 kB | 3.53 kB | 1.13 kB |
713
+ | Jotai | 3.32 kB | 3.44 kB | 1.14 kB |
714
+ | Vue | 2.24 kB | 2.25 kB | 0.81 kB |
715
+ | Solid | 2.21 kB | 2.24 kB | 0.77 kB |
716
+ | Svelte | 1.77 kB | 1.82 kB | 0.64 kB |
717
+ | Preact | 1.43 kB | 1.51 kB | 0.56 kB |
718
+ | React | 1.42 kB | 1.45 kB | 0.55 kB |
719
+
720
+ ## 系统要求
721
+
722
+ - **Electron**: ≥ 18.0.0(推荐 ≥ 32.0.0)
723
+ - **Node.js**: ≥ 16.9.0
724
+ - **TypeScript**: ≥ 5.0.0(如果使用 TypeScript)
725
+
726
+ **框架集成**(按需选择):
727
+
728
+ - **React**: ≥ 18.0.0
729
+ - **Vue**: ≥ 3.0.0
730
+ - **Svelte**: ≥ 3.0.0
731
+ - **SolidJS**: ≥ 1.0.0
732
+
733
+ **状态管理集成**(按需选择):
734
+
735
+ - **Zustand**: ≥ 4.0.0
736
+ - **TanStack Query**: ≥ 5.0.0
737
+ - **Jotai**: ≥ 2.0.0
738
+ - **Redux Toolkit**: ≥ 2.0.0
739
+ - **React Redux**: ≥ 9.0.0(用于 Redux Toolkit 集成)
740
+
741
+ ## License
742
+
743
+ MIT