vue-page-store 0.5.0 → 0.5.2
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 +109 -0
- package/dist/index.cjs.js +360 -34
- package/dist/index.d.ts +58 -2
- package/dist/index.esm.js +360 -34
- package/dist/index.umd.js +360 -34
- package/dist/index.umd.min.js +9 -5
- package/package.json +3 -2
- package/src/debug/DevPanel.vue +277 -0
- package/src/debug/emit.js +116 -0
- package/src/debug/installPanel.js +26 -0
- package/src/debug/registry.js +118 -0
package/README.md
CHANGED
|
@@ -641,6 +641,103 @@ __VUE_PAGE_STORE__.registry.forEach(...)
|
|
|
641
641
|
- `__VUE_PAGE_STORE__` 是 dev-only 调试接口,shape 和键名可能在后续版本变化,**不要在生产代码里依赖**
|
|
642
642
|
- 微前端场景下,多个子应用都加载 vue-page-store 时,最后挂载的会覆盖前面的。如需共存,请退回手动挂载并用自己的命名
|
|
643
643
|
|
|
644
|
+
### `window.PAGE_STORE_DEVTOOLS` —— debug 注册表 *(v0.5.1 新增)*
|
|
645
|
+
|
|
646
|
+
v0.5.1 新增了 dev-only 的 debug 模块,在 `window.PAGE_STORE_DEVTOOLS` 上暴露结构化调试数据:
|
|
647
|
+
|
|
648
|
+
```js
|
|
649
|
+
window.PAGE_STORE_DEVTOOLS
|
|
650
|
+
// {
|
|
651
|
+
// stores: Map, ← store 元信息(id、active、destroyed、storeRef…)
|
|
652
|
+
// events: [], ← 事件时间线(最近 500 条)
|
|
653
|
+
// seq: number ← 全局递增计数
|
|
654
|
+
// }
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
#### 自动采集的事件
|
|
658
|
+
|
|
659
|
+
| 事件 | 说明 |
|
|
660
|
+
|---|---|
|
|
661
|
+
| `store:create` | store 实例创建 |
|
|
662
|
+
| `store:destroy` | store 实例销毁 |
|
|
663
|
+
| `action:start` | action 调用开始(含参数快照) |
|
|
664
|
+
| `action:end` | action 调用结束(含 duration) |
|
|
665
|
+
| `action:error` | action 抛错或 reject(含错误信息和 duration) |
|
|
666
|
+
|
|
667
|
+
每条事件包含 `seq`(全局序号)、`ts`(时间戳)、`storeId`、`type`、`payload`。
|
|
668
|
+
|
|
669
|
+
#### 控制台用法
|
|
670
|
+
|
|
671
|
+
```js
|
|
672
|
+
// 查看当前存活的 store
|
|
673
|
+
PAGE_STORE_DEVTOOLS.stores
|
|
674
|
+
|
|
675
|
+
// 查看最近的事件
|
|
676
|
+
PAGE_STORE_DEVTOOLS.events.slice(-5)
|
|
677
|
+
|
|
678
|
+
// 筛选某个 store 的 action 事件
|
|
679
|
+
PAGE_STORE_DEVTOOLS.events
|
|
680
|
+
.filter(e => e.storeId === 'orderList' && e.type.startsWith('action:'))
|
|
681
|
+
|
|
682
|
+
// 查看 action 耗时
|
|
683
|
+
PAGE_STORE_DEVTOOLS.events
|
|
684
|
+
.filter(e => e.type === 'action:end')
|
|
685
|
+
.map(e => e.payload.action + ': ' + e.payload.duration + 'ms')
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
生产环境下 `PAGE_STORE_DEVTOOLS` 不会被挂载,所有 debug 逻辑为 no-op。
|
|
689
|
+
|
|
690
|
+
### DevPanel —— 页面内悬浮面板 *(v0.5.1 新增)*
|
|
691
|
+
|
|
692
|
+
v0.5.1 提供了一个最小的页面内调试面板,在右下角悬浮显示:
|
|
693
|
+
|
|
694
|
+
- **左侧**:store 列表(显示 active / idle / destroyed 状态)
|
|
695
|
+
- **右侧四个 tab**:
|
|
696
|
+
- `$state` — 当前选中 store 的业务状态
|
|
697
|
+
- `$source` — 页面输入 / 原始返回
|
|
698
|
+
- `getters` — 派生计算值
|
|
699
|
+
- `events` — 该 store 最近 50 条事件
|
|
700
|
+
|
|
701
|
+
面板每 500ms 刷新一次,实时反映 store 变化。
|
|
702
|
+
|
|
703
|
+
#### 接入方式
|
|
704
|
+
|
|
705
|
+
```js
|
|
706
|
+
// main.js — 仅开发环境加载
|
|
707
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
708
|
+
import('vue-page-store/debug/installPanel').then(m => m.installDevPanel())
|
|
709
|
+
}
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
面板会自动挂载到 `document.body`,不侵入业务组件树。生产环境下 `installDevPanel()` 是 no-op。
|
|
713
|
+
|
|
714
|
+
#### 注意事项
|
|
715
|
+
|
|
716
|
+
- DevPanel 是独立 Vue 实例,不影响业务组件树
|
|
717
|
+
- 需要构建链能处理 `.vue` SFC(webpack + vue-loader 或 Vite 默认支持)
|
|
718
|
+
- 微前端场景下,多个子应用各自挂面板,互不干扰
|
|
719
|
+
- 这是 dev-only 工具,不要在生产代码里依赖
|
|
720
|
+
|
|
721
|
+
#### 强制开启
|
|
722
|
+
|
|
723
|
+
Vite / webpack 5 等不 polyfill `process` 的环境下,如果 `isDev` 检测失败,可以在**页面加载前**手动设置:
|
|
724
|
+
|
|
725
|
+
```js
|
|
726
|
+
window.__VUE_PAGE_STORE_DEV__ = true
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
### debug 模块文件结构
|
|
730
|
+
|
|
731
|
+
```
|
|
732
|
+
src/debug/
|
|
733
|
+
├── registry.js ← window.PAGE_STORE_DEVTOOLS 数据层
|
|
734
|
+
├── emit.js ← emitDebugEvent 埋点入口
|
|
735
|
+
├── DevPanel.vue ← 悬浮面板组件
|
|
736
|
+
└── installPanel.js ← 面板挂载器
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
所有 debug 文件仅在 dev 环境生效。生产构建中:`registry.js` 和 `emit.js` 中的所有函数早 return / 返回空值,不执行任何逻辑,不挂 `window`,不占内存。如果你的打包器支持 tree-shaking 且 `import('...installPanel')` 走动态导入,面板代码不会进入生产包。
|
|
740
|
+
|
|
644
741
|
## 从 v0.3.x 升级
|
|
645
742
|
|
|
646
743
|
### Breaking Changes
|
|
@@ -708,11 +805,23 @@ v0.5 **完全向后兼容** v0.4.x:
|
|
|
708
805
|
|
|
709
806
|
升级只需要改版本号,无需改代码。
|
|
710
807
|
|
|
808
|
+
## 从 v0.5.0 升级到 v0.5.1
|
|
809
|
+
|
|
810
|
+
v0.5.1 **完全向后兼容** v0.5.0:
|
|
811
|
+
|
|
812
|
+
- **修复**:`isDev` 检测改用 `try/catch` 兜底,修复 Vite / webpack 5 不 polyfill `process` 时模块加载报错的问题
|
|
813
|
+
- **新增**:dev-only debug 模块(`debug/registry.js`、`debug/emit.js`、`debug/DevPanel.vue`、`debug/installPanel.js`)
|
|
814
|
+
- **新增**:`window.PAGE_STORE_DEVTOOLS` 调试注册表,自动采集 store 创建/销毁和 action 调用事件
|
|
815
|
+
- **新增**:DevPanel 悬浮面板,可视化查看 store 列表、state/source/getters、事件时间线
|
|
816
|
+
|
|
817
|
+
升级只需要改版本号。debug 模块为可选接入,不接入等同于 v0.5.0 行为。
|
|
818
|
+
|
|
711
819
|
## Roadmap
|
|
712
820
|
|
|
713
821
|
- **Keyed instance** — `useStore(vm, scopeKey)` 支持同定义多实例
|
|
714
822
|
- **Official plugins** — 随着 `vue-page-runtime` 等生态库成熟,补充第一方 plugin 文档
|
|
715
823
|
- **More page runtime helpers** — 在不增加心智负担的前提下继续补页面层能力
|
|
824
|
+
- **Vue Devtools 集成** — 在 debug 模块基础上对接 Vue Devtools inspector / timeline API
|
|
716
825
|
|
|
717
826
|
## License
|
|
718
827
|
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* vue-page-store v0.5.
|
|
2
|
+
* vue-page-store v0.5.2
|
|
3
3
|
* (c) 2026 weijianjun
|
|
4
4
|
* @license MIT
|
|
5
5
|
*/
|
|
@@ -9,13 +9,221 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
9
9
|
|
|
10
10
|
var Vue = require('vue');
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* vue-page-store debug / registry
|
|
14
|
+
*
|
|
15
|
+
* 纯 JS 数据层:不依赖 Vue,不涉及 UI。
|
|
16
|
+
*
|
|
17
|
+
* 对外:
|
|
18
|
+
* window.PAGE_STORE_DEVTOOLS = { stores: Map, events: [], seq: number }
|
|
19
|
+
*
|
|
20
|
+
* API:
|
|
21
|
+
* nextId() — 递增并返回 seq
|
|
22
|
+
* addStore(meta) — stores.set(meta.id, meta)
|
|
23
|
+
* updateStore(id, patch) — 浅合并进已有 meta
|
|
24
|
+
* removeStore(id) — stores.delete(id)
|
|
25
|
+
* addEvent(event) — events.push + 环形裁剪
|
|
26
|
+
* getStores() — 返回 Map(生产返回空 Map)
|
|
27
|
+
* getEvents() — 返回数组(生产返回空数组)
|
|
28
|
+
*
|
|
29
|
+
* 仅开发环境生效。生产环境下 ensure() 返回 null,
|
|
30
|
+
* 所有写 API 静默 no-op,不挂 window,不占内存。
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
var MAX_EVENTS = 500;
|
|
34
|
+
|
|
35
|
+
// ---- isDev:稳健检测 ----
|
|
36
|
+
// webpack 4 / 旧 CRA 有 process;Vite / webpack 5 不 polyfill process 的情况用 try/catch 兜住。
|
|
37
|
+
// 额外允许 window.__VUE_PAGE_STORE_DEV__ = true 强制开启(staging 临开调试面板用)。
|
|
38
|
+
var _isDev = false;
|
|
39
|
+
try {
|
|
40
|
+
if (typeof process !== 'undefined'
|
|
41
|
+
&& process.env
|
|
42
|
+
&& process.env.NODE_ENV !== 'production') {
|
|
43
|
+
_isDev = true;
|
|
44
|
+
}
|
|
45
|
+
} catch (e) { /* no process polyfill */ }
|
|
46
|
+
try {
|
|
47
|
+
if (typeof window !== 'undefined' && window.__VUE_PAGE_STORE_DEV__ === true) {
|
|
48
|
+
_isDev = true;
|
|
49
|
+
}
|
|
50
|
+
} catch (e) { /* SSR */ }
|
|
51
|
+
|
|
52
|
+
var isDev$1 = _isDev;
|
|
53
|
+
|
|
54
|
+
// ---- 单例 ----
|
|
55
|
+
var _devtools = null;
|
|
56
|
+
|
|
57
|
+
function ensure() {
|
|
58
|
+
if (!isDev$1) return null;
|
|
59
|
+
if (_devtools) return _devtools;
|
|
60
|
+
|
|
61
|
+
_devtools = {
|
|
62
|
+
stores: new Map(),
|
|
63
|
+
events: [],
|
|
64
|
+
seq: 0,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
if (typeof window !== 'undefined') {
|
|
68
|
+
try { window.PAGE_STORE_DEVTOOLS = _devtools; } catch (e) { /* locked window */ }
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return _devtools;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ---- API ----
|
|
75
|
+
|
|
76
|
+
function nextId() {
|
|
77
|
+
var dt = ensure();
|
|
78
|
+
if (!dt) return 0;
|
|
79
|
+
dt.seq++;
|
|
80
|
+
return dt.seq;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function addStore(meta) {
|
|
84
|
+
var dt = ensure();
|
|
85
|
+
if (!dt) return;
|
|
86
|
+
if (!meta || !meta.id) return;
|
|
87
|
+
dt.stores.set(meta.id, meta);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function updateStore(id, patch) {
|
|
91
|
+
var dt = ensure();
|
|
92
|
+
if (!dt) return;
|
|
93
|
+
if (!id || !patch) return;
|
|
94
|
+
var m = dt.stores.get(id);
|
|
95
|
+
if (!m) return;
|
|
96
|
+
for (var k in patch) {
|
|
97
|
+
if (Object.prototype.hasOwnProperty.call(patch, k)) {
|
|
98
|
+
m[k] = patch[k];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function addEvent(event) {
|
|
104
|
+
var dt = ensure();
|
|
105
|
+
if (!dt) return;
|
|
106
|
+
if (!event) return;
|
|
107
|
+
dt.events.push(event);
|
|
108
|
+
// 环形裁剪,防止长时间开发内存无限增长
|
|
109
|
+
var overflow = dt.events.length - MAX_EVENTS;
|
|
110
|
+
if (overflow > 0) dt.events.splice(0, overflow);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* vue-page-store debug / emit
|
|
115
|
+
*
|
|
116
|
+
* 唯一对外高层入口:
|
|
117
|
+
* emitDebugEvent(store, type, payload)
|
|
118
|
+
*
|
|
119
|
+
* 职责:
|
|
120
|
+
* 1. 把一次领域调用写入 events 时间线(带 seq + ts)
|
|
121
|
+
* 2. 对特定 type 顺带同步 stores meta(create / destroy / lifecycle)
|
|
122
|
+
* 3. payload 做序列化快照,避免后续持有活引用
|
|
123
|
+
* 4. 仅 dev 生效;任何异常一律吞掉,绝不拖累业务
|
|
124
|
+
*
|
|
125
|
+
* 支持的 type:
|
|
126
|
+
* store:create payload = { name? } → addStore(meta)
|
|
127
|
+
* store:destroy payload = undefined → updateStore(id, { destroyed: true, active: false })
|
|
128
|
+
* lifecycle:init payload = undefined → updateStore(id, { route })
|
|
129
|
+
* lifecycle:enter payload = undefined → updateStore(id, { active: true, route })
|
|
130
|
+
* lifecycle:leave payload = undefined → updateStore(id, { active: false })
|
|
131
|
+
* action:start payload = { action, args } → 仅写事件
|
|
132
|
+
* action:end payload = { action, duration } → 仅写事件
|
|
133
|
+
* action:error payload = { action, duration, error } → 仅写事件
|
|
134
|
+
* state:set payload = { key, value } → 仅写事件
|
|
135
|
+
* state:patch payload = { patch } → 仅写事件
|
|
136
|
+
*/
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
// ---- helpers ----
|
|
140
|
+
|
|
141
|
+
function readRoute(store) {
|
|
142
|
+
try {
|
|
143
|
+
var vm = store && store.$vm;
|
|
144
|
+
if (vm && vm.$route) {
|
|
145
|
+
return {
|
|
146
|
+
path: vm.$route.path,
|
|
147
|
+
fullPath: vm.$route.fullPath,
|
|
148
|
+
name: vm.$route.name,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
} catch (e) { /* ignore */ }
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function snapshot(v) {
|
|
156
|
+
if (v === undefined) return undefined;
|
|
157
|
+
if (v === null) return null;
|
|
158
|
+
var t = typeof v;
|
|
159
|
+
if (t === 'function') return '[Function]';
|
|
160
|
+
if (t !== 'object') return v;
|
|
161
|
+
try {
|
|
162
|
+
return JSON.parse(JSON.stringify(v));
|
|
163
|
+
} catch (e) {
|
|
164
|
+
return '[Unserializable]';
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ---- 主入口 ----
|
|
169
|
+
|
|
170
|
+
function emitDebugEvent(store, type, payload) {
|
|
171
|
+
if (!isDev$1) return null;
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
var id = store && store.$id;
|
|
175
|
+
|
|
176
|
+
// 必要的 meta 同步
|
|
177
|
+
switch (type) {
|
|
178
|
+
case 'store:create':
|
|
179
|
+
addStore({
|
|
180
|
+
id: id,
|
|
181
|
+
name: (payload && payload.name) || id,
|
|
182
|
+
route: readRoute(store),
|
|
183
|
+
createdAt: Date.now(),
|
|
184
|
+
active: false,
|
|
185
|
+
destroyed: false,
|
|
186
|
+
keepAlive: false,
|
|
187
|
+
storeRef: store,
|
|
188
|
+
});
|
|
189
|
+
break;
|
|
190
|
+
case 'store:destroy':
|
|
191
|
+
updateStore(id, { destroyed: true, active: false });
|
|
192
|
+
break;
|
|
193
|
+
case 'lifecycle:init':
|
|
194
|
+
updateStore(id, { route: readRoute(store) });
|
|
195
|
+
break;
|
|
196
|
+
case 'lifecycle:enter':
|
|
197
|
+
updateStore(id, { active: true, route: readRoute(store) });
|
|
198
|
+
break;
|
|
199
|
+
case 'lifecycle:leave':
|
|
200
|
+
updateStore(id, { active: false });
|
|
201
|
+
break;
|
|
202
|
+
default:
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
var event = {
|
|
207
|
+
seq: nextId(),
|
|
208
|
+
ts: Date.now(),
|
|
209
|
+
storeId: id,
|
|
210
|
+
type: type,
|
|
211
|
+
payload: snapshot(payload),
|
|
212
|
+
};
|
|
213
|
+
addEvent(event);
|
|
214
|
+
return event;
|
|
215
|
+
} catch (e) {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
12
220
|
/*!
|
|
13
|
-
* vue-page-store v0.5.
|
|
221
|
+
* vue-page-store v0.5.1
|
|
14
222
|
* (c) 2026 weijianjun
|
|
15
223
|
* @license MIT
|
|
16
224
|
*/
|
|
17
225
|
/**
|
|
18
|
-
* vue-page-store 0.5.
|
|
226
|
+
* vue-page-store 0.5.1 — Vue 2.6 Page Scope Runtime
|
|
19
227
|
*
|
|
20
228
|
* 页面级作用域运行时容器:
|
|
21
229
|
* source · state · getters · actions · watch · init/enter/leave · $setInterval · event bus · plugin
|
|
@@ -32,15 +240,25 @@ var Vue = require('vue');
|
|
|
32
240
|
* v0.5 新增:
|
|
33
241
|
* plugin → registerPlugin 注册外部扩展,声明字段 + 生命周期钩子
|
|
34
242
|
*
|
|
243
|
+
* v0.5.1 修复/新增:
|
|
244
|
+
* isDev → try/catch 兜底,修复 Vite / webpack 5 不 polyfill process 时报错
|
|
245
|
+
* debug → dev-only 调试工具:store 创建/销毁追踪、action 埋点、DevPanel
|
|
246
|
+
*
|
|
35
247
|
* @author weijianjun
|
|
36
248
|
* @license MIT
|
|
37
249
|
*/
|
|
38
250
|
|
|
39
251
|
|
|
40
252
|
// ====== dev-only warning ======
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
253
|
+
// v0.5.1 修复:try/catch 兜底,Vite / webpack 5 不 polyfill process 时不再报错
|
|
254
|
+
var isDev = false;
|
|
255
|
+
try {
|
|
256
|
+
isDev = typeof process !== 'undefined'
|
|
257
|
+
&& process.env
|
|
258
|
+
&& process.env.NODE_ENV !== 'production';
|
|
259
|
+
} catch (e) {
|
|
260
|
+
// Vite / webpack 5 等环境下 process 未定义或 process.env 被 getter 拦截
|
|
261
|
+
}
|
|
44
262
|
|
|
45
263
|
function warn(msg) {
|
|
46
264
|
if (isDev) {
|
|
@@ -48,6 +266,54 @@ function warn(msg) {
|
|
|
48
266
|
}
|
|
49
267
|
}
|
|
50
268
|
|
|
269
|
+
// ====== debug: wrapAction helper ======
|
|
270
|
+
function _now() {
|
|
271
|
+
return typeof performance !== 'undefined' && performance.now
|
|
272
|
+
? performance.now()
|
|
273
|
+
: Date.now();
|
|
274
|
+
}
|
|
275
|
+
function wrapAction(store, name, boundFn) {
|
|
276
|
+
return function () {
|
|
277
|
+
var args = Array.prototype.slice.call(arguments);
|
|
278
|
+
var t0 = _now();
|
|
279
|
+
emitDebugEvent(store, 'action:start', { action: name, args: args });
|
|
280
|
+
var result;
|
|
281
|
+
try {
|
|
282
|
+
result = boundFn.apply(null, args);
|
|
283
|
+
} catch (syncErr) {
|
|
284
|
+
emitDebugEvent(store, 'action:error', {
|
|
285
|
+
action: name,
|
|
286
|
+
duration: +(_now() - t0).toFixed(2),
|
|
287
|
+
error: (syncErr && syncErr.message) || String(syncErr),
|
|
288
|
+
});
|
|
289
|
+
throw syncErr;
|
|
290
|
+
}
|
|
291
|
+
if (result && typeof result.then === 'function') {
|
|
292
|
+
Promise.resolve(result).then(
|
|
293
|
+
function () {
|
|
294
|
+
emitDebugEvent(store, 'action:end', {
|
|
295
|
+
action: name,
|
|
296
|
+
duration: +(_now() - t0).toFixed(2),
|
|
297
|
+
});
|
|
298
|
+
},
|
|
299
|
+
function (err) {
|
|
300
|
+
emitDebugEvent(store, 'action:error', {
|
|
301
|
+
action: name,
|
|
302
|
+
duration: +(_now() - t0).toFixed(2),
|
|
303
|
+
error: (err && err.message) || String(err),
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
);
|
|
307
|
+
} else {
|
|
308
|
+
emitDebugEvent(store, 'action:end', {
|
|
309
|
+
action: name,
|
|
310
|
+
duration: +(_now() - t0).toFixed(2),
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
return result;
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
51
317
|
// Store 注册表(导出供调试 / devtools 使用)
|
|
52
318
|
var storeRegistry = new Map();
|
|
53
319
|
|
|
@@ -57,7 +323,9 @@ if (isDev && typeof window !== 'undefined') {
|
|
|
57
323
|
registry: storeRegistry,
|
|
58
324
|
get stores() {
|
|
59
325
|
var result = {};
|
|
60
|
-
storeRegistry.forEach(function (s, id) {
|
|
326
|
+
storeRegistry.forEach(function (s, id) {
|
|
327
|
+
result[id] = s;
|
|
328
|
+
});
|
|
61
329
|
return result;
|
|
62
330
|
},
|
|
63
331
|
};
|
|
@@ -80,9 +348,15 @@ function registerPlugin(plugin) {
|
|
|
80
348
|
throw new Error('[vue-page-store] plugin 需要一个 name 属性');
|
|
81
349
|
}
|
|
82
350
|
if (typeof plugin.install !== 'function') {
|
|
83
|
-
throw new Error(
|
|
351
|
+
throw new Error(
|
|
352
|
+
'[vue-page-store] plugin "' + plugin.name + '" 需要一个 install 方法',
|
|
353
|
+
);
|
|
84
354
|
}
|
|
85
|
-
if (
|
|
355
|
+
if (
|
|
356
|
+
_plugins.some(function (p) {
|
|
357
|
+
return p.name === plugin.name;
|
|
358
|
+
})
|
|
359
|
+
) {
|
|
86
360
|
warn('plugin "' + plugin.name + '" 已注册,跳过重复注册');
|
|
87
361
|
return;
|
|
88
362
|
}
|
|
@@ -93,7 +367,8 @@ function createStoreInstance(Vue, id, options) {
|
|
|
93
367
|
var initialState = options.state();
|
|
94
368
|
|
|
95
369
|
// ====== v0.4 新增:source ======
|
|
96
|
-
var initialSource =
|
|
370
|
+
var initialSource =
|
|
371
|
+
typeof options.source === 'function' ? options.source() : {};
|
|
97
372
|
|
|
98
373
|
var getters = options.getters || {};
|
|
99
374
|
var actions = options.actions || {};
|
|
@@ -119,19 +394,19 @@ function createStoreInstance(Vue, id, options) {
|
|
|
119
394
|
data: function () {
|
|
120
395
|
return {
|
|
121
396
|
$$state: initialState,
|
|
122
|
-
$$source: initialSource,
|
|
123
|
-
$$loading: {},
|
|
397
|
+
$$source: initialSource, // v0.4 新增
|
|
398
|
+
$$loading: {}, // v0.4 新增
|
|
124
399
|
$$status: {
|
|
125
400
|
mounted: false,
|
|
126
|
-
active: false
|
|
127
|
-
}
|
|
401
|
+
active: false,
|
|
402
|
+
},
|
|
128
403
|
};
|
|
129
404
|
},
|
|
130
405
|
computed: computedDefs,
|
|
131
406
|
});
|
|
132
407
|
|
|
133
408
|
var rawState = vm.$data.$$state;
|
|
134
|
-
var rawSource = vm.$data.$$source;
|
|
409
|
+
var rawSource = vm.$data.$$source; // v0.4 新增
|
|
135
410
|
var rawLoading = vm.$data.$$loading; // v0.4 新增
|
|
136
411
|
var rawStatus = vm.$data.$$status;
|
|
137
412
|
|
|
@@ -140,7 +415,9 @@ function createStoreInstance(Vue, id, options) {
|
|
|
140
415
|
Object.defineProperty(store, key, {
|
|
141
416
|
enumerable: true,
|
|
142
417
|
configurable: true,
|
|
143
|
-
get: function () {
|
|
418
|
+
get: function () {
|
|
419
|
+
return rawState[key];
|
|
420
|
+
},
|
|
144
421
|
set: function (val) {
|
|
145
422
|
if (store.$disposed) {
|
|
146
423
|
warn('store "' + id + '" 已销毁,忽略对 "' + key + '" 的写入');
|
|
@@ -161,12 +438,14 @@ function createStoreInstance(Vue, id, options) {
|
|
|
161
438
|
Object.keys(getters).forEach(function (key) {
|
|
162
439
|
Object.defineProperty(store, key, {
|
|
163
440
|
enumerable: true,
|
|
164
|
-
get: function () {
|
|
441
|
+
get: function () {
|
|
442
|
+
return vm[key];
|
|
443
|
+
},
|
|
165
444
|
});
|
|
166
445
|
});
|
|
167
446
|
|
|
168
447
|
// ====== actions —— v0.4 变更:自动增强 async action ======
|
|
169
|
-
var _loadingCounts = {};
|
|
448
|
+
var _loadingCounts = {}; // 并发计数器,防止先返回的 finally 提前关 loading
|
|
170
449
|
|
|
171
450
|
function finishLoading(key) {
|
|
172
451
|
_loadingCounts[key]--;
|
|
@@ -180,7 +459,7 @@ function createStoreInstance(Vue, id, options) {
|
|
|
180
459
|
var originalFn = actions[key];
|
|
181
460
|
var boundFn = originalFn.bind(store);
|
|
182
461
|
|
|
183
|
-
|
|
462
|
+
var withLoading = function () {
|
|
184
463
|
var result = boundFn.apply(null, arguments);
|
|
185
464
|
|
|
186
465
|
// 检测是否返回 Promise,如果是则自动追踪 loading
|
|
@@ -191,13 +470,20 @@ function createStoreInstance(Vue, id, options) {
|
|
|
191
470
|
|
|
192
471
|
var tracked = Promise.resolve(result);
|
|
193
472
|
tracked.then(
|
|
194
|
-
function () {
|
|
195
|
-
|
|
473
|
+
function () {
|
|
474
|
+
finishLoading(key);
|
|
475
|
+
},
|
|
476
|
+
function () {
|
|
477
|
+
finishLoading(key);
|
|
478
|
+
},
|
|
196
479
|
);
|
|
197
480
|
}
|
|
198
481
|
|
|
199
482
|
return result;
|
|
200
483
|
};
|
|
484
|
+
|
|
485
|
+
// v0.5.1:debug 包装套在 $loading 追踪外层
|
|
486
|
+
store[key] = wrapAction(store, key, withLoading);
|
|
201
487
|
});
|
|
202
488
|
|
|
203
489
|
// ====== watch —— 声明式副作用,生命周期自动回收 ======
|
|
@@ -219,14 +505,20 @@ function createStoreInstance(Vue, id, options) {
|
|
|
219
505
|
|
|
220
506
|
if (!handler) {
|
|
221
507
|
warn(
|
|
222
|
-
'watch "' +
|
|
508
|
+
'watch "' +
|
|
509
|
+
path +
|
|
510
|
+
'" in store "' +
|
|
511
|
+
id +
|
|
512
|
+
'" 缺少 handler,该 watcher 将被跳过',
|
|
223
513
|
);
|
|
224
514
|
return;
|
|
225
515
|
}
|
|
226
516
|
}
|
|
227
517
|
|
|
228
518
|
var expr = function () {
|
|
229
|
-
return path.split('.').reduce(function (obj, k) {
|
|
519
|
+
return path.split('.').reduce(function (obj, k) {
|
|
520
|
+
return obj && obj[k];
|
|
521
|
+
}, store);
|
|
230
522
|
};
|
|
231
523
|
vm.$watch(expr, handler.bind(store), watchOpts);
|
|
232
524
|
});
|
|
@@ -241,7 +533,9 @@ function createStoreInstance(Vue, id, options) {
|
|
|
241
533
|
Object.defineProperty(store, '$vm', {
|
|
242
534
|
enumerable: true,
|
|
243
535
|
configurable: false,
|
|
244
|
-
get: function () {
|
|
536
|
+
get: function () {
|
|
537
|
+
return _vm_ref;
|
|
538
|
+
},
|
|
245
539
|
set: function () {
|
|
246
540
|
warn('$vm 是只读属性,不允许业务侧重写');
|
|
247
541
|
},
|
|
@@ -281,7 +575,8 @@ function createStoreInstance(Vue, id, options) {
|
|
|
281
575
|
});
|
|
282
576
|
|
|
283
577
|
// v0.4 新增:重置 source
|
|
284
|
-
var freshSource =
|
|
578
|
+
var freshSource =
|
|
579
|
+
typeof options.source === 'function' ? options.source() : {};
|
|
285
580
|
Object.keys(freshSource).forEach(function (key) {
|
|
286
581
|
Vue.set(rawSource, key, freshSource[key]);
|
|
287
582
|
});
|
|
@@ -297,7 +592,10 @@ function createStoreInstance(Vue, id, options) {
|
|
|
297
592
|
|
|
298
593
|
store.$emit = function (event, payload) {
|
|
299
594
|
var fns = _listeners[event];
|
|
300
|
-
if (fns)
|
|
595
|
+
if (fns)
|
|
596
|
+
fns.slice().forEach(function (fn) {
|
|
597
|
+
fn(payload);
|
|
598
|
+
});
|
|
301
599
|
};
|
|
302
600
|
|
|
303
601
|
store.$on = function (event, handler) {
|
|
@@ -373,7 +671,9 @@ function createStoreInstance(Vue, id, options) {
|
|
|
373
671
|
}
|
|
374
672
|
store.$emit('page:enter');
|
|
375
673
|
// v0.5: plugin enter hooks
|
|
376
|
-
_pluginHooks.forEach(function (h) {
|
|
674
|
+
_pluginHooks.forEach(function (h) {
|
|
675
|
+
if (h.enter) h.enter();
|
|
676
|
+
});
|
|
377
677
|
}
|
|
378
678
|
|
|
379
679
|
function runLeave() {
|
|
@@ -387,7 +687,9 @@ function createStoreInstance(Vue, id, options) {
|
|
|
387
687
|
}
|
|
388
688
|
store.$emit('page:leave');
|
|
389
689
|
// v0.5: plugin leave hooks
|
|
390
|
-
_pluginHooks.forEach(function (h) {
|
|
690
|
+
_pluginHooks.forEach(function (h) {
|
|
691
|
+
if (h.leave) h.leave();
|
|
692
|
+
});
|
|
391
693
|
}
|
|
392
694
|
|
|
393
695
|
// ====== bindTo 去重标记 ======
|
|
@@ -401,7 +703,10 @@ function createStoreInstance(Vue, id, options) {
|
|
|
401
703
|
}
|
|
402
704
|
|
|
403
705
|
function addBoundVm(vm) {
|
|
404
|
-
if (_boundVms) {
|
|
706
|
+
if (_boundVms) {
|
|
707
|
+
_boundVms.add(vm);
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
405
710
|
_boundVmsFallback.push(vm);
|
|
406
711
|
}
|
|
407
712
|
|
|
@@ -466,12 +771,19 @@ function createStoreInstance(Vue, id, options) {
|
|
|
466
771
|
clearAllIntervals();
|
|
467
772
|
|
|
468
773
|
// v0.5: plugin destroy hooks
|
|
469
|
-
_pluginHooks.forEach(function (h) {
|
|
774
|
+
_pluginHooks.forEach(function (h) {
|
|
775
|
+
if (h.destroy) h.destroy();
|
|
776
|
+
});
|
|
470
777
|
|
|
471
778
|
store.$disposed = true;
|
|
472
|
-
Object.keys(_listeners).forEach(function (key) {
|
|
779
|
+
Object.keys(_listeners).forEach(function (key) {
|
|
780
|
+
delete _listeners[key];
|
|
781
|
+
});
|
|
473
782
|
vm.$destroy();
|
|
474
783
|
storeRegistry.delete(id);
|
|
784
|
+
|
|
785
|
+
// v0.5.1 debug: 放在所有清理之后
|
|
786
|
+
emitDebugEvent(store, 'store:destroy');
|
|
475
787
|
};
|
|
476
788
|
|
|
477
789
|
store._vm = vm;
|
|
@@ -485,6 +797,16 @@ function createStoreInstance(Vue, id, options) {
|
|
|
485
797
|
if (hooks) _pluginHooks.push(hooks);
|
|
486
798
|
});
|
|
487
799
|
|
|
800
|
+
// ====== v0.5.1 debug: 注册到 devtools registry ======
|
|
801
|
+
var _debugId = nextId();
|
|
802
|
+
Object.defineProperty(store, '_debugId', {
|
|
803
|
+
value: _debugId,
|
|
804
|
+
writable: false,
|
|
805
|
+
enumerable: false,
|
|
806
|
+
configurable: false,
|
|
807
|
+
});
|
|
808
|
+
emitDebugEvent(store, 'store:create', { debugId: _debugId });
|
|
809
|
+
|
|
488
810
|
return store;
|
|
489
811
|
}
|
|
490
812
|
|
|
@@ -503,10 +825,14 @@ function createStoreInstance(Vue, id, options) {
|
|
|
503
825
|
function definePageStore(id, options) {
|
|
504
826
|
// 入参校验
|
|
505
827
|
if (!id || typeof id !== 'string') {
|
|
506
|
-
throw new Error(
|
|
828
|
+
throw new Error(
|
|
829
|
+
'[vue-page-store] definePageStore 需要一个非空字符串作为 id',
|
|
830
|
+
);
|
|
507
831
|
}
|
|
508
832
|
if (!options || typeof options.state !== 'function') {
|
|
509
|
-
throw new Error(
|
|
833
|
+
throw new Error(
|
|
834
|
+
'[vue-page-store] definePageStore("' + id + '") 需要 state 为函数',
|
|
835
|
+
);
|
|
510
836
|
}
|
|
511
837
|
|
|
512
838
|
var _Vue = Vue;
|
|
@@ -534,7 +860,7 @@ function definePageStore(id, options) {
|
|
|
534
860
|
var index = {
|
|
535
861
|
definePageStore: definePageStore,
|
|
536
862
|
registerPlugin: registerPlugin,
|
|
537
|
-
storeRegistry: storeRegistry
|
|
863
|
+
storeRegistry: storeRegistry,
|
|
538
864
|
};
|
|
539
865
|
|
|
540
866
|
exports.default = index;
|