@vitarx/plugin-vite 1.0.4 → 1.0.6
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/dist/constants/index.d.ts +0 -9
- package/dist/constants/index.js +0 -9
- package/dist/hmr-client/index.d.ts +27 -1
- package/dist/hmr-client/index.js +108 -12
- package/dist/hmr-client/update.d.ts +11 -2
- package/dist/hmr-client/update.js +25 -59
- package/dist/hmr-client/weak-refs.d.ts +37 -0
- package/dist/hmr-client/weak-refs.js +41 -0
- package/dist/passes/directives/processDirectives.d.ts +1 -1
- package/dist/passes/directives/processDirectives.js +15 -10
- package/dist/passes/hmr/index.d.ts +1 -1
- package/dist/passes/hmr/index.js +1 -1
- package/dist/passes/hmr/inject-state.d.ts +16 -10
- package/dist/passes/hmr/inject-state.js +141 -135
- package/dist/passes/hmr/inject.d.ts +10 -0
- package/dist/passes/hmr/inject.js +47 -10
- package/dist/passes/imports/collectRefVariables.d.ts +1 -0
- package/dist/passes/imports/collectRefVariables.js +52 -19
- package/dist/passes/index.d.ts +1 -1
- package/dist/passes/index.js +1 -1
- package/dist/passes/jsx/processJSXFragment.js +18 -1
- package/dist/passes/props/attribute.d.ts +3 -0
- package/dist/passes/props/attribute.js +13 -3
- package/dist/transform.js +16 -5
- package/dist/types.d.ts +1 -1
- package/dist/utils/branch-factory.js +26 -6
- package/dist/utils/component-collect.d.ts +7 -0
- package/dist/utils/component-collect.js +12 -236
- package/dist/utils/default-export.d.ts +38 -0
- package/dist/utils/default-export.js +119 -0
- package/dist/utils/forward-export.d.ts +28 -0
- package/dist/utils/forward-export.js +76 -0
- package/dist/utils/function-extract.d.ts +45 -0
- package/dist/utils/function-extract.js +98 -0
- package/dist/utils/generate.d.ts +7 -0
- package/dist/utils/generate.js +20 -0
- package/dist/utils/index.d.ts +3 -2
- package/dist/utils/index.js +4 -2
- package/dist/utils/jsx-helpers.d.ts +1 -4
- package/dist/utils/jsx-helpers.js +2 -15
- package/dist/utils/vif-helpers.js +6 -3
- package/package.json +5 -4
- package/dist/hmr-client/utils.d.ts +0 -23
- package/dist/hmr-client/utils.js +0 -137
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vitarx 运行时 API 名称
|
|
3
|
-
* 这些 API 需要从 vitarx 包导入
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* UI 相关的 API 名称列表
|
|
7
|
-
* 这些 API 用于创建视图,在 HMR 时需要识别
|
|
8
|
-
*/
|
|
9
|
-
export declare const UI_API_NAMES: readonly ["createView", "branch", "dynamic", "expr", "accessor", "withDirectives"];
|
|
10
1
|
/**
|
|
11
2
|
* Ref 相关 API 名称
|
|
12
3
|
* 用于识别 ref 变量
|
package/dist/constants/index.js
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vitarx 运行时 API 名称
|
|
3
|
-
* 这些 API 需要从 vitarx 包导入
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* UI 相关的 API 名称列表
|
|
7
|
-
* 这些 API 用于创建视图,在 HMR 时需要识别
|
|
8
|
-
*/
|
|
9
|
-
export const UI_API_NAMES = ['createView', 'branch', 'dynamic', 'expr', 'accessor', 'withDirectives'];
|
|
10
1
|
/**
|
|
11
2
|
* Ref 相关 API 名称
|
|
12
3
|
* 用于识别 ref 变量
|
|
@@ -46,6 +46,17 @@ export default class HMRManager {
|
|
|
46
46
|
* const state = __$VITARX_HMR$__.instance.memo(__$VITARX_HMR_VIEW_NODE$__, 'state') ?? ref(0)
|
|
47
47
|
*/
|
|
48
48
|
memo(view: ComponentView, name: string): any;
|
|
49
|
+
/**
|
|
50
|
+
* 无条件恢复状态(不做 Ref/reactive 门禁)
|
|
51
|
+
*
|
|
52
|
+
* 供解构声明的隐藏变量使用:composable 返回的控制器是普通对象
|
|
53
|
+
* (内含 Ref 与函数),memo 的响应式门禁会将其拒收导致恢复永远
|
|
54
|
+
* 落空。仅限转换器为解构声明生成的代码调用。
|
|
55
|
+
*
|
|
56
|
+
* @param {ComponentView} view - 组件视图
|
|
57
|
+
* @param {string} name - 变量名称
|
|
58
|
+
*/
|
|
59
|
+
memoRaw(view: ComponentView, name: string): any;
|
|
49
60
|
/**
|
|
50
61
|
* 注册节点
|
|
51
62
|
*
|
|
@@ -59,10 +70,25 @@ export default class HMRManager {
|
|
|
59
70
|
* @param component - 组件构造函数
|
|
60
71
|
*/
|
|
61
72
|
getId(component: Component): string;
|
|
73
|
+
/**
|
|
74
|
+
* 登记视图状态 getter
|
|
75
|
+
*
|
|
76
|
+
* 由转换器注入的代码在组件函数每次执行时调用,登记当前作用域的
|
|
77
|
+
* 惰性状态读取器(同一视图重复登记覆盖旧的)。读取动作延迟到
|
|
78
|
+
* 热更新快照时发生,正常挂载/重挂载不会产生任何状态残留。
|
|
79
|
+
*
|
|
80
|
+
* @param {ComponentView} view - 组件视图
|
|
81
|
+
* @param {Record<string, () => any>} getters - 变量名到读取器的映射
|
|
82
|
+
*/
|
|
83
|
+
trackState(view: ComponentView, getters: Record<string, () => any>): void;
|
|
62
84
|
/**
|
|
63
85
|
* 模块更新
|
|
64
86
|
*
|
|
65
87
|
* @param newModule - 新模块对象
|
|
88
|
+
* @returns 模块导出中是否包含至少一个已绑定 HMR id 的组件。
|
|
89
|
+
* 供导入转发模块的 accept 回调判断兜底:转发目标若不含
|
|
90
|
+
* 可识别组件(如换成未转换模块的组件),调用方应
|
|
91
|
+
* invalidate 触发整页刷新,避免界面静默 stale。
|
|
66
92
|
*/
|
|
67
|
-
update(newModule: ModuleNamespace):
|
|
93
|
+
update(newModule: ModuleNamespace): boolean;
|
|
68
94
|
}
|
package/dist/hmr-client/index.js
CHANGED
|
@@ -3,10 +3,11 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
3
3
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
4
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
5
|
};
|
|
6
|
-
var _HMRManager_idMapToView, _HMRManager_idMapToComponent;
|
|
6
|
+
var _HMRManager_instances, _HMRManager_idMapToView, _HMRManager_idMapToComponent, _HMRManager_viewStateGetters, _HMRManager_snapshotState;
|
|
7
7
|
import { isComponent, isReactive, isRef } from 'vitarx';
|
|
8
8
|
import { HMR } from '../constants/index.js';
|
|
9
9
|
import { processUpdate } from './update.js';
|
|
10
|
+
import { collectLiveViews, registerViewRef } from './weak-refs.js';
|
|
10
11
|
/**
|
|
11
12
|
* HMR管理器
|
|
12
13
|
*
|
|
@@ -14,10 +15,18 @@ import { processUpdate } from './update.js';
|
|
|
14
15
|
*/
|
|
15
16
|
class HMRManager {
|
|
16
17
|
constructor() {
|
|
18
|
+
_HMRManager_instances.add(this);
|
|
17
19
|
/**
|
|
18
20
|
* id模块映射到组件虚拟节点集合
|
|
19
21
|
*
|
|
20
|
-
* 模块id ->
|
|
22
|
+
* 模块id -> 组件虚拟节点(WeakRef 弱引用)
|
|
23
|
+
*
|
|
24
|
+
* 为什么用 WeakRef:register 在组件每次执行时都会调用,若用强引用,
|
|
25
|
+
* 已销毁的视图(弹窗关闭、路由切换)会被本 Map 永久钉住造成内存泄漏。
|
|
26
|
+
* WeakRef 借助 GC 可达性天然区分「Freeze 停用」与「真销毁」——
|
|
27
|
+
* Freeze 缓存持有视图强引用,停用中的视图不会被回收,热更新照常处理;
|
|
28
|
+
* 真销毁的视图无引用可回收,deref() 返回空,遍历时剪枝。
|
|
29
|
+
* 剪枝时机为 register/update 遍历(dev 下高频),泄漏上限极小。
|
|
21
30
|
*/
|
|
22
31
|
_HMRManager_idMapToView.set(this, new Map()
|
|
23
32
|
/**
|
|
@@ -28,6 +37,20 @@ class HMRManager {
|
|
|
28
37
|
* id映射到组件构造函数
|
|
29
38
|
*/
|
|
30
39
|
_HMRManager_idMapToComponent.set(this, new Map()
|
|
40
|
+
/**
|
|
41
|
+
* 视图状态 getter 登记表
|
|
42
|
+
*
|
|
43
|
+
* 组件函数每次执行时通过 trackState 登记当前作用域状态的惰性读取器,
|
|
44
|
+
* 热更新时读取一次生成快照;视图销毁/重挂载不会保留,避免旧状态泄漏。
|
|
45
|
+
*/
|
|
46
|
+
);
|
|
47
|
+
/**
|
|
48
|
+
* 视图状态 getter 登记表
|
|
49
|
+
*
|
|
50
|
+
* 组件函数每次执行时通过 trackState 登记当前作用域状态的惰性读取器,
|
|
51
|
+
* 热更新时读取一次生成快照;视图销毁/重挂载不会保留,避免旧状态泄漏。
|
|
52
|
+
*/
|
|
53
|
+
_HMRManager_viewStateGetters.set(this, new WeakMap()
|
|
31
54
|
/**
|
|
32
55
|
* 获取单实例
|
|
33
56
|
*/
|
|
@@ -81,6 +104,21 @@ class HMRManager {
|
|
|
81
104
|
return undefined;
|
|
82
105
|
return state;
|
|
83
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* 无条件恢复状态(不做 Ref/reactive 门禁)
|
|
109
|
+
*
|
|
110
|
+
* 供解构声明的隐藏变量使用:composable 返回的控制器是普通对象
|
|
111
|
+
* (内含 Ref 与函数),memo 的响应式门禁会将其拒收导致恢复永远
|
|
112
|
+
* 落空。仅限转换器为解构声明生成的代码调用。
|
|
113
|
+
*
|
|
114
|
+
* @param {ComponentView} view - 组件视图
|
|
115
|
+
* @param {string} name - 变量名称
|
|
116
|
+
*/
|
|
117
|
+
memoRaw(view, name) {
|
|
118
|
+
if (!view)
|
|
119
|
+
return undefined;
|
|
120
|
+
return view[HMR.state]?.[name];
|
|
121
|
+
}
|
|
84
122
|
/**
|
|
85
123
|
* 注册节点
|
|
86
124
|
*
|
|
@@ -92,11 +130,12 @@ class HMRManager {
|
|
|
92
130
|
return;
|
|
93
131
|
component ?? (component = view.component);
|
|
94
132
|
const id = this.getId(component);
|
|
95
|
-
|
|
96
|
-
|
|
133
|
+
const refs = __classPrivateFieldGet(this, _HMRManager_idMapToView, "f").get(id);
|
|
134
|
+
if (refs) {
|
|
135
|
+
registerViewRef(refs, view);
|
|
97
136
|
}
|
|
98
137
|
else {
|
|
99
|
-
__classPrivateFieldGet(this, _HMRManager_idMapToView, "f").set(id, new Set([view]));
|
|
138
|
+
__classPrivateFieldGet(this, _HMRManager_idMapToView, "f").set(id, new Set([new WeakRef(view)]));
|
|
100
139
|
}
|
|
101
140
|
}
|
|
102
141
|
/**
|
|
@@ -107,14 +146,33 @@ class HMRManager {
|
|
|
107
146
|
getId(component) {
|
|
108
147
|
return Reflect.get(component, HMR.id);
|
|
109
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* 登记视图状态 getter
|
|
151
|
+
*
|
|
152
|
+
* 由转换器注入的代码在组件函数每次执行时调用,登记当前作用域的
|
|
153
|
+
* 惰性状态读取器(同一视图重复登记覆盖旧的)。读取动作延迟到
|
|
154
|
+
* 热更新快照时发生,正常挂载/重挂载不会产生任何状态残留。
|
|
155
|
+
*
|
|
156
|
+
* @param {ComponentView} view - 组件视图
|
|
157
|
+
* @param {Record<string, () => any>} getters - 变量名到读取器的映射
|
|
158
|
+
*/
|
|
159
|
+
trackState(view, getters) {
|
|
160
|
+
if (!view)
|
|
161
|
+
return;
|
|
162
|
+
__classPrivateFieldGet(this, _HMRManager_viewStateGetters, "f").set(view, getters);
|
|
163
|
+
}
|
|
110
164
|
/**
|
|
111
165
|
* 模块更新
|
|
112
166
|
*
|
|
113
167
|
* @param newModule - 新模块对象
|
|
168
|
+
* @returns 模块导出中是否包含至少一个已绑定 HMR id 的组件。
|
|
169
|
+
* 供导入转发模块的 accept 回调判断兜底:转发目标若不含
|
|
170
|
+
* 可识别组件(如换成未转换模块的组件),调用方应
|
|
171
|
+
* invalidate 触发整页刷新,避免界面静默 stale。
|
|
114
172
|
*/
|
|
115
173
|
update(newModule) {
|
|
116
174
|
if (!newModule)
|
|
117
|
-
return;
|
|
175
|
+
return false;
|
|
118
176
|
try {
|
|
119
177
|
const components = [];
|
|
120
178
|
const updatedView = new Set();
|
|
@@ -135,23 +193,39 @@ class HMRManager {
|
|
|
135
193
|
for (const component of components) {
|
|
136
194
|
const id = this.getId(component);
|
|
137
195
|
// 模块活跃的虚拟节点集合
|
|
138
|
-
const
|
|
139
|
-
if (!
|
|
196
|
+
const refs = __classPrivateFieldGet(this, _HMRManager_idMapToView, "f").get(id);
|
|
197
|
+
if (!refs)
|
|
198
|
+
continue;
|
|
199
|
+
// 收集存活视图(死引用已剪枝),遍历使其更新
|
|
200
|
+
const views = collectLiveViews(refs);
|
|
201
|
+
// 集合清空则移除 Map 条目,避免空壳残留长期积累
|
|
202
|
+
if (refs.size === 0) {
|
|
203
|
+
__classPrivateFieldGet(this, _HMRManager_idMapToView, "f").delete(id);
|
|
140
204
|
continue;
|
|
141
|
-
|
|
205
|
+
}
|
|
142
206
|
for (const view of views) {
|
|
143
207
|
// 跳过已更新过的视图,避免同一次更新中同一个视图被更新多次
|
|
144
208
|
if (updatedView.has(view))
|
|
145
209
|
continue;
|
|
146
210
|
// 仅在视图是活跃状态,且已挂载时才更新
|
|
147
211
|
if (view.isActive && view.isMounted) {
|
|
148
|
-
//
|
|
149
|
-
|
|
212
|
+
// 重执行前快照当前状态,使组件函数重执行时 memo 命中、保留状态
|
|
213
|
+
__classPrivateFieldGet(this, _HMRManager_instances, "m", _HMRManager_snapshotState).call(this, view);
|
|
214
|
+
try {
|
|
215
|
+
// 处理视图更新
|
|
216
|
+
processUpdate(view, this.resolveComponent(view.component));
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
// 快照仅服务于本次热更新,用完即清——
|
|
220
|
+
// 保证后续正常的「卸载→重挂载」拿到全新状态,与生产行为一致
|
|
221
|
+
delete view[HMR.state];
|
|
222
|
+
}
|
|
150
223
|
// 标记视图已更新
|
|
151
224
|
updatedView.add(view);
|
|
152
225
|
}
|
|
153
226
|
}
|
|
154
227
|
}
|
|
228
|
+
return components.length > 0;
|
|
155
229
|
}
|
|
156
230
|
catch (e) {
|
|
157
231
|
if (import.meta.hot) {
|
|
@@ -161,7 +235,29 @@ class HMRManager {
|
|
|
161
235
|
throw e;
|
|
162
236
|
}
|
|
163
237
|
}
|
|
238
|
+
return false;
|
|
164
239
|
}
|
|
165
240
|
}
|
|
166
|
-
_HMRManager_idMapToView = new WeakMap(), _HMRManager_idMapToComponent = new WeakMap()
|
|
241
|
+
_HMRManager_idMapToView = new WeakMap(), _HMRManager_idMapToComponent = new WeakMap(), _HMRManager_viewStateGetters = new WeakMap(), _HMRManager_instances = new WeakSet(), _HMRManager_snapshotState = function _HMRManager_snapshotState(view) {
|
|
242
|
+
const getters = __classPrivateFieldGet(this, _HMRManager_viewStateGetters, "f").get(view);
|
|
243
|
+
if (!getters)
|
|
244
|
+
return;
|
|
245
|
+
const snapshot = {};
|
|
246
|
+
for (const name in getters) {
|
|
247
|
+
try {
|
|
248
|
+
// 注意:getters 是 getter 属性对象——读取属性即执行 getter 并返回值,
|
|
249
|
+
// 不能再加调用括号(否则会把返回值当函数调用,全部抛 TypeError)
|
|
250
|
+
const value = getters[name];
|
|
251
|
+
// 防御:函数值(旧编译产物仍可能登记函数变量)不进入快照,
|
|
252
|
+
// 绝不调用组件内的业务函数
|
|
253
|
+
if (typeof value === 'function')
|
|
254
|
+
continue;
|
|
255
|
+
snapshot[name] = value;
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
// 单个变量读取失败(如暂时性死区)不影响其余状态恢复
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
view[HMR.state] = snapshot;
|
|
262
|
+
};
|
|
167
263
|
export default HMRManager;
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMR 组件更新:统一「卸载 → 重建 → 挂载」语义
|
|
3
|
+
*
|
|
4
|
+
* 任何代码变更(含纯 UI/注释)都走完整重挂载:dispose 触发旧实例的
|
|
5
|
+
* onDispose/onScopeDispose 清理副作用,init 重执行组件函数(状态经
|
|
6
|
+
* memo/快照协议保留),mount 触发 onMounted——dev 与生产的生命周期
|
|
7
|
+
* 语义完全一致,不区分 logic/非 logic 分叉,不存在钩子滞留或孤儿监听。
|
|
8
|
+
* @module hmr-client/update
|
|
9
|
+
*/
|
|
1
10
|
import { Component, ComponentView } from 'vitarx';
|
|
2
11
|
/**
|
|
3
|
-
*
|
|
12
|
+
* 处理组件更新:卸载旧实例 → 重建(重执行组件函数,状态经快照恢复)→ 挂载
|
|
4
13
|
* @param view - 组件视图对象,包含当前组件的实例和节点信息
|
|
5
|
-
* @param newComponent -
|
|
14
|
+
* @param newComponent - 新的组件定义
|
|
6
15
|
*/
|
|
7
16
|
export declare function processUpdate(view: ComponentView, newComponent: Component): void;
|
|
@@ -1,72 +1,38 @@
|
|
|
1
|
-
import { createCommentView, EffectScope, getRenderer, runComponent } from 'vitarx';
|
|
2
|
-
import { diffComponentChange } from './utils.js';
|
|
3
1
|
/**
|
|
4
|
-
*
|
|
2
|
+
* HMR 组件更新:统一「卸载 → 重建 → 挂载」语义
|
|
3
|
+
*
|
|
4
|
+
* 任何代码变更(含纯 UI/注释)都走完整重挂载:dispose 触发旧实例的
|
|
5
|
+
* onDispose/onScopeDispose 清理副作用,init 重执行组件函数(状态经
|
|
6
|
+
* memo/快照协议保留),mount 触发 onMounted——dev 与生产的生命周期
|
|
7
|
+
* 语义完全一致,不区分 logic/非 logic 分叉,不存在钩子滞留或孤儿监听。
|
|
8
|
+
* @module hmr-client/update
|
|
9
|
+
*/
|
|
10
|
+
import { getRenderer } from 'vitarx';
|
|
11
|
+
/**
|
|
12
|
+
* 处理组件更新:卸载旧实例 → 重建(重执行组件函数,状态经快照恢复)→ 挂载
|
|
5
13
|
* @param view - 组件视图对象,包含当前组件的实例和节点信息
|
|
6
|
-
* @param newComponent -
|
|
14
|
+
* @param newComponent - 新的组件定义
|
|
7
15
|
*/
|
|
8
16
|
export function processUpdate(view, newComponent) {
|
|
9
|
-
//
|
|
17
|
+
// 获取渲染器实例,创建占位注释节点并插入到旧元素位置,
|
|
18
|
+
// 作为新旧子树替换的锚点
|
|
10
19
|
const renderer = getRenderer();
|
|
11
|
-
// 创建一个占位符注释节点
|
|
12
20
|
const placeholder = renderer.createComment('');
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// 将占位符插入到旧元素的位置
|
|
16
|
-
renderer.insert(placeholder, oldElement);
|
|
17
|
-
// 获取组件实例
|
|
18
|
-
const instance = view.instance;
|
|
19
|
-
// 比较新旧组件的差异,检查逻辑是否有变化
|
|
20
|
-
const { logic } = diffComponentChange(view.component.toString(), newComponent.toString());
|
|
21
|
-
// 更新视图中的组件引用
|
|
21
|
+
renderer.insert(placeholder, view.node);
|
|
22
|
+
// 更新视图中的组件引用为最新模块导出
|
|
22
23
|
Object.defineProperty(view, 'component', {
|
|
23
24
|
value: newComponent,
|
|
24
25
|
writable: true,
|
|
25
26
|
enumerable: true,
|
|
26
27
|
configurable: true
|
|
27
28
|
});
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
instance.scope.dispose();
|
|
38
|
-
// 重新创建新的作用域
|
|
39
|
-
const scope = new EffectScope({
|
|
40
|
-
name: view.name,
|
|
41
|
-
errorHandler: (error, source) => {
|
|
42
|
-
instance.reportError(error, `effect:${source}`);
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
Object.defineProperty(instance, 'scope', {
|
|
46
|
-
value: scope,
|
|
47
|
-
writable: true,
|
|
48
|
-
enumerable: true,
|
|
49
|
-
configurable: true
|
|
50
|
-
});
|
|
51
|
-
// 销毁旧的子树
|
|
52
|
-
instance.subView.dispose();
|
|
53
|
-
// 创建新的子树
|
|
54
|
-
let subView;
|
|
55
|
-
try {
|
|
56
|
-
subView = runComponent(instance, () => newComponent(view.props));
|
|
57
|
-
}
|
|
58
|
-
catch (e) {
|
|
59
|
-
instance.reportError(e, 'component:run');
|
|
60
|
-
subView = createCommentView(`Component<${view.name}>:failed`);
|
|
61
|
-
}
|
|
62
|
-
Object.defineProperty(instance, 'subView', {
|
|
63
|
-
value: instance['normalizeView'](subView),
|
|
64
|
-
writable: true,
|
|
65
|
-
enumerable: true,
|
|
66
|
-
configurable: true
|
|
67
|
-
});
|
|
68
|
-
// 初始化新的子树
|
|
69
|
-
instance.subView.init(instance.subViewContext);
|
|
70
|
-
// 挂载新的子树
|
|
71
|
-
instance.subView.mount(placeholder, 'replace');
|
|
29
|
+
// 完整重挂载:dispose(清理旧副作用,触发 onDispose/onScopeDispose)
|
|
30
|
+
// → init(重执行组件函数,memo 命中快照保留状态)
|
|
31
|
+
// → mount(触发 onMounted,重新注册副作用)
|
|
32
|
+
// 注意:ctx 必须在 dispose 之前捕获——dispose 会拆除 provide 链,
|
|
33
|
+
// 之后读取 ctx 会导致重执行时 inject(如 useRouter)解析失败
|
|
34
|
+
const ctx = view.ctx;
|
|
35
|
+
view.dispose();
|
|
36
|
+
view.init(ctx);
|
|
37
|
+
view.mount(placeholder, 'replace');
|
|
72
38
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HMR 视图弱引用集合工具
|
|
3
|
+
*
|
|
4
|
+
* #idMapToView 借助 WeakRef 让已销毁视图可被 GC 回收,避免被管理器
|
|
5
|
+
* 永久强引用造成内存泄漏。GC 可达性天然区分「Freeze 停用」与
|
|
6
|
+
* 「真销毁」——Freeze 缓存持有视图强引用,停用中的视图不会被回收,
|
|
7
|
+
* 热更新照常处理;真销毁的视图无引用可回收,deref() 返回空,遍历时剪枝。
|
|
8
|
+
*
|
|
9
|
+
* 剪枝/去重逻辑抽为纯函数并接受 WeakRefLike 结构类型,便于以伪引用
|
|
10
|
+
* 做确定性单元测试(真实 WeakRef 的 deref 时点依赖 GC,无法确定性断言)。
|
|
11
|
+
* @module hmr-client/weak-refs
|
|
12
|
+
*/
|
|
13
|
+
import type { ComponentView } from 'vitarx';
|
|
14
|
+
/**
|
|
15
|
+
* WeakRef 结构类型
|
|
16
|
+
*
|
|
17
|
+
* 真实 WeakRef 天然满足此结构;测试可注入自定义伪引用。
|
|
18
|
+
*/
|
|
19
|
+
export interface WeakRefLike<T extends object> {
|
|
20
|
+
deref(): T | undefined;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 向引用集合登记视图:剪枝死引用 + 按 deref 比对去重
|
|
24
|
+
*
|
|
25
|
+
* WeakRef 身份 ≠ 视图身份,同一视图多次登记(组件每次执行都会
|
|
26
|
+
* register)必须按解引用结果比对,否则集合会被同视图的多个
|
|
27
|
+
* WeakRef 无限撑大。
|
|
28
|
+
* @param refs - 视图弱引用集合(原地修改)
|
|
29
|
+
* @param view - 待登记的视图
|
|
30
|
+
*/
|
|
31
|
+
export declare function registerViewRef(refs: Set<WeakRefLike<ComponentView>>, view: ComponentView): void;
|
|
32
|
+
/**
|
|
33
|
+
* 收集集合中仍存活(未被 GC)的视图,并原地剪枝死引用
|
|
34
|
+
* @param refs - 视图弱引用集合(原地修改)
|
|
35
|
+
* @returns 存活视图数组
|
|
36
|
+
*/
|
|
37
|
+
export declare function collectLiveViews(refs: Set<WeakRefLike<ComponentView>>): ComponentView[];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 向引用集合登记视图:剪枝死引用 + 按 deref 比对去重
|
|
3
|
+
*
|
|
4
|
+
* WeakRef 身份 ≠ 视图身份,同一视图多次登记(组件每次执行都会
|
|
5
|
+
* register)必须按解引用结果比对,否则集合会被同视图的多个
|
|
6
|
+
* WeakRef 无限撑大。
|
|
7
|
+
* @param refs - 视图弱引用集合(原地修改)
|
|
8
|
+
* @param view - 待登记的视图
|
|
9
|
+
*/
|
|
10
|
+
export function registerViewRef(refs, view) {
|
|
11
|
+
let exists = false;
|
|
12
|
+
for (const ref of refs) {
|
|
13
|
+
const cached = ref.deref();
|
|
14
|
+
if (!cached) {
|
|
15
|
+
refs.delete(ref);
|
|
16
|
+
}
|
|
17
|
+
else if (cached === view) {
|
|
18
|
+
exists = true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (!exists)
|
|
22
|
+
refs.add(new WeakRef(view));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 收集集合中仍存活(未被 GC)的视图,并原地剪枝死引用
|
|
26
|
+
* @param refs - 视图弱引用集合(原地修改)
|
|
27
|
+
* @returns 存活视图数组
|
|
28
|
+
*/
|
|
29
|
+
export function collectLiveViews(refs) {
|
|
30
|
+
const live = [];
|
|
31
|
+
for (const ref of refs) {
|
|
32
|
+
const view = ref.deref();
|
|
33
|
+
if (view) {
|
|
34
|
+
live.push(view);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
refs.delete(ref);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return live;
|
|
41
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import * as t from '@babel/types';
|
|
7
7
|
import { TransformContext } from '../../context.js';
|
|
8
|
-
import type { DirectiveInfo } from '
|
|
8
|
+
import type { DirectiveInfo } from '../props/types.js';
|
|
9
9
|
/**
|
|
10
10
|
* 处理指令
|
|
11
11
|
* 将指令转换为 withDirectives 调用
|
|
@@ -52,16 +52,21 @@ ctx // 转换上下文,包含别名、导入等信息
|
|
|
52
52
|
const properties = []; // 定义对象属性数组,可以是属性或方法
|
|
53
53
|
// 添加 value 属性
|
|
54
54
|
if (isIdentifier(value)) {
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
55
|
+
// 三态决策:已知 ref → .value,已知非 ref → 原样,未知 → unref()
|
|
56
|
+
if (ctx.refVariables.has(value.name)) {
|
|
57
|
+
// 已知 ref:直接使用 .value
|
|
58
|
+
properties.push(t.objectMethod('get', t.identifier('value'), [], t.blockStatement([t.returnStatement(t.memberExpression(value, t.identifier('value')))])));
|
|
59
|
+
}
|
|
60
|
+
else if (ctx.nonRefVariables.has(value.name)) {
|
|
61
|
+
// 已知非 ref(函数等):原样返回,无需 unref
|
|
62
|
+
properties.push(t.objectMethod('get', t.identifier('value'), [], t.blockStatement([t.returnStatement(value)])));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// 未知标识符:使用 unref() 解包(非 ref 原样返回)
|
|
66
|
+
markImport(ctx, 'unref');
|
|
67
|
+
const unrefAlias = getAlias(ctx.vitarxAliases, 'unref');
|
|
68
|
+
properties.push(t.objectMethod('get', t.identifier('value'), [], t.blockStatement([t.returnStatement(t.callExpression(t.identifier(unrefAlias), [value]))])));
|
|
69
|
+
}
|
|
65
70
|
}
|
|
66
71
|
else {
|
|
67
72
|
// 如果值不是标识符
|
package/dist/passes/hmr/index.js
CHANGED
|
@@ -20,15 +20,21 @@ export declare function createHMRRegistrationStatements(variableNames: string[])
|
|
|
20
20
|
*/
|
|
21
21
|
export declare function createWrappedComponentHMRStatements(componentName: string): t.Statement[];
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* 为函数体注入状态恢复代码并收集需要登记(trackState)的变量名
|
|
24
|
+
*
|
|
25
|
+
* 处理规则:
|
|
26
|
+
* - 标识符声明(非函数初始值):初始值包装为 memo(view, '变量名') ?? 原始初始值
|
|
27
|
+
* - useModel 调用:豁免(桥接状态每次重建,见 isUseModelCall)
|
|
28
|
+
* - 解构声明(如 `const { a } = useFoo()`):拆分为隐藏变量整包 memo +
|
|
29
|
+
* 原声明解构隐藏变量——composable 返回的状态对象得以跨重执行保留,
|
|
30
|
+
* 其内部创建的响应式状态与已挂载的副作用保持一致
|
|
31
|
+
* - 函数类型初始值 / 无初始值:不注入
|
|
32
|
+
* - for 循环头部声明:循环局部绑定,不注入(仅递归循环体)
|
|
33
|
+
*
|
|
34
|
+
* 单趟完成注入与收集,返回按执行顺序排列的登记名列表(标识符名 +
|
|
35
|
+
* 隐藏变量名),供 trackState 生成 getter。隐藏变量采用用户标识符不可能
|
|
36
|
+
* 命中的 `__$VITARX_D<N>$__` 命名,按组件函数内确定性计数。
|
|
25
37
|
* @param functionBody - 函数体语句块
|
|
26
|
-
* @returns
|
|
38
|
+
* @returns 需要状态登记的变量名数组
|
|
27
39
|
*/
|
|
28
|
-
export declare function
|
|
29
|
-
/**
|
|
30
|
-
* 为函数体内的变量声明注入状态恢复代码
|
|
31
|
-
* 遍历函数体中的所有变量声明语句
|
|
32
|
-
* @param functionBody - 函数体语句块
|
|
33
|
-
*/
|
|
34
|
-
export declare function injectStatePreservation(functionBody: t.BlockStatement): void;
|
|
40
|
+
export declare function injectStatePreservation(functionBody: t.BlockStatement): string[];
|