前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2020-5-16-React-Router源码简析

2020-5-16-React-Router源码简析

作者头像
黄腾霄
发布2020-06-10 15:21:05
9280
发布2020-06-10 15:21:05
举报
文章被收录于专栏:黄腾霄的博客黄腾霄的博客

今天来和大家解析下React-Router的源码。


React-Router是React生态中最重要的组件之一。

他提供了动态的前端路由功能,能让我们在前端应用实现,高效的SPA应用。

那么这个东西是怎么实现的呢?

我们来一起看下它的源码

Router.js

constructor(props) {
    super(props);

    this.state = {
      location: props.history.location
    };

    // This is a bit of a hack. We have to start listening for location
    // changes here in the constructor in case there are any <Redirect>s
    // on the initial render. If there are, they will replace/push when
    // they mount and since cDM fires in children before parents, we may
    // get a new location before the <Router> is mounted.
    this._isMounted = false;
    this._pendingLocation = null;

    if (!props.staticContext) {
      this.unlisten = props.history.listen(location => {
        if (this._isMounted) {
          this.setState({ location });
        } else {
          this._pendingLocation = location;
        }
      });
    }
  }

render() {
    return (
      <RouterContext.Provider
        value=
      >
        <HistoryContext.Provider
          children={this.props.children || null}
          value={this.props.history}
        />
      </RouterContext.Provider>
    );
  }

在Router中我们主要看它的构造函数和render函数。

这里构造函数中将location作为自己的state,并且监听了location的变化。

在render中利用了React的Context提供了RouterContext,HistoryContext两个Context信息,供子元素使用。

值得注意的是match: Router.computeRootMatch(this.state.location.pathname),

这里Router利用了当前location的pathname计算,指向了根地址

Route.js

class Route extends React.Component {
  render() {
    return (
      <RouterContext.Consumer>//获取RouterContext共享的state
        {context => {
          invariant(context, "You should not use <Route> outside a <Router>");
			//计算location,match,并组装至props
          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 };
			//从this.props解析children,component,render
          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) && children.length === ) {
            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>
    );
  }
}

Route.js的源码如上,比较长,我在关键处做了注释,我们一步步解析。

首先最外层是RouterContext.Consumer,用于获取父组件定义的RouterContext的状态。

用于辅助计算location,和match,并且封装仅props,在下面的渲染中,又作为RouterContext.Provider 的参数向下传递。

这样的好处主要是实现,嵌套路由,父元素Route处理部分路由,子元素继续处理。

核心渲染

{props.match
  ? children
    ? typeof children === "function"
      ? children(props)
      : children
    : component
      ? React.createElement(component, props)
      : render
        ? render(props)
        : null
  : typeof children === "function"
    ? children(props)
    : null}

上面一段是Route的核心渲染方法,利用了嵌套的三元函数,决定了如何进行组件渲染(已删减调试方法)。

思维导图如下

image-20200516191553253
image-20200516191553253

当props匹配了路由时,先判断是否匹配,如果不匹配就将props向下传递。

如果匹配了,先判断是否存在children,如果存在优先选择children。

否则再判断是否存在component,如果是,就调用React的createElement,创建React组件

否则,如果有render,则调用render方法。

源码解析

我们可以从上述的源码中看到:

  • Route的component,render,children三个属性是互斥的
  • 优先级children>component>render
  • children在无论路由匹配与否,都会渲染

这一点也可以在React-Router的官网中得到相应的信息

image-20200516192349663
image-20200516192349663

小结

通过分析源码我们了解到了

  • React-Router通过监听location变化触发刷新,实现路由更新
  • 利用React的Context机制,实现嵌套路由分析,和状态传递
  • Route组件中component,render,children三个属性的渲染机制
  • 所有的机制都在render中,所以能够在渲染时进行动态路由

参考文档:


本文会经常更新,请阅读原文: https://xinyuehtx.github.io/post/React-Router%E6%BA%90%E7%A0%81%E7%AE%80%E6%9E%90.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名黄腾霄(包含链接: https://xinyuehtx.github.io ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-05-16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Router.js
  • Route.js
    • 核心渲染
      • 源码解析
      • 小结
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档