history apiから見る主流フレームワークのルーティングメカニズム


フロントエンドルーティングライブラリの役割は、アドレスバーを変更し、ブラウザの前進、後退をサポートし、ルーティングに対応するビューを同期することです.ここでreact-routerとその依存historyライブラリでルーティングメカニズムについて説明します.
テキストアドレス
前提条件
まず,フロントエンドルーティングメカニズムに依存するpushState,popstateイベント,hashおよび対応するhashChangeイベントについて簡単に紹介する.
  • pushState,popstate
  • html html 5のpushState、replaceStateメソッドの追加をサポートするブラウザでは、pushStateを設定することでブラウザhistoryスタックにレコード
  • を追加できます.
  • pushState()を設定、replaceState()の場合popstateイベントはトリガーされず、popstateイベントはブラウザの前進、後退ボタンまたはhistoryを呼び出すのみである.back()、history.forward()等時トリガ
  • pushState()メソッドの最初のパラメータは、stateオブジェクトを指定し、history.stateまたはpopstateイベントコールバックeventオブジェクト取得
  • 
    history.pushState(state,title,path)
    
    console.log(history.state)
    
    window.addEventListener('popstate',(e)=>{
    
        console.log(e.state)
    
    })
    
  • location.hash hashChange

  • pushStateメソッドがサポートされていないブラウザではlocationを変更できます.hashとhashChangeイベントによるルーティング機能
    window.addEventListener('hashchange',e=>{
    
    })
    
    location.hash="test"
    
  • 比較
  • historyを設定するpushState(state,title,path)では、対応するルーティングにstateを設定できます.これにより、ルーティング間のデータ転送に新しいルートが提供されます.また、stateオブジェクトはローカルに保存され、リフレッシュページは依然として存在しますが、hash方式で実現されたルーティングは使用できません.react-router v 4バージョンではstateのシミュレーションも削除されます.
    historyライブラリの紹介
    historyライブラリは、ブラウザにwindowを内蔵する3つの異なる方法でhistoryオブジェクトを作成します.historyメソッドの拡張、 push,go,goBack,goForward , location、listen は、ブラウザ環境以外のpolyfillを実現します.
    createBrowserHistory()
    createHashHistory()
    createMemoryHistory()

    react-routerのルーティング実装(BrowserRouterとcreateBrowserHistory)
    react-routerルーティング実装の概要
  • historyを呼び出す.Pushジャンプルーティングの場合、内部でwindowを実行する.history.pushStateはブラウザhistoryスタックにレコードを追加しurlを変更し、コンポーネント登録のコールバック関数を実行し、
  • createBrowserHistoryにpopstateイベントを登録し、ユーザーがブラウザをクリックして前進、後退するとpopstateイベントで現在のeventを取得する.state,locationを再アセンブリし,コンポーネント登録のコールバック関数
  • を実行する
  • historyライブラリはcreateBrowserHistoryメソッドを外部に暴露し、react-routerでcreateBrowserHistoryメソッドオブジェクトをインスタンス化し、コンポーネントにhistoryを登録する.Listen()コールバック関数、コンポーネントでlocation、同期UI
  • がルーティングが変更された場合
    別々に見る
    history.push
    reactではhistoryを呼び出すことができます.Push(path,state)はルーティングをジャンプし、実際に実行されるのはcreateBrowserHistoryのpushメソッドです.
    この方法では主に3つのことをします
  • 転送pathに基づいてstateパラメータはwindowとは異なるlocationを作成する.location,ここでlocationはこれらの属性
  • のみ
       location= {
          path:
          search:
          hash:
          state:
          key
        };
        const location = createLocation(path, state, createKey(), history.location);

    このlocationは、およびコンポーネントで使用され、locationの値とのpathマッチングに基づいて、正常なRouteコンポーネントレンダリングで指定されたcomponentに一致します.
  • globalHistoryを実行する.pushState({ key, state }, null, href);
  • 実行Routerに登録されているlistener
  • const action = "PUSH"
    setState({ action, location });
    
    const setState = nextState => {
        Object.assign(history, nextState);
    
        history.length = globalHistory.length;
    
        transitionManager.notifyListeners(history.location, history.action);
    };
    

    historyにおけるpopstateイベントの登録
    popstateイベントがトリガーするとeventが得られる.state,createBrowserHistoryではこのstateと現在のwindowに基づいています.locationはlocationオブジェクトを再生成し、Routerコンポーネント登録のlistenerを実行し、UIを同期する
    const setState = nextState => {
        Object.assign(history, nextState);
    
        history.length = globalHistory.length;
    
        transitionManager.notifyListeners(history.location, history.action);
      };
    
    const handlePop = location => {
        const action = "POP";
        setState({action,location)
    }
    
    コンポーネント
    BrowserRouterコンポーネントにはcreateBrowserHistoryオブジェクトがインスタンス化され、Routerコンポーネントに渡されます.
    class BrowserRouter extends React.Component{
    
        history = createHistory(this.props);
    
        render() {
            return ;
        }
    
    }

    Routerコンポーネントにhistoryを登録するListen()のリスニング関数で、サブコンポーネント(Route)で使用されるデータを保存します.
    getChildContext() {
        return {
          router: {
            ...this.context.router,
            history: this.props.history,
            route: {
              location: this.props.history.location, //history  location
              match: this.state.match
            }
          }
        };
      }
    componentWillMount{
        this.unlisten = history.listen(() => {
            this.setState({
                match: this.computeMatch(history.location.pathname)
            });
        });
    }
    
    

    呼び出し時pushまたはpopstateイベントをトリガーすると、ここに登録されているlistenerはcreateBrowserHistoryによって実行され、setStateがトリガーされ、Routerのサブコンポーネントで一致するものが再レンダリングされます.
    
    
    
    
    
    

    Routeにmatch状態があり、親コンポーネントpropsが変化したときに再計算されます.
    state = {
        match: this.computeMatch(this.props, this.context.router)
      };
    
    componentWillReceiveProps(nextProps, nextContext) {
        this.setState({
          match: this.computeMatch(nextProps, nextContext.router)
        });
    }
    //computeMatch                path             ,       
    
    render() {
    
        if (component) return match ? React.createElement(component, props) : null;
    
        if (render) return match ? render(props) : null;
    
    }

    まとめ
    まとめるとreact-routerのルーティングメカニズムは
  • historyライブラリを用いて、historyではpush,go,goBackなどの方法を実現し、popstateイベントを登録し、ルーティングジャンプ時にブラウザ内蔵のhistory apiを使用してhistoryスタック
  • を操作する
  • historyライブラリが外部に露出したhistoryオブジェクトはlistenメソッドを提供し、コンポーネントはlistener
  • を登録します.
  • hsitoryを呼び出す.Pushまたはpopstateイベントがトリガーされた場合、listener
  • を実行します.
  • 登録された傍受関数内部でsetState更新状態
  • のサブアセンブリのcomponentWillReceivePropsライフサイクル関数ではRouterのcontextが得られ、現在のpathとブラウザの現在のlocationから現在のrouteがmatchであるかどうかを判断し、一致するとcomponent
  • がレンダリングされる
    本稿ではreact-routerでルーティングメカニズムを紹介するが,主流のルーティングライブラリの実現原理はそれほど悪くない, pushState hash url, リファレンス
  • createBrowserHistory.js
  • locationUtil.js
  • BrowserRouter.js
  • Router.js
  • Route.js