import { getParamsUrl, closure, genKey } from '../utils/routeUtils.js';
import { addClass, removeClass } from '../utils/domUtils.js';
import { loadScript } from '../utils/scriptLoader.js';
class Router {
constructor() {
this.routes = {};
this.beforeFun = null;
this.afterFun = null;
this.routerViewId = 'router-view';
this.redirectRoute = null;
this.stackPages = true;
this.routerMap = [];
this.historyFlag = '';
this.history = [];
}
/**
* 初始化路由
*/
init(config) {
this.routerMap = config?.routes || this.routerMap;
this.routerViewId = config?.routerViewId || this.routerViewId;
this.stackPages = config?.stackPages ?? this.stackPages;
// 映射路由表
this.map();
window.linkTo = (path) => {
if (path.includes('?')) {
location.hash = `${path}&key=${genKey()}`;
} else {
location.hash = `${path}?key=${genKey()}`;
}
};
window.addEventListener('load', (e) => {
this.historyChange(e);
this.updateActiveButton(e);
});
window.addEventListener('hashchange', (e) => {
this.historyChange(e);
this.updateActiveButton(e);
});
}
async loadPage(route) {
console.log('加载页面:', route);
const html = await fetch(route.html).then((r) => r.text());
document.getElementById(this.routerViewId).innerHTML = html;
const initName = route.name + 'Init';
await loadScript(route.script, initName);
if (window[initName] && route.script) {
await window[initName]();
}
}
/**
* 加载 HTML 文件
*/
async loadHTML(htmlPath) {
try {
const resp = await fetch(htmlPath);
if (!resp.ok) throw new Error(`HTML 加载失败: ${resp.status}`);
return await resp.text();
} catch (err) {
return `
页面加载失败:${err.message}
`;
}
}
/**
* 处理路由历史变化
*/
historyChange(event) {
const { path, query } = getParamsUrl();
console.log('路由变化page{}, query{}', path, query);
this.urlChange();
}
/**
* 路由解析与渲染
*/
urlChange() {
const currentHash = getParamsUrl();
const { path, query } = currentHash;
if (!this.routes[path]) {
location.hash = this.redirectRoute;
return;
}
const next = () => this.changeView(currentHash);
if (this.beforeFun) {
this.beforeFun({ to: { path, query }, next });
} else {
next();
}
}
/**
* 渲染页面
*/
async changeView(currentHash) {
const { path } = currentHash;
const route = await this.routes[path];
const mountEl = document.getElementById(this.routerViewId);
if (!mountEl) {
console.error('挂载点不存在:', this.routerViewId);
return;
}
console.log('路由:', route);
this.loadPage(route);
// 执行 after 钩子
if (this.afterFun) this.afterFun(currentHash);
}
/**
* 构建路由映射
*/
map() {
for (let r of this.routerMap) {
if (r.name === 'redirect') {
this.redirectRoute = r.path;
} else if (!this.redirectRoute) {
this.redirectRoute = this.routerMap[0].path;
}
this.routes[r.path] = {
name: r.name,
html: r.html,
script: r.script,
};
}
}
/**
* before 钩子
*/
beforeEach(callback) {
if (typeof callback === 'function') {
this.beforeFun = callback;
} else {
console.trace('beforeEach 必须是函数');
}
}
/**
* after 钩子
*/
afterEach(callback) {
if (typeof callback === 'function') {
this.afterFun = callback;
} else {
console.trace('afterEach 必须是函数');
}
}
updateActiveButton(event) {
const currentRoute = location.hash.replace('#', '') || '/home';
document.querySelectorAll('[data-route]').forEach((btn) => {
const route = btn.getAttribute('data-route');
if (route === currentRoute) {
console.log('当前路由:', route);
addClass(btn, 'active');
} else {
removeClass(btn, 'active');
}
});
}
}
export { Router };