webpack指南(代码分离)

发布时间 2024-01-05 23:25:17作者: 心意12

代码分离

常用的代码分离方法有三种:

  • 入口起点:使用 entry 配置手动地分离代码。
  • 防止重复:使用 入口依赖 或者 SplitChunksPlugin 去重和分离 chunk
  • 动态导入:通过模块的内联函数调用分离代码。

入口起点

 project
webpack-demo
|- package.json
|- package-lock.json
|- webpack.config.js
|- /dist
|- /src
  |- index.js
 |- another-module.js
|- /node_modules

another-module.js

import _ from 'lodash';

console.log(_.join(['Another', 'module', 'loaded!'], ' '));

webpack.config.js

 const path = require('path');

 module.exports = {
  mode: 'development',
  entry: {
    index: './src/index.js',
    another: './src/another-module.js',
  },
   output: {
    filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
 };

这种方式存在一些隐患:

  • 如果入口 chunk 之间包含一些重复的模块,那么这些重复模块会被引入到各个 bundle 中。
  • 这种方法不够灵活,并且不能动态地拆分应用程序逻辑中的核心代码。

防止重复

入口依赖

 在配置文件中配置 dependOn 选项,以在多个 chunk 之间共享模块:

webpack.config.js

const path = require('path');

 module.exports = {
   mode: 'development',
   entry: {
    index: {
      import: './src/index.js',
      dependOn: 'shared',
    },
    another: {
      import: './src/another-module.js',
      dependOn: 'shared',
    },
    shared: 'lodash',
   },
   output: {
     filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
  // 在一个html中使用多个入口起点,需要设置optimization.runtimeChumk为single
  optimization: {
    runtimeChunk: 'single',
  },
};

使用上面配置进行构建。可以看到,除了 shared.bundle.jsindex.bundle.js 和 another.bundle.js 之外,还生成了一个 runtime.bundle.js 文件。

尽管 webpack 允许每个页面使用多个入口起点,但在可能的情况下,应该避免使用多个入口起点,而使用具有多个导入的单个入口起点:entry: { page: ['./analytics', './app'] }。这样可以获得更好的优化效果,并在使用异步脚本标签时保证执行顺序一致。
注意:上面 entry: { page: ['./analytics', './app'] } 的page里的元素可以是文件路径

SplitChunksPlugin

SplitChunksPlugin 插件可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。让我们使用这个插件去除之前示例中重复的 lodash 模块:

webpack.config.js

 
 const path = require('path');

  module.exports = {
    mode: 'development',
    entry: {
      index: './src/index.js',
      another: './src/another-module.js',
    },
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist'),
    },
   optimization: {
     splitChunks: {
       chunks: 'all',
     },
   },
  };

使用 optimization.splitChunks 配置选项后构建,将会发现 index.bundle.js 和 another.bundle.js 已经移除了重复的依赖模块。从插件将 lodash 分离到单独的 chunk,并且将其从 main bundle 中移除,减轻了 bundle 大小。

 

动态导入

两种方式实现动态导入:

注意:调用 import() 会在内部使用 promise。因此如果在旧版本浏览器中(如 IE 11)使用 import(),需要使用一个 polyfill 库(例如 es6-promise 或 promise-polyfill)shim Promise
提示

在稍后示例中,当需要根据计算后的变量导入特定模块时,可以通过向 import() 传入一个 动态表达式 实现。

预获取/预加载模块

 

声明 import 时使用下列内置指令可以让 webpack 输出“Resource Hint”告知浏览器:

  • 预获取(prefetch):将来某些导航下可能需要的资源
  • 预加载(preload):当前导航下可能需要资源

LoginButton.js

import(/* webpackPrefetch: true */ './path/to/LoginModal.js');

上面的代码在构建时会生成 <link rel="prefetch" href="login-modal-chunk.js"> 并追加到页面头部,指示浏览器在闲置时间预获取 login-modal-chunk.js 文件。

只要父 chunk 完成加载,webpack 就会添加预获取提示。

与预获取指令相比,预加载指令有许多不同之处:

  • 预加载 chunk 会在父 chunk 加载时以并行方式开始加载;而预获取 chunk 会在父 chunk 加载结束后开始加载。
  • 预加载 chunk 具有中等优先级,并会立即下载;而预获取 chunk 则在浏览器闲置时下载。
  • 预加载 chunk 会在父 chunk 中立即请求,用于当下时刻;而预获取 chunk 则用于未来的某个时刻。
  • 浏览器支持程度不同。
    import(/* webpackPreload: true */ 'ChartingLibrary');

不正确地使用 webpackPreload 会有损性能,请谨慎使用。

分析 bundle

分析bundle,官方分析工具 是一个不错的开始。

还有其他的工具:

  • webpack-chart:webpack stats 可交互饼图。
  • webpack-visualizer:分析并可视化 bundle,检查哪些模块占用空间,哪些可能是重复使用的。
  • webpack-bundle-analyzer:一个 plugin 和 CLI 工具,它将 bundle 内容展示为一个便捷的、交互式、可缩放的树状图形式。
  • webpack bundle optimize helper:这个工具会分析 bundle,并提供可操作的改进措施,以减少 bundle 的大小。
  • bundle-stats:生成一个 bundle 报告(bundle 大小、资源、模块),并比较不同构建之间的结果。