Webpack系列-懒加载 Lazy Loading

2,615 阅读2分钟

现在的H5项目大多是单页应用,项目可能有多个页面,而我们在打包时一般会打包在一个bundle中,这就可能导致bundle过大,而且用户也并不是使用所有的页面。最好的方式是在用户使用某个页面时,在把这个页面的bundle.js加载执行。

懒加载或者按需加载即可解决这个问题,这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。

如何实现

要实现懒加载需要修改两个点:

  • webpack.config.js中output的修改,支持输出多个bundle。
  • 需要拆分的模块,引入的时候需要使用动态引入。

配置文件修改

module.exports = {
    entry: {
        'app': './src/app.js'
    },
    output: {
        path: resolve('dist'),
        filename: 'js/[name].js',
        chunkFilename: 'js/[name].chunk.js',
    }
}

动态导入

当我们访问某个页面的时候在去加载对应的js bundle,所以需要用到动态导入,webpack 提供了两个类似的技术。第一种,也是推荐选择的方式是,使用符合 ECMAScript 提案 的 import() 语法 来实现动态导入。第二种,则是 webpack 的遗留功能,使用 webpack 特定的 require.ensure。

这里使用第一种方法。

在Route中动态导入页面

例子:


// webpackChunkName: "home" 定义生成的js bundle名字即为home
const Home = asyncComponent(() => import(/* webpackChunkName: "home" */ '../containers/Chain/Home'))
const AllChain = asyncComponent(() => import(/* webpackChunkName: "home" */ '../containers/Chain/AllChain'))


const ApproveList = asyncComponent(() => import(/* webpackChunkName: "approve" */ '../containers/Approve/List'));
const ApproveSearch = asyncComponent(() => import(/* webpackChunkName: "approve" */ '../containers/Approve/Search'));

export default function Routes () {
    return (
        <Switch>{/* home */}
            <Route exact path="/chain" component={Home}/>
            <Route exact path="/chain/all" component={AllChain}/>

            {/* approve */}
            <Route path="/approve/list/:id" component={ApproveList}/>
            <Route path="/approve/search/:id" component={ApproveSearch}/>
        </Switch>
    )
}

这里我们写了高阶组件 asyncComponent 来处理动态导入的组件。动态导入的组件是异步的,导致不能把组件拿来直接使用,我们需要等组件加载完之后,在去render component。

asyncComponent 的实现:

import React, { Component } from 'react';

export default function asyncComponent(importComponent) {
    class AsyncComponent extends Component {
        constructor(props) {
            super(props);
            this.state = {
                component: null,
            };
        }
        async componentDidMount() {
            const { default: component } = await importComponent();
            this.setState({
                component: component
            });
        }
        render() {
            const C = this.state.component;
            return C
                ? <C {...this.props} />
                : null;
        }
    }
    return AsyncComponent;
}

写这些代码的时候React还没有推出懒加载的功能,这里或许可以使用React.lazy来改造。