成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

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

ISherry / 1768人閱讀

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

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

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

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

說(shuō)一下前端路由實(shí)現(xiàn)的簡(jiǎn)要原理,以 hash 形式(也可以使用 History API 來(lái)處理)為例,當(dāng) url 的 hash 發(fā)生變化時(shí),觸發(fā) hashchange 注冊(cè)的回調(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 對(duì)象實(shí)現(xiàn),主要提供三個(gè)方法

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

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

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

Router 調(diào)用方式以及呈現(xiàn)效果如下:點(diǎn)擊觸發(fā) url 的 hash 改變,并對(duì)應(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è)前端路由的簡(jiǎn)單實(shí)現(xiàn),點(diǎn)擊查看完整代碼,雖然簡(jiǎn)單,但實(shí)際上很多路由系統(tǒng)的根基都立于此,其他路由系統(tǒng)主要是對(duì)自身使用的框架機(jī)制的進(jìn)行配套及優(yōu)化,如與 react 配套的 react-router。

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

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

// 原對(duì)象
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 對(duì)象,并對(duì)其包裝
var Router = {
    source: {},
    init: function(source){
        this.source = source;
    },
    // 對(duì) 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 注冊(cè)監(jiān)聽(tīng)
Router.listen(function(location){
    console.log(location + "-> Router setState.");
})
// historyModule 觸發(fā)回調(diào)
historyModule.updateLocation();

返回:

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

react-router 使用方式

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

render((
  
    
      
      
        
      
      
    
  
), document.body)

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

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

"joey"

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

從點(diǎn)擊 Link 到 render 對(duì)應(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 去注冊(cè)了 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 對(duì) history 操作便是對(duì)其做一層包裝,所以調(diào)用的 this.history 實(shí)際為包裝以后的對(duì)象,該對(duì)象含有 _useRoutes2 中的 listen 方法,如下

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

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

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

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

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

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

這里還得從如何更新 url 說(shuō)起。一般來(lái)說(shuō),url 更新主要有兩種方式:簡(jiǎn)單的 hash 更新或使用 history api 進(jìn)行地址更新。在 react-router 中,其提供了 Link 組件,該組件能在 render 中使用,最終會(huì)表現(xiàn)為 a 標(biāo)簽,并將 Link 中的各個(gè)參數(shù)組合放它的 href 屬性中??梢詮?react-router/ Link 中看到,對(duì)該組件的點(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);
};

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

更新 url 的顯示是一部分,另一部分是根據(jù) url 去更新展示,也就是觸發(fā)前面的監(jiān)聽(tīng)。這是在前面 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 方法。

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

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

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

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/79509.html

相關(guān)文章

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

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

    Miyang 評(píng)論0 收藏0
  • react-router 2.7.0源碼深度分析

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

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

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

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

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

    imingyu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<