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 相关文章推荐
jquery 元素相对定位代码
Oct 15 Javascript
js跨浏览器实现将字符串转化为xml对象的方法
Sep 25 Javascript
Javascript实现真实字符串剩余字数提示的实例代码
Oct 22 Javascript
js实现两点之间画线的方法
May 12 Javascript
7个去伪存真的JavaScript面试题
Jan 07 Javascript
Bootstrap企业网站实战项目4
Oct 14 Javascript
easyui combotree加载静态数据问题(选不上)解决方法
Dec 26 Javascript
用JavaScript实现让浏览器停止载入页面的方法
Jan 19 Javascript
elementui的默认样式修改方法
Feb 23 Javascript
Vue发布项目实例讲解
Jul 17 Javascript
vue中的mescroll搜索运用及各种填坑处理
Oct 30 Javascript
JS中FormData类实现文件上传
Mar 27 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
Email+URL的判断和自动转换函数
2006/10/09 PHP
收藏的一个php小偷的核心程序
2007/04/09 PHP
利用static实现表格的颜色隔行显示的代码
2007/09/02 PHP
PHP操作FTP类 (上传、下载、移动、创建等)
2016/03/31 PHP
Jquery动态更改一张位图的src与Attr的使用
2013/07/31 Javascript
实现图片预加载的三大方法及优缺点分析
2014/11/19 Javascript
JS实现新浪微博效果带遮罩层的弹出框代码
2015/10/12 Javascript
12种JavaScript常用的MVC框架比较分析
2015/11/16 Javascript
JavaScript测试工具之Karma-Jasmine的安装和使用详解
2015/12/03 Javascript
深入学习AngularJS中数据的双向绑定机制
2016/03/04 Javascript
浅析JavaScript回调函数应用
2016/05/22 Javascript
JS获取html元素的标记名实现方法
2016/10/08 Javascript
jquery插件ContextMenu设置右键菜单
2017/03/13 Javascript
Angular.js去除页面中显示的空行方法示例
2017/03/30 Javascript
vue+mockjs模拟数据实现前后端分离开发的实例代码
2017/08/08 Javascript
jQuery实现经典的网页3D轮播图封装功能【附源码下载】
2019/02/15 jQuery
vue使用axios上传文件(FormData)的方法
2019/04/14 Javascript
微信小程序模板消息限制实现无限制主动推送的示例代码
2019/08/27 Javascript
详解React中共享组件逻辑的三种方式
2021/02/02 Javascript
python益智游戏计算汉诺塔问题示例
2014/03/05 Python
用Python程序抓取网页的HTML信息的一个小实例
2015/05/02 Python
Python文件读取的3种方法及路径转义
2015/06/21 Python
Python机器学习logistic回归代码解析
2018/01/17 Python
Django开发中复选框用法示例
2018/03/20 Python
Python基于scipy实现信号滤波功能
2019/05/08 Python
用Python实现童年贪吃蛇小游戏功能的实例代码
2020/12/07 Python
Python tkinter之Bind(绑定事件)的使用示例
2021/02/05 Python
关于读书的演讲稿
2014/05/07 职场文书
离职证明标准格式
2014/09/15 职场文书
个人遵守党的政治纪律情况对照检查材料思想汇报
2014/09/25 职场文书
人民调解协议书范本
2014/10/11 职场文书
史上最牛的辞职信
2015/02/28 职场文书
农村党员干部承诺书
2015/05/04 职场文书
消费者投诉书范文
2015/07/02 职场文书
宾馆安全管理制度
2015/08/06 职场文书
2019企业文化管理制度范本!
2019/08/06 职场文书