React-router 4 按需加载的实现方式及原理详解


Posted in Javascript onMay 25, 2017

React-router 4

介绍了在router4以后,如何去实现按需加载Component,在router4以前,我们是使用getComponent的的方式来实现按需加载的,router4中,getComponent方法已经被移除,下面就介绍一下react-router4是入围和来实现按需加载的。

1.router3的按需加载方式

route3中实现按需加载只需要按照下面代码的方式实现就可以了。

const about = (location, cb) => {
  require.ensure([], require => {
    cb(null, require('../Component/about').default)
  },'about')
}

//配置route
<Route path="helpCenter" getComponent={about} />

2.router4按需加载方式(three steps)

one step:

创建Bundle.js文件,这个文件其实是个通过bundle-loader包装后的组件来使用,下面会具体讲这个东西。

import React from 'react';
import PropTypes from 'prop-types';

class Bundle extends React.Component {
 state = {
  // short for "module" but that's a keyword in js, so "mod"
  mod: null
 }

 componentWillMount() {
  // 加载初始状态
  this.load(this.props);
 }

 componentWillReceiveProps(nextProps) {
  if (nextProps.load !== this.props.load) {
   this.load(nextProps);
  }
 }

 load(props) {
  // 重置状态
  this.setState({
   mod: null
  });
  // 传入组件的组件
  props.load((mod) => {
   this.setState({
    // handle both es imports and cjs
    mod: mod.default ? mod.default : mod
   });
  });
 }

 render() {
  // if state mode not undefined,The container will render children
  return this.state.mod ? this.props.children(this.state.mod) : null;
 }
}

Bundle.propTypes = {
 load: PropTypes.func,
 children: PropTypes.func
};

export default Bundle;

second step:

import aContainer from 'bundle-loader?lazy!./containers/A'

const A = (props) => (
 <Bundle load={aContainer}>
   //这里只是给this.props.child传一个方法,最后在Bundle的render里面调用
  {(Container) => <Container {...props}/>}
 </Bundle>
)

third step:

render() {
  return (
   <div>
    <h1>Welcome!</h1>
    <Route path="/about" component={About}/>
    <Route path="/dashboard" component={A}/>
   </div>
  )
 }

3.router4按需加载方方式解析

(1).首先解释一下按需加载,通俗的将就是我当前的location在Home,那么我只应该加载Home的东西,而不应该去加载About等等其他的。

(2).Bundle.js这个文件的作用

先看这段代码:

module.exports = function (cb) {
  __webpack_require__.e/* require.ensure */(2).then((function (require) {
    cb(__webpack_require__(305));
  }).bind(null, __webpack_require__)).catch(__webpack_require__.oe);
};

这里是我们通过import loadDashboard from 'bundle-loader?lazy!./containers/A'这种方式引入的container控件。我们使用了bundle-loader将A的源码转化成了上面的代码,具体实现大家可以看bundle-loader源码,代码很少。

上面说到Bundle.js其实就使用来处理这个文件的,这个文件需要一个callback的参数,在Bundle的load方法中,我们会设置这个callback,当路由要调到A Container这里的时候,就回去加载A Container,然后调用这个callback,这个callback会调用setState方法,将我们之前传入的load设置给mod,然后渲染出来。

4.webpack进行bundle-loader统一配置

这里匹配的是src/routers/下面的containers文件夹下面所有的js文件,包括二级目录。

{
   // 匹配routers下面所有文件
   // ([^/]+)\/?([^/]*) 匹配xxx/xxx 或者 xxx
   test: /containers\/([^/]+)\/?([^/]*)\.jsx?$/,
   include: path.resolve(__dirname, 'src/routers/'),
   // loader: 'bundle-loader?lazy'
   loaders: ['bundle-loader?lazy', 'babel-loader']
  }

5.部分源码

1.bundle-loader的源码

var loaderUtils = require("loader-utils");

module.exports = function() {};
module.exports.pitch = function(remainingRequest) {
  this.cacheable && this.cacheable();
  var query = loaderUtils.getOptions(this) || {};
  if(query.name) {
    var options = {
      context: query.context || this.options.context,
      regExp: query.regExp
    };
    var chunkName = loaderUtils.interpolateName(this, query.name, options);
    var chunkNameParam = ", " + JSON.stringify(chunkName);
  } else {
    var chunkNameParam = '';
  }
  var result;
  if(query.lazy) {
    result = [
      "module.exports = function(cb) {\n",
      "  require.ensure([], function(require) {\n",
      "    cb(require(", loaderUtils.stringifyRequest(this, "!!" + remainingRequest), "));\n",
      "  }" + chunkNameParam + ");\n",
      "}"];
  } else {
    result = [
      "var cbs = [], \n",
      "  data;\n",
      "module.exports = function(cb) {\n",
      "  if(cbs) cbs.push(cb);\n",
      "  else cb(data);\n",
      "}\n",
      "require.ensure([], function(require) {\n",
      "  data = require(", loaderUtils.stringifyRequest(this, "!!" + remainingRequest), ");\n",
      "  var callbacks = cbs;\n",
      "  cbs = null;\n",
      "  for(var i = 0, l = callbacks.length; i < l; i++) {\n",
      "    callbacks[i](data);\n",
      "  }\n",
      "}" + chunkNameParam + ");"];
  }
  return result.join("");
}

/*
Output format:

  var cbs = [],
    data;
  module.exports = function(cb) {
    if(cbs) cbs.push(cb);
    else cb(data);
  }
  require.ensure([], function(require) {
    data = require("xxx");
    var callbacks = cbs;
    cbs = null;
    for(var i = 0, l = callbacks.length; i < l; i++) {
      callbacks[i](data);
    }
  });

*/

2.A的源码

import React from 'react';
import PropTypes from 'prop-types';
import * as reactRedux from 'react-redux';
import BaseContainer from '../../../containers/ReactBaseContainer';

class A extends BaseContainer {
 constructor(props) {
  super(props);
  this.renderCustom = function renderCustom() {
   return (
    <div >
     Hello world In A
    </div>
   );
  };
 }
 render() {
  // 返回父级view
  return super.render();
 }
}

A.propTypes = {
 dispatch: PropTypes.func,
};

function mapStateToProps(state) {
 return { state };
}

export default reactRedux.connect(mapStateToProps)(A);

3.route.js的源码

import React from 'react';
import { BrowserRouter, Switch, Link } from 'react-router-dom';
import { Route } from 'react-router';
import PostContainer from '../containers/PostsContainer';
// 设置trunk文件的名字 the basename of the resource
import aContainer from './containers/A';
import bContainer from './containers/B';
import cContainer from './containers/C';
import Bundle from '../utils/Bundle';

const A = () => (
 <Bundle load={aContainer}>
  {Component => <Component />}
 </Bundle>
)

const app = () =>
 <div>
  {/* path = "/about" */}
  {/* "/about/" 可以,但"/about/1"就不可以了 exact 配置之后,需要路径绝对匹配,多个斜杠没有关系,这里直接在浏览器里面设置还有问题*/}
  {/* path = "/about/" */}
  {/* "/about/1" 可以,但"/about"就不可以了 用了strict,path要大于等于的关系,少一个斜杠都不行 */}
  {/* exact 和 strick 都用了就必须一模一样,连斜杠都一样 */}
  <Link to="/about/"> Link to about</Link>
  <Route path="/" component={PostContainer} />
  <Route path="/about/" component={A} />
  {/* <Route path="/home" component={B} />
  <Route component={C} /> */}
 </div>
;
export default function () {
 // 用来判断本地浏览器是否支持刷新
 const supportsHistory = 'pushState' in window.history;
 return (
  <BrowserRouter forceRefresh={!supportsHistory} keyLength={12}>
   <div>
    {app()}
   </div>
  </BrowserRouter>

 );
}

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

Javascript 相关文章推荐
Javascript 对象的解释
Nov 24 Javascript
Javascript动态引用CSS文件的2种方法介绍
Jun 06 Javascript
了不起的node.js读书笔记之mongodb数据库交互
Dec 22 Javascript
jQuery实现自定义checkbox和radio样式
Jul 13 Javascript
JavaScript数组各种常见用法实例分析
Aug 04 Javascript
使用jQuery操作HTML的table表格的实例解析
Mar 13 Javascript
原生js编写基于面向对象的分页组件
Dec 05 Javascript
jQuery中用on绑定事件时需注意的事项
Mar 19 Javascript
JavaScript队列的应用实例详解【经典数据结构】
Apr 12 Javascript
bootstrap paginator分页前后台用法示例
Jun 17 Javascript
node.js遍历目录的方法示例
Aug 01 Javascript
vue tab切换,解决echartst图表宽度只有100px的问题
Jul 19 Javascript
node.js操作mysql简单实例
May 25 #Javascript
基于vue实现swipe分页组件实例
May 25 #Javascript
Javascript 实现匿名递归的实例代码
May 25 #Javascript
Kotlin学习第一步 kotlin语法特性
May 25 #Javascript
jQuery Masonry瀑布流布局神器使用详解
May 25 #jQuery
jQuery模拟实现天猫购物车动画效果实例代码
May 25 #jQuery
jquery.masonry瀑布流效果
May 25 #jQuery
You might like
虫族 Zerg 历史背景
2020/03/14 星际争霸
PHP4与PHP3中一个不兼容问题的解决方法
2006/10/09 PHP
《PHP边学边教》(01.开篇――准备工作)
2006/12/13 PHP
PHP编码转换
2012/11/05 PHP
weiphp微信公众平台授权设置
2016/01/04 PHP
PHP实现适用于文件内容操作的分页类
2016/06/15 PHP
简单的JavaScript互斥锁分享
2014/02/02 Javascript
js实现身份证号码验证的简单实例
2014/02/19 Javascript
基于jquery的手风琴图片展示效果实现方法
2014/12/16 Javascript
js制作简易年历完整实例
2015/01/28 Javascript
JQuery PHP图片在线裁剪实例
2020/07/27 Javascript
PHP获取当前页面完整URL的方法
2016/12/02 Javascript
详谈js遍历集合(Array,Map,Set)
2017/04/06 Javascript
Node.js开发第三方微信公众平台
2017/06/05 Javascript
微信小程序 监听手势滑动切换页面实例详解
2017/06/15 Javascript
详解vue-resource promise兼容性问题
2017/06/20 Javascript
20行js代码实现的贪吃蛇小游戏
2017/06/20 Javascript
浅谈webpack打包过程中因为图片的路径导致的问题
2018/02/21 Javascript
关于layui 弹出层一闪而过就消失的解决方法
2019/09/09 Javascript
vue 路由meta 设置导航隐藏与显示功能的示例代码
2020/09/04 Javascript
Python3读取UTF-8文件及统计文件行数的方法
2015/05/22 Python
Python使用django框架实现多人在线匿名聊天的小程序
2017/11/29 Python
python3+PyQt5重新实现QT事件处理程序
2018/04/19 Python
通过python的matplotlib包将Tensorflow数据进行可视化的方法
2019/01/09 Python
django 数据库返回queryset实现封装为字典
2020/05/19 Python
Django多层嵌套ManyToMany字段ORM操作详解
2020/05/19 Python
详解Tensorflow不同版本要求与CUDA及CUDNN版本对应关系
2020/08/04 Python
Python批量修改xml的坐标值全部转为整数的实例代码
2020/11/26 Python
Html5移动端获奖无缝滚动动画实现示例
2018/06/25 HTML / CSS
手把手教你实现一个canvas智绘画板的方法
2019/03/04 HTML / CSS
俄罗斯小米家用电器、电子产品和智能家居商店:Poood.ru
2020/04/03 全球购物
财务管理专业推荐信
2013/11/19 职场文书
企业演讲稿范文
2013/12/28 职场文书
财务部绩效考核方案
2014/05/04 职场文书
电话客服工作职责
2014/07/27 职场文书
淘宝好评语句大全
2014/12/31 职场文书