尝试了解 react-router-dom 源码(二) -- Route

279 阅读1分钟

Route源码

上篇 BrowserRouter通过context的value中提供history/location/match属性,那么Route可以通过RouterContext.Consumer 获取对应的熟悉。

  • Rout主要配置项:

    1. path: string | string[]
    2. exact: bool
    3. component
    4. children: func
    5. render: func

    在Route在都配置 children, component, render的时候,这三者是互斥的。优先级 children > component > render。

    ⚠️ 当为 component 时,是通过 React.createElement创建一个新的React Element 。如果为内联函数 那么每次都会产生个新的React Element(频繁的卸载组件)。

在了解优先级的情况下 看match 匹配规则部分就比较清楚了。

  • props.match
    1. 不匹配 -- 如果存在children 并且为 children === "function" 则执行children(props),否则返回null。
    2. 匹配 -- 按照 children/component/render 优先级处理。
      • . 如果为 children 如果存在 children 为函数的时候执行 children(props),不为函数 返回children
      • . 如果为component执行 React.createElement(component, props)
      • . 如果为render 执行 render(props)
      • . 否则 返回 null
 
{props.match? children
                    ? typeof children === "function"
                      ?children(props)
                      : children
                    : component
                    ? React.createElement(component, props)
                    : render
                    ? render(props)
                    : null
                  : typeof children === "function"
                  ?children(props): null}

  import React from "react";
  import invariant from "tiny-invariant";
  import RouterContext from "./RouterContext.js";
  import matchPath from "./matchPath.js";

  function isEmptyChildren(children) {
    return React.Children.count(children) === 0;
  }

  function evalChildrenDev(children, props, path) {
    const value = children(props);
    return value || null;
  }

  /**
   * The public API for matching a single path and rendering.
   */
  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) && isEmptyChildren(children)) {
              children = null;
            }

            return (
              <RouterContext.Provider value={props}>
                {props.match
                  ? children
                    ? typeof children === "function"
                      ? __DEV__
                        ? evalChildrenDev(children, props, this.props.path)
                        : children(props)
                      : children
                    : component
                    ? React.createElement(component, props)
                    : render
                    ? render(props)
                    : null
                  : typeof children === "function"
                  ? __DEV__
                    ? evalChildrenDev(children, props, this.props.path)
                    : children(props)
                  : null}
              </RouterContext.Provider>
            );
          }}
        </RouterContext.Consumer>
      );  
    }
  }
  export default Route;