浅谈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 相关文章推荐
优化javascript的执行效率一些方法总结
Dec 25 Javascript
原生Javascript封装的一个AJAX函数分享
Oct 11 Javascript
jquery无限级联下拉菜单简单实例演示
Nov 23 Javascript
jQuery解决IE6、7、8不能使用 JSON.stringify 函数的问题
May 31 Javascript
再谈javascript常见错误及解决方法
Sep 16 Javascript
完美解决IE9浏览器出现的对象未定义问题
Sep 29 Javascript
Html5 js实现手风琴效果
Apr 17 Javascript
vue中实现methods一个方法调用另外一个方法
Feb 08 Javascript
koa socket即时通讯的示例代码
Sep 07 Javascript
详解微信图片防盗链“此图片来自微信公众平台 未经允许不得引用”的解决方案
Apr 04 Javascript
Vue中keep-alive组件的深入理解
Aug 23 Javascript
教你一步步实现一个简易promise
Nov 02 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图片上传程序
2008/03/27 PHP
让PHP开发者事半功倍的十大技巧小结
2010/04/20 PHP
PHP7匿名类用法分析
2016/09/26 PHP
jquery 表单进行客户端验证demo
2009/08/24 Javascript
JS中的prototype与面向对象的实例讲解
2013/05/22 Javascript
jquery.cookie() 方法的使用(读取、写入、删除)
2013/12/05 Javascript
特殊情况下如何获取span里面的值
2014/05/20 Javascript
jquery处理页面弹出层查询数据等待操作实例
2015/03/25 Javascript
图片旋转、鼠标滚轮缩放、镜像、切换图片js代码
2020/12/13 Javascript
JavaScript中的Reflect对象详解(ES6新特性)
2016/07/22 Javascript
JS 根据子网掩码,网关计算出所有IP地址范围示例
2020/04/23 Javascript
JS排序算法之冒泡排序,选择排序与插入排序实例分析
2017/12/13 Javascript
vue组件横向树实现代码
2018/08/02 Javascript
vue-cli的工程模板与构建工具详解
2018/09/27 Javascript
解决layUI的页面显示不全的问题
2019/09/20 Javascript
JavaScript设计模型Iterator实例解析
2020/01/22 Javascript
使用Vue+Django+Ant Design做一个留言评论模块的示例代码
2020/06/01 Javascript
urllib2自定义opener详解
2014/02/07 Python
python使用PyFetion来发送短信的例子
2014/04/22 Python
python实现画圆功能
2018/01/25 Python
Python cookbook(数据结构与算法)实现优先级队列的方法示例
2018/02/18 Python
在cmd命令行里进入和退出Python程序的方法
2018/05/12 Python
使用PyInstaller将python转成可执行文件exe笔记
2018/05/26 Python
python+openCV利用摄像头实现人员活动检测
2019/06/22 Python
解决python中import文件夹下面py文件报错问题
2020/06/01 Python
Python使用struct处理二进制(pack和unpack用法)
2020/11/12 Python
python自动生成证件号的方法示例
2021/01/14 Python
python程序实现BTC(比特币)挖矿的完整代码
2021/01/20 Python
CSS3之2D与3D变换的实现方法
2019/01/28 HTML / CSS
墨西哥皇宫度假村预订:Palace Resorts
2018/06/16 全球购物
银行贷款收入证明
2014/10/17 职场文书
学生会个人总结范文
2015/02/15 职场文书
校运会通讯稿
2015/07/18 职场文书
解决sql server 数据库,sa用户被锁定的问题
2021/06/11 SQL Server
SpringBoot整合MongoDB的实现步骤
2021/06/23 MongoDB
CSS 鼠标选中文字后改变背景色的实现代码
2023/05/21 HTML / CSS