浅谈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实现页面打印的三种方法
Mar 05 Javascript
Jquery 模板数据绑定插件的使用方法详解
Jul 08 Javascript
JS加jquery简单实现标签元素的显示或隐藏
Sep 23 Javascript
基于Node.js的JavaScript项目构建工具gulp的使用教程
May 20 Javascript
Bootstrap下拉菜单样式
Feb 07 Javascript
基于Vue实现页面切换左右滑动效果
Jun 29 Javascript
ES6使用Set数据结构实现数组的交集、并集、差集功能示例
Oct 31 Javascript
JavaScript图片处理与合成总结
Mar 04 Javascript
记录一篇关于redux-saga的基本使用过程
Aug 18 Javascript
vue+ElementUI 关闭对话框清空验证,清除form表单的操作
Aug 06 Javascript
详解如何在Javascript中使用Object.freeze()
Oct 18 Javascript
微信小程序实现可长按移动控件
Nov 01 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中的正规表达式(二)
2006/10/09 PHP
php学习之 认清变量的作用范围
2010/01/26 PHP
比较discuz和ecshop的截取字符串函数php版
2012/09/03 PHP
php调用c接口无错版介绍
2014/03/11 PHP
PHP实现获取中英文首字母
2015/06/19 PHP
Laravel多域名下字段验证的方法
2019/04/04 PHP
laravel 获取某个查询的查询SQL语句方法
2019/10/12 PHP
传智播客学习之JavaScript基础篇
2009/11/13 Javascript
js实现瀑布流的一种简单方法实例分享
2013/11/04 Javascript
判断JS对象是否拥有某种属性的两种方式
2013/12/02 Javascript
Jquery方式获取iframe页面中的 Dom元素
2014/05/07 Javascript
使用Promise解决多层异步调用的简单学习心得
2016/05/17 Javascript
javascript cookie用法基础教程(概念,设置,读取及删除)
2016/09/20 Javascript
原生js实现新闻列表展开/收起全文功能
2017/01/20 Javascript
微信小程序扫描二维码获取信息实例详解
2019/05/07 Javascript
vue 路由守卫(导航守卫)及其具体使用
2020/02/25 Javascript
python中split方法用法分析
2015/04/17 Python
在Python中使用列表生成式的教程
2015/04/27 Python
解决Mac安装scrapy失败的问题
2018/06/13 Python
python编辑用户登入界面的实现代码
2018/07/16 Python
python pandas读取csv后,获取列标签的方法
2018/11/12 Python
Python matplotlib的使用并自定义colormap的方法
2018/12/13 Python
Python面向对象进阶学习
2019/05/21 Python
pytorch实现focal loss的两种方式小结
2020/01/02 Python
详解向scrapy中的spider传递参数的几种方法(2种)
2020/09/28 Python
一款纯css3实现的响应式导航
2014/10/31 HTML / CSS
html5 Canvas画图教程(8)—canvas里画曲线之bezierCurveTo方法
2013/01/09 HTML / CSS
美国顶级防滑鞋:Shoes For Crews
2017/03/27 全球购物
学生打架检讨书
2014/02/14 职场文书
社区班子对照检查材料
2014/08/27 职场文书
2014年国庆节演讲稿精选范文1500字
2014/09/25 职场文书
机关作风建设整改方案
2014/10/27 职场文书
2014小学一年级班主任工作总结
2014/12/05 职场文书
团代会闭幕词
2015/01/28 职场文书
土建施工员岗位职责
2015/04/11 职场文书
起诉书格式范文
2015/05/20 职场文书