分析工具

  • webpackbar:在打包时实时显示进度
  • speed-measure-webpack-plugin:看到每个 loader 和 plugin 的耗时
  • webpack-bundle-analyzer:可以看到打包的 bundle 中到底包含哪些模块,以及每个模块体积包的大小

猿辅导二面

加快构建速度

最快捷的方式就是更新 webpack 版本到 5

webpack 5 较于 webpack 4 ,新增了 持久化缓存改进缓存算法等优化

cache

通过配置 webpack 持久缓存 cache: filesystem | memory, 来缓存生成的 webpack 模块和 chunk,改善构建速度,当构建突然中断,二次进行构建时,可以直接从缓存中拉取,可以提速 90%左右

  1. module.exports = {
  2. cache: {
  3. type: 'filesystem' // 使用文件缓存
  4. }
  5. }

通过 持久缓存,再也不用配置dllcache-loader了。

减少 loader、plugins

每个 loader、plugin 都有启动时间。尽量少地使用工具,将非必要地 loader、plugins 删除

指定 include

为 loader 指定 include,减少 loader 应用范围,仅应用于最少数量地必要模块

rule.exclude 可以排除模块范围

资源管理

使用 asset module 代替旧的 assets loader(如 file-loader、url-loader、raw-loader)

优化 resolve 配置

  1. 减少 resolve.modulesresolve.extensionsresolve.mainFilesresolve.descriptionFiles 中条目数量,因为他们会增加文件系统调用的次数

extensions 表示需要解析的文件类型列表
modules 表示 webpack 解析模块时需要解析的目录

  1. 如果项目中不适用 symlinks (例如 npm link 或者 yarn link),可以设置 resolve.symlinks: false,减少解析工作量
  2. 如果使用自定的 resolve plugin 规则,并且没有指定 context 上下文,可以设置 resolve.cacheWithContext: false

    多进程

    通过 thread-loader 将耗时的 laoder 放在一个独立的 worker 池中运行,加快 loader 构建速度。
    比如,对于 sass:

    1. module.exports = {
    2. rules: [
    3. {
    4. test: /\.module\.(scss|sass)$/,
    5. include: paths.appSrc,
    6. use: [
    7. 'style-loader',
    8. {
    9. loader: 'css-loader',
    10. options: {
    11. modules: true,
    12. importLoaders: 2,
    13. },
    14. },
    15. {
    16. loader: 'postcss-loader',
    17. options: {
    18. postcssOptions: {
    19. plugins: [['postcss-preset-env']],
    20. },
    21. },
    22. },
    23. {
    24. loader: 'thread-loader',
    25. options: {
    26. workerParallelJobs: 2,
    27. },
    28. },
    29. 'sass-loader',
    30. ].filter(Boolean),
    31. },
    32. ],
    33. }

    node-sass 中有个来自 Node.js 线程池阻塞线程池的 bug,当使用 thread-loader 时,需要设置 workerParallelJobs: 2
    不要使用太多的 worker,因为 nodejs 的 runtime 和 loader 都有启动开销。最小化 worker 和 主进程之间的模块传输。 进程间通信(IPC)是非常消耗资源的。
    happypack 也是用来设置多线程的,但是不推荐了

    devtool

    eval 的性能最好,但是不利于调试。大多数情况下,最佳选择是 eval-cheap-module-source-map

    启动HMR(热模块替换)

    在程序运行中,替换、添加和删除模块,而无需重新加载整个页面

    使用 oneOf

    根据文件类型加载对应的 loader、只要能匹配一个即可退出

    最小化 entry chunk

    通过配置 optimization.runtimeChunk = true为运行时代码创建一个额外的 chunk ,减少 entry chunk 体积,提高性能。

    1. module.exports = {
    2. optimization: {
    3. runtimeChunk: true,
    4. },
    5. }

    减少打包体积

    代码压缩

  3. JS 压缩
    使用 TerserWebpackPlugin来压缩 JavaScript,但是 webpack5 已经自带了。terser-webpack-plugin默认开启了parallel: true,并发运行的默认数量:os.cups().length - 1。使用多进程并发运行压缩以提高构建速度。(使用了这个就不用再用 ParallelUglifyPlugin 插件了)

  4. CSS 压缩
    使用 CssMinimizerWebpackPlugin压缩 CSS 文件。将在 webpack 构建期间搜索 css 文件,优化、压缩 css
    optimize-css-assets-webpack-plugin相比,css-minimizer-webpack-plugin在 souce maps 和 assets 中使用查询字符串会更准确,而且支持缓存和并发模式下运行。

    1. module.exports = {
    2. optimization: {
    3. minimizer: {
    4. new CssMinimizerPlugin([
    5. parallel: 4
    6. ])
    7. }
    8. }
    9. }

    代码分离

    代码分离能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件,代码分离可以获取更小的 bundle,以及控制资源加载优先级,可以缩短页面加载时间。

  5. 代码分割 SplitChunksPlugin

  6. CSS 文件分离:mini-css-extract-plugin, 将 css 提起到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载 ```javascript const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’)

module.exports = { plugins: [new MiniCssExtractPlugin()], module: { rules: [ { test: /.module.(scss|sass)$/, include: paths.appSrc, use: [ ‘style-loader’, isEnvProduction && MiniCssExtractPlugin.loader, // 仅生产环境 { loader: ‘css-loader’, options: { modules: true, importLoaders: 2, }, }, { loader: ‘postcss-loader’, options: { postcssOptions: { plugins: [[‘postcss-preset-env’]], }, }, }, { loader: ‘thread-loader’, options: { workerParallelJobs: 2, }, }, ‘sass-loader’, ].filter(Boolean), }, ], }, }

  1. > **MiniCssExtractPlugin.loader 要放在 style-loader 后面**
  2. <a name="vOgX0"></a>
  3. #### Tree Shaking
  4. > 必要条件:CommonJsAMDCMD 等是高度动态的,难以预测的。ESM 从规范层面避免了这一行为,要求所有的导入导出模块必须在最顶层,且导入导出的模块必须是字符串常量。所以 ESM 下模块之间的依赖关系是高度确定的,与运行状态无关,编译工具只需要对 ESM 模块做静态分析,就可以从代码字面量中推断出哪些模块值未曾被其他模块使用。**这就是 Tree Shaking 的必要条件**
  5. 1. JS<br />我们可以设置 `package.json``sideEffects`属性作为标记,向 compiler 提供提示,表明项目中哪些文件是 'pure' ,由此可以安全的删除文件中未使用的部分。<br />Dead Code 一般具有一下几个特征:
  6. - 代码不会执行,不可到达
  7. - 代码执行的结果不会用到
  8. - 代码只会影响死变量(只写不读)
  9. ```json
  10. {
  11. "name": "your-project",
  12. "sideEffects": false
  13. }
  14. // 当代码有副作用时,需要将sideEffects 写成数组的形式
  1. CSS
    使用purgecss-webpack-plugin进行 css tree shaking。配合 mini-css-extract-plugin ```javascript const glob = require(‘glob’) const MiniCssExtractPlugin = require(‘mini-css-extract-plugin’) const PurgeCssPlugin = require(‘purgecss-webpack-plugin’) const paths = require(‘pahts’)

module.exports = { plugins: [ new MiniCssExtractPlugin({ filename: ‘[name].css’, }), new PurgeCssPlugin({ paths: glob.sync(${paths.appSrc}/**/*, { nodir: true }), }), ], }

  1. <a name="Jn9OP"></a>
  2. #### CDN
  3. 这里引入 CDN 的首要目的时为了减少打包体积,因此仅仅是将一大部分**静态资源手动上传至 CDN**,并修改本地引入路径
  4. - 字体:压缩并上传 CDN
  5. - 图片:压缩并上传至 CDN
  6. <a name="Ql8ai"></a>
  7. ## 加快加载速度
  8. <a name="mk1y0"></a>
  9. #### 按需加载
  10. <a name="gmqz3"></a>
  11. #### 浏览器缓存
  12. webpack 支持根据资源内容,**创建 hash id**,当资源内容发生变化时,创建新的 hash id
  13. ```javascript
  14. module.exports = {
  15. output: {
  16. // 仅在生产环境添加hash
  17. filename: ctx.isEnvProduction ? '[name].[contenthash].bundle.js' : '[name].bundle.js',
  18. },
  19. }

配置 CSS bundle hash

  1. module.exports = {
  2. plugins: [
  3. new MiniCssExtractPlugin({
  4. filename: '[hash].[name].css',
  5. }),
  6. ],
  7. }

配置 optimization.moduleIds, 让公共包 splitChunks 的 hash 不因为新依赖而改变,减少非必要的 hash 变动。

  1. module.exports = {
  2. optimization: {
  3. moduleIds: 'deterministic',
  4. },
  5. }

CDN

将所有静态资源,上传至 CDN,通过 CDN 加速来提升加载速度。

  1. module.exports = {
  2. output: {
  3. publicPath: ctx.isEnvProduction ? 'https:xxx.com' : '', // cdn域名
  4. },
  5. }