feat(router): 重构路由模块并优化页面加载逻辑

- 引入 ScriptManager、TimerManager 和 EventManager 统一管理资源
- 路由配置支持默认重定向路径 '/'
- 移除旧版 scriptLoader 与 routeUtils,使用新的模块化加载机制
- 增加路由生命周期控制,自动清理定时器和事件监听器
- 改进点击代理逻辑,增强按钮激活状态切换
- 页面切换时卸载旧脚本及资源,防止内存泄漏
- 修复初始化日志冗余问题,提升代码可维护性
This commit is contained in:
雨霖铃
2025-12-09 22:22:31 +08:00
parent 2761dbe13f
commit 28fc3b409f
8 changed files with 282 additions and 199 deletions
+113
View File
@@ -0,0 +1,113 @@
export class ScriptManager {
constructor() {
this.currentScriptEl = null;
this.currentModule = null;
this.currentName = null;
}
async loadScript({ path, name, isModule = false, deps = [] }) {
// 卸载现脚本
await this.unloadScript();
for (const dep of deps) {
await this._appendScript({ path: dep, isModule: false });
}
if (isModule) {
const absPath = (path = '?t' + Date.now());
this.currentModule = await import(absPath);
this.currentScriptEl = null;
this.currentName = name;
return this.currentModule;
} else {
await this._appendScript({ path, isModule: false });
this.currentName = name;
return window[name] || null;
}
}
async _appendScript({ path, isModule }) {
const s = document.createElement('script');
try {
s.src = path + '?t=' + Date.now();
s.type = isModule ? 'module' : 'text/javascript';
s.async = false;
s.onload = () => {
this.currentScriptEl = s;
};
} catch (e) {
throw new Error(`Failed to load script: ${path}`);
} finally {
document.body.appendChild(s);
}
}
async unloadScript() {
try {
if (this.currentName && window[this.currentName]) {
const mod = window[this.currentName];
const unName = this._findUnmountName(mod);
if (unName && typeof mod[unName] === 'function') {
await mod[unName]();
}
}
if (this.currentModule) {
const mod = this.currentModule;
const unName = this._findUnmountName(mod);
if (unName && typeof mod[unName] === 'function') {
await mod[unName]();
}
}
} catch (e) {
console.error(e);
}
if (this.currentScriptEl) {
try {
this.currentScriptEl.remove();
} catch (e) {
console.error(e);
}
}
this.currentModule = null;
this.currentName = null;
}
_findInitName(obj) {
if (!obj) return null;
const candidates = ['init', 'start', 'main'];
for (const c of candidates) {
if (typeof obj[c] === 'function') {
return c;
}
}
return null;
}
_findUnmountName(obj) {
if (!obj) return null;
const candidates = ['unmount', 'stop', 'destroy'];
for (const c of candidates) {
if (typeof obj[c] === 'function') {
return c;
}
}
return null;
}
async runInit(name) {
if (this.currentModule) {
const mod = this.currentModule;
const init = this._findInitName(mod);
if (init) return mod[init]();
return null;
} else if (name && window[name]) {
const mod = window[name];
const init = this._findInitName(mod);
if (init) return mod[init]();
}
return null;
}
}