浅谈react-router@4.0 使用方法和源码分析


Posted in Javascript onJune 04, 2019

react-router-dom@4.3.0 || react-router@4.4.1

react-router 使用方法

配置 router.js

import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';

const router = [{
  path: '/',
  exact: true,
  component:importPath({
   loader: () => import(/* webpackChunkName:"home" */ "pages/home/index.js"),
  }),
 },]
const Routers = () => (
 <main>
  <Switch>
   {
    router.map(({component,path,exact},index)=>{
     return <Route exact={exact} path={path} component={component} key={path} />
    })
   }
  </Switch>
 </main>
);

export default Routers;

入口 index.js

import {HashRouter} from 'react-router-dom';
import React from 'react';
import ReactDOM from 'react-dom';
import Routers from './router';

ReactDOM.render (
   <HashRouter>
    <Routers />
   </HashRouter>,
 document.getElementById ('App')
);

home.js

import { withRouter } from "react-router-dom";

@withRouter
class Home extends React.Component<PropsType, stateType> {
 constructor(props: PropsType) {
  super(props);
  this.state = {};
 }
 goPath=()=>{
   this.props.history.push('/home')
 }
 render() {
  return (
   <div onClick={this.goPath}>home</div>
  );
 }
export default Home;

react-router 源码解析

下面代码中会移除部分的类型检查和提醒代码,突出重点代码

第一步 Switch react-router

function _possibleConstructorReturn(self, call) {
 if (!self) {
  throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
 }
 if(call&&(typeof call === "object" || typeof call === "function") ){
  return call
 }else {
  return self
 }
}
var Switch = function (_React$Component) {
 function Switch() {
  //使用传递进来的组件覆盖本身
  return _possibleConstructorReturn(this, _React$Component.apply(this, arguments)); 
 }
 Switch.prototype.render = function render() {
  var route = this.context.router.route;
  var children = this.props.children;
  var location = this.props.location || route.location;
  var match = void 0,child = void 0;
  
  //检查element是否是react组件,初始match为null,
  React.Children.forEach(children, function (element) {
   //如果match符合,forEach不会进入该if
   if (match == null && React.isValidElement(element)) { 
    var _element$props = element.props,
      pathProp = _element$props.path,
      exact = _element$props.exact,
      strict = _element$props.strict,
      sensitive = _element$props.sensitive,
      from = _element$props.from;
    var path = pathProp || from;
    child = element; 
    //检查当前配置是否符合,
    match = matchPath(location.pathname, { path: path, exact: exact, strict: strict, sensitive: sensitive }, route.match); 
   }
  });
  //如果有匹配元素,则返回克隆child
  return match ? React.cloneElement(child, { location: location, computedMatch: match }) : null;
 };

 return Switch;
}(React.Component);

总结:switch根据location.pathname,path,exact,strict,sensitive获取元素并返回element

第二步 Route react-router

var Route = function (_React$Component) {
 function Route() {
  var _temp, _this, _ret;
  //获取参数
  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
   args[_key] = arguments[_key];
  }
  //修改this
  return _ret = (
   _temp = (_this = _possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), 
   //检查当前元素是否符合match
   _this.state = {match: _this.computeMatch(_this.props,_this.context.router)},_temp),
    //这里是真正return
    _possibleConstructorReturn(_this, _ret); 
 }
 // 设置content
 Route.prototype.getChildContext = function getChildContext() {
  return {
   router: _extends({}, this.context.router, {
    route: {
     location: this.props.location || this.context.router.route.location,
     match: this.state.match
    }
   })
  };
 };
 // 根据参数检查当前元素是否符合匹配规则
 Route.prototype.computeMatch = function computeMatch(_ref, router) {
  var computedMatch = _ref.computedMatch,
    location = _ref.location,
    path = _ref.path,
    strict = _ref.strict,
    exact = _ref.exact,
    sensitive = _ref.sensitive;

  if (computedMatch) return computedMatch;

  var route = router.route;

  var pathname = (location || route.location).pathname;

  return matchPath(pathname, { path: path, strict: strict, exact: exact, sensitive: sensitive }, route.match);
 };
 // 设置match
 Route.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps, nextContext) {
  this.setState({
   match: this.computeMatch(nextProps, nextContext.router)
  });
 };

 Route.prototype.render = function render() {
  var match = this.state.match;
  var _props = this.props,
    children = _props.children,
    component = _props.component,
    render = _props.render;
  var _context$router = this.context.router,
    history = _context$router.history,
    route = _context$router.route,
    staticContext = _context$router.staticContext;

  var location = this.props.location || route.location;
  var props = { match: match, location: location, history: history, staticContext: staticContext };
  //检查route 是否有component组
  if (component) return match ? React.createElement(component, props) : null; 
  // 检查是否包含render 组件
  if (render) return match ? render(props) : null;
  // withRouter 使用的方式
  if (typeof children === "function") return children(props);

  if (children && !isEmptyChildren(children)) return React.Children.only(children);

  return null;
 };

 return Route;
}(React.Component);

总结:route 渲染的方式: component render children,代码示例用的是component,route 是检查当前组件是否符合路由匹配规则并执行创建过程

第三步 HashRouter react-router-dom

import Router from './Router'
import {createHistory} from 'history'
var HashRouter = function (_React$Component) {
 function HashRouter() {
  var _temp, _this, _ret;
  //参数转换为数组
  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { 
   args[_key] = arguments[_key];
  }
  return _ret = (
   _temp = (_this = _possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this),
    _this.history = createHistory(_this.props), _temp), //创建history
    _possibleConstructorReturn(_this, _ret); //真正返回的东西 返回this
 }
 HashRouter.prototype.render = function render() {
  // 返回一个Router,并且把history,children传递给Router
  return React.createElement(Router, { history: this.history, children: this.props.children });
 };
 return HashRouter;
}(React.Component);

总结 通过 history库里面 createHistory 创建路由系统

第四部 Router react-router

var Router = function (_React$Component) {
 function Router() {
  var _temp, _this, _ret;
  //获取参数,和其他组件一样
  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
   args[_key] = arguments[_key];
  }
  return _ret = (_temp = (_this = _possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.state = {
   match: _this.computeMatch(_this.props.history.location.pathname) //返回路由对象
  }, _temp), _possibleConstructorReturn(_this, _ret); //返回this
 }
 // 返回context
 Router.prototype.getChildContext = function getChildContext() {
  return {
   router: _extends({}, this.context.router, {
    history: this.props.history,
    route: {
     location: this.props.history.location,
     match: this.state.match
    }
   })
  };
 };
  
 Router.prototype.computeMatch = function computeMatch(pathname) {
  return {
   path: "/",
   url: "/",
   params: {},
   isExact: pathname === "/"
  };
 };

 Router.prototype.componentWillMount = function componentWillMount() {
  var _this2 = this;

  var _props = this.props,
    children = _props.children,
    history = _props.history;

  // 启动监听 当hash 改变是做一次检查,并返回unlisten 取消事件
  this.unlisten = history.listen(function () {
   _this2.setState({
    match: _this2.computeMatch(history.location.pathname)
   });
  });
 };
 //销毁前取消监听
 Router.prototype.componentWillUnmount = function componentWillUnmount() {
  this.unlisten();
 };
 // children是HashRouter 传递进来的
 Router.prototype.render = function render() {
  var children = this.props.children;
  return children ? React.Children.only(children) : null;
 };

 return Router;
}(React.Component);

总结 history是一个JavaScript库,可让您在JavaScript运行的任何地方轻松管理会话历史记录。history抽象出各种环境中的差异,并提供最小的API,使您可以管理历史堆栈,导航,确认导航以及在会话之间保持状态。

第五部 withRouter <react-router>

var withRouter = function withRouter(Component) {
 var C = function C(props) {
  //获取props
  var wrappedComponentRef = props.wrappedComponentRef,
    remainingProps = _objectWithoutProperties(props, ["wrappedComponentRef"]);
  // Route 组件 children方式
  return React.createElement(Route, {
   children: function children(routeComponentProps) {
    // 这里使用的是route 组件 children(props)
    //routeComponentProps 实际等于 { match: match, location: location, history: history, staticContext: staticContext };
    return React.createElement(Component, _extends({}, remainingProps, routeComponentProps, {
     ref: wrappedComponentRef
    }));
   }
  });
 };

 C.displayName = "withRouter(" + (Component.displayName || Component.name) + ")";
 C.WrappedComponent = Component;
 // 该类似于object.assign(C,Component),得到的结果是C
 return hoistStatics(C, Component);
};

到这里真个流程基本结束了,这只是react-router的一种使用方式的解析,本文的目的是理解react-router的运行机制,如果有什么错误还望指出,谢谢

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
juqery 学习之五 文档处理 包裹、替换、删除、复制
Feb 11 Javascript
SOSO地图API使用(一)在地图上画圆实现思路与代码
Jan 15 Javascript
jquery取消事件冒泡的三种方法(推荐)
May 28 Javascript
手机端 HTML5使用photoswipe.js仿微信朋友圈图片放大效果
Aug 25 Javascript
AngularJS自定义插件实现网站用户引导功能示例
Nov 07 Javascript
JavaScript cookie详解及简单实例应用
Dec 31 Javascript
js中开关变量使用实例
Feb 24 Javascript
基于jQuery Ajax实现下拉框无刷新联动
Dec 06 jQuery
JS数组进阶示例【数组的几种函数用法】
Jan 16 Javascript
vue实现购物车结算功能
Jun 18 Javascript
vue 实现动态路由的方法
Jul 06 Javascript
Vue实现购物车基本功能
Nov 08 Javascript
vue axios post发送复杂对象问题
Jun 04 #Javascript
vue 2.5.1 源码学习 之Vue.extend 和 data的合并策略
Jun 04 #Javascript
vue实现分环境打包步骤(给不同的环境配置相对应的打包命令)
Jun 04 #Javascript
JavaScript实现页面中录音功能的方法
Jun 04 #Javascript
vue elementUI 表单校验功能之数组多层嵌套
Jun 04 #Javascript
小程序根据手机机型设置自定义底部导航距离
Jun 04 #Javascript
js回文数的4种判断方法示例
Jun 04 #Javascript
You might like
星际争霸兵种名称对照表
2020/03/04 星际争霸
PHP连接MongoDB示例代码
2012/09/06 PHP
PHP设计模式之PHP迭代器模式讲解
2019/03/22 PHP
在第一个input框内输入内容.textarea自动得到第一个文件框的值的javascript代码
2007/04/20 Javascript
js function使用心得
2010/05/10 Javascript
javascript对数组的常用操作代码 数组方法总汇
2011/01/27 Javascript
使用jQuery避免鼠标双击的解决方案
2013/08/21 Javascript
Nodejs全栈框架StrongLoop推荐
2014/11/09 NodeJs
js获取当前时间显示在页面上并每秒刷新
2014/12/24 Javascript
avalonjs实现仿微博的图片拖动特效
2015/05/06 Javascript
使用ngView配合AngularJS应用实现动画效果的方法
2015/06/19 Javascript
jQuery实现可关闭固定于底(顶)部的工具条菜单效果
2015/11/06 Javascript
AngularJS 使用 UI Router 实现表单向导
2016/01/29 Javascript
微信小程序 form组件详解
2016/10/25 Javascript
jQuery实现的简单在线计算器功能
2017/05/11 jQuery
VUE利用vuex模拟实现新闻点赞功能实例
2017/06/28 Javascript
mui 打开新窗口的方式总结及注意事项
2017/08/20 Javascript
深入浅析javascript继承体系
2017/10/23 Javascript
Vue 框架之动态绑定 css 样式实例分析
2018/11/14 Javascript
Bootstrap4 gulp 配置详解
2019/01/06 Javascript
JQuery特殊效果和链式调用操作示例
2019/05/13 jQuery
微信小程序本地存储实现每日签到、连续签到功能
2019/10/09 Javascript
vue.js实现三级菜单效果
2019/10/19 Javascript
[04:30]显微镜下的DOTA2第五期——拉比克
2013/09/26 DOTA
Django中的CBV和FBV示例介绍
2018/02/25 Python
Python产生Gnuplot绘图数据的方法
2018/11/09 Python
解决python Markdown模块乱码的问题
2019/02/14 Python
python基于pygame实现飞机大作战小游戏
2020/11/19 Python
python中用ggplot绘制画图实例讲解
2021/01/26 Python
便携式太阳能系统的创新者:GOAL ZERO
2018/02/04 全球购物
美国全球旅游运营商:Pacific Holidays
2018/06/18 全球购物
优秀信贷员先进事迹
2014/01/31 职场文书
意向书范本
2014/07/29 职场文书
入党积极分子个人总结
2015/03/02 职场文书
Python办公自动化之教你用Python批量识别发票并录入到Excel表格中
2021/06/26 Python
Python中文纠错的简单实现
2021/07/07 Python