hash這個玩意是地址欄上#及後面部分,表明網頁中的一個位置,#後面部分爲位置標識符。頁面打開後,會自動滾動到指定位置處。
位置標識符 ,一是使用錨點,好比<a name="demo"></a>
,二是使用id屬性,好比 <span id="demo" ></span>
javascript
當打開http://www.example.com/#print服務器實際收到的請求地址是http://www.example.com/,是不帶hash值的。
那麼你真想帶#字符咋辦,轉義啊, #轉義字符爲%23。也許有人會說,我咋知道這個轉義啊,呵呵噠encodeURIComponent。html
從The HashChangeEvent interface能夠看到hashchange事件的參數HashChangeEvent繼承了Event,僅僅多了兩個屬性前端
window.onhashchange = function(e){ console.log('old URL:', e.oldURL) console.log('new URL', e.newURL) }
hash | CAN I USE 上能夠看到除了IE8一下和那個尷尬的Opera Mini,hashchange事件都是支持得很好。那麼怎麼作到兼容,用MDN的代碼作個引子vue
function(window) { if ("onhashchange" in window) { return; } var location = window.location, oldURL = location.href, oldHash = location.hash; setInterval(function() { var newURL = location.href, newHash = location.hash; if (newHash != oldHash && typeof window.onhashchange === "function") { window.onhashchange({ type: "hashchange", oldURL: oldURL, newURL: newURL }); oldURL = newURL; oldHash = newHash; } }, 100); }
從上面能夠得知,咱們的實現思路就是監聽hashchange事件,這裏先拋開兼容性問題。
1 首先監聽hashchange事件,定義個RouterManager函數java
function RouterManager(list, index) { if (!(this instanceof RouterManager)) { return new RouterManager(arguments) } this.list = {} || list this.index = index this.pre = null this.current = null win.addEventListener('hashchange', function (ev) { var pre = ev.oldURL.split('#')[1], cur = ev.newURL.split('#')[1], preR = this.getByUrlOrName(pre), curR = this.getByUrlOrName(cur) this.loadWithRouter(curR, preR) }.bind(this)) }
2 定義添加,刪除,加載,和初始化等方法react
RouterManager.prototype = { add: function (router, callback) { if (typeof router === 'string') { router = { path: router, name: router, callback: callback } } this.list[router.name || router.path] = router }, remove: function (name) { delete this.list[name] }, get: function (name) { return this.getByUrlOrName(name) }, load: function (name) { if (!name) { name = location.hash.slice(1) } var r = this.getByUrlOrName(name) this.loadWithRouter(r, null) }, loadWithRouter(cur, pre) { if (cur && cur.callback) { this.pre = this.current || cur cur.callback(cur, pre) this.current = cur } else { this.NOTFOUND('未找到相關路由') } }, getByUrlOrName: function (nameOrUrl) { var r = this.list[nameOrUrl] if (!r) { r = Object.values(this.list).find(rt => rt.name === nameOrUrl || rt.path === nameOrUrl) } return r }, setIndex: function (nameOrUrl) { this.indexRouter = this.getByUrlOrName(nameOrUrl) }, go: function (num) { win.history.go(num) }, back: function () { win.history.back() }, forward: function () { win.history.forward() }, init: function () { // 直接輸入是帶hash的地址,還原 if (win.location.hash) { /* 模擬事件 var ev = document.createEvent('Event') ev.initEvent('hashchange', true, true) ev.oldURL = ev.newURL = location.href win.dispatchEvent(ev) */ this.load() } else if (this.indexRouter) { // 是不帶hash的地址,跳轉到指定的首頁 if ('replaceState' in win.history) { // 替換地址 win.history.replaceState(null, null, win.location.href + '#' + this.indexRouter.path) } else { win.location.hash = this.indexRouter.path } } } }
3 公佈函數git
RouterManager.prototype.use = RouterManager.prototype.add win.Router = RouterManager
4 頁面怎麼配置,簡單的利用a標籤hrefgithub
<ul> <li> <li> <a href="#/m1">菜單1</a> </li> <ul> <li> <a href="#/m11">菜單11</a> </li> <li> <a href="#/m12">菜單12</a> </li> </ul> </li> <li> <a href="#/m2">菜單2</a> </li> <li> <a href="#/m3">菜單3</a> </li> </ul>
5 註冊,固然你也能夠經過選擇器批量註冊web
var router = new Router() router.NOTFOUND = function (msg) { content.innerHTML = msg } router.use('/m1', function (r) { req(r.path.slice(1)) }) router.use('/m11', function (r) { req(r.path.slice(1)) }) router.use('/m12', function (r) { req(r.path.slice(1)) }) router.use('/m2', function (r) { req(r.path.slice(1)) }) router.use('/m3', function (r) { req(r.path.slice(1)) }) router.setIndex('/m1') router.init()
爲了方便演示,定義req,ajax方法,模擬ajax請求ajax
function req(url) { ajax(url, function (res) { content.innerHTML = res }) } function ajax(id, callback) { callback( { 'm1': '菜單1的主區域內容', 'm11': '菜單11的主區域內容', 'm12': '菜單12的主區域內容', 'm2': '菜單2的主區域內容', 'm3': '菜單3的主區域內容' }[id] || '404 Not Found!') }
6 demo地址
7 源碼地址
簡單的前端hash路由
8 下一步
這就成了最簡單最基本的路由了。讓然還有不少要考慮,好比以下
hash | CAN I USE
The HashChangeEvent interface
onhashchange | MDN
window.location.hash 使用說明
JS單頁面應用實現前端路由(hash)
Ajax保留瀏覽器歷史的兩種解決方案(Hash&Pjax)
理解瀏覽器的歷史記錄
理解瀏覽器歷史記錄(2)-hashchange、pushState
Web開發中 前端路由 實現的幾種方式和適用場景
本身動手寫一個前端路由插件
vue-router
react-router