亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

ReactRouter的實現(xiàn)方法

 更新時間:2021年01月25日 09:53:43   作者:WindrunnerMax  
這篇文章主要介紹了ReactRouter的實現(xiàn),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

ReactRouter的實現(xiàn)

ReactRouterReact的核心組件,主要是作為React的路由管理器,保持UIURL同步,其擁有簡單的API與強大的功能例如代碼緩沖加載、動態(tài)路由匹配、以及建立正確的位置過渡處理等。

描述

React Router是建立在history對象之上的,簡而言之一個history對象知道如何去監(jiān)聽瀏覽器地址欄的變化,并解析這個URL轉(zhuǎn)化為location對象,然后router使用它匹配到路由,最后正確地渲染對應的組件,常用的history有三種形式: Browser HistoryHash History、Memory History。

Browser History

Browser History是使用React Router的應用推薦的history,其使用瀏覽器中的History對象的pushState、replaceStateAPI以及popstate事件等來處理URL,其能夠創(chuàng)建一個像https://www.example.com/path這樣真實的URL,同樣在頁面跳轉(zhuǎn)時無須重新加載頁面,當然也不會對于服務端進行請求,當然對于history模式仍然是需要后端的配置支持,用以支持非首頁的請求以及刷新時后端返回的資源,由于應用是個單頁客戶端應用,如果后臺沒有正確的配置,當用戶在瀏覽器直接訪問URL時就會返回404,所以需要在服務端增加一個覆蓋所有情況的候選資源,如果URL匹配不到任何靜態(tài)資源時,則應該返回同一個index.html應用依賴頁面,例如在Nginx下的配置。

location / {
 try_files $uri $uri/ /index.html;
}

Hash History

Hash符號即#原本的目的是用來指示URL中指示網(wǎng)頁中的位置,例如https://www.example.com/index.html#print即代表exampleindex.htmlprint位置,瀏覽器讀取這個URL后,會自動將print位置滾動至可視區(qū)域,通常使用<a>標簽的name屬性或者<div>標簽的id屬性指定錨點。
通過window.location.hash屬性能夠讀取錨點位置,可以為Hash的改變添加hashchange監(jiān)聽事件,每一次改變Hash,都會在瀏覽器的訪問歷史中增加一個記錄,此外Hash雖然出現(xiàn)在URL中,但不會被包括在HTTP請求中,即#及之后的字符不會被發(fā)送到服務端進行資源或數(shù)據(jù)的請求,其是用來指導瀏覽器動作的,對服務器端沒有效果,因此改變Hash不會重新加載頁面。
ReactRouter的作用就是通過改變URL,在不重新請求頁面的情況下,更新頁面視圖,從而動態(tài)加載與銷毀組件,簡單的說就是,雖然地址欄的地址改變了,但是并不是一個全新的頁面,而是之前的頁面某些部分進行了修改,這也是SPA單頁應用的特點,其所有的活動局限于一個Web頁面中,非懶加載的頁面僅在該Web頁面初始化時加載相應的HTML、JavaScriptCSS文件,一旦頁面加載完成,SPA不會進行頁面的重新加載或跳轉(zhuǎn),而是利用JavaScript動態(tài)的變換HTML,默認Hash模式是通過錨點實現(xiàn)路由以及控制組件的顯示與隱藏來實現(xiàn)類似于頁面跳轉(zhuǎn)的交互。

Memory History

Memory History不會在地址欄被操作或讀取,這就可以解釋如何實現(xiàn)服務器渲染的,同時其也非常適合測試和其他的渲染環(huán)境例如React Native,和另外兩種History的一點不同是我們必須創(chuàng)建它,這種方式便于測試。

const history = createMemoryHistory(location);

實現(xiàn)

我們來實現(xiàn)一個非常簡單的Browser History模式與Hash History模式的實現(xiàn),因為H5pushState方法不能在本地文件協(xié)議file://運行,所以運行起來需要搭建一個http://環(huán)境,使用webpack、Nginx、Apache等都可以,回到Browser History模式路由,能夠?qū)崿F(xiàn)history路由跳轉(zhuǎn)不刷新頁面得益與H5提供的pushState()、replaceState()等方法以及popstate等事件,這些方法都是也可以改變路由路徑,但不作頁面跳轉(zhuǎn),當然如果在后端不配置好的情況下路由改編后刷新頁面會提示404,對于Hash History模式,我們的實現(xiàn)思路相似,主要在于沒有使用pushStateH5API,以及監(jiān)聽事件不同,通過監(jiān)聽其hashchange事件的變化,然后拿到對應的location.hash更新對應的視圖。

<!-- Browser History -->
<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="UTF-8">
 <title>Router</title>
</head>

<body>
 <ul>
  <li><a href="/home" rel="external nofollow" >home</a></li>
  <li><a href="/about" rel="external nofollow" >about</a></li>
  <div id="routeView"></div>
 </ul>
</body>
<script>
 function Router() {
  this.routeView = null; // 組件承載的視圖容器
  this.routes = Object.create(null); // 定義的路由
 }

 // 綁定路由匹配后事件
 Router.prototype.route = function (path, callback) {
  this.routes[path] = () => this.routeView.innerHTML = callback() || "";
 };

 // 初始化
 Router.prototype.init = function(root, rootView) {
  this.routeView = rootView; // 指定承載視圖容器
  this.refresh(); // 初始化即刷新視圖
  root.addEventListener("click", (e) => { // 事件委托到root
   if (e.target.nodeName === "A") {
    e.preventDefault();
    history.pushState(null, "", e.target.getAttribute("href"));
    this.refresh(); // 觸發(fā)即刷新視圖
   }
  })
  // 監(jiān)聽用戶點擊后退與前進
  // pushState與replaceState不會觸發(fā)popstate事件
  window.addEventListener("popstate", this.refresh.bind(this), false); 
 };

 // 刷新視圖
 Router.prototype.refresh = function () {
  let path = location.pathname;
  console.log("refresh", path);
  if(this.routes[path]) this.routes[path]();
  else this.routeView.innerHTML = "";
 };

 window.Router = new Router();
 
 Router.route("/home", function() {
  return "home";
 });
 Router.route("/about", function () {
  return "about";
 });

 Router.init(document, document.getElementById("routeView"));

</script>
</html>
<!-- Hash History -->
<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="UTF-8">
 <title>Router</title>
</head>

<body>
 <ul>
  <li><a href="#/home" rel="external nofollow" >home</a></li>
  <li><a href="#/about" rel="external nofollow" >about</a></li>
  <div id="routeView"></div>
 </ul>
</body>
<script>
 function Router() {
  this.routeView = null; // 組件承載的視圖容器
  this.routes = Object.create(null); // 定義的路由
 }

 // 綁定路由匹配后事件
 Router.prototype.route = function (path, callback) {
  this.routes[path] = () => this.routeView.innerHTML = callback() || "";
 };

 // 初始化
 Router.prototype.init = function(root, rootView) {
  this.routeView = rootView; // 指定承載視圖容器
  this.refresh(); // 初始化觸發(fā)
  // 監(jiān)聽hashchange事件用以刷新
  window.addEventListener("hashchange", this.refresh.bind(this), false); 
 };

 // 刷新視圖
 Router.prototype.refresh = function () {
  let hash = location.hash;
  console.log("refresh", hash);
  if(this.routes[hash]) this.routes[hash]();
  else this.routeView.innerHTML = "";
 };

 window.Router = new Router();
 
 Router.route("#/home", function() {
  return "home";
 });
 Router.route("#/about", function () {
  return "about";
 });

 Router.init(document, document.getElementById("routeView"));

</script>
</html>

分析

  • 我們可以看一下ReactRouter的實現(xiàn),commit ideef79d5,TAG4.4.0,在這之前我們需要先了解一下history庫,history庫,是ReactRouter依賴的一個對window.history加強版的history庫,其中主要用到的有match對象表示當前的URLpath的匹配的結(jié)果,location對象是history庫基于window.location的一個衍生。
  • ReactRouter將路由拆成了幾個包: react-router負責通用的路由邏輯,react-router-dom負責瀏覽器的路由管理,react-router-native負責react-native的路由管理。
  • 我們以BrowserRouter組件為例,BrowserRouterreact-router-dom中,它是一個高階組件,在內(nèi)部創(chuàng)建一個全局的history對象,可以監(jiān)聽整個路由的變化,并將history作為props傳遞給react-routerRouter組件,Router組件再會將這個history的屬性作為context傳遞給子組件。
// packages\react-router-dom\modules\HashRouter.js line 10
class BrowserRouter extends React.Component {
 history = createHistory(this.props);

 render() {
 return <Router history={this.history} children={this.props.children} />;
 }
}

接下來我們到Router組件,Router組件創(chuàng)建了一個React Context環(huán)境,其借助contextRoute傳遞context,這也解釋了為什么Router要在所有Route的外面。在RoutercomponentWillMount中,添加了history.listen,其能夠監(jiān)聽路由的變化并執(zhí)行回調(diào)事件,在這里即會觸發(fā)setState。當setState時即每次路由變化時 -> 觸發(fā)頂層Router的回調(diào)事件 -> Router進行setState -> 向下傳遞 nextContext此時context中含有最新的location -> 下面的Route獲取新的nextContext判斷是否進行渲染。

// line packages\react-router\modules\Router.js line 10
class Router extends React.Component {
 static computeRootMatch(pathname) {
 return { path: "/", url: "/", params: {}, isExact: pathname === "/" };
 }

 constructor(props) {
 super(props);

 this.state = {
  location: props.history.location
 };

 // This is a bit of a hack. We have to start listening for location
 // changes here in the constructor in case there are any <Redirect>s
 // on the initial render. If there are, they will replace/push when
 // they mount and since cDM fires in children before parents, we may
 // get a new location before the <Router> is mounted.
 this._isMounted = false;
 this._pendingLocation = null;

 if (!props.staticContext) {
  this.unlisten = props.history.listen(location => {
  if (this._isMounted) {
   this.setState({ location });
  } else {
   this._pendingLocation = location;
  }
  });
 }
 }

 componentDidMount() {
 this._isMounted = true;

 if (this._pendingLocation) {
  this.setState({ location: this._pendingLocation });
 }
 }

 componentWillUnmount() {
 if (this.unlisten) this.unlisten();
 }

 render() {
 return (
  <RouterContext.Provider
  children={this.props.children || null}
  value={{
   history: this.props.history,
   location: this.state.location,
   match: Router.computeRootMatch(this.state.location.pathname),
   staticContext: this.props.staticContext
  }}
  />
 );
 }
}

我們在使用時都是使用Router來嵌套Route,所以此時就到Route組件,Route的作用是匹配路由,并傳遞給要渲染的組件propsRoute接受上層的Router傳入的context,Router中的history監(jiān)聽著整個頁面的路由變化,當頁面發(fā)生跳轉(zhuǎn)時,history觸發(fā)監(jiān)聽事件,Router向下傳遞nextContext,就會更新Routepropscontext來判斷當前Routepath是否匹配location,如果匹配則渲染,否則不渲染,是否匹配的依據(jù)就是computeMatch這個函數(shù),在下文會有分析,這里只需要知道匹配失敗則matchnull,如果匹配成功則將match的結(jié)果作為props的一部分,在render中傳遞給傳進來的要渲染的組件。Route接受三種類型的render props,<Route component>、<Route render>、<Route children>,此時要注意的是如果傳入的component是一個內(nèi)聯(lián)函數(shù),由于每次的props.component都是新創(chuàng)建的,所以Reactdiff的時候會認為進來了一個全新的組件,所以會將舊的組件unmountre-mount。這時候就要使用render,少了一層包裹的component元素,render展開后的元素類型每次都是一樣的,就不會發(fā)生re-mount了,另外children也不會發(fā)生re-mount。

// \packages\react-router\modules\Route.js line 17
class Route extends React.Component {
 render() {
 return (
  <RouterContext.Consumer>
  {context => {
   invariant(context, "You should not use <Route> outside a <Router>");

   const location = this.props.location || context.location;
   const match = this.props.computedMatch
   ? this.props.computedMatch // <Switch> already computed the match for us
   : this.props.path
    ? matchPath(location.pathname, this.props)
    : context.match;

   const props = { ...context, location, match };

   let { children, component, render } = this.props;

   // Preact uses an empty array as children by
   // default, so use null if that's the case.
   if (Array.isArray(children) && children.length === 0) {
   children = null;
   }

   if (typeof children === "function") {
   children = children(props);
   // ...
   }

   return (
   <RouterContext.Provider value={props}>
    {children && !isEmptyChildren(children)
    ? children
    : props.match
     ? component
     ? React.createElement(component, props)
     : render
      ? render(props)
      : null
     : null}
   </RouterContext.Provider>
   );
  }}
  </RouterContext.Consumer>
 );
 }
}

我們實際上我們可能寫的最多的就是Link這個標簽了,所以我們再來看一下<Link>組件,我們可以看到Link最終還是創(chuàng)建一個a標簽來包裹住要跳轉(zhuǎn)的元素,在這個a標簽的handleClick點擊事件中會preventDefault禁止默認的跳轉(zhuǎn),所以實際上這里的href并沒有實際的作用,但仍然可以標示出要跳轉(zhuǎn)到的頁面的URL并且有更好的html語義。在handleClick中,對沒有被preventDefault、鼠標左鍵點擊的、非_blank跳轉(zhuǎn)的、沒有按住其他功能鍵的單擊進行preventDefault,然后pushhistory中,這也是前面講過的路由的變化與 頁面的跳轉(zhuǎn)是不互相關(guān)聯(lián)的,ReactRouterLink中通過history庫的push調(diào)用了HTML5 historypushState,但是這僅僅會讓路由變化,其他什么都沒有改變。在Router中的listen,它會監(jiān)聽路由的變化,然后通過context更新propsnextContext讓下層的Route去重新匹配,完成需要渲染部分的更新。

// packages\react-router-dom\modules\Link.js line 14
class Link extends React.Component {
 handleClick(event, history) {
 if (this.props.onClick) this.props.onClick(event);

 if (
  !event.defaultPrevented && // onClick prevented default
  event.button === 0 && // ignore everything but left clicks
  (!this.props.target || this.props.target === "_self") && // let browser handle "target=_blank" etc.
  !isModifiedEvent(event) // ignore clicks with modifier keys
 ) {
  event.preventDefault();

  const method = this.props.replace ? history.replace : history.push;

  method(this.props.to);
 }
 }

 render() {
 const { innerRef, replace, to, ...rest } = this.props; // eslint-disable-line no-unused-vars

 return (
  <RouterContext.Consumer>
  {context => {
   invariant(context, "You should not use <Link> outside a <Router>");

   const location =
   typeof to === "string"
    ? createLocation(to, null, null, context.location)
    : to;
   const href = location ? context.history.createHref(location) : "";

   return (
   <a
    {...rest}
    onClick={event => this.handleClick(event, context.history)}
    href={href}
    ref={innerRef}
   />
   );
  }}
  </RouterContext.Consumer>
 );
 }
}

每日一題

https://github.com/WindrunnerMax/EveryDay

參考

https://zhuanlan.zhihu.com/p/44548552 https://github.com/fi3ework/blog/issues/21 https://juejin.cn/post/6844903661672333326 https://juejin.cn/post/6844904094772002823 https://juejin.cn/post/6844903878568181768 https://segmentfault.com/a/1190000014294604 https://github.com/youngwind/blog/issues/109 http://react-guide.github.io/react-router-cn/docs/guides/basics/Histories.html

到此這篇關(guān)于ReactRouter的實現(xiàn)方法的文章就介紹到這了,更多相關(guān)ReactRouter的實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React捕獲并處理異常的方式

    React捕獲并處理異常的方式

    這篇文章主要給大家介紹了React優(yōu)雅的捕獲并處理渲染異常方式,文章通過代碼示例給大家介紹的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2023-11-11
  • 深入理解react 組件類型及使用場景

    深入理解react 組件類型及使用場景

    這篇文章主要介紹了深入理解react 組件類型及使用場景,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-03-03
  • React??memo允許你的組件在?props?沒有改變的情況下跳過重新渲染的問題記錄

    React??memo允許你的組件在?props?沒有改變的情況下跳過重新渲染的問題記錄

    使用?memo?將組件包裝起來,以獲得該組件的一個?記憶化?版本,只要該組件的?props?沒有改變,這個記憶化版本就不會在其父組件重新渲染時重新渲染,這篇文章主要介紹了React??memo允許你的組件在?props?沒有改變的情況下跳過重新渲染,需要的朋友可以參考下
    2024-06-06
  • react配置px轉(zhuǎn)換rem的方法

    react配置px轉(zhuǎn)換rem的方法

    這篇文章主要介紹了react配置px轉(zhuǎn)換rem的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-04-04
  • react+ant.d添加全局loading方式

    react+ant.d添加全局loading方式

    這篇文章主要介紹了react+ant.d添加全局loading方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • React子組件調(diào)用父組件的方法

    React子組件調(diào)用父組件的方法

    本文主要介紹了React子組件調(diào)用父組件的方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-08-08
  • react-router-domV6版本的路由和嵌套路由寫法詳解

    react-router-domV6版本的路由和嵌套路由寫法詳解

    本文主要介紹了react-router-domV6版本的路由和嵌套路由寫法詳解,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • react實現(xiàn)拖拽模態(tài)框

    react實現(xiàn)拖拽模態(tài)框

    這篇文章主要為大家詳細介紹了react實現(xiàn)拖拽模態(tài)框,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • React中props使用介紹

    React中props使用介紹

    props是組件(包括函數(shù)組件和class組件)間的內(nèi)置屬性,用其可以傳遞數(shù)據(jù)給子節(jié)點,props用來傳遞參數(shù)。組件實例化過程中,你可以向其中傳遞一個參數(shù),這個參數(shù)會在實例化過程中被引用
    2022-12-12
  • react echarts tooltip 區(qū)域新加輸入框編輯保存數(shù)據(jù)功能

    react echarts tooltip 區(qū)域新加輸入框編輯保存數(shù)據(jù)功能

    這篇文章主要介紹了react echarts tooltip 區(qū)域新加輸入框編輯保存數(shù)據(jù)功能,大概思路是用一個div包裹echarts, 然后在echarts的同級新建一個div用來用來模擬真實tooltip,通過鼠標移入移出事件控制真實tooltip的顯示與隱藏,需要的朋友可以參考下
    2023-05-05

最新評論