前端路由實(shí)現(xiàn)之 #hash
背景介紹
用了許多前端框架來做spa
應(yīng)用食呻,比如說backbone,angular澎现,vue
他們都有各自的路由系統(tǒng)仅胞,管理著前端的每一個(gè)頁面切換,想要理解其中路由的實(shí)現(xiàn)剑辫,最好的方法就是手動(dòng)實(shí)現(xiàn)一個(gè)干旧。
前端路由有2種實(shí)現(xiàn)方式,一種是html5推出的historyapi
妹蔽,我們這里說的是另一種hash
路由椎眯,就是常見的 #
號(hào)挠将,這種方式兼容性更好。
需求分析
我們這里只是簡單的實(shí)現(xiàn)一個(gè)路由輪子编整,基本的功能包含以下:
- 切換頁面
- 異步加載js
- 異步傳參
實(shí)現(xiàn)步驟
-
切換頁面:路由的最大作用就是切換頁面舔稀,以往后臺(tái)的路由是直接改變了頁面的url方式促使頁面刷新。但是前端路由通過 # 號(hào)不能刷新頁面掌测,只能通過 window 的監(jiān)聽事件 hashchange 來監(jiān)聽hash的變化内贮,然后捕獲到具體的hash值進(jìn)行操作
//路由切換 window.addEventListener('hashchange',function(){ //do something this.hashChange() })
-
注冊(cè)路由:我們需要把路由規(guī)則注冊(cè)到頁面,這樣頁面在切換的時(shí)候才會(huì)有不同的效果汞斧。
//注冊(cè)函數(shù) map:function(path,callback){ path = path.replace(/\s*/g,"");//過濾空格 //在有回調(diào)夜郁,且回調(diào)是一個(gè)正確的函數(shù)的情況下進(jìn)行存儲(chǔ) 以 /name 為key的對(duì)象 {callback:xx} if(callback && Object.prototype.toString.call(callback) === '[object Function]' ){ this.routers[path] ={ callback:callback,//回調(diào) fn:null //存儲(chǔ)異步文件狀態(tài),用來記錄異步的js文件是否下載粘勒,下文有提及 } }else{ //打印出錯(cuò)的堆棧信息 console.trace('注冊(cè)'+path+'地址需要提供正確的的注冊(cè)回調(diào)') } } //調(diào)用方式 map('/detail',function(transition){ ... })
-
異步加載js:一般單頁面應(yīng)用為了性能優(yōu)化竞端,都會(huì)把各個(gè)頁面的文件拆分開,按需加載庙睡,所以路由里面要加入異步加載js文件的功能事富。異步加載我們就采用最簡單的原生方法,創(chuàng)建script標(biāo)簽埃撵,動(dòng)態(tài)引入js赵颅。
var _body= document.getElementsByTagName('body')[0], scriptEle= document.createElement('script'); scriptEle.type= 'text/javascript'; scriptEle.src= xxx.js; scriptEle.async = true; scriptEle.onload= function(callback){ //為了避免重復(fù)引入js虽另,我們需要在這里記錄一下已經(jīng)加載過的文件暂刘,對(duì)應(yīng)的 fn需要賦值處理 callback() } _body.appendChild(scriptEle);
-
參數(shù)傳遞:在我們動(dòng)態(tài)引入單獨(dú)模塊的js之后,我們可能需要給這個(gè)模塊傳遞一些單獨(dú)的參數(shù)捂刺。這里借鑒了一下jsonp的處理方式谣拣,我們把單獨(dú)模塊的js包裝成一個(gè)函數(shù),提供一個(gè)全局的回調(diào)方法族展,加載完成時(shí)候再調(diào)用回調(diào)函數(shù)森缠。
SPA_RESOLVE_INIT = function(transition) { document.getElementById("content").innerHTML = '<p style="color:#F8C545;">當(dāng)前異步渲染列表頁'+ JSON.stringify(transition) +'</p>' console.log("首頁回調(diào)" + JSON.stringify(transition)) }
擴(kuò)展:以上我們已經(jīng)完成了基本功能,我們?cè)賹?duì)齊進(jìn)行擴(kuò)展仪缸,在頁面切換之前beforeEach
和切換完成afterEach
的時(shí)候增加2個(gè)方法進(jìn)行處理贵涵。思路是,注冊(cè)了這2個(gè)方法之后恰画,在切換之前就調(diào)用beforeEach
宾茂,切換之后,需要等待下載js完成拴还,在onload
里面進(jìn)行調(diào)用 afterEach
//切換之前一些處理
beforeEach:function(callback){
if(Object.prototype.toString.call(callback) === '[object Function]'){
this.beforeFun = callback;
}else{
console.trace('路由切換前鉤子函數(shù)不正確')
}
},
//切換成功之后
afterEach:function(callback){
if(Object.prototype.toString.call(callback) === '[object Function]'){
this.afterFun = callback;
}else{
console.trace('路由切換后回調(diào)函數(shù)不正確')
}
},
通過以上的思路分析跨晴,再加以整合,我們就完成了一個(gè)簡單的前端路由片林,并且可以加到頁面進(jìn)行實(shí)際的SPA開發(fā)端盆,不過還是非常簡陋怀骤。