浅谈如何使用 webpack 优化资源(3)

其中主要需要定义的则是 `output` 中的 `chunkFilename`,它是导出的拆分代码的文件名,这里给它设置为 `[name].[chunkhash:4].child.js`,其中的 `name` 对应模块名称或者 id,`chunkhash` 是模块内容的 hash。

之后在业务代码中 webpack 提供了两种方式来动态导入:

  • `import('path/to/module') -> Promise`,
  • `require.ensure(dependencies: String[], callback: function(require), errorCallback: function(error), chunkName: String)`

对于最新版本的 webpack 主要推荐使用 `import()` 的方式(注意:import 使用到了 Promise,因此需要确保代码中支持了 Promise 的 polyfill)。

// src/index.js
function getComponent() {
 return import(
  /* webpackChunkName: "lodash" */
  'lodash'
 ).then(_ => {
  var element = document.createElement('div');

  element.innerHTML = _.join(['Hello', 'webpack'], ' ');

  return element;

 }).catch(error => 'An error occurred while loading the component');
}

getComponent().then(component => {
 document.body.appendChild(component);
})

可以看到打包的信息:

Hash: d6ba79fe5995bcf9fa4d
Version: webpack 3.6.0
Time: 7022ms
        Asset   Size Chunks       Chunk Names
lodash.89f0.child.js 85.4 kB    0 [emitted] lodash
    index.316e.js 1.96 kB    1 [emitted] index
  [0] ./src/index.js 441 bytes {1} [built]
  [2] (webpack)/buildin/global.js 509 bytes {0} [built]
  [3] (webpack)/buildin/module.js 517 bytes {0} [built]
  + 1 hidden module

可以看到打包出来的代码生成了 `index.316e.js` 和 `lodash.89f0.child.js` 两个文件,后者则是通过 `import` 来实现拆分的。

`import` 它接收一个 `path` 参数,指的是该子模块对于的路径,同时还注意到其中可以添加一行注释 `/* webpackChunkName: "lodash" */`,该注释并非是无用的,它定义了该子模块的 name,其对应与 `output.chunkFilename` 中的 `[name]`。

`import` 函数返回一个 Promise,当异步加载到子模块代码是会执行后续操作,比如更新视图等。

(1)React 中的按需加载

在 React 配合 React-Router 开发中,往往就需要代码根据路由按需加载的能力,下面是一个基于 webpack 代码动态导入技术实现的 React 动态载入的组件:

import React, { Component } from 'react';

export default function lazyLoader (importComponent) {
 class AsyncComponent extends Component {
  state = { Component: null }

  async componentDidMount () {
   const { default: Component } = await importComponent();

   this.setState({
    Component: Component
   });
  }

  render () {
   const Component = this.state.Component;

   return Component
    ? <Component {...this.props} />
    : null;
  }
 }

 return AsyncComponent;
};

在 `Route` 中: