详解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 08 Javascript
jQuery 行级解析读取XML文件(附源码)
Oct 12 Javascript
为jQuery.Treeview添加右键菜单的实现代码
Oct 22 Javascript
用javascript模仿ie的自动完成类似自动完成功的表单
Dec 12 Javascript
Javascript中的for in循环和hasOwnProperty结合使用
Jun 05 Javascript
jQuery调用Webservice传递json数组的方法
Aug 06 Javascript
利用jQuery的动画函数animate实现豌豆发射效果
Aug 28 Javascript
vue 里面使用axios 和封装的示例代码
Sep 01 Javascript
使用Angular Cli如何创建Angular私有库详解
Jan 30 Javascript
JS常见面试试题总结【去重、遍历、闭包、继承等】
Aug 27 Javascript
Node.JS获取GET,POST数据之queryString模块使用方法详解
Feb 06 Javascript
JS创建自定义对象的六种方法总结
Dec 15 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
1.PHP简介
2006/10/09 PHP
浅谈PHP中foreach/in_array的使用
2015/11/02 PHP
WordPress伪静态规则设置代码实例
2020/12/10 PHP
jquery CSS选择器笔记
2010/03/29 Javascript
iframe 父窗口和子窗口相互的调用方法集锦
2010/12/15 Javascript
js中获取事件对象的方法小结
2011/03/13 Javascript
jQuery load方法用法集锦
2011/12/06 Javascript
实用的JS正则表达式(手机号码/IP正则/邮编正则/电话等)
2013/01/11 Javascript
Linux下使用jq友好的打印JSON技巧分享
2014/11/18 Javascript
jQuery form插件的使用之处理server返回的JSON, XML,HTML数据
2016/01/26 Javascript
详解AngularJS中的表单验证(推荐)
2016/11/17 Javascript
bootstrap table实例详解
2017/01/06 Javascript
jquery submit()不能提交表单的解决方法
2017/04/24 jQuery
深入理解JavaScript 参数按值传递
2017/05/24 Javascript
JS解析后台返回的JSON格式数据实例
2018/08/06 Javascript
Django模板继承 extend标签实例代码详解
2019/05/16 Javascript
JS实现滑动插件
2020/01/15 Javascript
[48:52]DOTA2上海特级锦标赛A组小组赛#2 Secret VS CDEC第一局
2016/02/25 DOTA
[54:33]2018DOTA2亚洲邀请赛小组赛 A组加赛 Liquid vs Optic
2018/04/03 DOTA
[38:39]完美世界DOTA2联赛循环赛 IO vs GXR BO2第二场 11.04
2020/11/05 DOTA
利用Fn.py库在Python中进行函数式编程
2015/04/22 Python
python开发之for循环操作实例详解
2015/11/12 Python
Python+django实现简单的文件上传
2016/08/17 Python
python模块简介之有序字典(OrderedDict)
2016/12/01 Python
python实现一个简单的并查集的示例代码
2018/03/19 Python
Python实现删除时保留特定文件夹和文件的示例
2018/04/27 Python
selenium设置proxy、headers的方法(phantomjs、Chrome、Firefox)
2018/11/29 Python
在Django中URL正则表达式匹配的方法
2018/12/20 Python
python实现秒杀商品的微信自动提醒功能(代码详解)
2020/04/27 Python
Python学习笔记之装饰器
2020/08/06 Python
html5嵌入内容_动力节点Java学院整理
2017/07/07 HTML / CSS
html5实现微信打飞机游戏
2014/03/27 HTML / CSS
计算机应用专业应届毕业生中文求职信范文
2013/11/29 职场文书
建筑公司员工自我鉴定
2014/04/08 职场文书
大一新生检讨书
2014/10/29 职场文书
python 多态 协议 鸭子类型详解
2021/11/27 Python