浅谈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 相关文章推荐
Ext.FormPanel 提交和 Ext.Ajax.request 异步提交函数的区别
Nov 12 Javascript
写js时遇到的一些小问题
Dec 06 Javascript
关于javascript中的typeof和instanceof介绍
Dec 04 Javascript
如何使用json在前后台进行数据传输实例介绍
Apr 11 Javascript
javascript学习总结之js使用技巧
Sep 02 Javascript
jQuery实现日期联动效果实例
Jul 26 Javascript
JavaScript探测CSS动画是否已经完成的方法
Aug 30 Javascript
jQuery Validate 数组 全部验证问题
Jan 12 Javascript
bootstrap表单按回车会自动刷新页面的解决办法
Mar 08 Javascript
微信小程序实现发送验证码按钮效果
Dec 20 Javascript
微信小程序之onLaunch与onload异步问题详解
Mar 28 Javascript
Layui实现数据表格中鼠标悬浮图片放大效果,离开时恢复原图的方法
Sep 11 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
PHP 获取远程文件大小的3种解决方法
2013/07/11 PHP
php实现XSS安全过滤的方法
2015/07/29 PHP
一段实用的php验证码函数
2016/05/19 PHP
PHP中使用OpenSSL生成证书及加密解密
2017/02/05 PHP
Javascript的IE和Firefox兼容性汇编(zz)
2007/02/02 Javascript
Zero Clipboard js+swf实现的复制功能使用方法
2010/03/07 Javascript
JS 进度条效果实现代码整理
2011/05/21 Javascript
jquery获取复选框被选中的值
2014/03/22 Javascript
javascript实现input file上传图片预览效果
2015/12/31 Javascript
Javascript实现汉字和拼音互转的终极方案
2016/10/19 Javascript
利用python分析access日志的方法
2016/10/26 Javascript
jquery实现输入框实时输入触发事件代码
2016/12/21 Javascript
Bootstrap select下拉联动(jQuery cxselect)
2017/01/04 Javascript
关于vuex的学习实践笔记
2017/04/05 Javascript
jQuery插件artDialog.js使用与关闭方法示例
2017/10/09 jQuery
Popup弹出框添加数据实现方法
2017/10/27 Javascript
使用elementUI实现将图片上传到本地的示例
2018/09/04 Javascript
webpack4 SplitChunks实现代码分隔详解
2019/05/23 Javascript
layui富文本编辑器前端无法取值的解决方法
2019/09/18 Javascript
JS PHP字符串截取函数实现原理解析
2020/08/29 Javascript
[01:38:19]夜魇凡尔赛茶话会 第五期
2021/03/11 DOTA
Python中optionParser模块的使用方法实例教程
2014/08/29 Python
在Python下利用OpenCV来旋转图像的教程
2015/04/16 Python
python实现备份目录的方法
2015/08/03 Python
关于Django外键赋值问题详解
2017/08/13 Python
PyTorch 1.0 正式版已经发布了
2018/12/13 Python
简单了解python关系(比较)运算符
2019/07/08 Python
python入门:argparse浅析 nargs='+'作用
2020/07/12 Python
HTML5 canvas基本绘图之绘制阴影效果
2016/06/27 HTML / CSS
HTML5本地数据库基础操作详解
2016/04/26 HTML / CSS
股指期货心得体会
2014/09/13 职场文书
征用土地赔偿协议书
2014/09/26 职场文书
大学生助学金感谢信
2015/01/21 职场文书
Python实现打乒乓小游戏
2021/09/25 Python
java多态注意项小结
2021/10/16 Java/Android
vue-cil之axios的二次封装与proxy反向代理使用说明
2022/04/07 Vue.js