@vitarx/plugin-vite 1.0.1 → 1.0.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.
@@ -11,6 +11,11 @@ declare module 'vitarx' {
11
11
  [HMR.state]?: Record<string, any>;
12
12
  }
13
13
  }
14
+ /**
15
+ * HMR管理器
16
+ *
17
+ * 此管理器负责管理模块和组件的映射关系,并在模块更新时,更新对应的组件。
18
+ */
14
19
  export default class HMRManager {
15
20
  #private;
16
21
  /**
@@ -25,11 +30,11 @@ export default class HMRManager {
25
30
  */
26
31
  bindId(component: Function, id: string): void;
27
32
  /**
28
- * 置换新模块
33
+ * 置换新组件
29
34
  *
30
35
  * 此方法提供给`createView`函数调用,保持每次创建组件实例都是最新的模块!
31
36
  *
32
- * @param component
37
+ * @param component - 组件构造函数
33
38
  */
34
39
  resolveComponent(component: Component): Component;
35
40
  /**
@@ -49,10 +54,15 @@ export default class HMRManager {
49
54
  */
50
55
  register(view: ComponentView, component?: Component): void;
51
56
  /**
52
- * 获取模块id
57
+ * 获取组件的唯一id
53
58
  *
54
- * @param component
59
+ * @param component - 组件构造函数
55
60
  */
56
61
  getId(component: Component): string;
57
- update(mod: ModuleNamespace): void;
62
+ /**
63
+ * 模块更新
64
+ *
65
+ * @param newModule - 新模块对象
66
+ */
67
+ update(newModule: ModuleNamespace): void;
58
68
  }
@@ -4,9 +4,14 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
4
4
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
5
  };
6
6
  var _HMRManager_idMapToView, _HMRManager_idMapToComponent;
7
- import { isReactive, isRef } from 'vitarx';
7
+ import { isComponent, isReactive, isRef } from 'vitarx';
8
8
  import { HMR } from '../constants/index.js';
9
9
  import { processUpdate } from './update.js';
10
+ /**
11
+ * HMR管理器
12
+ *
13
+ * 此管理器负责管理模块和组件的映射关系,并在模块更新时,更新对应的组件。
14
+ */
10
15
  class HMRManager {
11
16
  constructor() {
12
17
  /**
@@ -49,11 +54,11 @@ class HMRManager {
49
54
  }
50
55
  }
51
56
  /**
52
- * 置换新模块
57
+ * 置换新组件
53
58
  *
54
59
  * 此方法提供给`createView`函数调用,保持每次创建组件实例都是最新的模块!
55
60
  *
56
- * @param component
61
+ * @param component - 组件构造函数
57
62
  */
58
63
  resolveComponent(component) {
59
64
  const id = this.getId(component);
@@ -95,38 +100,55 @@ class HMRManager {
95
100
  }
96
101
  }
97
102
  /**
98
- * 获取模块id
103
+ * 获取组件的唯一id
99
104
  *
100
- * @param component
105
+ * @param component - 组件构造函数
101
106
  */
102
107
  getId(component) {
103
108
  return Reflect.get(component, HMR.id);
104
109
  }
105
- update(mod) {
106
- if (!mod)
110
+ /**
111
+ * 模块更新
112
+ *
113
+ * @param newModule - 新模块对象
114
+ */
115
+ update(newModule) {
116
+ if (!newModule)
107
117
  return;
108
118
  try {
109
- for (const modKey in mod) {
110
- // 新模块
111
- const newComponent = mod[modKey];
112
- // 模块id
113
- const moduleId = this.getId(newComponent);
114
- // 模块ID不存在,跳过
115
- if (!moduleId)
119
+ const components = [];
120
+ const updatedView = new Set();
121
+ // 先遍历模块更新id->component映射
122
+ for (const modKey in newModule) {
123
+ // 新组件
124
+ const newComponent = newModule[modKey];
125
+ // 如果不是组件则跳过
126
+ if (!isComponent(newComponent))
116
127
  continue;
128
+ // 更新模块
129
+ const id = this.getId(newComponent);
130
+ if (id) {
131
+ __classPrivateFieldGet(this, _HMRManager_idMapToComponent, "f").set(id, newComponent);
132
+ components.push(newComponent);
133
+ }
134
+ }
135
+ for (const component of components) {
136
+ const id = this.getId(component);
117
137
  // 模块活跃的虚拟节点集合
118
- const nodes = __classPrivateFieldGet(this, _HMRManager_idMapToView, "f").get(moduleId);
119
- // 模块不存在,跳过
120
- if (!nodes)
138
+ const views = __classPrivateFieldGet(this, _HMRManager_idMapToView, "f").get(id);
139
+ if (!views)
121
140
  continue;
122
- for (const node of nodes) {
123
- // 更新模块
124
- const id = this.getId(newComponent);
125
- if (id)
126
- __classPrivateFieldGet(this, _HMRManager_idMapToComponent, "f").set(id, newComponent);
127
- // 更新节点视图
128
- if ((node.isActive || node.active) && node.isMounted) {
129
- processUpdate(node, newComponent);
141
+ // 遍历关联的所有视图,使其更新
142
+ for (const view of views) {
143
+ // 跳过已更新过的视图,避免同一次更新中同一个视图被更新多次
144
+ if (updatedView.has(view))
145
+ continue;
146
+ // 仅在视图是活跃状态,且已挂载时才更新
147
+ if (view.isActive && view.isMounted) {
148
+ // 处理视图更新
149
+ processUpdate(view, this.resolveComponent(view.component));
150
+ // 标记视图已更新
151
+ updatedView.add(view);
130
152
  }
131
153
  }
132
154
  }
@@ -4,7 +4,10 @@
4
4
  * @module passes/hmr/inject-imports
5
5
  */
6
6
  import * as t from '@babel/types';
7
- /** getComponentView 的内部别名,避免与用户代码冲突 */
7
+ /**
8
+ * getComponentView 的内部别名,使用特殊命名避免与用户代码冲突
9
+ * 格式:__$VITARX_XXX$__
10
+ */
8
11
  export declare const GET_COMPONENT_VIEW_ALIAS = "__$VITARX_GET_COMPONENT_VIEW$__";
9
12
  /**
10
13
  * 注入 HMR 客户端导入
@@ -5,14 +5,19 @@
5
5
  */
6
6
  import * as t from '@babel/types';
7
7
  import { HMR } from '../../constants/index.js';
8
- /** getComponentView 的内部别名,避免与用户代码冲突 */
8
+ /**
9
+ * getComponentView 的内部别名,使用特殊命名避免与用户代码冲突
10
+ * 格式:__$VITARX_XXX$__
11
+ */
9
12
  export const GET_COMPONENT_VIEW_ALIAS = '__$VITARX_GET_COMPONENT_VIEW$__';
10
13
  /**
11
14
  * 注入 HMR 客户端导入
12
15
  * @param program - AST Program 节点
13
16
  */
14
17
  export function injectHMRImport(program) {
18
+ // 创建 import 语句: import __$VITARX_HMR$__ from '@vitarx/vite-plugin/hmr-client'
15
19
  const importDecl = t.importDeclaration([t.importDefaultSpecifier(t.identifier(HMR.manager))], t.stringLiteral('@vitarx/vite-plugin/hmr-client'));
20
+ // 将导入语句插入到程序体最前面
16
21
  program.body.unshift(importDecl);
17
22
  }
18
23
  /**
@@ -21,6 +26,8 @@ export function injectHMRImport(program) {
21
26
  * @param program - AST Program 节点
22
27
  */
23
28
  export function injectGetComponentViewImport(program) {
29
+ // 创建 import 语句: import { getComponentView as __$VITARX_GET_COMPONENT_VIEW$__ } from 'vitarx'
24
30
  const importDecl = t.importDeclaration([t.importSpecifier(t.identifier(GET_COMPONENT_VIEW_ALIAS), t.identifier('getComponentView'))], t.stringLiteral('vitarx'));
31
+ // 将导入语句插入到程序体最前面
25
32
  program.body.unshift(importDecl);
26
33
  }
@@ -6,24 +6,29 @@
6
6
  import * as t from '@babel/types';
7
7
  /**
8
8
  * 创建组件函数体内的 HMR 注册代码
9
+ * 生成的代码包括:获取视图节点、注册到 HMR 管理器、保存状态
9
10
  * @param variableNames - 需要追踪的变量名列表
10
11
  * @returns HMR 注册语句数组
11
12
  */
12
13
  export declare function createHMRRegistrationStatements(variableNames: string[]): t.Statement[];
13
14
  /**
14
15
  * 创建包装组件的 HMR 注册代码
15
- * 只注册节点,不保存状态,传入组件函数自身作为第二个参数
16
+ * 包装组件(如 defineComponent、builder)只注册节点,不保存状态
17
+ * 传入组件函数自身作为第二个参数,便于更新时重新执行包装函数
16
18
  * @param componentName - 组件函数名称
17
19
  * @returns HMR 注册语句数组
18
20
  */
19
21
  export declare function createWrappedComponentHMRStatements(componentName: string): t.Statement[];
20
22
  /**
21
23
  * 从函数体中收集局部变量名
24
+ * 递归遍历函数体中的所有变量声明,用于状态恢复
22
25
  * @param functionBody - 函数体语句块
23
26
  * @returns 变量名数组
24
27
  */
25
28
  export declare function collectLocalVariableNames(functionBody: t.BlockStatement): string[];
26
29
  /**
27
30
  * 为函数体内的变量声明注入状态恢复代码
31
+ * 遍历函数体中的所有变量声明语句
32
+ * @param functionBody - 函数体语句块
28
33
  */
29
34
  export declare function injectStatePreservation(functionBody: t.BlockStatement): void;
@@ -9,16 +9,26 @@ import { collectPatternBindings } from '../../utils/index.js';
9
9
  import { GET_COMPONENT_VIEW_ALIAS } from './inject-imports.js';
10
10
  /**
11
11
  * 创建组件函数体内的 HMR 注册代码
12
+ * 生成的代码包括:获取视图节点、注册到 HMR 管理器、保存状态
12
13
  * @param variableNames - 需要追踪的变量名列表
13
14
  * @returns HMR 注册语句数组
14
15
  */
15
16
  export function createHMRRegistrationStatements(variableNames) {
16
17
  const statements = [];
18
+ // 语句1: const __$VITARX_HMR_VIEW_NODE$__ = __$VITARX_GET_COMPONENT_VIEW$__(true)
19
+ // 获取当前组件的视图节点
17
20
  statements.push(t.variableDeclaration('const', [
18
21
  t.variableDeclarator(t.identifier(HMR.view), t.callExpression(t.identifier(GET_COMPONENT_VIEW_ALIAS), [t.booleanLiteral(true)]))
19
22
  ]));
23
+ // 语句2: __$VITARX_HMR$__.instance.register(__$VITARX_HMR_VIEW_NODE$__)
24
+ // 将视图节点注册到 HMR 管理器
20
25
  statements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('register')), [t.identifier(HMR.view)])));
26
+ // 构建状态对象的 getter 属性,延迟求值避免不必要的计算
21
27
  const stateProperties = variableNames.map(name => t.objectMethod('get', t.identifier(name), [], t.blockStatement([t.returnStatement(t.identifier(name))])));
28
+ // 语句3: __$VITARX_HMR_VIEW_NODE$__ && Promise.resolve().then(() => {
29
+ // __$VITARX_HMR_VIEW_NODE$__.__$vitarx_hmr_state$__ = { get 变量名() { return 变量名 } }
30
+ // })
31
+ // 异步保存状态到视图节点,确保 DOM 更新完成后再保存
22
32
  statements.push(t.expressionStatement(t.logicalExpression('&&', t.identifier(HMR.view), t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.identifier('Promise'), t.identifier('resolve')), []), t.identifier('then')), [
23
33
  t.arrowFunctionExpression([], t.blockStatement([
24
34
  t.expressionStatement(t.assignmentExpression('=', t.memberExpression(t.identifier(HMR.view), t.identifier(HMR.state)), t.objectExpression(stateProperties)))
@@ -28,20 +38,25 @@ export function createHMRRegistrationStatements(variableNames) {
28
38
  }
29
39
  /**
30
40
  * 创建包装组件的 HMR 注册代码
31
- * 只注册节点,不保存状态,传入组件函数自身作为第二个参数
41
+ * 包装组件(如 defineComponent、builder)只注册节点,不保存状态
42
+ * 传入组件函数自身作为第二个参数,便于更新时重新执行包装函数
32
43
  * @param componentName - 组件函数名称
33
44
  * @returns HMR 注册语句数组
34
45
  */
35
46
  export function createWrappedComponentHMRStatements(componentName) {
36
47
  const statements = [];
48
+ // 语句1: const __$VITARX_HMR_VIEW_NODE$__ = __$VITARX_GET_COMPONENT_VIEW$__(true)
37
49
  statements.push(t.variableDeclaration('const', [
38
50
  t.variableDeclarator(t.identifier(HMR.view), t.callExpression(t.identifier(GET_COMPONENT_VIEW_ALIAS), [t.booleanLiteral(true)]))
39
51
  ]));
52
+ // 语句2: __$VITARX_HMR$__.instance.register(__$VITARX_HMR_VIEW_NODE$__, Component)
53
+ // 第二个参数传入组件函数,支持热更新时重新执行
40
54
  statements.push(t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('register')), [t.identifier(HMR.view), t.identifier(componentName)])));
41
55
  return statements;
42
56
  }
43
57
  /**
44
58
  * 从函数体中收集局部变量名
59
+ * 递归遍历函数体中的所有变量声明,用于状态恢复
45
60
  * @param functionBody - 函数体语句块
46
61
  * @returns 变量名数组
47
62
  */
@@ -50,20 +65,35 @@ export function collectLocalVariableNames(functionBody) {
50
65
  collectFromBlock(functionBody, variableNames);
51
66
  return Array.from(variableNames);
52
67
  }
68
+ /**
69
+ * 从语句块中收集变量名
70
+ * @param block - 语句块节点
71
+ * @param variableNames - 存储变量名的集合
72
+ */
53
73
  function collectFromBlock(block, variableNames) {
74
+ // 遍历语句块中的每个语句
54
75
  for (const stmt of block.body) {
55
76
  collectFromStatement(stmt, variableNames);
56
77
  }
57
78
  }
79
+ /**
80
+ * 从单个语句中收集变量名
81
+ * 支持多种语句类型:变量声明、条件语句、循环语句、switch、try-catch 等
82
+ * @param stmt - 语句节点
83
+ * @param variableNames - 存储变量名的集合
84
+ */
58
85
  function collectFromStatement(stmt, variableNames) {
86
+ // 处理变量声明语句:const/let/var
59
87
  if (stmt.type === 'VariableDeclaration') {
60
88
  for (const decl of stmt.declarations) {
89
+ // 跳过 void 模式(如 void 0)
61
90
  if (decl.id.type !== 'VoidPattern') {
62
91
  collectPatternBindings(decl.id, variableNames);
63
92
  }
64
93
  }
65
94
  }
66
95
  else if (stmt.type === 'IfStatement') {
96
+ // 递归处理 if 语句的两个分支
67
97
  if (stmt.consequent.type === 'BlockStatement') {
68
98
  collectFromBlock(stmt.consequent, variableNames);
69
99
  }
@@ -80,6 +110,7 @@ function collectFromStatement(stmt, variableNames) {
80
110
  }
81
111
  }
82
112
  else if (stmt.type === 'ForStatement' || stmt.type === 'WhileStatement' || stmt.type === 'DoWhileStatement') {
113
+ // 处理 for/while/do-while 循环
83
114
  if (stmt.body.type === 'BlockStatement') {
84
115
  collectFromBlock(stmt.body, variableNames);
85
116
  }
@@ -88,6 +119,7 @@ function collectFromStatement(stmt, variableNames) {
88
119
  }
89
120
  }
90
121
  else if (stmt.type === 'ForInStatement' || stmt.type === 'ForOfStatement') {
122
+ // 处理 for...in/for...of 循环
91
123
  if (stmt.body.type === 'BlockStatement') {
92
124
  collectFromBlock(stmt.body, variableNames);
93
125
  }
@@ -96,9 +128,11 @@ function collectFromStatement(stmt, variableNames) {
96
128
  }
97
129
  }
98
130
  else if (stmt.type === 'BlockStatement') {
131
+ // 处理嵌套语句块
99
132
  collectFromBlock(stmt, variableNames);
100
133
  }
101
134
  else if (stmt.type === 'SwitchStatement') {
135
+ // 处理 switch 语句,遍历所有 case
102
136
  for (const c of stmt.cases) {
103
137
  for (const s of c.consequent) {
104
138
  collectFromStatement(s, variableNames);
@@ -106,6 +140,7 @@ function collectFromStatement(stmt, variableNames) {
106
140
  }
107
141
  }
108
142
  else if (stmt.type === 'TryStatement') {
143
+ // 处理 try-catch-finally
109
144
  collectFromBlock(stmt.block, variableNames);
110
145
  if (stmt.handler) {
111
146
  collectFromBlock(stmt.handler.body, variableNames);
@@ -117,35 +152,53 @@ function collectFromStatement(stmt, variableNames) {
117
152
  }
118
153
  /**
119
154
  * 判断表达式是否为函数类型
155
+ * 函数类型不需要状态恢复(函数定义本身不会改变)
156
+ * @param expr - 表达式节点
157
+ * @returns 是否为函数类型
120
158
  */
121
159
  function isFunctionExpression(expr) {
122
160
  if (!expr)
123
161
  return false;
124
- return (expr.type === 'ArrowFunctionExpression' ||
125
- expr.type === 'FunctionExpression' ||
126
- expr.type === 'ClassExpression');
162
+ return (expr.type === 'ArrowFunctionExpression' || // 箭头函数
163
+ expr.type === 'FunctionExpression' || // 函数表达式
164
+ expr.type === 'ClassExpression' // 类表达式
165
+ );
127
166
  }
128
167
  /**
129
168
  * 创建状态恢复表达式
130
169
  * 格式:__$VITARX_HMR$__.instance.memo(__$VITARX_HMR_VIEW_NODE$__, '变量名') ?? 原始初始值
170
+ * 如果 memo 方法返回保存的状态则使用,否则使用原始初始值
171
+ * @param variableName - 变量名
172
+ * @param originalInit - 原始初始化表达式
173
+ * @returns 状态恢复表达式
131
174
  */
132
175
  function createMemoExpression(variableName, originalInit) {
133
- return t.logicalExpression('??', t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('memo')), [t.identifier(HMR.view), t.stringLiteral(variableName)]), originalInit);
176
+ return t.logicalExpression('??', // 使用空值合并运算符
177
+ t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('memo')), [t.identifier(HMR.view), t.stringLiteral(variableName)]), originalInit // 原始初始值作为 fallback
178
+ );
134
179
  }
135
180
  /**
136
181
  * 为单个变量声明注入状态恢复代码
182
+ * 将 const x = value 转换为 const x = memo(view, 'x') ?? value
183
+ * @param decl - 变量声明节点
137
184
  */
138
185
  function injectStatePreservationForDeclaration(decl) {
186
+ // 只处理标识符形式的变量名(跳过解构赋值)
139
187
  if (decl.id.type !== 'Identifier')
140
188
  return;
189
+ // 跳过没有初始值的声明
141
190
  if (!decl.init)
142
191
  return;
192
+ // 跳过函数类型的初始值(不需要状态恢复)
143
193
  if (isFunctionExpression(decl.init))
144
194
  return;
195
+ // 替换初始值为状态恢复表达式
145
196
  decl.init = createMemoExpression(decl.id.name, decl.init);
146
197
  }
147
198
  /**
148
199
  * 为函数体内的变量声明注入状态恢复代码
200
+ * 遍历函数体中的所有变量声明语句
201
+ * @param functionBody - 函数体语句块
149
202
  */
150
203
  export function injectStatePreservation(functionBody) {
151
204
  for (const stmt of functionBody.body) {
@@ -10,6 +10,6 @@ import type { ComponentInfo } from '../../utils/index.js';
10
10
  * 主入口函数,为所有组件注入完整的 HMR 支持
11
11
  * @param program - AST Program 节点
12
12
  * @param components - 组件信息列表
13
- * @param filename - 文件名
13
+ * @param filename - 文件名(用于生成组件唯一 ID)
14
14
  */
15
15
  export declare function injectHMRSupport(program: t.Program, components: ComponentInfo[], filename: string): void;
@@ -11,34 +11,47 @@ import { createHMRRegistrationStatements, createWrappedComponentHMRStatements, c
11
11
  * 为组件函数注入 HMR 注册代码
12
12
  * @param func - 函数声明/表达式/箭头函数
13
13
  * @param variableNames - 需要追踪的变量名列表
14
- * @param isWrapped - 是否为包装组件
14
+ * @param isWrapped - 是否为包装组件(defineComponent/builder)
15
15
  * @param componentName - 组件名称(包装组件需要)
16
16
  */
17
17
  function injectHMRIntoFunction(func, variableNames, isWrapped = false, componentName) {
18
+ // 如果是箭头函数且函数体不是块语句(单行箭头函数),转换为块语句
18
19
  if (func.type === 'ArrowFunctionExpression' && func.body.type !== 'BlockStatement') {
19
20
  const returnStmt = t.returnStatement(func.body);
20
21
  func.body = t.blockStatement([returnStmt]);
21
22
  }
23
+ // 确保函数体是块语句
22
24
  if (!func.body || func.body.type !== 'BlockStatement')
23
25
  return;
26
+ // 非包装组件需要注入状态恢复代码
24
27
  if (!isWrapped) {
25
28
  injectStatePreservation(func.body);
26
29
  }
30
+ // 根据组件类型创建对应的 HMR 注册语句
27
31
  const statements = isWrapped
28
32
  ? createWrappedComponentHMRStatements(componentName)
29
33
  : createHMRRegistrationStatements(variableNames);
34
+ // 将注册语句插入到函数体开头
30
35
  func.body.body.unshift(...statements);
31
36
  }
32
37
  /**
33
38
  * 创建 bindId 语句
39
+ * 将组件名称与唯一 ID 绑定,用于热更新时识别组件
40
+ * @param componentName - 组件名称
41
+ * @param componentId - 组件唯一 ID
42
+ * @returns bindId 调用语句
34
43
  */
35
44
  function createBindIdStatement(componentName, componentId) {
45
+ // 生成:__$VITARX_HMR$__.instance.bindId(Component, 'hash')
36
46
  return t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('bindId')), [t.identifier(componentName), t.stringLiteral(componentId)]));
37
47
  }
38
48
  /**
39
49
  * 创建 import.meta.hot.accept 语句
50
+ * 监听模块热更新事件,触发组件更新
51
+ * @returns hot.accept 调用语句
40
52
  */
41
53
  function createHotAcceptStatement() {
54
+ // 生成:import.meta.hot.accept(mod => { __$VITARX_HMR$__.instance.update(mod) })
42
55
  return t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.memberExpression(t.identifier('import'), t.identifier('meta')), t.identifier('hot')), t.identifier('accept')), [
43
56
  t.arrowFunctionExpression([t.identifier('mod')], t.blockStatement([
44
57
  t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier(HMR.manager), t.identifier('instance')), t.identifier('update')), [t.identifier('mod')]))
@@ -47,14 +60,20 @@ function createHotAcceptStatement() {
47
60
  }
48
61
  /**
49
62
  * 生成组件唯一 ID(文件路径 + 组件名称)
63
+ * 使用 FNV-1a 哈希算法生成短哈希值
64
+ * @param filename - 文件路径
65
+ * @param componentName - 组件名称
66
+ * @returns 组件唯一 ID(十六进制字符串)
50
67
  */
51
68
  function generateComponentId(filename, componentName) {
52
69
  const combined = `${filename}:${componentName}`;
70
+ // FNV-1a 哈希算法初始值
53
71
  let hash = 2166136261;
54
72
  for (let i = 0; i < combined.length; i++) {
55
73
  hash ^= combined.charCodeAt(i);
56
74
  hash = Math.imul(hash, 16777619);
57
75
  }
76
+ // 转换为无符号整数并转为十六进制
58
77
  return (hash >>> 0).toString(16);
59
78
  }
60
79
  /**
@@ -62,19 +81,25 @@ function generateComponentId(filename, componentName) {
62
81
  * 主入口函数,为所有组件注入完整的 HMR 支持
63
82
  * @param program - AST Program 节点
64
83
  * @param components - 组件信息列表
65
- * @param filename - 文件名
84
+ * @param filename - 文件名(用于生成组件唯一 ID)
66
85
  */
67
86
  export function injectHMRSupport(program, components, filename) {
87
+ // 如果没有组件,直接返回
68
88
  if (components.length === 0)
69
89
  return;
90
+ // 步骤1: 注入 HMR 相关的 import 语句
70
91
  injectHMRImport(program);
71
92
  injectGetComponentViewImport(program);
93
+ // 步骤2: 为每个组件函数注入 HMR 注册代码和状态恢复
72
94
  for (const { node, isWrapped, name } of components) {
95
+ // 收集函数体内的局部变量名(用于状态保存)
73
96
  const variableNames = node.body?.type === 'BlockStatement' ? collectLocalVariableNames(node.body) : [];
74
97
  injectHMRIntoFunction(node, variableNames, isWrapped, name);
75
98
  }
99
+ // 步骤3: 为每个组件创建 bindId 语句(用于热更新时识别)
76
100
  for (const { name } of components) {
77
101
  program.body.push(createBindIdStatement(name, generateComponentId(filename, name)));
78
102
  }
103
+ // 步骤4: 创建 import.meta.hot.accept 语句(监听热更新事件)
79
104
  program.body.push(createHotAcceptStatement());
80
105
  }
@@ -5,16 +5,16 @@
5
5
  */
6
6
  import * as t from '@babel/types';
7
7
  /**
8
- * 收集现有导入信息
9
- * 只收集普通的 import 语句,跳过 import type 语句
8
+ * 收集现有 vitarx 导入信息
9
+ * 只收集普通的 import 语句,跳过 import type/typeof 语句
10
10
  * @param program - AST Program 节点
11
- * @returns 本地变量名集合和 vitarx 导入映射
11
+ * @returns 映射表:导入名 -> 本地别名
12
12
  */
13
13
  export declare function collectExistingImports(program: t.Program): Map<string, string>;
14
14
  /**
15
15
  * 收集本地变量绑定
16
16
  * 包括导入绑定、变量声明、函数声明、类声明
17
- * 只收集运行时存在的绑定,跳过 import type
17
+ * 只收集运行时存在的绑定,跳过 import type/typeof
18
18
  * @param program - AST Program 节点
19
19
  * @returns 本地变量名集合
20
20
  */
@@ -1,26 +1,34 @@
1
1
  import { VITARX_MODULE } from '../../constants/index.js';
2
2
  import { collectPatternBindings } from '../../utils/index.js';
3
3
  /**
4
- * 收集现有导入信息
5
- * 只收集普通的 import 语句,跳过 import type 语句
4
+ * 收集现有 vitarx 导入信息
5
+ * 只收集普通的 import 语句,跳过 import type/typeof 语句
6
6
  * @param program - AST Program 节点
7
- * @returns 本地变量名集合和 vitarx 导入映射
7
+ * @returns 映射表:导入名 -> 本地别名
8
8
  */
9
9
  export function collectExistingImports(program) {
10
+ // 存储导入名到本地别名的映射
10
11
  const vitarxImports = new Map();
12
+ // 遍历程序体中的所有节点
11
13
  for (const node of program.body) {
14
+ // 跳过非导入声明节点
12
15
  if (node.type !== 'ImportDeclaration')
13
16
  continue;
17
+ // 跳过类型导入(不影响运行时)
14
18
  if (node.importKind === 'type' || node.importKind === 'typeof')
15
19
  continue;
20
+ // 只处理 vitarx 模块的导入
16
21
  const source = node.source.value;
17
22
  if (source !== VITARX_MODULE)
18
23
  continue;
24
+ // 遍历导入说明符
19
25
  for (const specifier of node.specifiers) {
20
26
  if (specifier.type === 'ImportSpecifier') {
27
+ // 获取导入的原始名称(支持字符串字面量和标识符)
21
28
  const importedName = specifier.imported.type === 'Identifier'
22
29
  ? specifier.imported.name
23
30
  : specifier.imported.value;
31
+ // 记录导入名到本地别名的映射
24
32
  vitarxImports.set(importedName, specifier.local.name);
25
33
  }
26
34
  }
@@ -30,31 +38,38 @@ export function collectExistingImports(program) {
30
38
  /**
31
39
  * 收集本地变量绑定
32
40
  * 包括导入绑定、变量声明、函数声明、类声明
33
- * 只收集运行时存在的绑定,跳过 import type
41
+ * 只收集运行时存在的绑定,跳过 import type/typeof
34
42
  * @param program - AST Program 节点
35
43
  * @returns 本地变量名集合
36
44
  */
37
45
  export function collectLocalBindings(program) {
38
46
  const bindings = new Set();
47
+ // 遍历程序体中的所有节点
39
48
  for (const node of program.body) {
49
+ // 处理导入声明
40
50
  if (node.type === 'ImportDeclaration') {
41
- // 跳过 import type 和 import typeof 语句
51
+ // 跳过类型导入(不影响运行时绑定)
42
52
  if (node.importKind === 'type' || node.importKind === 'typeof')
43
53
  continue;
54
+ // 收集所有导入的本地名称
44
55
  for (const specifier of node.specifiers) {
45
56
  bindings.add(specifier.local.name);
46
57
  }
47
58
  }
59
+ // 处理变量声明(支持解构赋值)
48
60
  if (node.type === 'VariableDeclaration') {
49
61
  for (const decl of node.declarations) {
62
+ // 跳过 void 模式(如 void 0)
50
63
  if (decl.id.type !== 'VoidPattern') {
51
64
  collectPatternBindings(decl.id, bindings);
52
65
  }
53
66
  }
54
67
  }
68
+ // 处理函数声明
55
69
  if (node.type === 'FunctionDeclaration' && node.id) {
56
70
  bindings.add(node.id.name);
57
71
  }
72
+ // 处理类声明
58
73
  if (node.type === 'ClassDeclaration' && node.id) {
59
74
  bindings.add(node.id.name);
60
75
  }
@@ -14,7 +14,7 @@ import type { RefApiAliases } from '../../context.js';
14
14
  export declare function collectRefApiAliases(program: t.Program): RefApiAliases;
15
15
  /**
16
16
  * 收集通过 ref API 定义的变量
17
- * 包括直接赋值和 toRefs 解构
17
+ * 包括直接赋值(const x = ref(0))和 toRefs 解构(const { a, b } = toRefs(obj))
18
18
  * @param program - AST Program 节点
19
19
  * @param refApiAliases - ref API 别名映射
20
20
  * @returns ref 变量名集合
@@ -22,7 +22,7 @@ export declare function collectRefApiAliases(program: t.Program): RefApiAliases;
22
22
  export declare function collectRefVariables(program: t.Program, refApiAliases: RefApiAliases): Set<string>;
23
23
  /**
24
24
  * 收集确定不是 ref 的变量
25
- * 包括函数声明、箭头函数和函数表达式变量
25
+ * 包括函数声明、箭头函数和函数表达式变量(这些不可能是 ref)
26
26
  * @param program - AST Program 节点
27
27
  * @returns 非 ref 变量名集合
28
28
  */