尝试了解 react-router-dom 源码(一)--BrowserRouter

155 阅读1分钟
  • react-router-dom源码

    • react-router-dom的目录结构:

      因为在使用的时候是使用BrowserRouter或者HashRouter包裹所有的Route。 (不过还有个MemoryRouter这个我还不了解)
      所以先看BrowserRouter或者HashRouter文件


    • BrowserRouter.js

      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
        history = createHistory(this.props);
    
        render() {
          return <Router history={this.history} children={this.props.children} />;
        }
      }
      export default BrowserRouter;
    

    • Router -- 目录 react-router/packages/react-router/modules/Router.js
      1. 创建history
      2. 监听 location,如果location发生改变,通过setState将子组件重新渲染;
      3. 通过context将history/location/
     import React from "react";
     import HistoryContext from "./HistoryContext.js";
     import RouterContext from "./RouterContext.js";
    
     /**
      * The public API for putting history on context.
      */
     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._isMounted = false;
         this._pendingLocation = null;
    
         if (!props.staticContext) {
         
           //监听location -- 如果路由发生变化 那么子路由就会重新渲染
           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();
           this._isMounted = false;
           this._pendingLocation = null;
         }
       }
    
       render() {
         return (
           <RouterContext.Provider
             value={{
               history: this.props.history,
               location: this.state.location,
               match: Router.computeRootMatch(this.state.location.pathname),
               staticContext: this.props.staticContext
             }}
           >
             <HistoryContext.Provider
               children={this.props.children || null}
               value={this.props.history}
             />
           </RouterContext.Provider>
         );
       }
     }
     export default Router;
    

    history的createBrowserHistory方法 -- 返回一个 history 对象 --其实使用的是H5的history

    ```js
    // var globalHistory = window.history;
    var history={
        length: globalHistory.length,//
        action: 'POP',
        location: initialLocation,
        createHref: createHref,
        push: push,
        replace: replace,
        go: go,// globalHistory.go(n);
        goBack: goBack, // go(-1);
        goForward: goForward, // go(1)
        block: block,// prompt
        listen: listen //监听
    }
    ```
    
    • 小结
      1. BrowserRouter 通过RouterContext (context ) 向所有子组件提供 history/location/match属性。
      2. BrowserRouter 通过history.listen 监听 location,如果路由发生变化通过this.setState({ location })更新location并提供给Route。
    • 下一篇 route