<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官又叫我手寫 React-router,我決定好好理解路由本質(zhì)

          共 5576字,需瀏覽 12分鐘

           ·

          2021-04-06 18:20

          閱讀源碼小 tips:從整體到細(xì)節(jié),剛開始不要太拘泥于一行代碼的實(shí)現(xiàn),先從整體去理解,理解好各自的關(guān)系,再去讀源碼。

          首先,我們先不糾結(jié)于源碼細(xì)節(jié)。先用最簡(jiǎn)單的話來(lái)概括一下 React-router 到底做了什么?

          本質(zhì)上, React-Router 就是在頁(yè)面 URL 發(fā)生變化的時(shí)候,通過我們寫的 path 去匹配,然后渲染對(duì)應(yīng)的組件。

          那么,從這句話,我們想一下如何分步驟實(shí)現(xiàn):

          1. 如何監(jiān)聽 url 的變化 ?
          2. 如何匹配 path,按什么規(guī)則 ?
          3. 渲染對(duì)應(yīng)的組件

          了解好需要實(shí)現(xiàn)的關(guān)鍵步驟,我們來(lái)將倉(cāng)庫(kù)源碼下載下來(lái)。

          接下來(lái)我們看一下 GitHub, 它使用 lerna 管理同時(shí)管理多個(gè)包.也就是 Multirepo 概念。

          react-router 使用 lerna 來(lái)同時(shí)管理多個(gè)包. ( lerna  的好處特別多,對(duì)于依賴關(guān)系大,同類型的包推薦使用 lerna 來(lái)統(tǒng)一管理。)

          核心庫(kù)是 react-router. react-router-dom 是在瀏覽器中使用的,react-router-native是在 rn 中使用的。

          如果不理解,直接看一下源碼就懂了。其實(shí) react-router-dom 只是多了下面四個(gè)組件 BrowserRouter、 Link、NavLink、HashRouter, 其他其實(shí)都是直接引用 react-router 的。

          了解完多包的組織關(guān)系之后,我們回到前面如何實(shí)現(xiàn) react-router 的 3個(gè)關(guān)鍵步驟,如下:

          1. 如何監(jiān)聽 url 的變化 ?
          2. 如何匹配 path ?
          3. 渲染對(duì)應(yīng)的組件

          我們不自己來(lái)實(shí)現(xiàn),直接看源碼,站在巨人的肩膀上來(lái)學(xué)習(xí)??。接下來(lái)我們來(lái)看一下 react-router-dom 官方文檔 的基本使用。

          export default function App({
            return (
              <BrowserRouter>
                <div>
                   <Link to="/">Home</Link>
                   <Link to="/about">About</Link>
                   <Link to="/topics">Topics</Link>
                  <Switch>
                    <Route path="/about">
                      <About />
                    </Route>
                    <Route path="/">
                      <Home />
                    </Route>
                  </Switch>
                </div>
              </BrowserRouter>

            );
          }

          從代碼中,我們可以觀察到下面幾點(diǎn):

          1. 最外層包裹了<BrowserRouter> ,它有什么意義?
          2. <Route />匹配的外層,包裹了<Switch>,作用是如果匹配了一個(gè),則不會(huì)再繼續(xù)渲染另外一個(gè)。如何實(shí)現(xiàn)?
          3. Route 中有 path 匹配路徑,包裹的則是渲染的組件。

          整體設(shè)計(jì)

          我們用一張圖來(lái)理解一下整個(gè) react-router 是怎么實(shí)現(xiàn)的:

          接下來(lái)我們看看每一個(gè)步驟是怎么實(shí)現(xiàn)的。

          一、監(jiān)聽 URL 的變化

          正常情況下,當(dāng) URL 發(fā)生變化時(shí),瀏覽器會(huì)像服務(wù)端發(fā)送請(qǐng)求,但使用以下2種辦法不會(huì)向服務(wù)端發(fā)送請(qǐng)求:

          • 基于 hash
          • 基于 history

          react-router 使用了 history 這個(gè)核心庫(kù)。

          1. 選擇方式:  history 或 hash

          HashRouter 先是從 history 中引用 createBrowserHistory ,然后將 history 和 children 傳入到 Router 。BrowseHistory同理。

          BrowseHistory 必須依賴服務(wù)器讓 url 都映射到 index.html ,否則會(huì) 404 。

          2. 監(jiān)聽 URL 的變化,拿到對(duì)應(yīng)的 history,location,match 等通過 Provider 注入到子組件中。

          二、Route 中匹配渲染組件

          這代碼可以分兩部分理解:

          1. 是否匹配
          2. 渲染組件
          1. 是否匹配

          computedMatch 是使用 Switch 包裹的子組件才有的值,Switch的作用是從上到下開始渲染,只要匹配到一個(gè),其他的就不匹配。所以這里會(huì)先判斷 computedMatch 。

          匹配解析 path ,這里使用了第三方庫(kù)  path-to-regexp
          // Make sure you consistently `decode` segments.
          const fn = match("/user/:id", { decodedecodeURIComponent });

          fn("/user/123"); //=> { path: '/user/123', index: 0, params: { id: '123' } }
          fn("/invalid"); //=> false
          fn("/user/caf%C3%A9"); //=> { path: '/user/caf%C3%A9', index: 0, params: { id: 'café' } }
          2. 組件渲染方式

          從文檔來(lái)看,它支持三種方式的渲染,如下:

          // children 方式
          <Route exact path="/">
             <HomePage />
          </Route>

          /
          / func 方式
          <Route
             path="/
          blog/:slug"
             render={({ match }) => {
               // Do whatever you want with the match...
               return <div />;
             }}
          />

          // component 方式
          <Route path="
          /user/:username" component={User} />

          源碼部分如下:


          吐槽一下,作者怎么就不能好好用 if else 來(lái)寫,非要寫這么多變態(tài)的 ?:,請(qǐng)不要學(xué)習(xí),除非你的項(xiàng)目只有你一個(gè)前端??。


          一下子看不懂也沒關(guān)系,我們來(lái)看下面的流程圖。

          從上面的代碼我們可以看出:

          1. Router 渲染的優(yōu)先級(jí):children >  component > render,三種方式互斥,只能使用一種。
          2. 不匹配的情況下,只要 children 是函數(shù),也會(huì)渲染
          3. component 是使用 createComponent 來(lái)創(chuàng)建的, 這會(huì)導(dǎo)致不再更新現(xiàn)有組件,而是直接卸載再去掛載一個(gè)新的組件。如果是使用匿名函數(shù)來(lái)傳入 component ,每次 render 的時(shí)候,這個(gè) props 都不同,會(huì)導(dǎo)致重新渲染掛載組件,導(dǎo)致性能特別差。因此,當(dāng)使用匿名函數(shù)的渲染時(shí),請(qǐng)使用 render 或 children 。
          // 不要這么使用
          <Route path="/user/:username" component={() => <User/> } />

          結(jié)論

          1. 對(duì)于依賴關(guān)系大,同類型的包使用 lerna 來(lái)統(tǒng)一管理。盡量抽象出共用不可變的地方,比如 react-router 中的方法。

          2. React-router 使用了Compound components(復(fù)合組件模式),在這種模式中,組件將被一起使用,它們可以方便的共享一種隱式的狀態(tài),比如 Switch , 可以在這里通過 React.children 來(lái)控制包裹組件的渲染優(yōu)先級(jí),而無(wú)須使用者去控制。再比如我們經(jīng)常使用的 <select /><option>, 可以通過 React.children 和 React.cloneElement 來(lái)劫持修改子組件,讓組件使用者通過更少的 api 來(lái)觸發(fā)更強(qiáng)大的功能。


             
          關(guān), 時(shí)優(yōu)質(zhì). 



          公眾號(hào)后臺(tái)回復(fù)「加群」,拉你進(jìn)交流劃水聊天群,有看到好文章/代碼都會(huì)發(fā)在群里。

          如果你不想加群,只是想加我也是可以。

          如果覺得這篇文章還不錯(cuò),來(lái)個(gè)【轉(zhuǎn)發(fā)、收藏、在看】三連吧,讓更多的人也看到~

          ?? 順手點(diǎn)個(gè)在看唄 ↓


          瀏覽 51
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲人成电影网网站 | 豆花视频免费看 | 东京热中文 | 欧美黄A片视频 | 成人自拍小视频 |