详解webpack + react + react-router 如何实现懒加载


Posted in Javascript onNovember 20, 2017

在 Webpack 1 中主要是由bundle-loader进行懒加载,而 Webpack 2 中引入了类似于 SystemJS 的System.import语法,首先我们对于System.import的执行流程进行简单阐述:

  1. Webpack 会在编译过程中扫描代码库时将发现的System.import调用引入的文件及其相关依赖进行单独打包,注意,Webpack 会保证这些独立模块及其依赖不会与主应用的包体相冲突。
  2. 当我们访问到这些独立打包的组件模块时,Webpack 会发起 JSONP 请求来抓取相关的包体。
  3. System.import 同样也是 Promise,在请求完成之后System.import会将抓取到的模块作为参数传入then中的回调函数。
  4. 如果我们重复访问已经加载完毕的模块,Webpack 不会重复执行抓取与解析的过程。

而 React Router 路由的懒加载实际上分为动态路由与与懒加载两步,典型的所谓动态路由配置如下:

/**
 * <Route path="/" component={Core}>
 *  <IndexRoute component={Home}/>
 *  <Route path="about" component={About}/>
 *  <Route path="users" component={Users}>
 *  <Route path="*" component={Home}/>
 * </Route>
 */
export default {
 path: '/', 
 component: Core,
 indexRoute: { 
  getComponent(location, cb) {
    ...
  },
 },
 childRoutes: [
  {
   path: 'about', 
   getComponent(location, cb) {
    ...
   },
  },
  {
   path: 'users', 
   getComponent(location, cb) {
    ...
   },
  },
  {
   path: '*', 
   getComponent(location, cb) {
    ...
   },
  },
 ],
};

正常打包

import IndexPage from './views/app.jsx'
import AboutPage from './views/about.jsx'
export default function({history}) {
  return (
    <Router history={history}>
      <Route path="/" component={IndexPage} />
      <Route path="/about" component={AboutPage} />
    </Router>
  )
}

这是一个正常打包的路由写法, 如果需要分割代码, 我们需要改造下路由, 借助getComponent和require.ensure

webpack 代码分割

export default function({history}) {
  return (
    <Router history={history}>
      <Route path="/" getComponent={(location, callback) => {
        require.ensure([], function(require) {
          callback(null, require('./HomePage.jsx'))
        })
      }} />
      <Route path="/about" getComponent={(location, callback) => {
        require.ensure([], function(require) {
          callback(null, require('./AboutPage.jsx'))
        })
      }} />
    </Router>
  )
}

这样看来代码有点累, 我们稍微改造下

const home = (location, callback) => {
 require.ensure([], require => {
  callback(null, require('./HomePage.jsx'))
 }, 'home')
}

const about = (location, callback) => {
 require.ensure([], require => {
  callback(null, require('./AboutPage.jsx'))
 }, 'about')
}
export default function({history}) {
  return (
    <Router history={history}>
      <Route path="/" getComponent={home}></Route>
      <Route path="/about" getComponent={about}></Route>
    </Router>
  )
}

这样看起来是不是简洁了很多

注意: 由于webpack的原因, 如果直接require('./AboutPage.jsx')不能正常加载, 请尝试require('./AboutPage.jsx').default

webpack2 代码分割

上面的代码看起来好像都是webpack1的写法, 那么webpack2呢?

webpac2就需要借助System.import了

export default function({history}) {
  return (
    <Router history={history}>
      <Route path="/" getComponent={(location, callback) => {
        System.import('./HomePage.jsx').then(component => {
          callback(null, component.default || component)
        })
      }} />
      <Route path="/about" getComponent={(location, callback) => {
        System.import('./AboutPage.jsx').then(component => {
          callback(null, component.default || component)
        })
      }} />
    </Router>
  )
}

我们一样可以把上面的代码优化一下

const home = (location, callback) => {
  System.import('./HomePage.jsx').then(component => {
    callback(null, component.default || component)
  })
}
const about = (location, callback) => {
  System.import('./AboutPage.jsx').then(component => {
    callback(null, component.default || component)
  })
}

export default ({ history }) => {
  return (
    <Router history={history}>
      <Route name="home" path="/" getComponent={home} />
      <Route name="about" path="/about" getComponent={about} />
    </Router>
  )
}

webpack2 + dva 实现路由和 models 懒加载

const routerThen = (app, callback, [component, model]) => {
  app.model(model.default || model)
  callback(null, component.default || component)
}

export default ({ history, app }) => {
  return (
    <Router history={history}>
      <Route name="home" path="/" getComponent={(location, callback) => {
        Promise.all([
          System.import('./views/app.jsx'),
          System.import('./models/topics')
        ]).then(routerThen.bind(null, app, callback))
      }} />
      <Route name="article" path="/article/:id" getComponent={(location, callback) => {
        Promise.all([
          System.import('./views/article.jsx'),
          System.import('./models/topic')
        ]).then(routerThen.bind(null, app, callback))
      }} />
    </Router>
  )
}

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

Javascript 相关文章推荐
js打印纸函数代码(递归)
Jun 18 Javascript
javascript多物体运动实现方法分析
Jan 08 Javascript
JavaScript表单验证实例之验证表单项是否为空
Jan 10 Javascript
JS实现的颜色实时渐变效果完整实例
Mar 25 Javascript
js通过classname来获取元素的方法
Nov 24 Javascript
AngularJS Select(选择框)使用详解
Jan 18 Javascript
jQuery插件echarts实现的多折线图效果示例【附demo源码下载】
Mar 04 Javascript
vue-lazyload使用总结(推荐)
Nov 01 Javascript
基于vue2的canvas时钟倒计时组件步骤解析
Nov 05 Javascript
JS执行控制之节流模式实例分析
Dec 21 Javascript
Vue父子传递实例讲解
Feb 14 Javascript
vue 通过绑定事件获取当前行的id操作
Jul 27 Javascript
详细分析jsonp的原理和实现方式
Nov 20 #Javascript
three.js中文文档学习之通过模块导入
Nov 20 #Javascript
JS写XSS cookie stealer来窃取密码的步骤详解
Nov 20 #Javascript
浅谈Vue SSR 的 Cookies 问题
Nov 20 #Javascript
three.js中文文档学习之创建场景
Nov 20 #Javascript
Vue 中批量下载文件并打包的示例代码
Nov 20 #Javascript
VueJs 搭建Axios接口请求工具
Nov 20 #Javascript
You might like
PHP写UltraEdit插件脚本实现方法
2011/12/26 PHP
Thinkphp和onethink实现微信支付插件
2016/04/13 PHP
Javascript YUI 读码日记之 YAHOO.util.Dom - Part.2 0
2008/03/22 Javascript
js调用activeX获取u盘序列号的代码
2011/11/21 Javascript
jquery中常用的SET和GET$(”#msg”).html循环介绍
2013/10/09 Javascript
JS控件ASP.NET的treeview控件全选或者取消(示例代码)
2013/12/16 Javascript
JavaScript的Polymer框架中dom-repeat与VM的相关操作
2015/07/29 Javascript
纯CSS3代码实现滑动开关效果
2015/08/19 Javascript
JS+CSS实现类似QQ好友及黑名单效果的树型菜单
2015/09/22 Javascript
JS实现浏览器状态栏显示时间的方法
2015/10/27 Javascript
js模糊查询实例分享
2016/12/26 Javascript
jQuery为某个div加入行样式
2017/06/09 jQuery
用nodejs实现json和jsonp服务的方法
2017/08/25 NodeJs
vue axios同步请求解决方案
2017/09/29 Javascript
JavaScript中错误正确处理方式小结你用对了吗
2017/10/10 Javascript
p5.js入门教程之鼠标交互的示例
2018/03/16 Javascript
微信小程序制作表格的方法
2019/02/14 Javascript
如何在项目中使用log4.js的方法步骤
2019/07/16 Javascript
详解vue 命名视图
2019/08/14 Javascript
TypeScript中使用getElementXXX()的示例代码
2019/09/12 Javascript
layui实现checkbox的目录树tree的例子
2019/09/12 Javascript
[58:15]2018DOTA2亚洲邀请赛 4.1 小组赛 A组 NB vs Liquid
2018/04/02 DOTA
[06:45]DOTA2-DPC中国联赛 正赛 Magma vs LBZS 选手采访
2021/03/11 DOTA
Python挑选文件夹里宽大于300图片的方法
2015/03/05 Python
Python解析命令行读取参数--argparse模块使用方法
2018/01/23 Python
Python中@property的理解和使用示例
2019/06/11 Python
python函数局部变量、全局变量、递归知识点总结
2019/11/15 Python
手把手教你安装Windows版本的Tensorflow
2020/03/26 Python
Django添加bootstrap框架时无法加载静态文件的解决方式
2020/03/27 Python
python编写实现抽奖器
2020/09/10 Python
CSS3 border-radius圆角的实现方法及用法详解
2020/09/14 HTML / CSS
Myprotein芬兰官网:欧洲第一运动营养品牌
2019/05/05 全球购物
英语专业大学生求职简历的自我评价
2013/10/18 职场文书
计算机求职自荐信范文
2014/04/19 职场文书
临床医学生职业规划书范文
2014/10/25 职场文书
西部计划志愿者工作总结
2015/08/11 职场文书