編程式導(dǎo)航
1 .用在可復(fù)用的路由視圖里面,比如所有的需要跳轉(zhuǎn)到一個文章具體內(nèi)容的路由,每一次跳轉(zhuǎn)到新路由的時候,其實傳的東西都是這個文章的id,在里面再次進行請求,別的東西都是一樣的,還有就是在這個情況下返回。
2 .本質(zhì)就是監(jiān)聽URL的變化,然后匹配路由規(guī)則,顯示相應(yīng)的頁面,而且無需刷新。
代碼實現(xiàn)方面
哈希路由
1 .通過location.hash獲取到網(wǎng)址#號后面的網(wǎng)頁位置標(biāo)識符
2 .onhashchange事件來監(jiān)聽哈希的變化然后執(zhí)行響應(yīng)的回調(diào)函數(shù)
3 .不同的#值,表示不同的訪問狀態(tài),向用戶發(fā)出某個狀態(tài)的訪問鏈接,也就是說用所有的哈希值來索引到具體顯示的某一個頁面
4 .代碼
// 對window事件進行包裝和兼容
// 兩種思路吧,一種就是直接使用next,pre這樣的指針來指定,一種就是把所有的操作都放到一個棧里面。
const addEvent=(function(){
if(window.addEventListener){
return function(ele,event,handle,isBuble){
isBuble=isBuble||false
ele.addEventListener(event,handle,isBuble)
}
}else if(window.attachEvent){
return function(ele,event,handle){
ele.attachEvent('on'+event,handle)
}
}else{
return function(ele,event,handle){
return function(ele,event,handle){
ele['on'+event]=handle
}
}
}
})()
class Router{
constructor(){
// this.routers={}
// this.pre=this.pre||null
// this.next=this.next||null
// 不是必須
// 也就是說當(dāng)定義一個和系統(tǒng)api相關(guān)的類的時候,
this.currentUrl=null
this.currentIndex=null
this.routerArr=[]
this.len=0
this.canpush=true
//一個是否記錄的開關(guān),現(xiàn)在就是go,和back的所有操作都不會記錄,只會記錄點擊的歷史記錄,就是因為如果要記錄go,和back的操作的話,可能會造成死循環(huán)。
window.addEventListener('load',()=>{
this.updateUrl()
})
// 這個是關(guān)鍵。這個類需要檢測的東西也可以加在這里么?
//constructor里面竟然也可以加這個東西,真的奇怪,類這個東西還得探索一下其他的功能
window.addEventListener('hashchange',()=>{
if(this.canpush){
this.updateUrl()
}else{
console.log('')
}
})
}
// 初始化route,以及更新路由
updateUrl(){
this.currentUrl=window.location.hash.slice(1)||'/'
this.routerArr.push(this.currentUrl)
this.len=this.routerArr.length
this.currentIndex=this.routerArr.length-1
this.canpush=true
}
back(){
console.log('back')
console.log(this)
this.canpush=false
if(this.currentIndex==0){
window.location.hash='#'+this.routerArr[this.len-1]
this.currentIndex=this.len-1
}else{
window.location.hash='#'+this.routerArr[this.currentIndex-1]
this.currentIndex--
}
}
go(){
// 還需要計算下沒有進行記錄的時候做了前進點擊
console.log('go')
console.log(this)
this.canpush=false
if(this.currentIndex==this.len-1){
window.location.hash='#'+this.routerArr[0]
this.currentIndex=0
}else{
window.location.hash='#'+this.routerArr[this.currentIndex+1]
this.currentIndex++
}
}
}
5 .但是這個只能做一些前后倒退的簡單操作,想要一一對應(yīng)起來,的需要key-value形式的數(shù)據(jù)結(jié)構(gòu)
6 .或者是最后渲染出來的頁面,真正自己構(gòu)造路由的時候還是想要上面那種結(jié)果,感覺這個適合服務(wù)端渲染,每一個或者每一種情況渲染出來的頁面對應(yīng)著唯一的哈希url
7 .升級改造的話,其實數(shù)組里面應(yīng)該是這樣的,記錄頁面的訪問次數(shù),訪問順序。
{
hash:'/',
count:1
}
history路由
1 .缺點:pushState,replaceState只會對url進行改變,不會觸發(fā)頁面刷新,只是導(dǎo)致history對象發(fā)生變化,不能跨域。這就導(dǎo)致如果直接輸入url,但是后端沒有處理的話,直接是404,哈希路由可以定位到某個具體的dom
2 .代碼
3 .怎么樣升級下既能用上哈希的準(zhǔn)確性,有可以方便使用的時候傳入key-value的一個地址對應(yīng)組件的操作
4 .點擊一個地址的時候,現(xiàn)在我傳入的數(shù)組里面找我是否定義了這個地址對應(yīng)的組件,如果沒有,可以選擇定義新的地址然后加入,也可以彈出報錯,把這個加入
5 .還是想做的就是go.back不記錄到歷史記錄里面。
6 .整個思路其實是這樣的,router-link之類的標(biāo)簽點擊觸發(fā)的時候,先在router傳入的對象里面去尋找,是否有對應(yīng)的組件來展示對應(yīng)的路由地址
7 .那其實這個升級版的路由其實有兩個功能,一個是存儲歷史記錄,一個是顯示對應(yīng)的組件,上一個路由組件需要存儲功能我們交給了history API,但是這個api是不會保存和組件有關(guān)的操作,所以我們需要把和組件有關(guān)的歷史記錄人為的添加到這個api里面
8 .代碼
window.history的簡單方法
1 .history.length:當(dāng)前窗口訪問過網(wǎng)址的數(shù)量
2 .history.state:history堆棧最上層的狀態(tài)值
3 .history.back():前一個網(wǎng)址
4 .history.forward():移動到下一個網(wǎng)址。
5 .history.go(n):以當(dāng)前網(wǎng)址為基準(zhǔn),進行移動。正數(shù)為前,負數(shù)為后。0的話相當(dāng)于刷新當(dāng)前頁面。
6 .window.history.pushState(state,title,url)
7 .最后一個url必須是同一域名的url,不然會認為是有安全隱患
8 .pushState是不會觸發(fā)刷新頁面,只是導(dǎo)致history對象發(fā)生變化,地址欄會有反應(yīng)
9 .history.replaceState()修改當(dāng)前history的記錄,參數(shù)和上個函數(shù)的一樣
10 .特殊事件:popstate,用戶點擊前進,后退按鈕,或者使用js調(diào)用History.back(),History.go(),History.forward()方法才會觸發(fā),而且這個事件只針對同一個文檔,如果瀏覽歷史發(fā)生了改變,導(dǎo)致加載了不同的文檔,事件也不會觸發(fā),事件的回調(diào)參數(shù)就是以上兩個方法傳入的第一個參數(shù)。
window.onpopstate = function (event) {
console.log('location: ' + document.location);
console.log('state: ' + JSON.stringify(event.state));
};
// 或者
window.addEventListener('popstate', function(event) {
console.log('location: ' + document.location);
console.log('state: ' + JSON.stringify(event.state));
});
組件源碼分析
Vue.use
1 .在Vue路由里面增加覆蓋所有情況的候選資源,如果url匹配不到任何資源,返回一個404頁面
2 .使用方法:每一個路由映射一個組件
3 .Vue.use(router):將組件插入到Vue中去,use方法會檢測組件內(nèi)是否有install方法,如果有的話,執(zhí)行install方法
4 .對于路由注冊來說,核心就是調(diào)用Vue.use(vueRouter),使VueRouter可以調(diào)用Vue,然后通過Vue來調(diào)用VueRouter的install函數(shù),在該函數(shù)中,核心就是給組件混入鉤子函數(shù)和全局注冊兩個路由組件
5 .全局混合方法來初始化VueRouter
6 .給當(dāng)下所有組件注入route對象
install
1 .對插入組件在beforecreate鉤子操作,在Vue聲明周期階段就可以調(diào)用到組件的相關(guān)方法
2 .通過Vue.prototype定義router,route屬性,這樣所有的組件都可以獲取到這兩個屬性
3 .Vue上注冊router-link,router-view組件
生成router實例
1 .根據(jù)配置數(shù)組生成路由配置記錄表
2 .根據(jù)不同的模式生成監(jiān)控路由變化的History對象
3 .創(chuàng)建一個路由匹配對象,根據(jù)mode來踩去不同的路由方式
生成Vue實例
1 .把router傳入new Vue()中
2 .進入Vue的生命周期,執(zhí)行第一步router對Vue混入beforeCreate鉤子函數(shù),判斷實例化的options是否包含routr-為組件的router-view組件渲染提供$route,保證router.init只被調(diào)用一次
3 .初始化結(jié)束,界面將顯示默認首頁
4 .當(dāng)根組件調(diào)用beforeCreate鉤子函數(shù)的時候。初始化路由,因為只有根組件會有router屬性
5 .在路由初始化的時候,核心就是i進行路由的跳轉(zhuǎn),改變URL然后渲染對應(yīng)的組件。
路由更新方式
1 .router-link綁定的click方法,觸發(fā)history.push或者history.replace,從而進行路由轉(zhuǎn)換,更新路由。在BeforeCreate有劫持_route方法,當(dāng)_route發(fā)生變化后,router-view自動變化。
2 .地址變化:HashHistory或者HTMLHistory會自動監(jiān)控hashchange和popState來對路由變化做處理,從而執(zhí)行上面那一套程序。
Vue插件機制
1 .
組件特殊方法
1 .this.route:返回當(dāng)前路由
動態(tài)路由
1 .不僅僅是具體的歌曲頁面可以使用這個功能,某種模式全需要匹配到一個路由,就好像一個帶子可以把雜亂的路由全都梳理起來,只不過這個使用之后無法在每個具體的路徑下面在添加子路徑,所以這個一般才會用在最末端的路徑。
2 ./user/:username/post/:post_id 他是可以分段約束的。這樣有更大的靈活性。這樣上面那個問題應(yīng)該可以解決了。那這個可以隨便用了,只不過還是需要一些約束,可以看下別人路由是如何設(shè)計的
3 .可以變化的那些路徑參數(shù)都是可以在this.$router.params里面訪問到,這個如果是漢字的路徑參數(shù)的話,是可以直接在組件里面顯示的,但是如果是英文的話,還需要一個映射表。
4 .這個還是有局限性,雖然路徑是可以類似的正則匹配到,但是組件是只能對應(yīng)一個的。所以還是還是需要拆開。他適用的是一套模板但是對應(yīng)不同數(shù)據(jù)的情況,轉(zhuǎn)到對應(yīng)的界面之前,把請求的資源的標(biāo)記存到vuex里面,到了這個組件里面開始ajax請求,填充頁面內(nèi)容。
5 .這樣又出現(xiàn)個問題,路由參數(shù)變化的時候,對應(yīng)組件勢力會被復(fù)用,比起銷毀在創(chuàng)建,復(fù)用更加高效。此時組件的生命周期是不會被調(diào)用的,那么mounted里面定義的函數(shù)可能不會初始化,所以需要watch監(jiān)視下路由參數(shù)的響應(yīng),一旦發(fā)生變化,而且符合變化的條件,那就進行初始化。
6 .匹配優(yōu)先級是按照定義的位置順序來算的,先到先匹配
編程式導(dǎo)航
1 .this.$router.push('home') 它實現(xiàn)的效果和router-link實現(xiàn)的方式一樣。
2 .適用場景:返回操作,只是知道下一步要怎么操作,但是沒有具體的地址,或者在頁面上不方便表現(xiàn)出來操作的UI,就需要在底層使用編程導(dǎo)航
3 .編程式導(dǎo)航的語法完全類似于window.history語法
4 .
命名路由
1 .在index里面給特殊的路由加上name,這樣在跳轉(zhuǎn)的時候就會很方便,比如跳轉(zhuǎn)到首頁以及重定向的時候。
2 .一張頁面需要顯示多個路由窗口的時候。類似iframe的效果
3 .這個時候使用編程式導(dǎo)航的時候傳參數(shù)的時候非常方便
4 .鏈接的時候傳的是數(shù)組,里面有name關(guān)鍵字一般的傳的是path
5 .編程式導(dǎo)航的時候也是push這樣的數(shù)組參數(shù)
6 .
命名視圖
1 .components:{default:}默認參數(shù)
2 .router-view 里面有name屬性。
3 .components:里面?zhèn)魅雽?yīng)的組件數(shù)組 name:組件名
重定向,別名
1 .redirect:訪問當(dāng)前路由地址的時候,重定向到另一個路由地址下的視圖
2 .alais:給任意一個路由加一個這參數(shù),起個名字,然后直接router-link 的to就可以過來
3 .那別名和命名路由的區(qū)別是什么?看理論感覺用法差不多,還是實踐下看看差距在哪里吧。
重要api
router-link
1 .無論是HTML history模式還是hash模式,行為表現(xiàn)一致。切換路由模式的時候,無需做任何的改動
2 .在html history模式下,roter-link會令守衛(wèi)點擊事件,讓瀏覽器不在加載頁面
3 .在hisitory下使用base選項之后,所有的to屬性都不需要寫(基路徑)了。
4 .to:當(dāng)路由目標(biāo)被點擊后,會立刻把to的值傳到router.push(),這個值可以是一個字符串或者描述目標(biāo)位置的對象,那這樣我們在寫組件的時候其實可以是任意一個值,只要他有to這個傳入屬性,那么就可以點擊觸發(fā)router.push函數(shù)進行路由轉(zhuǎn)換
1 .可以使用v-bind:傳入動態(tài)參數(shù),比如動態(tài)渲染一個樹形導(dǎo)航
2 .傳入對象的時候,可以是path,name,path+query
5 .replace參數(shù):設(shè)置replace的話,點擊的對象,進行跳轉(zhuǎn)的時候不記錄歷史記錄
6 .append:相對于傳入的路徑前面添加一個基路徑,這個基路徑是什么?
7 .tag:‘router-link渲染出什么結(jié)果’,默認是a標(biāo)簽
8 .router-link-active:選中的時候自動給標(biāo)簽價加上這個樣式,你可以把想定義的樣式放在這個里面
9 .event:觸發(fā)的事件,默認是click
10 .exact:這個看文檔看不懂。
11 .exact-active-class:
router-view
1 .因為他也是個組件,所以可以配合transition,keepalive使用,如果兩個一起使用,確保內(nèi)層使用keep-alive
2 .name:router-view設(shè)置了名稱,則會渲染對應(yīng)路由配置中conponents中下的對應(yīng)組件
index.js文件里面
routes
1 .path
2 .components
3 .component
4 .redirect
5 .alias
6 .props
7 .children
8 .meta
9 .beforeEnter
10 .caseSencetive:是否對匹配規(guī)則大小敏感
11 .pathToregexOptions:object編譯匹配正則的選項
mode
1 .hash:使用URL hash值來做路由,支持所有瀏覽器
2 .history:依賴HTML History和服務(wù)器配置。需要后臺支持,如果后臺沒有配置對應(yīng)的路由,就會返回404,所以必須增加一個能夠覆蓋所有情況的候選資源,如果url匹配不到任何靜態(tài)資源,返回一個默認的頁面。這個需要前端和后端來把保證對url資源的共同配置。