webpack性能优化

404 阅读2分钟

性能优化分为两个方面:打包产物优化、开发体验优化

打包产物优化

打包产物优化主要遵循以下三点

  • 减小打包的整体体积
  • Code Splitting: 按需加载,优化页面首次加载体积。
  • Bundle Splitting: 分包,根据模块更改频率分层次打包,重复利用缓存

减小打包的整体体积

  1. 代码压缩。webpack自作主张在生产环境中默认就把这件事情给做了
  2. 移除不必要的模块。引入一个模块,虽然没有使用,但是webpack仍然会给打包进去,比如:
// 仅仅引入而未在代码中使用,该模块仍然会被打包
import _ from'lodash'
  1. 选择可替代的体积较小的模块。比如使用day.js代替moment.js
  2. 按需引入模块,而不是整体全部引入。 比如:
import DatePicker from'antd/es/date-picker'; // for js
import get from 'lodash.get'
  1. 开启tree-shaking

Code Splitting以按需加载

  1. 使用动态加载模块:import(), 比如:
// multiple possible targets
import(
/* webpackInclude: /\.json$/ */
/* webpackExclude: /\.noimport\.json$/ */
/* webpackChunkName: "my-chunk-name" */
/* webpackMode: "lazy" */
`./locale/${language}`
);
  1. lodable-component 动态加载路由,比如:
import React from 'react'
import { Route } from 'react-router-dom'
import { loadable } from 'react-common-lib'

const Test = loadable({
loader: () => import('./test'),
})

const AppRouter = () => (
<>
<Route path="/test" exact component={Test} />
</>
)

Bundle Splitting

该操作是为了优化缓存,当修改文件后,造成最小范围的缓存失效,这样便能够更充分的利用缓存。 比如将react/loash等常用第三方,打包在一起。 可以使用 webpack配置项 splitChunks 进行分包:

splitChunks: {
    chunks: "async",
    minSize: 30000,
    minChunks: 1,
    maxAsyncRequests: 5,
    maxInitialRequests: 3,
    automaticNameDelimiter: '~',
    name: true,
    cacheGroups: {
        vendors: {
            test: /[\\/]node_modules[\\/]/,
            priority: -10
        },
    default: {
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true
        }
    }
}

开发体验优化

优化webpack一系列配置,主要是减少webpack工作量

1. 优化 Loader 配置

由于 Loader 对文件的转换操作很耗时,所以需要让尽可能少的文件被 Loader 处理。可以通过 test include exclude 三个配置项来命中 Loader 要应用规则的文件。

2. 优化 resolve.modules 配置

resolve.modules 的默认值是['node_modules'],含义是先去当前目录的 node_modules 目录下去找我们想找的模块,如果没找到就去上一级目录 ../node_modules 中找,再没有就去 ../../node_modules 中找,以此类推。 这和 Node.js 的模块寻找机制很相似。

当安装的第三方模块都放在项目根目录的 node_modules 目录下时,就没有必要按照默认的方式去一层层地寻找,可以指明存放第三方模块的绝对路径,以减少寻找.

module.exports = {
    resolve: {
        modules: [path.resolve( __dirname,'node modules')] 
    },
}
3. 优化 resolve.alias 配置

resolve.alias 配置项通过别名来将原导入路径映射成一个新的导入路径。

4. 优化 resolve.extensions 配置

后缀尝试列表要尽可能小,不要将项目中不可能存在的情况写到后缀尝试列表中。频率出现最高的文件后缀要优先放在最前面.

module.exports = { 
    resolve : { 
        //尽可能减少后缀尝试的可能性
        extensions : ['js'],
    }
} 
5. 使用 HappyPack

Webpack 是单线程模型的,也就是说 Webpack 需要一个一个地处理任务,不能同时处理多个任务。HappyPack将任 务分解给多个子进程去并发执行,子进程处理完后再将结果发送给主进程,从而发挥多核 CPU 电脑的威力。

const HappyPack = require('happypack')
const os = require('os')
const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length })

{
    test: /\.js$/,
    // loader: 'babel-loader',
    loader: 'happypack/loader?id=happy-babel-js', // 增加新的HappyPack构建loader
    include: [resolve('src')],
    exclude: /node_modules/,
}

plugins: [
    new HappyPack({
    id: 'happy-babel-js',
    loaders: ['babel-loader?cacheDirectory=true'],
    threadPool: happyThreadPool
    })
]
6. 优化文件监昕的性能

在开启监听模式时,默认情况下会监听配置的 Entry 文件和所有 Entry 递归依赖的文件,在这些文件中会有很多存在于 node_modules 下,因为如今的 Web 项目会依赖大量的第三方模块, 所以在大多数情况下我们都不可能去编辑 node_modules 下的文件,而是编辑自己建立的源码文件,而一个很大的优化点就是忽略 node_modules 下的文件,不监听它们。

module.export = { 
    watchOptions : { 
        //不监听的 node_modules 目录下的文件
        ignored : /node_modules/, 
    }
}