前端路由hash & history
路由 hash history 前端
hash 模式
hash模式是基于location.hash来实现的,通过onhashchange事件监听 hash 改变,从而对页面进行跳转(渲染)
比如下面这个网站,它的 location.hash 的值为 #home:
https://www.xiejunyi.com#home
特点
- URL 中的 hash 值只是客户端的一种状态,服务器发出请求时,hash 部分不会被发送
- hash 值的改变,都会在浏览器历史中增加一个记录,因此我们可以通过浏览器的前进、后退控制 hash 的切换
- 可以通过 onhashchange 事件来监听 hash 的变化,render 页面
history 模式
history 模式通过 H5 中History API来实现 url 的变化。主要通过 pushState()和replaceState()这两个 api 结合popstate事件实现页面跳转(渲染)
history 模式与原 url 无异,不带#字符,比如下面这个网站
https://www.xiejunyi.com/home
特点
pushState、repalceState两个 API 来操作实现当前 URL 的变化- 通过
popstate事件来监听 URL 变化(点击浏览器前进、后退等操作时) pushState、replaceState不会触发popstate事件,需要手动触发页面跳转(渲染)
区别:
- hash 模式,#后的信息不会包含在 http 请求中,history 需要后端(nginx)进行适配,否则刷新会访问真实 url
- 兼容性:
hash兼容 IE8 以上、history兼容 IE10 以上 - 描述性:
hash只能添加字符串,history可以添加title、state等描述对象 - 局限性:
hash只能改变#后的字符串,history模式可以修改同源的任意 URL
手写实现:
hash 模式
class Router {
constructor() {
this.routers = []; //存放路由配置
}
add(route, callback) {
this.routers.push({
path: route,
render: callback,
});
}
listen(callback) {
window.onhashchange = this.hashChange(callback);
this.hashChange(callback)(); //首次进入页面的时候没有触发hashchange,要单独调用一下
}
hashChange(callback) {
let self = this;
return function () {
let hash = location.hash;
for (let i = 0; i < self.routers.length; i++) {
let route = self.routers[i];
if (hash === route.path) {
callback(route.render());
return;
}
}
};
}
}
let router = new Router();
router.add("#index", () => {
return "<h1>这是首页内容</h1>";
});
router.add("#article", () => {
return "<h1>这是文章内容</h1>";
});
router.add("#user", () => {
return "<h1>这是个人内容</h1>";
});
router.listen((renderHtml) => {
let app = document.getElementById("app");
app.innerHTML = renderHtml;
});
history 模式
class Router {
constructor() {
this.routers = [];
this.renderCallback = null;
}
add(route, callback) {
this.routers.push({
path: route,
render: callback,
});
}
pushState(path, data = {}) {
window.history.pushState(data, "", path);
this.renderHtml(path);
}
listen(callback) {
this.renderCallback = callback;
this.changeA();
window.onpopstate = () => this.renderHtml(this.getCurrentPath());
this.renderHtml(this.getCurrentPath());
}
changeA() {
document.addEventListener("click", (e) => {
if (e.target.nodeName === "A") {
e.preventDefault();
let path = e.target.getAttribute("href");
this.pushState(path);
}
});
}
getCurrentPath() {
return location.pathname;
}
renderHtml(path) {
for (let i = 0; i < this.routers.length; i++) {
let route = this.routers[i];
if (path === route.path) {
this.renderCallback(route.render());
return;
}
}
}
}
let router = new Router();
router.add("/index", () => {
return "<h1>这是首页内容</h1>";
});
router.add("/article", () => {
return "<h1>这是文章内容</h1>";
});
router.add("/user", () => {
return "<h1>这是个人内容</h1>";
});
router.listen((renderHtml) => {
let app = document.getElementById("app");
app.innerHTML = renderHtml;
});