博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React-router-v4 - Webpack 实现按需加载(code-splitting)
阅读量:4087 次
发布时间:2019-05-25

本文共 7424 字,大约阅读时间需要 24 分钟。

方法一、结合 bundle-loader 实现按需加载

转载

1. 首先创建一个包装组件 Bundle

一下是 react-router4.0 中给出的例子

import React from 'react';    export default class Bundle extends React.Component {       state = {          mod: null       }       componentWillMount() {          this.load(this.props);       }       componentWillReceiveProps(nextProps) {          if (nextProps.load !== this.props.load) {             this.load(nextProps);          }       }       // load 方法,用于更新 mod 状态       load(props) {          // 初始化          this.setState({             mod: null          });          /*             调用传入的 load 方法,并传入一个回调函数             这个回调函数接收 在 load 方法内部异步获取到的组件,并将其更新为 mod           */           props.load(mod => {             this.setState({                mod: mod.default ? mod.default : mod             });          });       }       render() {          /*             将存在状态中的 mod 组件作为参数传递给当前包装组件的'子'          */           return this.state.mod ? this.props.children(this.state.mod) : null;       }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  1. Bundle 组件会接受一个名为 load 的 props
  2. load 值一是一个组件异步加载的方法 load -> function (cb) {...cb(/* 异步加载的组件 */)}
  3. 这个方法需要传入一个回调函数作为参数
  4. 回调函数会在在方法内异步接收加载完的组件

2. 创建包装组件的方法

import React from 'react';    import Bundle from './Bundle';    // 默认加载组件,可以直接返回 null     const Loading = () => 
Loading...
; /* 包装方法,第一次调用后会返回一个组件(函数式组件) 由于要将其作为路由下的组件,所以需要将 props 传入 */ const lazyLoad = loadComponent => props => (
{Comp => (Comp ?
:
)}
); export default lazyLoad;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

结合第一步中 this.props.children(this.state.mod) 加载后的组件会被传入 {Comp => (Comp ? <Comp {...props} /> : <Loading />)} 中渲染

3. 在 Router 配置中使用

import React from 'react';    import { Route, Switch, Redirect } from 'react-router-dom';    import { hot } from 'react-hot-loader';    // 引入包装函数    import lazyLoad from './lazyLoad';    import App from '../containers/App';    /*        使用 bundle-loader 插件引用组件,返回的实际上是包装后的组件异步加载的函数    */    import Home from 'bundle-loader?lazy&name=home!../containers/Home';    import Test from 'bundle-loader?lazy&name=test!../containers/Test';    const Root = () => (       
(
} />
)} />
} />
); export default hot(module)(Root);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

方法二、bundle-loader 的另一种使用方式 - 在 webpack 中配置

1. 创建一个包装组件 Bundle

创建的组件与 方法一 中完全一致

2. 创建包装组件的函数

函数与 方法一 中完全一致

3. webpack 中配置 loader

由于不是所有 js 文件都是需要包装的组件,因此这里要做一个区分,就是 需要包装的组件要写为 [name].bundle.js

module.exports = {        module: {            rules: [                {                    test: /\.bundle\.js$/,                    loader: 'bundle-loader',                    include: path.join(__dirname, 'src'), // 源码目录                    options: {                        lazy: true,                        name: '[name]'                    }                }            ]        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

4. Router 配置中使用

import lazyLoad from './lazyLoad';    import App from '../containers/App';    /*        其他配置使用与 方法一 一致        只是引用组件的时候不在需要在路径中配置 bundle-loader         需要按需加载的组件也要文件名变为 [name].bundle.js 的格式    */    // import Home from 'bundle-loader?lazy&name=home!../containers/Home';    // import Test from 'bundle-loader?lazy&name=test!../containers/Test';    import Home from '../containers/Home.bundle';    import Test from '../containers/Test.bundle';    ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

方法三、使用 import() 方法代替 bundle-loader 实现

import('../xxx.js') 返回的是一个 promise,因此需要改写 Bundle 组件,此外不在需要 bundle-loader ,其在 webpack 中的配置应该删除

1. 创建一个包装组件 Bundle

import React from 'react';    export default class Bundle extends React.Component {       state = {          mod: null       }       componentWillMount() {          this.load(this.props);       }       componentWillReceiveProps(nextProps) {          if (nextProps.load !== this.props.load) {             this.load(nextProps);          }       }       // 更改 load 方法为异步函数       async load(props) {          this.setState({             mod: null          });          /*            使用 props.load() 返回的是一个 promise           */           const mod = await props.load();          this.setState({             mod: mod.default ? mod.default : mod          });       }       render() {          return this.state.mod ? this.props.children(this.state.mod) : null;       }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

2. 创建包装组件的函数

函数与 方法一 中完全一致

3. Router 配置中使用

import React from 'react';    import { Route, Switch, Redirect } from 'react-router-dom';    import { hot } from 'react-hot-loader';    import lazyLoad from './lazyLoad';    import App from '../containers/App';    // 动态引入    const Home = lazyLoad(() => import('../containers/Home'));    const Test = lazyLoad(() => import('../containers/Test'));    const Root = () => (       
(
} />
)} />
} />
); export default hot(module)(Root);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

问题

1. 虽然在配置 webpack 时设置了 output: {chunkFilename: 'chunk/[name].[chunkhash].js'} 但是使用 ‘方法三’ 拆分后的包名还是以数字开始的,如下

Time: 9556ms                                 Asset     Size  Chunks             Chunk Names                 chunk/0.7429253c8b.js  5.86 kB       0  [emitted]                 chunk/1.e5cc1c11d3.js  1.32 kB       1  [emitted]    static/js/app.f61c516b85.bundle.js    40 kB       2  [emitted]  app static/js/vendor.9f5f812985.bundle.js   209 kB       3  [emitted]  vendorstatic/js/runtime.1bbb4b5baf.bundle.js  1.44 kB       4  [emitted]  runtime         static/css/app.ec6e1795a1.css  2.54 kB       2  [emitted]  app                            index.html  2.16 kB          [emitted]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

可以使用 webpack 提供的一个特殊的注释语法 /* webpackChunkName: "Home" */ 来提供 chunk name (需要 Webpack > 2.4),这样生成的模块就能与 bundle-loader 一致的命名

const Home = lazyLoad(() => import(/* webpackChunkName: "Home" */'../containers/Home'));    const Test = lazyLoad(() => import(/* webpackChunkName: "Test" */'../containers/Test'));
  • 1
  • 2

2. 而使用 bundle-loader 的方法,由于配置了参数 name ,所以打包后会有模块命名如下

Time: 9190ms                          Asset     Size  Chunks             Chunk Names       chunk/Home.4a8c77815d.js  21.6 kB       0  [emitted]  Home       chunk/Test.283681f60c.js  1.32 kB       1  [emitted]  Test    static/js/app.3edc8970d4.js  24.2 kB       2  [emitted]  app static/js/vendor.9f5f812985.js   209 kB       3  [emitted]  vendorstatic/js/runtime.0f777aeb81.js  1.46 kB       4  [emitted]  runtime  static/css/app.ec6e1795a1.css  2.54 kB       2  [emitted]  app                     index.html  2.14 kB          [emitted]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

参考

你可能感兴趣的文章
C++ 重载自增和自减操作符
查看>>
C++ 重载调用操作符和函数对象
查看>>
C++ 顺序容器的操作(6) 删除元素 erase、clear、pop_back、pop_front
查看>>
C++ 顺序容器的操作(7) 赋值与交换=、swap、 assign
查看>>
C++ vector容器的自增长
查看>>
C++ 容器的选用
查看>>
C++ 再谈string类型(1) 构造string对象的方法
查看>>
C++ 基于const的重载
查看>>
C++ 从基类到派生类的转换
查看>>
C++ 派生类构造函数
查看>>
C++ 拷贝控制和继承
查看>>
C++ 虚析构函数
查看>>
C++ 虚构造函数和虚析构函数
查看>>
C++ 继承中类作用域
查看>>
C++ 编译时期的名字查找
查看>>
C++ 名字冲突和继承
查看>>
C++ 类作用域和函数成员
查看>>
C++ 虚函数和类作用域
查看>>
C++ 纯虚函数
查看>>
C++ 容器与继承
查看>>