webpack5-高级配置

162 阅读10分钟

官方文档:webpack.docschina.org/
webpack高级配置其实就是进行webpack优化,让代码编译运行时更好。
我们从以下角度进行优化:

1、提升开发体验
2、提升打包构建速度
3、减少代码体积
4、优化代码运行性能

SourceMap

为什么?
开发时在浏览器运行的代码是经过webpack编译过的,如下图:

image.png
假如程序内有报错,在控制台提示的错误位置是编译后代码的位置,不太友好,而我们想提示源文件的位置。
是什么?
它称之为源代码映射,是一个用来生成源代码与构建后代码---映射的文件的方案。
它会生成一个xxx.map文件,里面包含源代码和构建后代码每一行、每一列的映射关系,当构建后代码出错了,会通过xxx.map文件,从构建后代码出错位置,找到映射后代码出错位置,从而让浏览器提示源代码文件出错位置,帮助我们更快的找到问题根源。
怎么用?
文档链接:webpack.docschina.org/configurati…
devtool文档里有很多方式,但是我们只关注常用的两种模式:
. 开发模式: cheap-module-source-map

优点:打包编译速度快,只包含行映射
缺点:没有列映射
mode: 'development',
// SourceMap
devtool: 'cheap-module-source-map'

. 生产模式: source-map

优点:包含行/列映射
缺点:打包编译速度更慢
mode: 'production',
// SourceMap
devtool: 'source-map'

提升打包构建速度

HotModuleReplacement
为什么?
打包时我们修改一个模块的代码,webpack默认会将所有模块重新打包编译,速度很慢。 是什么?
HotModuleReplacement(HRM/热模块替换):在程序运行中、替换、添加或删除模块,而无需重新加载整个页面。
怎么用?

热模块替换只对css有效
devServer: {
    hot: true, // 默认开启
},
如果想让js模块也有热更新,比如想让count.js模块热更新,可以这样处理
import count from './js/count'
if(module.hot) {
  module.hot.accept('./js/count') // 当更改count.js内部的代码时就会只更新这一个js模块
}
这样手动写会写很多,vue-loader和react-hot-loader都给我们做好了

OneOf

为什么?
打包时每个文件都会经过所有loader处理,虽然因为test正则原因实际没有处理上,但是都要过一遍,比较慢。
是什么?
顾名思义,就是只能匹配上一个loader,剩下的就不匹配了。
怎么用?
用{ oneOf: []}把所有loader配置包裹起来,这个配置支持开发环境和生产环境。

// 加载器
  module: {
    rules: [
      // loader的配置
      {
        oneOf: [
          // css-loader
          {
            test: /\.css$/i, // 检测.css结尾的文件
            use: [
              "style-loader", // 将js中css通过创建style标签添加到html文件生效
              "css-loader" // 将css文件编译成commonjs模块到js中
            ], // use执行顺序从右向左
          },
          ...好多loader
        ]
      }
    ]
  },

Include-Exclude

为什么?
开发时我们需要用到第三方库和插件,所有文件都下载到node_modules中了,而这些文件是不需要要编译可以直接使用的。
是什么?
include:包含 值处理xxx文件 exclude: 排除 出了xxx文件以外其他文件都处理
这两个配置只能写一个,要不包含,要不排除
怎么用?
主要针对js文件

// 配置babel
{
    test: /\.js$/,
    exclude: /node_modules/, // 排除node_modules文件,其它文件都处理
    // include: path.resolve(__dirname, '../src'), // 只处理src下的文件,其它文件不处理
    use: {
      loader: 'babel-loader'
    }
}
eslint插件配置:
// eslint配置
new ESLintPlugin({
  // 检测的目录
  context: path.resolve(__dirname, '../src'),
  exclude: ['node_modules'], // 排除node_modules文件(默认会排除node_modules)
}),

Cache

为什么?
每次打包时js文件都会经过Eslint检查和babel编译,速度比较慢,
我们可以缓存之前的eslint检查和babel编译结果,这样第二次打包时速度就会更快了。 是什么?
对eslint检查和babel编译结果进行缓存。
怎么用?
对eslint和babel开启缓存:

// 配置babel
{
    test: /\.js$/,
    exclude: /node_modules/,
    use: {
      loader: 'babel-loader',
      options: {
        cacheDirectory: true, // 开启babel缓存
        cacheCompression: false, // 关闭缓存文件压缩
      }
    }
}

plugins
// eslint配置
new ESLintPlugin({
  context: path.resolve(__dirname, '../src'),
  exclude: ['node_modules'], 
  cache: true, // 开启eslint缓存
  cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // 设置eslint缓存目录
}),

Thead

为什么?
当项目越来越庞大时,打包速度越来越慢,甚至一个下午才能打包出来,这个速度是比较慢的。
我们想要提升打包速度,其实就是提升js打包速度,因为其他文件都比较少,
而对js处理,主要就是eslint、babel、Terser三个工具,所以我们要提升他们的运行速度。
我们可以开启很多进程同时处理js文件,这样速度就比之前单进程打包速度更快了。
是什么?
多进程打包:开启电脑的多个进程同时干一件事,速度更快。
注意:请仅在特别耗时的操作中使用,因为每个进程启动就有大约为600ms左右开销。
怎么用?
我们启动进程的数量就是我们cpu的核数。
1、如何获电脑cpu的核数?

const os = require('os');
const threads = os.cpus().length;
console.log(threads, 'threads')

2、下载包

npm i thread-loader -D

3、thread-loader要放在babel-loader前面,
我们分别对处理js的三个库babel、eslint、terser开启多进程

const os = require('os'); // 内置
const TerserWebpackPlugin = require('terser-webpack-plugin'); // 内置(压缩js)
const threads = os.cpus().length;


// 配置babel
{
    test: /\.js$/,
    exclude: /node_modules/,
    use: [
      {
        loader: 'thread-loader', // 开启多进程,对babel做处理
        options: {
          workers: threads, // 设置进程数量
        }
      },
      {
        loader: 'babel-loader',
        options: {
          // presets: ['@babel/preset-env']
          cacheDirectory: true,
          cacheCompression: false,
        }
      }
    ]
}

eslint开启多进程
// eslint配置
new ESLintPlugin({
  // 检测的目录
  context: path.resolve(__dirname, '../src'),
  exclude: ['node_modules'],
  cache: true,
  cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'),
  threads, // 开启多进程和设置进程数量
}),

terser开启多进程
optimization: {
    // webpack5推荐把压缩放这里
    minimizer: [
      // css压缩
      new CssMinimizerPlugin(),
      // js压缩配置(生产环境默认开启)
      new TerserWebpackPlugin({
        parallel: threads, // 开启多进程和设置进程数量
      })
    ]
},

减少代码体积

Tree Shaking 去除没用到的代码

为什么?
开发时我们引用第三方工具函数库或组件库。如果没有特殊处理的话,打包时会引入整个库,可实际上我们可能只用到了极小部分的功能,这样整个库打包出来,体积就太大了。
是什么?
tree shaking 是一个术语,通常用于描述javascript中没有使用上的代码。
注意:它依赖ES Moudle
怎么用?
webpack默认开启了这个功能,无需额外配置。

babel 公共方法提取

为什么?
babel为编译代码都插入了辅助代码,使代码体积变大。
babel对一些公共代码使用了非常小的辅助代码,比如_extend,默认情况下会被定义在每一个需要他的文件中。
你可以将这些公共方法作为一个独立模块,来避免重复定义。\

是什么?\

@babel/plugin-transform-runtime : 禁用了babel对每个文件的runtime注入,而是引入
@babel/plugin-transform-runtime,并且使用所有辅助代码从这里引用。

怎么用?
1.下载包

npm install @babel/plugin-transform-runtime -D

2.配置

// 加载器
module: {
  rules: [
      // loader的配置
      // 配置babel
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: [
          {
            loader: 'thread-loader',
            options: {
              workers: threads,
            }
          },
          {
            loader: 'babel-loader',
            options: {
              cacheDirectory: true,
              cacheCompression: false,
              plugins: ['@babel/plugin-transform-runtime'] , // 减少代码体积,开发/生产环境都能用
            }
          }
        ]
      }
    ]
  }
  ]
},

Image Minimizer

为什么?
开发如果引入的图片比较多,那么图片体积会比较大,将来请求速度比较慢。
我们可以对图片进行压缩,减少图片体积。
注意:如果项目中图片都是在线链接,那么就不需要了,本地项目静态图片才需要压缩。
是什么?\

image-minimizer-webpack-plugin:用来压缩图片的插件

怎么用?
1.下载包:

npm install image-minimizer-webpack-plugin imagemin -D
还有剩下的包需要下载,有两种模式:
1、无损压缩
npm install imagemin-gifsicle imagemin-jpegtran imagemin-optipng imagemin-svgo -D
2、有损压缩
npm install imagemin-gifsicle imagemin-mozjpeg imagemin-pngquant imagemin-svgo -D

2.配置
以无损压缩为例

optimization: {
    // webpack5推荐把压缩放这里
      // 压缩图片(也可以放到plugin配置中)
      new ImageMinimizerPlugin({
        minimizer: {
          implementation: ImageMinimizerPlugin.imageminGenerate,
          options: {
            plugins: [
              ["gifsicle", { interlaced: true }],
              ["jpegtran", { progressive: true }],
              ["optipng", { optimizationLevel: 5 }],
              [
                "svgo",
                {
                  plugins: [
                    "preset-default",
                    "prefixIds",
                    {
                      name: "sortAttrs",
                      params: {
                        xmlnsOrder: "alphabetical"
                      }
                    }
                  ]
                }
              ]
            ]
          }
        }
      })
    ]
  },

优化代码性能

Code Split

为什么?
打包文件时会把所有js代码打包到一个文件中,体积太大了,我们如果只要渲染首页,就应该只加载首页的js文件,其它文件不应该加载。
所以我们应该将打包生成的文件进行代码分割,生成多个js文件,渲染哪个页面,就只加载某个js文件,这样加载的资源就少,速度就更快。
是什么?

code split 主要做两件事:
1、分割文件,将打包生成的文件进行分割,生成多个js文件。
2、按需加载,需要哪个文件就加载哪个文件。

怎么用?
代码分割实现方式有不同的方式\

1、多入口对应多输出

image.png

我们配置两个入口main.js和app.js
依赖下载:

npm i webpack webpack-cli html-webpack-plugin -D

webpack配置:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'production',
  // entry: './src/main.js', // 单入口
  entry: { // 多入口
    app: './src/app.js',
    main: './src/main.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js', // webpack的命名方式, [name]:以文件名自己命名
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, 'public/index.html')
    })
  ]
}
2、多入口提取公共模块

webpack配置:

optimization: {
    // 代码分割配置
    splitChunks: {
      chunks: 'all', // 对所有模块进行分割
      // 以下是默认配置
      // minSize: 20000, // 分割代码最小的大小(分割的最小体积是20kb)
      // minRemainingSize: 0, // 类似于minSize,最后确保提取的文件大小不能为0(带下为0的模块没必要提取)
      // minChunks: 1, // 至少被引用的次数,满足条件才会代码分割
      // maxAsyncRequests: 30, // 按需加载时并行加载的文件的最大数量(请求数量大于30时,就不会再分割代码了,并行请求数量大对服务器的压力变大)
      // maxInitialRequests: 30, // 入口js文件最大并行请求数量(初始请求数量不能超过30个)
      // enforceSizeThreshold: 50000, // 超过50kb一定会单独打包(此时会忽略minRemainingSize、maxAsyncRequests、maxInitialRequests)
      // cacheGroups: { // 组,哪些模块要打包到一个组
      //   defaultVendors: { // 组名
      //     test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
      //     priority: -10, // 权重(越大越高)
      //     reuseExistingChunk: true, // 如果当前chunk包含已从主bundle中拆分出的模块,则它将被重用,而不是生成新的模块
      //   },
      //   default: { // 其它没有写的配置会使用上面的默认值
      //     minChunks: 2, // 这里的minChunks权重更大
      //     priority: -20,
      //     reuseExistingChunk: true,
      //   }
      // }
      // 修改配置
      cacheGroups: {
        // 组,哪些模块要打包到一组
        //   defaultVendors: { // 组名
        //     test: /[\\/]node_modules[\\/]/, // 需要打包到一起的模块
        //     priority: -10, // 权重(越大越高)
        //     reuseExistingChunk: true, // 如果当前chunk包含已从主bundle中拆分出的模块,则它将被重用,而不是生成新的模块
        //   },
        default: {
          // 其它没有写的配置会使用上面的默认值
          minSize: 0, // 我们定义的文件体积太小了,所以要改最小的打包文件体积
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        }
      }
    }
  }

有了上面代码分割的配置,有几个入口就会打包成多少个文件,有多少个按需加载就会打包多少个文件,公共代码块也会根据规则打包生成单独文件。

3、多入口按需加载
点击按钮的时候才会加载对应的js模块(加载后将不会加载)
document.getElementById('btn').onclick = function () {
  import('./count').then((res) => {
    console.log('模块加载成功', res.default(2,2))
  }).catch((err) => {
    console.log('模块加载失败',err)
  })
}
4、单入口

开发时,我们可能是单页面应用(SPA),只有一个入口(单入口);

optimization: {
    // 代码分割配置
    splitChunks: {
      chunks: "all",
      // 其它的都用默认值即可(由于只有一个入口)
      // 不需要配置cacheGroup相关配置了,因为只有一个入口,其它的配置沿用默认值
    }
  },
5、code split 给模块命名

按需加载时,定义好模块名:

document.getElementById('btn').onclick = function() {
  // /* webpackChunkName: "math" */ webpack魔法命名
  import(/* webpackChunkName: "math" */ './js/math').then(res => {
    console.log(res.mul(3,4), '按需加载')
  })
}

webpack配置:

output: {
    path: path.resolve(__dirname, '../dist'),
    // 入口文件名
    filename: 'static/js/main.js',
    // 除入口文件外其它代码块儿的命名
    chunkFilename: 'static/js/[name].js',
    clean: true
  },
6、统一命名配置

我们之前配置了,入口文件、其它模块、图片、字体图标、样式命名,下面来统一配置这些文件的命名

入口文件、其它模块、其它资源的命名都可以在output中配置
output: {
    // 所有文件输出路径
    path: path.resolve(__dirname, '../dist'), // 要求用绝对路径
    // 入口文件打包输出的文件名
    filename: 'static/js/[name].[contenthash:8].js', // 把static/js/main.js改成static/js/[name].js,如果将来入口文件名称变了,还可以兼容
    // 除入口文件外其它代码块儿的命名
    chunkFilename: 'static/js/[name].chunk.[contenthash:8].js', // 文件名后加上.chunk可以区分那个是入口文件,哪些是模块文件
    // 图片字体等,通过type:asset 处理资源命名方法
    assetModuleFilename: 'static/media/[hash:10][ext][query]',
  },
样式文件命名:
plugins: [
    // 提取css成单独文件
    new MiniCssExtractPlugin({
      // 改变css文件输出后的路径
      filename: 'static/css/[name].css',
      // 模块命名 (如果有动态导入的css,也会打包成单独模块)
      chunkFilename: 'static/css/[name].chunk.[contenthash:8].css',
    }),
]

preload/prefetch

为什么?
我们已经做过了代码分割,也用了import动态导入来实现按需加载,
但是还不够好,当用户点击时,如果动态导入的文件很大,就会出现卡顿。
我们想在浏览器空闲时加载后续需要的资源,就需要用到preload或prefetch技术。 是什么?\

preload:告诉浏览器立即加载资源
prefetch: 告诉浏览器在空闲时才开始加载资源

他们的共同点:

1.都只会加载资源,并不执行
2.都有缓存

他们的区别:

preload:加载优先级高; prefetch:加载优先级低
preload:只能加载当前页面需要使用的资源,prefetch可以加载当前页面使用的资源,也可以加载下一个页面需要的资源

总结:

当前页面优先级高的资源用preload加载
下一个页面需要的资源用prefetch加载

共同存在的问题:
他们的兼容性很差,从can i use网站看,preload的兼容性要稍微好些。\

@vue/preload-webpack-plugin 插件可以将资源自动变成prelaod或prefetch的方式加载
下载插件:

npm install @vue/preload-webpack-plugin -D

webpack配置:

const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');
plugins: [
    // plugin的配置
    new PreloadWebpackPlugin({
       rel: 'preload', // js用preload的方式去加载,也可以用prefetch
       as: 'script', // 作为script标签的优先级去执行
       // as: 'style', // 如果想要优先级最高,可以设置为style
    })
  ],

缓存处理

为什么?
上面我们用了[contenthash]来保证每次打包,每个文件的名称变化,来刷新缓存。
如果A依赖b、c、d模块,如果b模块改了一点代码,那么a文件打包出来的文件的hash值也会变化,其实只更新b模块就行了,这样有助于打包速度。 怎么做?
可以让文件名放到runtime文件中,这样即使依赖文件更新,a也不会重新打包;
webpack配置:

optimization: {
    // runtime文件来保存文件的hash值
    runtimeChunk: {
      name: entrypoint => `runtime-${entrypoint.name}.js`
    }
  },

解决js兼容问题 CoreJS

为什么?
过去我们用bable对js进行兼容性处理,其中使用@babel/preset-env智能预设来处理兼容性处理。 他能将es6的语法进行编译转换,比如箭头函数,点点点运算符等,但是如果是async函数promise对象,数组的一些方法includes等,它就没法处理。
所以此时我们的代码仍然存在兼容性问题,一旦遇到低版本浏览器会直接报错,想把js兼容问题彻底解决,需要用到core-js
是什么?
core-js是专门用来做es6以及以上api的polyfill。
polyfill翻译过来叫做垫片、补丁。就是用社区上提供的一段代码,让我们在不兼容某些新特性的浏览器上,使用该特性。 怎么用?
1、完整引入:

下载依赖
npm i core-js
入口文件引入core-js
import 'core-js'

2、按需引入

// 按需引入
import 'core-js/es/promise'

3、自动引入
需要配置babel.config.js

module.exports = {
  // 预设
  presets: [
    // '@babel/preset-env' // 智能预设,允许使用更高级的js语法
    ['@babel/preset-env', {
      useBuiltIns: 'usage', // 按需引入core-js
      corejs: 3
    }]
  ]
}

pwa

为什么?
开发web app项目,项目一旦处于网络离线情况,就没法访问了。
我们希望给项目提供离线体验。 是什么?
渐进式网络应用程序(progressive web application PWA):是一种可以提供类似native app(原生应用程序)体验的web app的技术,期中最重要的是,在离线(offline)时,应用程序可以继续运行功能。
内部通过service workers技术实现的。
怎么用?

下载包
npm install workbox-webpack-plugin --save-dev
引入
const WorkboxPlugin = require('workbox-webpack-plugin');
配置
plugins: [
    new WorkboxPlugin.GenerateSW({
      // 这些选项帮助快速启用 ServiceWorkers
      // 不允许遗留任何“旧的” ServiceWorkers
      clientsClaim: true,
      skipWaiting: true,
    }),
],
在main.js中假如一段代码,注册生成service worker
// 注册生成service worker
if ('serviceWorker' in navigator) {
  window.addEventListener('load', () => {
    navigator.serviceWorker.register('/service-worker.js').then(registration => {
      console.log('SW registered: ', registration);
    }).catch(registrationError => {
      console.log('SW registration failed: ', registrationError);
    });
  });
}

截图来自尚硅谷

image.png

开发环境配置:

const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const os = require('os'); // 内置
const TerserWebpackPlugin = require('terser-webpack-plugin'); // 内置(压缩js)
const threads = os.cpus().length;
module.exports = {
  // 入口
  entry: './src/main.js', // 用相对路径
  // 出口
  output: {
    // 所有文件输出路径
    path: path.resolve(__dirname, '../dist'), // 要求用绝对路径(开发模式没有输出,这里可以省去)
    // 入口文件打包输出的文件名
    filename: 'static/js/[name].js', // 把static/js/main.js改成static/js/[name].js,如果将来入口文件名称变了,还可以兼容
    // 除入口文件外其它代码块儿的命名
    chunkFilename: 'static/js/[name].chunk.js', // 文件名后加上.chunk可以区分那个是入口文件,哪些是模块文件
    // 图片字体等,通过type:asset 处理资源命名方法
    assetModuleFilename: 'static/media/[hash:10][ext][query]',
    // 自动清除上次打包的内容
    // 原理:在打包前,将整个path目录清空,再进行打包
    clean: true , // 开发模式没有输出,可以不配置
  },
  // 加载器
  module: {
    rules: [
      // loader的配置
      {
        oneOf: [
          // css-loader
          {
            test: /\.css$/i, // 检测.css结尾的文件
            use: [
              "style-loader", // 将js中css通过创建style标签添加到html文件生效
              "css-loader" // 将css文件编译成commonjs模块到js中
            ], // use执行顺序从右向左
          },
          {
            test: /\.less$/i,
            use: [
              // compiles Less to CSS
              'style-loader',
              'css-loader',
              'less-loader',
            ],
          },
          {
            test: /\.s[ac]ss$/i,
            use: [
              'style-loader',
              'css-loader',
              // 将 Sass 编译成 CSS
              'sass-loader',
            ],
          },
          {
            test: /\.styl$/i,
            use: [
              'style-loader',
              'css-loader',
              // 将 stylus 编译成 CSS
              'stylus-loader',
            ],
          },
          {
            // 只需要打开此配置就行,不需要下载额外loader
            // 如果不对图片做额外优化可以没有此配置
            test: /.(png|jpe?g|gif|webp|svg)$/, 
            type: "asset",
            parser: {
              dataUrlCondition: {
                // 小于10kb的图片会转base64
                // 有点:减少请求数量。缺点:小图的体积会变大一点点(大图的体积会变大很多,这里一般限制10kb一下的图片转base64)
                maxSize: 10 * 1024 // 10kb
              }
            },
            generator: {
              // 输出图片名称
              // 将图片输出在static/images文件下
              // filename: 'static/images/[hash][ext][query]'
              // [hash:10] 代表hash值只取前10位
              // filename: 'static/images/[hash:10][ext][query]'
            }
          },
          {
            // 打包字体资源
            // 若不需要进行额外配置,不需要进行针对media做任何配置,webpack会原封不动的输出出去
            // 如果需要打包mp3、MP4、avi等音视频资源,webpack识别不了他们,只需要在后面加配置即可
            test: /.(ttf|woff2?|mp3|mp4|avi)$/, 
            type: "asset/resource",
            generator: {
              // 改变资源输出后的路径
              // filename: 'static/media/[hash:10][ext][query]'
            }
          },
          // 配置babel
          {
            test: /\.js$/,
            exclude: /node_modules/, // 排除node_modules文件,其它文件都处理
            // include: path.resolve(__dirname, '../src'), // 只处理src下的文件,其它文件不处理
            use: [
              {
                loader: 'thread-loader', // 开启多进程,对babel做处理
                options: {
                  workers: threads, // 指定开启多少个进程
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  // presets: ['@babel/preset-env']
                  cacheDirectory: true, // 开启babel缓存
                  cacheCompression: false, // 关闭缓存文件压缩
                  plugins: ['@babel/plugin-transform-runtime'] , // 减少代码体积
                }
              }
            ]
          }
        ]
      }
    ]
  },
  // 插件
  plugins: [
    // plugin的配置
    // eslint配置
    new ESLintPlugin({
      // 检测的目录
      context: path.resolve(__dirname, '../src'),
      exclude: ['node_modules'], // 排除node_modules文件(默认会排除node_modules)
      cache: true, // 开启eslint缓存
      cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // 设置eslint缓存目录
      threads, // 开启多进程和设置进程数量
    }),
    new HtmlWebpackPlugin({
      // 模板,以public/index.html为模板,创建新的html文件
      // 新html文件的特点:1、和原来结构一致,2、自动引入打包输出的资源
      template: path.resolve(__dirname, '../public/index.html')
    })
  ],
  // 开发服务器(开发服务器是不会输出资源的,它是在内存中编译打包的)
  devServer: {
    host: 'localhost', // 启动服务器的域名
    port: 3000, // 启动服务器端口号
    open: false, // 是否自动打开浏览器
    open: true,
    hot: true, // 默认开启
  },
  // 模式
  mode: 'development',
  // SourceMap
  devtool: 'cheap-module-source-map'
}

生产环境配置:

const ESLintPlugin = require('eslint-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
// const PreloadWebpackPlugin = require('@vue/preload-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');
const path = require('path');
const os = require('os'); // 内置
const TerserWebpackPlugin = require('terser-webpack-plugin'); // 内置(压缩js)
const threads = os.cpus().length;
console.log(threads, 'threads')
// 获取处理样式的loader
const getStyleLoader = (pre) => {
  return [
    // compiles Less to CSS
    MiniCssExtractPlugin.loader,
    'css-loader',
    {
      loader: 'postcss-loader',
      options: {
        postcssOptions: {
          plugins: [
            [
              'postcss-preset-env', // 能解决大多数样式兼容性问题
              {
                // 其他选项
              },
            ],
          ],
        },
      },
    },
    pre,
  ].filter(Boolean)
}
module.exports = {
  // 入口
  entry: './src/main.js', // 用相对路径
  // 出口
  output: {
    // 所有文件输出路径
    path: path.resolve(__dirname, '../dist'), // 要求用绝对路径
    // 入口文件打包输出的文件名
    filename: 'static/js/[name].[contenthash:8].js', // 把static/js/main.js改成static/js/[name].js,如果将来入口文件名称变了,还可以兼容
    // 除入口文件外其它代码块儿的命名
    chunkFilename: 'static/js/[name].chunk.[contenthash:8].js', // 文件名后加上.chunk可以区分那个是入口文件,哪些是模块文件
    // 图片字体等,通过type:asset 处理资源命名方法
    assetModuleFilename: 'static/media/[hash:10][ext][query]',
    // 自动清除上次打包的内容
    // 原理:在打包前,将整个path目录清空,再进行打包
    clean: true
  },
  // 加载器
  module: {
    rules: [
      // loader的配置
      {
        oneOf: [
          // css-loader
          {
            test: /\.css$/i, // 检测.css结尾的文件
            use: getStyleLoader(),
          },
          {
            test: /\.less$/i,
            use:  getStyleLoader('less-loader'),
          },
          {
            test: /\.s[ac]ss$/i,
            use: getStyleLoader('sass-loader'),
          },
          {
            test: /\.styl$/i,
            use: getStyleLoader('stylus-loader'),
          },
          {
            // 只需要打开此配置就行,不需要下载额外loader
            // 如果不对图片做额外优化可以没有此配置
            test: /.(png|jpe?g|gif|webp|svg)$/, 
            type: "asset",
            parser: {
              dataUrlCondition: {
                // 小于10kb的图片会转base64
                // 有点:减少请求数量。缺点:小图的体积会变大一点点(大图的体积会变大很多,这里一般限制10kb一下的图片转base64)
                maxSize: 10 * 1024 // 10kb
              }
            },
            generator: {
              // 输出图片名称
              // 将图片输出在static/images文件下
              // filename: 'static/images/[hash][ext][query]'
              // [hash:10] 代表hash值只取前10位
              // filename: 'static/images/[hash:10][ext][query]'
            }
          },
          {
            // 打包字体资源
            // 若不需要进行额外配置,不需要进行针对media做任何配置,webpack会原封不动的输出出去
            // 如果需要打包mp3、MP4、avi等音视频资源,webpack识别不了他们,只需要在后面加配置即可
            test: /.(ttf|woff2?|mp3|mp4|avi)$/, 
            type: "asset/resource",
            generator: {
              // 改变资源输出后的路径
              // filename: 'static/media/[hash:10][ext][query]'
            }
          },
          // 配置babel
          {
            test: /\.js$/,
            exclude: /node_modules/, // 排除node_modules文件
            use: [
              {
                loader: 'thread-loader', // 开启多进程,对babel做处理
                options: {
                  workers: threads, // 指定开启多少个进程
                }
              },
              {
                loader: 'babel-loader',
                options: {
                  // presets: ['@babel/preset-env']
                  cacheDirectory: true, // 开启babel缓存
                  cacheCompression: false, // 关闭缓存文件压缩
                  plugins: ['@babel/plugin-transform-runtime'] , // 减少代码体积
                }
              }
            ]
          }
        ]
      }
    ]
  },
  // 插件
  plugins: [
    // plugin的配置
    // eslint配置
    new ESLintPlugin({
      // 检测的目录
      context: path.resolve(__dirname, '../src'),
      exclude: ['node_modules'], // 排除node_modules文件(默认会排除node_modules)
      cache: true, // 开启eslint缓存
      cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // 设置eslint缓存目录
      threads, // 开启多进程和设置进程数量
    }),
    new HtmlWebpackPlugin({
      // 模板,以public/index.html为模板,创建新的html文件
      // 新html文件的特点:1、和原来结构一致,2、自动引入打包输出的资源
      template: path.resolve(__dirname, '../public/index.html')
    }),
    // 提取css成单独文件
    new MiniCssExtractPlugin({
      // 改变css文件输出后的路径
      filename: 'static/css/[name].[contenthash:8].css',
      // 模块命名 (如果有动态导入的css,也会打包成单独模块)
      chunkFilename: 'static/css/[name].chunk.[contenthash:8].css',
    }),
    // // css压缩
    // new CssMinimizerPlugin(),
    // // js压缩配置(生产环境默认开启)
    // new TerserWebpackPlugin({
    //   parallel: threads, // 开启多进程和设置进程数量
    // })
    // new PreloadWebpackPlugin({
    //   rel: 'preload', // js用preload的方式去加载,
    //   as: 'script', // 作为script标签的优先级去执行
    //   // as: 'style', // 如果想要优先级最高,可以设置为style
    // }),
    new WorkboxPlugin.GenerateSW({
      // 这些选项帮助快速启用 ServiceWorkers
      // 不允许遗留任何“旧的” ServiceWorkers
      clientsClaim: true,
      skipWaiting: true,
    }),
  ],
  optimization: {
    // webpack5推荐把压缩放这里
    minimizer: [
      // css压缩
      new CssMinimizerPlugin(),
      // js压缩配置(生产环境默认开启)
      new TerserWebpackPlugin({
        parallel: threads, // 开启多进程和设置进程数量
      }),
      // 压缩图片(也可以放到plugin配置中)
      new ImageMinimizerPlugin({
        minimizer: {
          implementation: ImageMinimizerPlugin.imageminGenerate,
          options: {
            plugins: [
              ["gifsicle", { interlaced: true }],
              ["jpegtran", { progressive: true }],
              ["optipng", { optimizationLevel: 5 }],
              [
                "svgo",
                {
                  plugins: [
                    "preset-default",
                    "prefixIds",
                    {
                      name: "sortAttrs",
                      params: {
                        xmlnsOrder: "alphabetical"
                      }
                    }
                  ]
                }
              ]
            ]
          }
        }
      })
    ],
    // 代码分割配置
    splitChunks: {
      chunks: "all",
      // 其它的都用默认值即可
    },
    // runtime文件来保存文件的hash值
    runtimeChunk: {
      name: entrypoint => `runtime-${entrypoint.name}.js`
    }
  },
  // 模式
  mode: 'production',
  // SourceMap
  devtool: 'source-map'
}

package.json

{
  "name": "webpack5",
  "version": "1.0.0",
  "description": "{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**}",
  "main": "note.js",
  "scripts": {
    "start": "npm run dev",
    "dev": "npx webpack server --config ./config/webpack.dev.js",
    "build": "webpack --config ./config/webpack.prod.js"
  },
  "repository": {
    "type": "git",
    "url": "https://gitee.com/xxx/webpack5.git"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/core": "^7.20.2",
    "@babel/plugin-transform-runtime": "^7.19.6",
    "@babel/preset-env": "^7.20.2",
    "babel-loader": "^9.1.0",
    "css-loader": "^6.7.1",
    "css-minimizer-webpack-plugin": "^4.2.2",
    "eslint": "^8.26.0",
    "eslint-webpack-plugin": "^3.2.0",
    "html-webpack-plugin": "^5.5.0",
    "image-minimizer-webpack-plugin": "^3.8.0",
    "imagemin": "^8.0.1",
    "imagemin-gifsicle": "^7.0.0",
    "imagemin-jpegtran": "^7.0.0",
    "imagemin-mozjpeg": "^10.0.0",
    "imagemin-optipng": "^8.0.0",
    "imagemin-pngquant": "^9.0.2",
    "imagemin-svgo": "^10.0.1",
    "less": "^4.1.3",
    "less-loader": "^11.1.0",
    "mini-css-extract-plugin": "^2.6.1",
    "postcss": "^8.4.18",
    "postcss-loader": "^7.0.1",
    "postcss-preset-env": "^7.8.2",
    "sass": "^1.55.0",
    "sass-loader": "^13.1.0",
    "style-loader": "^3.3.1",
    "stylus-loader": "^7.1.0",
    "thread-loader": "^3.0.4",
    "webpack": "^5.74.0",
    "webpack-cli": "^4.10.0",
    "webpack-dev-server": "^4.11.1",
    "workbox-webpack-plugin": "^6.5.4"
  },
  "browserslist": [
    "last 2 version",
    "> 1%",
    "not dead"
  ],
  "dependencies": {
    "core-js": "^3.26.0"
  }
}