详解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获取时间(本周、本季度、本月..)
Nov 22 Javascript
jquery获取复选框被选中的值
Apr 10 Javascript
JavaScript简单实现鼠标移动切换图片的方法
Feb 23 Javascript
AngularJS 单元测试(二)详解
Sep 21 Javascript
微信开发 微信授权详解
Oct 21 Javascript
Sortable.js拖拽排序使用方法解析
Nov 04 Javascript
vue生命周期和react生命周期对比【推荐】
Sep 19 Javascript
vue给组件传递不同的值方法
Sep 29 Javascript
vue中子组件传递数据给父组件的讲解
Jan 27 Javascript
Element Popover 弹出框的使用示例
Jul 26 Javascript
vue-cli3项目打包后自动化部署到服务器的方法
Sep 16 Javascript
springboot+vue实现文件上传下载
Nov 17 Vue.js
详细分析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 获取mysql数据库信息代码
2009/03/12 PHP
PHP将两个关联数组合并函数提高函数效率
2014/03/18 PHP
浅谈PHP安全防护之Web攻击
2017/01/03 PHP
关于Blog顶部的滚动导航条代码
2006/09/25 Javascript
解决表单中第一个非隐藏的元素获得焦点的一个方案
2009/10/26 Javascript
javascript+xml实现简单图片轮换(只支持IE)
2012/12/23 Javascript
基于jquery固定于顶部的导航响应浏览器滚动条事件
2014/11/02 Javascript
asp.net+js实现金额格式化
2015/02/27 Javascript
JS+DIV+CSS排版布局实现美观的选项卡效果
2015/10/10 Javascript
JavaScript模拟鼠标右键菜单效果
2020/12/08 Javascript
关于javascript的一些知识以及循环详解
2016/09/12 Javascript
详解微信小程序开发之——wx.showToast(OBJECT)的使用
2017/01/18 Javascript
AngularJS使用拦截器实现的loading功能完整实例
2017/05/17 Javascript
详解使用angular-cli发布i18n多国语言Angular应用
2017/05/20 Javascript
axios的拦截请求与响应方法
2018/08/11 Javascript
vue绑定事件后获取绑定事件中的this方法
2018/09/15 Javascript
微信小程序canvas分享海报功能
2019/10/31 Javascript
JavaScript运动原理基础知识详解
2020/04/02 Javascript
jQuery实现动态向上滚动
2020/12/21 jQuery
[47:39]2018DOTA2亚洲邀请赛 3.31 小组赛 A组 LGD vs OPTIC
2018/03/31 DOTA
Python 命令行非阻塞输入的小例子
2013/09/27 Python
在Python的Django框架中用流响应生成CSV文件的教程
2015/05/02 Python
pandas数值计算与排序方法
2018/04/12 Python
python读取各种文件数据方法解析
2018/12/29 Python
Python基于time模块表示时间常用方法
2020/06/18 Python
海信商城:海信电视、科龙空调、容声冰箱官方专卖
2017/02/07 全球购物
StubHub意大利:购买和出售全球演唱会和体育赛事门票
2017/11/21 全球购物
医学实习生自我鉴定
2013/12/12 职场文书
计算机专业职业规划
2014/02/28 职场文书
购房协议书范本
2014/04/11 职场文书
个人担保书格式范文
2014/05/12 职场文书
厕所文明标语
2014/06/11 职场文书
公民授权委托书范本
2014/09/17 职场文书
五星红旗迎风飘扬观后感
2015/06/17 职场文书
学习党史心得体会2016
2016/01/23 职场文书
如何利用golang运用mysql数据库
2022/03/13 Golang