前端路由原理深度解析:从hash到history API
今天咱们聊聊前端路由那点事儿。作为一个在前后端反复横跳的老司机,全栈老李发现很多同学对路由的理解还停留在”会用vue-router”的层面,这就像只会开车却不懂发动机原理一样危险啊!
为什么需要前端路由?
想象一下你去餐厅吃饭,传统服务端渲染就像每次点菜都要服务员跑后厨问厨师(服务器请求页面),而前端路由则是服务员直接把菜单(数据)给你,你在桌上自己拼装(客户端渲染)。这样切换”菜品”(页面)时就不用每次都麻烦厨师了。
Hash模式:最朴素的解决方案
最早的前端路由方案是hash路由,它的原理简单粗暴:
// 全栈老李提示:这是最基础的hash路由实现
class HashRouter {
constructor() {
this.routes = {
};
window.addEventListener('hashchange', this.refresh);
}
addRoute(path, callback) {
this.routes[path] = callback;
}
refresh = () => {
const hash = window.location.hash.slice(1) || '/';
if (this.routes[hash]) {
this.routes[hash]();
}
}
}
// 使用示例
const router = new HashRouter();
router.addRoute('/home', () => {
document.getElementById('app').innerHTML = '<h1>首页</h1>';
});
router.addRoute('/about', () => {
document.getElementById('app').innerHTML = '<h1>关于我们</h1>';
});
hash路由的特点:
URL中带#,比如example.com/#/about
改变hash不会触发页面刷新
兼容性好,连IE6都能用
但它的缺点也很明显:URL不够优雅,SEO不友好,而且#后面的内容服务器根本收不到。这就好比用摩斯密码谈恋爱——能沟通但不够体面。
History API:现代前端路由的标配
HTML5带来了History API,终于让我们能优雅地操作URL了。核心是这三个方法:
// 全栈老李的history路由实现示例
class HistoryRouter {
constructor() {
this.routes = {
};
this.init();
}
init() {
window.addEventListener('popstate', this.handlePopState);
document.addEventListener('click', this.handleLinkClick);
}
addRoute(path, callback) {
this.routes[path] = callback;
}
navigate(path) {
history.pushState({
}, '', path);
this.dispatch(path);
}
handlePopState = () => {
const path = window.location.pathname;
this.dispatch(path);
}
handleLinkClick = (e) => {
if (e.target.tagName === 'A') {
e.preventDefault();
this.navigate(e.target.getAttribute('href'));
}
}
dispatch(path) {
if (this.routes[path]) {
this.routes[path]();
}
}
}
// 使用示例
const router = new HistoryRouter();
router.addRoute('/home', () => {
document.getElementById('app').innerHTML = '<h1>首页</h1>';
});
router.addRoute('/about', () => {
document.getElementById('app').innerHTML = '<h1>关于我们</h1>';
});
History路由的三大法宝:
history.pushState() – 添加历史记录但不刷新页面
history.replaceState() – 替换当前历史记录
popstate事件 – 监听前进后退操作
这就好比给你的网页装上了时光机,可以自由穿梭还不刷新页面。但要注意的是,直接访问history路由的URL会404,因为服务器没有对应的静态资源。解决方案嘛,要么让服务器把所有路由都指向index.html(SSR应用常用),要么配置nginx重定向。
实战中的路由守卫
现代框架的路由都提供了守卫功能,咱们可以自己实现个简易版:
// 全栈老李的路由守卫实现
class GuardedRouter extends HistoryRouter {
constructor(beforeEach) {
super();
this.beforeEach = beforeEach;
}
navigate(path) {
this.beforeEach(path, () => {
super.navigate(path);
});
}
}
// 使用示例
const router = new GuardedRouter((to, next) => {
console.log(`准备跳转到 ${
to}`);
if (to === '/admin') {
alert('无权访问!');
} else {
next();
}
});
这个守卫就像夜店门口的保安,不符合条件的统统拦在外面!
面试题时间
来道实战题检验下学习成果(评论区交作业,老李会随机点评哦):
/**
* 实现一个简易路由解析器
* 要求:
* 1. 支持动态参数 /user/:id
* 2. 返回匹配的参数对象
* 示例:
* const router = new Router();
* router.addRoute('/user/:id', (params) => {
* console.log(params.id);
* });
* router.dispatch('/user/123'); // 输出123
*
* 全栈老李提示:可以用正则表达式匹配路径
*/
class Router {
constructor() {
this.routes = [];
}
addRoute(path, handler) {
// 你的代码 here
}
dispatch(currentPath) {
// 你的代码 here
}
}
路由选择的艺术
最后聊聊如何选择路由方案:
需要兼容老浏览器?选hash
要做SSR?history是必须的
担心SEO?history+服务端渲染
移动端Hybrid App?hash可能更稳妥
记住全栈老李的忠告:技术选型没有银弹,只有最适合当前场景的方案。就像你不能用筷子吃牛排,也不能用刀叉喝汤对吧?
下次咱们聊聊路由的进阶玩法——路由懒加载和预加载,让你的应用飞起来!评论区见~
🔥 必看面试题
【初级】前端开发工程师面试100题(一)
【初级】前端开发工程师面试100题(二)
【初级】前端开发工程师的面试100题(速记版)
我是全栈老李,一个资深Coder!
写码不易,如果你觉得本文有收获,点赞 + 收藏走一波!感谢鼓励🌹🌹🌹

















暂无评论内容