Day07 從 React-router 看 History API


前言

注意:因系列專注討論 History API,並非完整看完 Reacr-Router Source Code,如有錯誤歡迎隨時留言跟我反應
History API 系列講了那麼多終於回到當初話題中 React-router 中的 Route 跟網址變換是怎麼實作出來的,其實看了前兩篇介紹 Anchor 及 History API,應該已經蠻能延伸理解 React-Router 的運作原理,本篇只是真正的揭開面紗,以下會先簡介 React-Router 基本的使用 RouterRouteSwitchLink,及背後是如何達成網頁及頁面的切換。

React Router 範例

直接取官網的 Basic Example 來回顧一下

function BasicExample() {
  return (
    <Router>
      <div>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/dashboard">Dashboard</Link></li>
        </ul>
        <hr />
        <Switch>
          <Route exact path="/"><Home /></Route>
          <Route path="/about"><About /></Route>
          <Route path="/dashboard"><Dashboard /></Route>
        </Switch>
      </div>
    </Router>
  );
}
  • 每次渲染時 Router 會依照當前 Router 路徑將 Route 內有寫的路徑 match 一輪,再將 match 到的 Route 內容交給 Switch 去 render。
  • 一進入到頁面時,Router 會使用網址上的路徑初始化一次,並執行對應渲染。
  • 點擊上方 Link 元件,會觸發將 Router 切換路徑為 Link prop 中 to 字串,進而重新 render Switch
  • 當點擊上頁、下頁跳轉時,網址切換會觸發 Router 路徑切換,進而重新 render Switch

閱讀 Source Code

範例選用 BrowserRouterRouter原始碼連結

一開頭註解就跟我們說了 React-Router 是使用 HTML5 History API 實作的 XD
但這邊使用的是一個再被包裝過的 History 套件,晚點來研究看看。

import React from "react";
import { Router } from "react-router";
import { createBrowserHistory as createHistory } from "history";

/**
 * The public API for a <Router> that uses HTML5 history.
 */
class BrowserRouter extends React.Component {
  history = createHistory(this.props);

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

Router 會使用 History 物件中的 location 網址路徑來初始化 Router 路徑(原始碼連結

/**
 * The public API for putting history on context.
 */
class Router extends React.Component {

    this.state = {
      location: props.history.location
    };
    ...以下省略70行
}

Link 元件會 render 出 LinkAnchor 元件,點擊 LinkAnchor 時(原始碼連結
預設會觸發 history 套件中的 push 或 replace method
沒錯,這兩個 method 也就是同 HTML5 history API 中的 pushState 及 replaceState。

const LinkAnchor = forwardRef(
    let props = {
        onClick= event => {
            ...省略
              event.preventDefault();
              navigate();
        }
    }
    return <a {...props} />;
)

const Link = forwardRef(
  (
      component = LinkAnchor,
      ...省略
  ) => {
    return (
      <RouterContext.Consumer>
        {context => {
          const { history } = context;

            navigate() {
              const location = resolveToLocation(to,context.location);
              const method = replace ? history.replace :history.push;

              method(location);
            }
          ...省略
          return React.createElement(component, props);
        }}
      </RouterContext.Consumer>
    );
  }
);

Switch 使用路徑找尋當前 render 對象(原始碼

class Switch extends React.Component {
  render() {
    return (
      <RouterContext.Consumer>
        {context => {
          const location = this.props.location || context.location;

          let element, match;
          React.Children.forEach(this.props.children, child => {
          ... 以下省略
        }}
      </RouterContext.Consumer>
    );
  }
}

再來稍微看一下 History 套件的使用方法

  • 結合了 location 物件,可以在套件中取得 history.location
  • 簡化了 pushState、replaceState 原本的 title 欄位,注重路徑對應的 State
  • 最大變化的事 onpopstate 事件也變成 history 內使用,可用 listen 及 unlisten 綁定及取消。
    import { createBrowserHistory } from 'history';
    
    const history = createBrowserHistory();
    
    // Get the current location.
    const location = history.location;
    
    // Listen for changes to the current location.
    const unlisten = history.listen((location, action) => {
      // location is an object like window.location
      console.log(action, location.pathname, location.state);
    });
    
    // Use push, replace, and go to navigate around.
    history.push('/home', { some: 'state' });
    
    // To stop listening, call the function returned from listen().
    unlisten();
    

小結

終於把 React-router 與 History API 關係這話題講完了,不知不覺也把七天文章寫完了,之後再來補寫一篇這七天寫作的心得吧,如果大家還有想了解哪些熱門套件背後的實作邏輯也歡迎在下方留言討論哦,先來去補眠了。

參考資源

#react-router #hisotry api #Web #javascript #Web API





近期正式轉職成為一個前端工程師 在工作的 Code Base 看多許多從未使用過的 Web API 想透過此次黑客松來翻翻文件 並把感覺實用、有趣的學習並紀錄起來 ~

留言討論