详解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 页面传参数时 参数值含特殊字符的问题
Dec 13 Javascript
JavaScript 模拟用户单击事件
Dec 31 Javascript
Javascript 倒计时源代码.(时.分.秒) 详细注释版
May 09 Javascript
完美兼容IE,chrome,ff的设为首页、加入收藏及保存到桌面js代码
Dec 17 Javascript
JavaScript实现广告的关闭与显示效果实例
Jul 02 Javascript
jQuery垂直多级导航菜单代码分享
Aug 18 Javascript
JS操作COOKIE实现备忘记录的方法
Apr 01 Javascript
JavaScript中的对象继承关系
Aug 01 Javascript
JavaScript之filter_动力节点Java学院整理
Jun 28 Javascript
微信小程序自定义组件实现tabs选项卡功能
Jul 14 Javascript
微信小程序实现九宫格抽奖
Apr 15 Javascript
微信小程序利用swiper+css实现购物车商品删除功能
Mar 06 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 分页分组类
2009/12/10 PHP
PHP中文件缓存转内存缓存的方法
2011/12/06 PHP
PHP排序算法的复习和总结
2012/02/15 PHP
PHP中SESSION使用中的一点经验总结
2012/03/30 PHP
Windows下的PHP 5.3.x安装 Zend Guard Loader教程
2014/09/06 PHP
yii2.0之GridView自定义按钮和链接用法
2014/12/15 PHP
[HTML/CSS/Javascript]WWTJS
2007/09/25 Javascript
JS HTML5 音乐天气播放器(Ajax获取天气信息)
2013/05/26 Javascript
获取当前点击按钮的id用this.id实现
2014/03/17 Javascript
JavaScript和jquery获取父级元素、子级元素、兄弟元素的方法
2016/06/05 Javascript
最丑的时钟效果!js canvas时钟制作方法
2016/08/15 Javascript
jquery实现input框获取焦点的简单实例
2017/01/26 Javascript
ES6中的Promise代码详解
2017/10/09 Javascript
分享vue.js devtools遇到一系列问题
2017/10/24 Javascript
微信小程序如何获取openid及用户信息
2018/01/26 Javascript
微信小程序实现左滑动删除效果
2020/03/30 Javascript
vue-cli随机生成port源码的方法
2019/09/02 Javascript
解决vue elementUI中table里数字、字母、中文混合排序问题
2020/01/07 Javascript
element跨分页操作选择详解
2020/06/29 Javascript
[32:39]完美世界DOTA2联赛循环赛 Forest vs Inki BO2第一场 11.04
2020/11/04 DOTA
详解如何在Apache中运行Python WSGI应用
2019/01/02 Python
pytorch对梯度进行可视化进行梯度检查教程
2020/02/04 Python
python实现提取COCO,VOC数据集中特定的类
2020/03/10 Python
python中查看.db文件中表格的名字及表格中的字段操作
2020/07/07 Python
Matlab中plot基本用法的具体使用
2020/07/17 Python
ECCO爱步加拿大官网:北欧丹麦鞋履及皮具品牌
2017/07/08 全球购物
精选干货:Java精选笔试题附答案
2014/01/18 面试题
运动会通讯稿150字
2014/02/15 职场文书
工程造价专业大学生职业规划范文
2014/03/09 职场文书
婚纱摄影师求职信范文
2014/04/17 职场文书
放飞梦想演讲稿
2014/05/05 职场文书
环保建议书500字
2014/05/14 职场文书
上课睡觉检讨书300字
2014/11/18 职场文书
初中英语教师个人工作总结
2015/02/09 职场文书
2019关于垃圾分类处理的调查报告
2019/12/26 职场文书
MySQL中datetime时间字段的四舍五入操作
2021/10/05 MySQL