• 优化构建速度

    • 优化resolve配置
      • alias:用的创建 import 或 require 的别名,用来简化模块引用 ```javascript const path = require(‘path’) // 路径处理方法 function resolve(dir){ return path.join(__dirname, dir); }

    const config = { resolve:{ // 配置别名 alias: {

    1. '~': resolve('src'),
    2. '@': resolve('src'),
    3. 'components': resolve('src/components'),

    } } } // 使用 src 别名 ~ import ‘~/fonts/iconfont.css’

// 使用 src 别名 @ import ‘@/fonts/iconfont.css’

// 使用 components 别名 import footer from “components/footer”


      - **extensions**:按照配置的数组从左到右的顺序去尝试解析模块;高频文件后缀名放前面;手动配置后,默认配置会被覆盖
```javascript
const config = {
  resolve: {
    extensions: ['.js', '.json', '.wasm']
    # 想保留默认配置,可以用 ... 扩展运算符代表默认配置
    // extensions: ['.ts', '...']
  }
}
# 引入模块时不带扩展名
import file from '../path/to/file'
  - **modules:**告诉 webpack 解析模块时应该搜索的目录
const path = require('path')

// 路径处理方法
function resolve(dir){
  return path.join(__dirname, dir)
}

const config = {
  resolve: {
     modules: [resolve('src'), 'node_modules']
  }
}
  • externals:提供了「从输出的 bundle 中排除依赖」的方法 ```javascript

    引入

    <script src=”https://code.jquery.com/jquery-3.1.0.js“ integrity=”sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk=” crossorigin=”anonymous”

    配置

    const config = { //… externals: { jquery: ‘jQuery’ } }

使用

import $ from ‘jquery’ $(‘.my-element’).animate(//)


   - 缩小范围 include / exclude
      - include:符合条件的模块进行解析
      - exclude:排除符合条件的模块,不解析
      - exclude 优先级更高
   - noParse
      - 不需要解析依赖的第三方大型类库等,可以通过这个字段进行配置,以提高构建速度
      - 使用 noParse 进行忽略的模块文件中不会解析 import、require 等语法    
```javascript
const path = require('path');

// 路径处理方法
function resolve(dir){
  return path.join(__dirname, dir);
}

const config = {
  module: { 
    noParse: /jquery|lodash/,
    rules: [
      {
        test: /\.js$/i,
        include: resolve('src'),
        exclude: /node_modules/,
        use: [
          'babel-loader'
        ]
      }
    ]
  }
}
  • IgnorePlugin(防止在 import 或 require 调用时,生成以下正则表达式匹配的模块)
    • requestRegExp 匹配(test)资源请求路径的正则表达式
    • contextRegExp 匹配(test)资源上下文(目录)的正则表达式 ```javascript new webpack.IgnorePlugin({ resourceRegExp, contextRegExp }) $ npm i -S moment

引入 webpack

const webpack = require(‘webpack’)

const config = { plugins:[ // 配置插件,目的是将插件中的非中文语音排除掉,大大节省打包体积 new webpack.IgnorePlugin({ resourceRegExp: /^.\/locale$/, contextRegExp: /moment$/ }) ]
}


   - 多进程配置
      - 在小型项目中,开启多进程打包反而会增加时间成本,因为启动进程和进程间通信都会有一定开销
      - thread-loader(配置在 [thread-loader](https://link.juejin.cn/?target=https%3A%2F%2Fwebpack.docschina.org%2Floaders%2Fthread-loader%2F%23root) 之后的 loader 都会在一个单独的 worker 池(worker pool)中运行)
```javascript
$ npm i -D  thread-loader
const path = require('path');

// 路径处理方法
function resolve(dir){
  return path.join(__dirname, dir);
}

const config = {
  //...
  module: { 
    noParse: /jquery|lodash/,
    rules: [
      {
        test: /\.js$/i,
        include: resolve('src'),
        exclude: /node_modules/,
        use: [
          {
            loader: 'thread-loader', // 开启多进程打包
            options: {
              worker: 3,
            }
          },
          'babel-loader',
        ]
      },
      // ...
    ]
  }
}
  • 利用缓存(大幅提升重复构建的速度)
    • babel-loader 开启缓存
      • babel 在转译 js 过程中时间开销比价大,将 babel-loader 的执行结果缓存起来,重新打包的时候,直接读取缓存
      • 缓存位置: node_modules/.cache/babel-loader
    • cache-loader
      • 缓存一些性能开销比较大的 loader 的处理结果
      • 缓存位置:node_modules/.cache/cache-loader
    • hard-source-webpack-plugin(为模块提供了中间缓存,重复构建时间大约可以减少 80%,但是在 webpack5 中已经内置了模块缓存,不需要再使用此插件)
    • dll(在webpack5.x 中已经不建议使用这种方式进行模块缓存,因为其已经内置了更好体验的 cache 方法
    • cache 持久化缓存 ```javascript $ npm i -D babel-loader $ npm i -D cache-loader

const config = { // 通过配置 cache 缓存生成的 webpack 模块和 chunk,来改善构建速度 cache: { type: ‘filesystem’, }, module: { noParse: /jquery|lodash/, rules: [ { test: /.js$/i, include: resolve(‘src’), exclude: /node_modules/, use: [ // … { loader: ‘babel-loader’, options: { cacheDirectory: true // 启用缓存 } }, ] }, { test: /.(s[ac]|c)ss$/i, //匹配所有的 sass/scss/css 文件 use: [ // ‘style-loader’, MiniCssExtractPlugin.loader, ‘cache-loader’, // 获取前面 loader 转换的结果 ‘css-loader’, ‘postcss-loader’, ‘sass-loader’, ] }, // … ] } }


- **优化构建结果**
   - 构建结果分析
```javascript
$ npm i -D webpack-bundle-analyzer
# 引入插件
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

const config = {
  // ...
  plugins:[ 
    // ...
    // 配置插件 
    new BundleAnalyzerPlugin({
      // analyzerMode: 'disabled',  // 不启动展示打包报告的http服务器
      // generateStatsFile: true, // 是否生成stats.json文件
    })
  ],
};
# 启动命令
"scripts": {
  // ...
  "analyzer": "cross-env NODE_ENV=prod webpack --progress --mode production"
},
  • 压缩CSS ```javascript $ npm install -D optimize-css-assets-webpack-plugin

    压缩css

    const OptimizeCssAssetsPlugin = require(‘optimize-css-assets-webpack-plugin’)

const config = { optimization: { minimize: true, minimizer: [ // 添加 css 压缩配置 new OptimizeCssAssetsPlugin({}) ] } }


   - 压缩JS
      - 在生成环境下打包默认会开启 js 压缩,但是当手动配置 optimization 选项之后,就不再默认对 js 进行压缩,需要手动去配置。
      - webpack5 内置了[terser-webpack-plugin](https://link.juejin.cn/?target=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Fterser-webpack-plugin) 插件,无需安装
```javascript
const TerserPlugin = require('terser-webpack-plugin');

const config = {
  // ...
  optimization: {
    minimize: true, // 开启最小化
    minimizer: [
      // ...
      new TerserPlugin({})
    ]
  }
}
  • 清除无用的CSS
    • purgecss-webpack-plugin 会单独提取 CSS 并清除用不到的 CSS ```javascript $ npm i -D purgecss-webpack-plugin // … const PurgecssWebpackPlugin = require(‘purgecss-webpack-plugin’) const glob = require(‘glob’); // 文件匹配模式 // …

function resolve(dir){ return path.join(__dirname, dir); }

const PATHS = { src: resolve(‘src’) }

const config = { plugins:[ // 配置插件 // … new PurgecssPlugin({ paths: glob.sync(${PATHS.src}/**/*, {nodir: true}) }) ] }


   - Tree-shaking
      - 剔除没有使用的代码,以降低包的体积
      - webpack 默认支持,需要在 .bablerc 里面设置 module:false,即可在生产环境下默认开启
```javascript
module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        module: false,
        useBuiltIns: "entry",
        corejs: "3.9.1",
        targets: {
          chrome: "58",
          ie: "11",
        },
      },
    ],
  ],
  plugins: [    
    ["@babel/plugin-proposal-decorators", { legacy: true }],
    ["@babel/plugin-proposal-class-properties", { loose: true }],
  ]
}
  • Scope Hoisting
    • 作用域提升,原理是将多个模块放在同一个作用域下,并重命名防止命名冲突,通过这种方式可以减少函数声明和内存开销
    • webpack 默认支持,在生产环境下默认开启
    • 只支持 es6 代码
      • 优化运行时体验(运行时优化的核心就是提升首屏的加载速度,主要的方式就是降低首屏加载文件体积,首屏不需要的文件进行预加载或者按需加载)
  • 入口点分割:配置多个打包入口,多页打包
  • splitChunks 分包配置
  • 代码懒加载
    optimization: {
    splitChunks: {
    chunks: 'all',
    }
    }
    
    ```javascript const ele = document.createElement(‘div’) ele.innerHTML = ‘我是图片描述’ module.exports = ele

import logo from ‘../public/avatar.png’

const a = ‘Hello ITEM’ console.log(a)

const img = new Image() img.src = logo

document.getElementById(‘imgBox’).appendChild(img)

// 按需加载 img.addEventListener(‘click’, () => { // 异步加载方式 import(‘./desc’).then(({ default: element }) => { console.log(element) document.body.appendChild(element) }) })


   - prefetch 与 preload
      - **prefetch** (预获取):浏览器空闲的时候进行资源的拉取
      - **preload** (预加载):提前加载后面会用到的关键资源;因为会提前拉取资源,如果不是特殊需要,谨慎使用
```javascript
// 按需加载
img.addEventListener('click', () => {
  import( /* webpackPrefetch: true */ './desc').then(({ default: element }) => {
    console.log(element)
    document.body.appendChild(element)
  })
})

import(/* webpackPreload: true */ 'ChartingLibrary');