feat(editor): 完善用户代码热更新和环境检测 (#275)

* fix: 更新 bundle-runtime 脚本使用正确的 platform-web 输出文件

原脚本引用的 runtime.browser.js 不存在,改为使用 index.mjs

* feat(editor): 完善用户代码热更新和环境检测

## 热更新改进
- 添加 hotReloadInstances() 方法,通过更新原型链实现真正的热更新
- 组件实例保留数据,仅更新方法
- ComponentRegistry 支持热更新时替换同名组件类

## 环境检测
- 启动时检测 esbuild 可用性
- 在启动页面底部显示环境状态指示器
- 添加 check_environment Rust 命令和前端 API

## esbuild 打包
- 将 esbuild 二进制文件打包到应用中
- 用户无需全局安装 esbuild
- 支持 Windows/macOS/Linux 平台

## 文件监视优化
- 添加 300ms 防抖,避免重复编译
- 修复路径分隔符混合问题

## 资源打包修复
- 修复 Tauri 资源配置,保留 engine 目录结构
- 添加 src-tauri/bin/ 和 src-tauri/engine/ 到 gitignore

* fix: 将热更新模式改为可选,修复测试失败

- ComponentRegistry 添加 hotReloadEnabled 标志,默认禁用
- 只有启用热更新模式时才会替换同名组件类
- 编辑器环境自动启用热更新模式
- reset() 方法中重置热更新标志

* test: 添加热更新模式的测试用例
This commit is contained in:
YHH
2025-12-05 14:24:09 +08:00
committed by GitHub
parent d7454e3ca4
commit 6702f0bfad
12 changed files with 755 additions and 57 deletions

View File

@@ -21,6 +21,14 @@ export class ComponentRegistry {
private static maskCache = new Map<string, BitMask64Data>();
private static nextBitIndex = 0;
/**
* 热更新模式标志,默认禁用
* Hot reload mode flag, disabled by default
* 编辑器环境应启用此选项以支持脚本热更新
* Editor environment should enable this to support script hot reload
*/
private static hotReloadEnabled = false;
/**
* 注册组件类型并分配位掩码
* @param componentType 组件类型
@@ -34,11 +42,27 @@ export class ComponentRegistry {
return existingIndex;
}
// 检查是否有同名但不同类的组件已注册
if (this.componentNameToType.has(typeName)) {
// 检查是否有同名但不同类的组件已注册(热更新场景)
// Check if a component with the same name but different class is registered (hot reload scenario)
if (this.hotReloadEnabled && this.componentNameToType.has(typeName)) {
const existingType = this.componentNameToType.get(typeName);
if (existingType !== componentType) {
console.warn(`[ComponentRegistry] Component name conflict: "${typeName}" already registered with different class. Existing: ${existingType?.name}, New: ${componentType.name}`);
// 热更新:替换旧的类为新的类,复用相同的 bitIndex
// Hot reload: replace old class with new class, reuse the same bitIndex
const existingIndex = this.componentTypes.get(existingType!)!;
// 移除旧类的映射
// Remove old class mapping
this.componentTypes.delete(existingType!);
// 用新类更新映射
// Update mappings with new class
this.componentTypes.set(componentType, existingIndex);
this.bitIndexToType.set(existingIndex, componentType);
this.componentNameToType.set(typeName, componentType);
console.log(`[ComponentRegistry] Hot reload: replaced component "${typeName}"`);
return existingIndex;
}
}
@@ -209,6 +233,32 @@ export class ComponentRegistry {
this.maskCache.clear();
}
/**
* 启用热更新模式
* Enable hot reload mode
* 在编辑器环境中调用以支持脚本热更新
* Call in editor environment to support script hot reload
*/
public static enableHotReload(): void {
this.hotReloadEnabled = true;
}
/**
* 禁用热更新模式
* Disable hot reload mode
*/
public static disableHotReload(): void {
this.hotReloadEnabled = false;
}
/**
* 检查热更新模式是否启用
* Check if hot reload mode is enabled
*/
public static isHotReloadEnabled(): boolean {
return this.hotReloadEnabled;
}
/**
* 重置注册表(用于测试)
*/
@@ -219,5 +269,6 @@ export class ComponentRegistry {
this.componentNameToId.clear();
this.maskCache.clear();
this.nextBitIndex = 0;
this.hotReloadEnabled = false;
}
}