🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 前端路由 ## Hash **通过使用hash+hashChange实现** 我们知道,使用location.hash方法返回的是url中#后面的部分,比如说: ~~~ // https://www.baidu.com/#hello在窗口中输入 location.hash // 返回#hello ~~~ 这样我们知道,只要触发了hashchange方法,我们就可以监听一些变化。可以使用a标签或者直接使用js对location.hash进行操作来触发change方法。 在下面是一个最简单的Router实现。 ~~~ class Routers { constructor() { this.routes = {}; this.currentUrl = ''; this.refresh = this.refresh.bind(this); window.addEventListener('load', this.refresh, false); window.addEventListener('hashchange', this.refresh, false); } route(path, callback) { this.routes[path] = callback || function () { }; } refresh() { this.currentUrl = location.hash.slice(1) || '/'; this.routes[this.currentUrl](); console.log(this.routes); } } ~~~ ```html <body> <ul> <li> <a href="#/">turn yellow</a> </li> <li> <a href="#/blue">turn blue</a> </li> <li> <a href="#/green">turn green</a> </li> </ul> </body> ``` 在这个html中,我们实例化Router,就可以得到一个存有不同路由对应回调的routers对象。如下: ~~~ window.Router = new Routers(); var content = document.querySelector('body'); // change Page anything function changeBgColor(color) { content.style.backgroundColor = color; } Router.route('/', function () { changeBgColor('yellow'); }); Router.route('/blue', function () { changeBgColor('blue'); }); Router.route('/green', function () { changeBgColor('green'); }); ~~~ 这样我们点击不同的a标签时就可以触发不同路由的回调,对dom进行改变。 带有回退功能的router: ~~~ class Routers { constructor() { this.routes = {}; this.currentUrl = ''; this.history = []; this.currentIndex = this.history.length - 1; this.refresh = this.refresh.bind(this); this.isBack = false; window.addEventListener('load', this.refresh, false); window.addEventListener('hashchange', this.refresh, false); } route(path, callback) { this.routes[path] = callback || function () { }; } refresh() { this.currentUrl = location.hash.slice(1) || '/'; if (!this.isBack) { if (this.currentIndex < this.history.length - 1) this.history = this.history.slice(0, this.currentIndex + 1); this.history.push(this.currentUrl); this.currentIndex++; } this.routes[this.currentUrl](); console.log(this.history); this.isBack = false; } backOff() { this.isBack = true; this.currentIndex <= 0 ? (this.currentIndex = 0) : (this.currentIndex = this.currentIndex - 1); location.hash = `#${this.history[this.currentIndex]}` this.routes[this.history[this.currentIndex]](); } } ~~~ ## Hisotry 第一个实现形式是靠History API实现的。主要方法有history.pushState 和 history.replaceState。 #### history.pushState history.pushState可以传入三个参数:状态对象 标题 与URL。 状态对象:一个与指定网址相关的状态对象,popstate事件触发时,该对象会传入回调函数。如果不需要这个对象,此处可以填null。 **URL参数实现无刷新改变URL** 我们可以在浏览器中做这个实验。输入: ~~~ window.history.pushState(null,null,'https://www.baidu.com/?name=dodf') ~~~ 可以发现浏览器的url变成了https://www.baidu.com/?name=dodf。 页面并没有刷新,但是url存到了浏览器历史中。通过这个第三个参数,我们可以实现浏览器无刷新的情况下改变url。 #### history.replaceState 替换history,也是不发生跳转。 #### history.popState 在history发生变化时触发。 > 仅仅调用pushState方法或replaceState方法 ,并不会触发该事件,只有用户点击浏览器倒退按钮和前进按钮,或者使用 JavaScript 调用back、forward、go方法时才会触发。 > 简单实现如下: ~~~ class Routers { constructor() { this.routes = {}; this._bindPopState(); } init(path) { history.replaceState({ path: path }, null, path); this.routes[path] && this.routes[path](); } route(path, callback) { this.routes[path] = callback || function () { }; } go(path) { history.pushState({ path: path }, null, path); this.routes[path] && this.routes[path](); } _bindPopState() { window.addEventListener('popstate', e => { const path = e.state && e.state.path; this.routes[path] && this.routes[path](); }); } } ~~~ ## 参考 [面试官: 你了解前端路由吗?](https://juejin.im/post/5ac61da66fb9a028c71eae1b#comment) [前端路由](http://blog.xiaoboma.com/2017/01/24/%E5%89%8D%E7%AB%AF%E8%B7%AF%E7%94%B1/)