重學(xué)Vue(一)—— 前端路由

Vue寫(xiě)了段時(shí)間,開(kāi)發(fā)頁(yè)面起來(lái)已經(jīng)是唰唰唰了,但是對(duì)其中原理卻知之甚少。那可不行,為了不錯(cuò)過(guò)任何的學(xué)習(xí)點(diǎn)(在面試官前挺直腰板),得從每個(gè)角落,仔仔細(xì)細(xì)重新學(xué)一遍。

為什么要有前端路由

有些WEB每訪問(wèn)一個(gè)URL都會(huì)從服務(wù)端請(qǐng)求一個(gè)新的頁(yè)面(html),這樣做往往會(huì)造成多余的文件請(qǐng)求和重復(fù)的html代碼。

<ul>
  <li><a href="/index.html"></a></li>
  <li><a href="/user.html"></a></li>
  <li><a href="/device.html"></a></li>
</ul>

不知道有沒(méi)有人經(jīng)歷過(guò)上面這種項(xiàng)目。每次點(diǎn)擊a標(biāo)簽都會(huì)刷出一個(gè)新頁(yè)面。
ajax可以解決這個(gè)問(wèn)題,整體只刷新一次頁(yè)面(第一次訪問(wèn)域名)。之后的頁(yè)面可以通過(guò)ajax獲取html內(nèi)容,然后再通過(guò)dom操作渲染到某個(gè)頁(yè)面元素。這么做的缺點(diǎn)就是所有的頁(yè)面都是同一個(gè)url,用戶無(wú)法復(fù)制到想要的地址。
所以前端路由出現(xiàn)了,同時(shí)解決上面的兩個(gè)問(wèn)題。前端監(jiān)聽(tīng)url的變化,并實(shí)現(xiàn)頁(yè)面的更改,url雖然變化了,但實(shí)際的頁(yè)面還是原來(lái)的頁(yè)面,沒(méi)有刷新頁(yè)面。

實(shí)現(xiàn)原理

咦,我都訪問(wèn)不同url了,你怎么會(huì)不刷新頁(yè)面呢。
有這么一個(gè)常用的功能相信大家應(yīng)該都知道

命名錨
當(dāng)使用命名錨(named anchors)時(shí),我們可以創(chuàng)建直接跳至該命名錨(比如頁(yè)面中某個(gè)小節(jié))的鏈接,這樣使用者就無(wú)需不停地滾動(dòng)頁(yè)面來(lái)尋找他們需要的信息了。
假如瀏覽器找不到已定義的命名錨,那么就會(huì)定位到文檔的頂端

常用的就是定位文檔頂部。只要在url上加一個(gè)#,它就能回到頂部,且不刷新頁(yè)面。#號(hào)后面的字符是用于瀏覽器的,不會(huì)發(fā)送到服務(wù)端。所以我們可以監(jiān)聽(tīng)這個(gè)值來(lái)判斷加載的內(nèi)容。通過(guò)讀取location.hash可以知道當(dāng)前頁(yè)面所處的位置,比如url是‘jianshu.com/#/123’,那么他的location.hash就是‘/123’。通過(guò)hashchange事件可以監(jiān)聽(tīng)location.hash的變化,再通過(guò)hash值來(lái)判斷需要加載的頁(yè)面。大致就是ajax請(qǐng)求html渲染的同時(shí),還要改變一下hash值。反過(guò)來(lái)監(jiān)聽(tīng)到hash值的改變也要用ajax加載對(duì)應(yīng)的html。

history有兩個(gè)API也可以改變URL但不刷新頁(yè)面。
history.pushState,history.replaceState都可以無(wú)刷新的改變url。主要區(qū)別在于pushState會(huì)在瀏覽器中創(chuàng)建一條新的歷史紀(jì)錄,而replaceState僅僅替換將當(dāng)前地址為指定URL。
popstate: 瀏覽器點(diǎn)擊前進(jìn)后退時(shí)觸發(fā)的事件。
寫(xiě)個(gè)測(cè)試代碼在控制臺(tái)跑一下:

var stateObj= {foo:'bar'};
window.addEventListener('popstate',function(){
  alert(1);
});
history.pushState(stateObj, "page 2", "bar.html");

這個(gè)時(shí)候url已經(jīng)變了。但是pushState不會(huì)觸發(fā)popstate事件,再執(zhí)行下

history.back();

就能彈出1了。通過(guò)這點(diǎn)就可以實(shí)現(xiàn)前端路由,和上面通過(guò)hashchange事件監(jiān)聽(tīng)hash變化類(lèi)似。

實(shí)現(xiàn)一個(gè)簡(jiǎn)單路由

利用vue的is特性和hash的知識(shí)寫(xiě)一個(gè)簡(jiǎn)單路由。
首先實(shí)現(xiàn)一個(gè)router類(lèi)

class Router{
    constructor(){
        this.routes = new Map(); //維護(hù)一個(gè)路由表
        this.currentUrl = '';  //當(dāng)前路徑
        this.bind(); //綁定事件
    }

    bind(){
        //針對(duì)瀏覽器第一次輸url的情況
        window.addEventListener('load',this.refresh.bind(this),false);

        //針對(duì)瀏覽器改變url的情況
        window.addEventListener('hashchange',this.refresh.bind(this),false);
    }

    route(path,callback){

        //添加路由,并設(shè)置訪問(wèn)該路由的回調(diào)函數(shù)
        this.routes.set(path,callback);
    }

    replace(path){  //切換路由
        this.currentUrl = path;
        this.routes.get(path)(); //執(zhí)行添加路由時(shí)設(shè)置的回調(diào)函數(shù)
        location.hash = path; //切換路由的時(shí)候要修改url
    }

    refresh(){ //刷新,從url中獲取當(dāng)前路由
        this.currentURL = location.hash.slice(1) || '/one';
        if(this.currentURL == '/')  this.currentURL = '/one';
        this.routes.get(this.currentURL)();
    }
}

Vue文件

<div id="app">
  <ul>
    <li @click="$router.replace('/one')">1</li>
    <li @click="$router.replace('/two')">2</li>
    <li @click="$router.replace('/three')">3</li>
  </ul>
  <div :is="currentComponent">
  </div>
</div>

li調(diào)用路由的replace方法,觸發(fā)綁定的回調(diào),改變currentComponent從而達(dá)到更換組件的效果。

router.route('/one',()=>{
    this.currentComponent = 'tab-1';  //更換組件
})

整體代碼見(jiàn)Github ,有錯(cuò)誤望指正。為了讓route回調(diào)函數(shù)的this指向Vue(通過(guò)this.currentComponent = 'tab-1'來(lái)更換組件)我在Vue的created中生成route,并用箭頭函數(shù)綁定this,不知道還有沒(méi)有更好的辦法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容