參考Vue-router, 實現一個簡單的前端路由

So ?發表於2018-03-09

思路

目前只實現了hash,整個思路是,我們有一個主檔案Router.js,這個檔案中我們定義了兩大方法,use和route

use的作用是將所有的中介軟體全push到Router的middleware陣列裡面。route方法裡面,我們呼叫了this._matcher的addRoutes方法。this._matcher其實是createMatcher的例項。

route(path, controller){
        // 在執行controller之前, 先執行所有的中介軟體
        // 將所有routes及其controller新增進_matcher
        this._matcher.addRoutes(path, (req) => {
            this._middlewares.forEach(fn => {
                fn(req)
            });
            /**
             * res(response)實際是Router: this
             * callback(req, res)
             */
            console.log("傳進來了......")
            controller&&controller(req, this)
        })
    }
複製程式碼

createMatcher的作用是將配置檔案的所有路由規則正則,並且解析我們每一次請求的url。它的match方法就是用於解析這個url。但是match方法是在HashHistory裡面呼叫的。因為我們的Router 例項化的時候,傳給了它。並將例項化的賦值給this._history

this._history = this._mode === 'hash' 
            ? new HashHistory({matcher: this._matcher})
            : new Html5History({matcher: this._matcher});
複製程式碼

addRoutes接收一個path和一個handler,handler其實就是 路由配置檔案裡面的所有中介軟體,以及app.route(path, controller)裡每個路由對應的controller。path被正則化,用於我們的app頁面匹配每個url,push到createMatcher的_routes裡。

_toReg是將route(path, controller)的path正則化。

addRoutes(path, handler){
        let routeReg = this._toReg({
            path: path,
            handler: handler,
            params: []
        });
        this._routes.push(routeReg);
    }
複製程式碼

我們怎麼樣去截獲一個請求呢?實際上用截獲這個詞,並不十分準確。在HashHistory裡面,我們通過_setListener,實現對hash變化的監聽。

_setListener(){
        window.addEventListener('load', this._refresh);
        window.addEventListener('hashchange', this._refresh);
    }
複製程式碼

當我們要從頁面a到達頁面b的時候,是通過點選頁面a上面的連線,這個連結有一個onclick事件,go裡面的引數就是我們的請求url。

<li onclick="app.go('/order/fruit?price=100&time=now', {message: '訂單'})">訂單</li>
複製程式碼

當我們點選它的時候,實際是我們先修改window.location.hash這個值(點選事件先觸發了go方法)。this._cache[url] = body,儲存了我們想要傳入的資訊。比如獲取當前form表格的值。

go(url, body){
        this._cache[url] = body;
        console.log("come here........")
        window.location.hash = `${url}`;
    }
//..................................//
_getHash (){
        // 參考Vue-router
        // window.location.hash不穩定, Firefox會發布新版本
        const href = window.location.href;
        const index = href.indexOf('#');
        return index === -1 ? '' : href.slice(index + 1)
    }
複製程式碼

一旦hash被修改,我們的onhashchange事件就能監聽到,然後觸發_refresh方法,_refresh裡面呼叫_getHash,獲取這個hash值,然後呼叫傳參進來的createMatcher的match方法,解析這個path(實際就是上面提到的url);解析完之後返回一個matchedRoutes,這個matchedRoutes不僅包含解析完的param,還包括這個route對應的handler(handler包括中介軟體和路由對應的controller)。

this._refresh = () => {
            // 每次hash變化, 獲取變化後的hash
            console.log("refesh啦!............")
            const path = this._getHash();
            const matchedRoutes = this.matcher.match(path);
            this._matchedCount = matchedRoutes.length;
            this._fireHandlers(matchedRoutes, this._cache[path]);
        }
複製程式碼

_fireHandlers是最終call handler,並且處理請求的最終函式。

_fireHandlers(matchedRoutes, body) {
        console.log("I am the best one")
        for (let i = 0; i < matchedRoutes.length; i++) {
            // 匹配到的路由包含有待執行的controller
            const item = matchedRoutes[i];
            // 構造請求體
            const request = {
                body: body || {},
                query: item.query,
                params: item.params
            };
            def(request, 'route', item.path);
            def(request, 'url', item.url);
            item.handler(request)
        }
    }
複製程式碼

參考

非常感謝sme-router作者!

從他的程式碼中, 學習到了很多, 對Vue-router的理解也加深了更多!

相關文章