国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

前端路由實(shí)現(xiàn)與 react-router 源碼分析

ISherry / 1760人閱讀

摘要:回調(diào)函數(shù)將在更新時(shí)觸發(fā),回調(diào)中的起到了新的的作用。注冊回調(diào)在中使用注冊的回調(diào)函數(shù),最終放在模塊的回調(diào)函數(shù)數(shù)組中。

原文地址:https://github.com/joeyguo/blog/issues/2

在單頁應(yīng)用上,前端路由并不陌生。很多前端框架也會有獨(dú)立開發(fā)或推薦配套使用的路由系統(tǒng)。那么,當(dāng)我們在談前端路由的時(shí)候,還可以談些什么?本文將簡要分析并實(shí)現(xiàn)一個(gè)的前端路由,并對 react-router 進(jìn)行分析。

一個(gè)極簡前端路由實(shí)現(xiàn)

說一下前端路由實(shí)現(xiàn)的簡要原理,以 hash 形式(也可以使用 History API 來處理)為例,當(dāng) url 的 hash 發(fā)生變化時(shí),觸發(fā) hashchange 注冊的回調(diào),回調(diào)中去進(jìn)行不同的操作,進(jìn)行不同的內(nèi)容的展示。直接看代碼或許更直觀。

function Router() {
    this.routes = {};
    this.currentUrl = "";
}
Router.prototype.route = function(path, callback) {
    this.routes[path] = callback || function(){};
};
Router.prototype.refresh = function() {
    this.currentUrl = location.hash.slice(1) || "/";
    this.routes[this.currentUrl]();
};
Router.prototype.init = function() {
    window.addEventListener("load", this.refresh.bind(this), false);
    window.addEventListener("hashchange", this.refresh.bind(this), false);
}
window.Router = new Router();
window.Router.init();

上面路由系統(tǒng) Router 對象實(shí)現(xiàn),主要提供三個(gè)方法

init 監(jiān)聽瀏覽器 url hash 更新事件

route 存儲路由更新時(shí)的回調(diào)到回調(diào)數(shù)組routes中,回調(diào)函數(shù)將負(fù)責(zé)對頁面的更新

refresh 執(zhí)行當(dāng)前url對應(yīng)的回調(diào)函數(shù),更新頁面

Router 調(diào)用方式以及呈現(xiàn)效果如下:點(diǎn)擊觸發(fā) url 的 hash 改變,并對應(yīng)地更新內(nèi)容(這里為 body 背景色)

 
var content = document.querySelector("body");
// change Page anything
function changeBgColor(color) {
    content.style.backgroundColor = color;
}
Router.route("/", function() {
    changeBgColor("white");
});
Router.route("/blue", function() {
    changeBgColor("blue");
});
Router.route("/green", function() {
    changeBgColor("green");
});

以上為一個(gè)前端路由的簡單實(shí)現(xiàn),點(diǎn)擊查看完整代碼,雖然簡單,但實(shí)際上很多路由系統(tǒng)的根基都立于此,其他路由系統(tǒng)主要是對自身使用的框架機(jī)制的進(jìn)行配套及優(yōu)化,如與 react 配套的 react-router。

react-router 分析 react-router 與 history 結(jié)合形式

react-router 是基于 history 模塊提供的 api 進(jìn)行開發(fā)的,結(jié)合的形式本文記為 包裝方式。所以在開始對其分析之前,先舉一個(gè)簡單的例子來說明如何進(jìn)行對象的包裝。

// 原對象
var historyModule = {
    listener: [],
    listen: function (listener) {
        this.listener.push(listener);
        console.log("historyModule listen..")
    },
    updateLocation: function(){
        this.listener.forEach(function(listener){
            listener("new localtion");
        })
    }
}
// Router 將使用 historyModule 對象,并對其包裝
var Router = {
    source: {},
    init: function(source){
        this.source = source;
    },
    // 對 historyModule的listen進(jìn)行了一層包裝
    listen: function(listener) {
        return this.source.listen(function(location){
            console.log("Router listen tirgger.");
            listener(location);
        })
    }
}
// 將 historyModule 注入進(jìn) Router 中
Router.init(historyModule);
// Router 注冊監(jiān)聽
Router.listen(function(location){
    console.log(location + "-> Router setState.");
})
// historyModule 觸發(fā)回調(diào)
historyModule.updateLocation();

返回:

可看到 historyModule 中含有機(jī)制:historyModule.updateLocation() -> listener( ),Router 通過對其進(jìn)行包裝開發(fā),針對 historyModule 的機(jī)制對 Router 也起到了作用,即historyModule.updateLocation() 將觸發(fā) Router.listen 中的回調(diào)函數(shù) 。點(diǎn)擊查看完整代碼
這種包裝形式能夠充分利用原對象(historyModule )的內(nèi)部機(jī)制,減少開發(fā)成本,也更好的分離包裝函數(shù)(Router)的邏輯,減少對原對象的影響。

react-router 使用方式

react-router 以 react component 的組件方式提供 API, 包含 Router,Route,Redirect,Link 等等,這樣能夠充分利用 react component 提供的生命周期特性,同時(shí)也讓定義路由跟寫 react component 達(dá)到統(tǒng)一,如下

render((
  
    
      
      
        
      
      
    
  
), document.body)

就這樣,聲明了一份含有 path to component 的各個(gè)映射的路由表。

react-router 還提供的 Link 組件(如下),作為提供更新 url 的途徑,觸發(fā) Link 后最終將通過如上面定義的路由表進(jìn)行匹配,并拿到對應(yīng)的 component 及 state 進(jìn)行 render 渲染頁面。

"joey"

這里不細(xì)講 react-router 的使用,詳情可見:https://github.com/reactjs/react-router

從點(diǎn)擊 Link 到 render 對應(yīng) component ,路由中發(fā)生了什么 為何能夠觸發(fā) render component ?

主要是因?yàn)橛|發(fā)了 react setState 的方法從而能夠觸發(fā) render component。
從頂層組件 Router 出發(fā)(下面代碼從 react-router/Router 中摘取),可看到 Router 在 react component 生命周期之組件被掛載前 componentWillMount 中使用 this.history.listen 去注冊了 url 更新的回調(diào)函數(shù)。回調(diào)函數(shù)將在 url 更新時(shí)觸發(fā),回調(diào)中的 setState 起到 render 了新的 component 的作用。

Router.prototype.componentWillMount = function componentWillMount() {
    // .. 省略其他
    var createHistory = this.props.history;
    
    this.history = _useRoutes2["default"](createHistory)({
      routes: _RouteUtils.createRoutes(routes || children),
      parseQueryString: parseQueryString,
      stringifyQuery: stringifyQuery
    });
    
    this._unlisten = this.history.listen(function (error, state) {
        _this.setState(state, _this.props.onUpdate);
    });
  };

上面的 _useRoutes2 對 history 操作便是對其做一層包裝,所以調(diào)用的 this.history 實(shí)際為包裝以后的對象,該對象含有 _useRoutes2 中的 listen 方法,如下

function listen(listener) {
      return history.listen(function (location) {
          // .. 省略其他
          match(location, function (error, redirectLocation, nextState) {
            listener(null, nextState);
          });
      });
}

可看到,上面代碼中,主要分為兩部分

使用了 history 模塊的 listen 注冊了一個(gè)含有 setState 的回調(diào)函數(shù)(這樣就能使用 history 模塊中的機(jī)制)

回調(diào)中的 match 方法為 react-router 所特有,match 函數(shù)根據(jù)當(dāng)前 location 以及前面寫的 Route 路由表匹配出對應(yīng)的路由子集得到新的路由狀態(tài)值 state,具體實(shí)現(xiàn)可見 react-router/matchRoutes ,再根據(jù) state 得到對應(yīng)的 component ,最終執(zhí)行了 match 中的回調(diào) listener(null, nextState) ,即執(zhí)行了 Router 中的監(jiān)聽回調(diào)(setState),從而更新了展示。

以上,為起始注冊的監(jiān)聽,及回調(diào)的作用。

如何觸發(fā)監(jiān)聽的回調(diào)函數(shù)的執(zhí)行?

這里還得從如何更新 url 說起。一般來說,url 更新主要有兩種方式:簡單的 hash 更新或使用 history api 進(jìn)行地址更新。在 react-router 中,其提供了 Link 組件,該組件能在 render 中使用,最終會表現(xiàn)為 a 標(biāo)簽,并將 Link 中的各個(gè)參數(shù)組合放它的 href 屬性中。可以從 react-router/ Link 中看到,對該組件的點(diǎn)擊事件進(jìn)行了阻止了瀏覽器的默認(rèn)跳轉(zhuǎn)行為,而改用 history 模塊的 pushState 方法去觸發(fā) url 更新。

Link.prototype.render = function render() {
    // .. 省略其他
    props.onClick = function (e) {
      return _this.handleClick(e);
    };
    if (history) {
     // .. 省略其他
      props.href = history.createHref(to, query);
    }
    return _react2["default"].createElement("a", props);
};
  
Link.prototype.handleClick = function handleClick(event) {
    // .. 省略其他
    event.preventDefault();
    this.context.history.pushState(this.props.state, this.props.to, this.props.query);
};

對 history 模塊的 pushState 方法對 url 的更新形式,同樣分為兩種,分別在 history/createBrowserHistory 及 history/createHashHistory 各自的 finishTransition 中,如 history/createBrowserHistory 中使用的是 window.history.replaceState(historyState, null, path); 而 history/createHashHistory 則使用 window.location.hash = url,調(diào)用哪個(gè)是根據(jù)我們一開始創(chuàng)建 history 的方式。

更新 url 的顯示是一部分,另一部分是根據(jù) url 去更新展示,也就是觸發(fā)前面的監(jiān)聽。這是在前面 finishTransition 更新 url 之后實(shí)現(xiàn)的,調(diào)用的是 history/createHistory 中的 updateLocation 方法,changeListeners 中為 history/createHistory 中的 listen 中所添加的,如下

function updateLocation(newLocation) {
   // 示意代碼
    location = newLocation;
    changeListeners.forEach(function (listener) {
      listener(location);
    });
}
function listen(listener) {
     // 示意代碼
    changeListeners.push(listener);
}
總結(jié)

可以將以上 react-router 的整個(gè)包裝閉環(huán)總結(jié)為

回調(diào)函數(shù):含有能夠更新 react UI 的 react setState 方法。

注冊回調(diào):在 Router componentWillMount 中使用 history.listen 注冊的回調(diào)函數(shù),最終放在 history 模塊的 回調(diào)函數(shù)數(shù)組 changeListeners 中。

觸發(fā)回調(diào):Link 點(diǎn)擊觸發(fā) history 中回調(diào)函數(shù)數(shù)組 changeListeners 的執(zhí)行,從而觸發(fā)原來 listen 中的 setState 方法,更新了頁面

至于前進(jìn)與后退的實(shí)現(xiàn),是通過監(jiān)聽 popstate 以及 hashchange 的事件,當(dāng)前進(jìn)或后退 url 更新時(shí),觸發(fā)這兩個(gè)事件的回調(diào)函數(shù),回調(diào)的執(zhí)行方式 Link 大致相同,最終同樣更新了 UI ,這里就不再說明。

react-router 主要是利用底層 history 模塊的機(jī)制,通過結(jié)合 react 的架構(gòu)機(jī)制做一層包裝,實(shí)際自身的內(nèi)容并不多,但其包裝的思想筆者認(rèn)為很值得學(xué)習(xí),有興趣的建議閱讀下源碼,相信會有其他收獲。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/79509.html

相關(guān)文章

  • 路由原理出發(fā),深入閱讀理解react-router 4.0的源碼

    摘要:通過前端路由可以實(shí)現(xiàn)單頁應(yīng)用本文首先從前端路由的原理出發(fā),詳細(xì)介紹了前端路由原理的變遷。接著從的源碼出發(fā),深入理解是如何實(shí)現(xiàn)前端路由的。執(zhí)行上述的賦值后,頁面的發(fā)生改變。 ??react-router等前端路由的原理大致相同,可以實(shí)現(xiàn)無刷新的條件下切換顯示不同的頁面。路由的本質(zhì)就是頁面的URL發(fā)生改變時(shí),頁面的顯示結(jié)果可以根據(jù)URL的變化而變化,但是頁面不會刷新。通過前端路由可以實(shí)現(xiàn)...

    Miyang 評論0 收藏0
  • react-router 2.7.0源碼深度分析

    摘要:介紹為提供路由管理為基于格式的系統(tǒng)提供了方便的切換頁面功能。它在前端提供給了種方式通過或者瀏覽器原生的進(jìn)行地址更新上一篇介紹了的方式本文則以的形式切入分析。代碼剖析路由配置本生為組建內(nèi)部組建如等。 前言 在前端單頁面應(yīng)用里面,路由是比較重要的部分,筆者的上一篇博文簡單的路由介紹了簡單的路由內(nèi)部機(jī)制,本文則將分析react-router的內(nèi)部機(jī)制。 介紹 react-router為rea...

    tianyu 評論0 收藏0
  • react-router v5.x 源碼分析和理解 SPA router 的原理

    摘要:一般情況下,都是作為等其他子路由的上層路由,使用了,接收一個(gè)屬性,傳遞給消費(fèi)子組件。創(chuàng)建對象,兼容老瀏覽器,其他和沒有大區(qū)別總結(jié)分為四個(gè)包,分別為,其中是瀏覽器相關(guān),是相關(guān),是核心也是共同部分,是一些配置相關(guān)。 這篇文章主要講的是分析 react-router 源碼,版本是 v5.x,以及 SPA 路由實(shí)現(xiàn)的原理。 文章首發(fā)地址 單頁面應(yīng)用都用到了路由 router,目前來看實(shí)現(xiàn)路由有...

    Harriet666 評論0 收藏0
  • 深入redux技術(shù)棧

    摘要:另外,內(nèi)置的函數(shù)在經(jīng)過一系列校驗(yàn)后,觸發(fā),之后被更改,之后依次調(diào)用監(jiān)聽,完成整個(gè)狀態(tài)樹的更新。總而言之,遵守這套規(guī)范并不是強(qiáng)制性的,但是項(xiàng)目一旦稍微復(fù)雜一些,這樣做的好處就可以充分彰顯出來。 這一篇是接上一篇react進(jìn)階漫談的第二篇,這一篇主要分析redux的思想和應(yīng)用,同樣參考了網(wǎng)絡(luò)上的大量資料,但代碼同樣都是自己嘗試實(shí)踐所得,在這里分享出來,僅供一起學(xué)習(xí)(上一篇地址:個(gè)人博客/s...

    imingyu 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<