webpack高级配置
一、了解webpack
高级配置:
1、什么是webpack
高级配置:
进行
Webpack
优化,让代码在编译或者运行时性能更好
2、webpack
优化从哪些方面入手:
① 提升开发体验,增强开发和生产环境的代码调试:
如果代码编写出错了,在浏览器查看错误并且需要定位的时候,查看的是编译后的代码,难以定位具体的错误位置
② 提升打包构建速度:
(1) 模块更新:
开发时我们修改了其中一个模块代码,
Webpack
默认会将所有模块全部重新打包编译,速度很慢。
(2) 打包优化:
打包时每个文件都会匹配所有
loader
,速度比较慢。
(3) 第三方插件编译:
使用第三方的库或插件时,所有资源都会下载到
node_modules
中,这些资源是不需要编译可以直接使用的。
(4) eslint
和babel
缓存:
每次打包时
js
文件都要经过Eslint
检査 和Babel
编译,速度比较慢。
(5) 多进程js
文件打包:
当项目越来越庞大时,打包速度就会越来越慢。对
js
文件处理主要就是eslint
、babel
、Terser
三个工具,所以我们要提升它们的运行速度,。我们可以开启多进程同时处理js
文件,这样速度就比之前的单进程打包更快了。
③ 减少代码体积:
(1)第三方工具库引用:
对于自定义的工具函数库,或者引用第三方工具函数库、组件库。实际上可能只用上极小部分的功能,如果没有特殊处理,打包时会引入整个库,体积太大
(2)Babel
编译时代码重复插入:
Babel
为编译的每个文件都插入了辅助代码,使代码体积过大
④ 优化代码运行性能:
二、提升开发体验:SourceMap
中文文档:https://webpack.docschina.org/configuration/devtool/#root
1、SourceMap
的定义:
SourceMap
(源代码映射)是一个用来生成源代码与构建后代码一一映射的文件的方案。
2、为什么能优化调试:
① 使用SourceMap
后,会生成一个 xxx.map
文件,里面包含源代码和构建后的代码每一行、每一列的映射关系。
② 浏览器会自动查找xxx.map
文件
③ 当代码出错时,浏览器会提示源代码文件出错位置(通过 xxx.map
文件,从构建后代码出错位置找到映射后源代码出错位置)
3、SourceMap
的使用:
① 开发模式:devtool: 'cheap-module-source-map'
- 优点:打包编译速度快,只包含行映射
- 缺点:没有列映射
mode: 'development',
devtool: 'cheap-module-source-map'
② 生产模式:devtool: 'source-map'
- 优点:包含行&列映射
- 缺点:打包编译速度更慢
mode: 'production',
devtool: 'cheap-module-source-map'
三、提升打包构建速度:
1、模块热替换:HMR
中文文档:https://webpack.docschina.org/guides/hot-module-replacement
① 定义:
在程序运行中,替换、添加或删除模块,而无需重新加载整个页面(修改某个模块代码,就只有这个模块代码需要重新打包编译,其他模块不变)
② 使用:webpack5
中是默认开启的
// 开发服务器:不会输出资源,在内存中编译打包
devServer: {host: 'localhost', // 启动服务器的域名port: '3001', // 启动服务器的端口号open: true, // 是否自动打开浏览器hot: true, // 开启 HMR(默认值)
}
(1)CSS
模块热更新:
css
样式经过 style-loader
处理,已经具备 HMR
功能了
(2)JS
模块热更新:
a、常规写法:
语法:
// 1. 监听特定模块
module.hot.accept('字符串或数组(模块路径)', [,'模块更新后的回调函数'])
// 2. 监听当前模块自身
module.hot.accept()
项目中使用:
// 配置js模块热更新
if(module.hot) {// 如果支持模块热更新module.hot.accept('./js/sum.js')module.hot.accept('./js/util.js')
}
b、vue
或者React
项目写法:
vue-loader
:https://vue-loader.vuejs.org/
React Fast Refresh
:https://reactnative.dev/docs/fast-refresh
2、模块热替换:oneOf
中文文档:https://webpack.docschina.org/configuration/module/#ruleoneof
① 定义:
只要匹配到一个
loader
,剩下的就不匹配了
② 使用:
// 加载器module: {rules: [{ // 当规则匹配时,只使用第一个匹配规则oneOf: [{test: /\.css$/i, // 匹配以.css结尾的文件use: ['style-loader', // 将js中的css通过style标签的形式添加到html中'css-loader' // 将css资源编译成commonjs的模块化js], // loader的执行顺序是从后往前的},{test: /\.less$/i,use: ['style-loader','css-loader','less-loader', // 将less文件编译为css文件],},{test: /\.s[ac]ss$/i,use: [// 将 JS 字符串生成为 style 节点'style-loader',// 将 CSS 转化成 CommonJS 模块'css-loader',// 将 Sass 编译成 CSS'sass-loader',],},{test: /\.styl$/i,use: ['style-loader','css-loader','stylus-loader', // 将stylus文件编译为css文件],},{ // webpack4处理图片资源的方式:使用file-loader、url-loader。// file-loader将文件资源编译为webpack识别的资源原封不动的进行输出。// url-loader将图片资源进行优化// webpack5处理图片资源的方式(file-loader、url-loader已经内置):test: /\.(png|jpe?g|svg|webp|gif)$/,type: 'asset',parser: {dataUrlCondition: {maxSize: 30 * 1024 // 小于30kb的图片会转为base64 - 减少请求数量,但是体积会变大}},generator: { // 打包的图片输出的目录// [hash:10]表示只保留名称的前10位filename: 'static/images/[hash:10][ext][query]'}},{ // webpack4处理图片资源的方式:使用file-loader// file-loader将文件资源编译为webpack识别的资源原封不动的进行输出。// webpack5处理图片资源的方式(file-loader已经内置):test: /\.(ttf|woff2?)$/,type: 'asset/resource', // 只会对文件原封不动的输出,不会转为base64generator: { // 打包的字体输出的目录// [hash:10]表示只保留名称的前10位filename: 'static/fonts/[hash:10][ext][query]'}},{ // webpack4处理其它资源的方式:使用file-loader// file-loader将文件资源编译为webpack识别的资源原封不动的进行输出。// webpack5处理其它资源的方式(file-loader已经内置):test: /\.(mp3|MP4|avi|.doc)$/i,type: 'asset/resource', // 只会对文件原封不动的输出,不会转为base64generator: { // 打包的字体输出的目录// [name]表示保留原始文件名filename: 'static/media/[hash:10][ext][query]'}},{test: /\.js$/,exclude: /(node_modules)/, // 排除哪些文件不需要编译loader: 'babel-loader', // 只需要一个loader,不需要use// use: {// loader: 'babel-loader',// options: { // 智能预设,这个对象可以不写,那么就需要在项目的根目录下新建babel.config.js,添加智能预设// presets: ['@babel/preset-env'],// },// },}]}]},
3、第三方插件编译优化:exclude
或者include
中文文档:https://webpack.docschina.org/configuration/module#ruleexclude
① exclude
定义:
要排除符合条件的资源(对
js
文件处理时,排除node modules
下面的文件)
② include
定义:
只对当前包含的资源进行处理
③ 使用:
//加载器module: {rules: [{ // 当规则匹配时,只使用第一个匹配规则oneOf: [{test: /\.js$/,// exclude: /(node_modules)/, // 排除哪些文件不需要编译include: path.resolve(__dirname, '../src'), // 哪些文件需要编译loader: 'babel-loader', // 只需要一个loader,不需要use}]}]},// 插件plugins: [new ESLintPlugin({ // eslint检测的文件都有哪些exclude: "node_modules", // 默认值,排除哪些文件不需要编译context: path.resolve(__dirname, '../src')}),new HtmlWebpackPlugin({ // 使用 html资源引入 插件,并且保留原模版template: path.resolve(__dirname, '../index.html')})]
4、 eslint
和babel
缓存:Cache
中文文档:https://webpack.docschina.org/configuration/cache/#root
① 定义:
缓存之前的
Eslint
检查 和Babel
编译结果,加快后续打包速度
② 使用:
打包完成后的缓存js
文件,默认路径在node_modules/.cache/babel-loader中
module.exports = {// 加载器module: {rules: [{oneOf: [{test: /\.js$/,// exclude: /(node_modules)/, // 排除哪些文件不需要编译include: path.resolve(__dirname, '../src'), // 哪些文件需要编译loader: 'babel-loader', // 只需要一个loader,不需要useoptions: {cacheDirectory: true, // 开启babel缓存cacheCompression: false, // 不压缩缓存文件}},]}]},// 插件plugins: [new ESLintPlugin({ // eslint检测的文件都有哪些exclude: "node_modules", // 默认值,排除哪些文件不需要编译context: path.resolve(__dirname, '../src'),cache: true, // 开启缓存cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // 缓存文件存储的路径}),new HtmlWebpackPlugin({ // 使用 html资源引入 插件,并且保留原模版template: path.resolve(__dirname, '../index.html')}),new MiniCssExtractPlugin({ // 将所有的css提取到一个单独的文件中filename: 'static/css/main.css'}),new CssMinimizerPlugin()],
}
5、 多进程打包:Thead
中文文档:https://webpack.docschina.org/loaders/thread-loader/#root
① 定义:
开启电脑的多个进程同时干一件事,速度更快。
需要注意:请仅在特别耗时的操作中使用,因为每个进程启动就有大约为600ms
左右开销
② 使用:
(1)获取 cpu
的核数:
启动的进程最大数量就是cpu
的核数
// 引入os模块
const os = require('os')
// 获取cpu的核数
const threads = os.cpus().length;
(2)下载包:
npm install --save-dev thread-loader
(3)使用:
a、js
编译多核处理:
{test: /\.js$/,// exclude: /(node_modules)/, // 排除哪些文件不需要编译include: path.resolve(__dirname, '../src'), // 哪些文件需要编译use: [{loader: 'thread-loader', // 开启多进程options: {// 产生的 worker 的数量,默认是 (cpu 核心数 - 1)workers: threads,}},{loader: 'babel-loader', // 只需要一个loader,不需要useoptions: {cacheDirectory: true, // 开启babel缓存cacheCompression: false, // 不压缩缓存文件}}]
}
b、eslint
多核处理:
plugins: [new ESLintPlugin({ // eslint检测的文件都有哪些exclude: "node_modules", // 默认值,排除哪些文件不需要编译context: path.resolve(__dirname, '../src'),cache: true, // 开启缓存cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // 缓存文件存储的路径threads // 开启多进程})
],
c、压缩js
多核处理:
// 引入 js压缩 插件 -- 内置
const terserWebpackPlugin = require('terser-webpack-plugin')
module.exports = {plugins: [// new CssMinimizerPlugin(),// new terserWebpackPlugin({// parallel: threads, // 开启多进程// })],optimization: { // 不在这里写的话,可以在 plugins 中写minimizer: [new CssMinimizerPlugin(),new terserWebpackPlugin({parallel: threads, // 开启多进程})],},
}
③ 完整代码:
webpack.prod.js
:
// 引入os模块
const os = require('os')
// 引入path模块
const path = require('path')
// 引入 eslint 插件
const ESLintPlugin = require('eslint-webpack-plugin');
// 引入 html资源引入 插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 引入 提取css 的插件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 引入 css压缩 插件
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
// 引入 js压缩 插件 -- 内置
const terserWebpackPlugin = require('terser-webpack-plugin')// 获取cpu的核数
const threads = os.cpus().length;// 封装样式打包代码
function getStyleLoader (otherPre) {return [MiniCssExtractPlugin.loader, // 将 css 文件打包为一个单独的文件'css-loader', // 将css资源编译成commonjs的模块化js{ // 需要配置就写成对象的形式,不需要配置就写成字符串的形式loader: 'postcss-loader',options: { // css 的兼容性处理配置postcssOptions: {plugins: [['postcss-preset-env',{// 其他选项},],],},},},otherPre].filter(Boolean)
}module.exports = {// 入口 - 相对路径(因为编译的时候,命令是从根目录下执行的,所以不用修改)entry: './src/main.js',// 输出output: {// 所有打包的资源输出的目录 - 绝对路径(打包的时候,是根据该文件进行定位的)path: path.resolve(__dirname,'../dist'), // 入口文件打包输出的名称filename: 'static/js/main.js',// 在打包前,会清空path目录中的所有内容,然后再进行打包 -- webpack5中使用// 如果是webpack4,则需要使用clean-webpack-plugin插件进行清空clean: true // 开发时不需要},// 加载器module: {rules: [{oneOf: [{test: /\.css$/i, // 匹配以.css结尾的文件use: getStyleLoader(), // loader的执行顺序是从后往前的},{test: /\.less$/i,use: getStyleLoader('less-loader')},{test: /\.s[ac]ss$/i,use: getStyleLoader('sass-loader')},{test: /\.styl$/i,use: getStyleLoader('stylus-loader')},{ // webpack4处理图片资源的方式:使用file-loader、url-loader。// file-loader将文件资源编译为webpack识别的资源原封不动的进行输出。// url-loader将图片资源进行优化// webpack5处理图片资源的方式(file-loader、url-loader已经内置):test: /\.(png|jpe?g|svg|webp|gif)$/,type: 'asset',parser: {dataUrlCondition: {maxSize: 30 * 1024 // 小于30kb的图片会转为base64 - 减少请求数量,但是体积会变大}},generator: { // 打包的图片输出的目录// [hash:10]表示只保留名称的前10位filename: 'static/images/[hash:10][ext][query]'}},{ // webpack4处理图片资源的方式:使用file-loader// file-loader将文件资源编译为webpack识别的资源原封不动的进行输出。// webpack5处理图片资源的方式(file-loader已经内置):test: /\.(ttf|woff2?)$/,type: 'asset/resource', // 只会对文件原封不动的输出,不会转为base64generator: { // 打包的字体输出的目录// [hash:10]表示只保留名称的前10位filename: 'static/fonts/[hash:10][ext][query]'}},{ // webpack4处理其它资源的方式:使用file-loader// file-loader将文件资源编译为webpack识别的资源原封不动的进行输出。// webpack5处理其它资源的方式(file-loader已经内置):test: /\.(mp3|MP4|avi|.doc)$/i,type: 'asset/resource', // 只会对文件原封不动的输出,不会转为base64generator: { // 打包的字体输出的目录// [name]表示保留原始文件名filename: 'static/media/[hash:10][ext][query]'}},{test: /\.js$/,// exclude: /(node_modules)/, // 排除哪些文件不需要编译include: path.resolve(__dirname, '../src'), // 哪些文件需要编译// loader: 'babel-loader', // 只需要一个loader,不需要use// options: {// cacheDirectory: true, // 开启babel缓存// cacheCompression: false, // 不压缩缓存文件// }// use: {// loader: 'babel-loader',// options: { // 智能预设,这个对象可以不写,那么就需要在项目的根目录下新建babel.config.js,添加智能预设// presets: ['@babel/preset-env'],// },// },use: [{loader: 'thread-loader', // 开启多进程options: {// 产生的 worker 的数量,默认是 (cpu 核心数 - 1)workers: threads,}},{loader: 'babel-loader', // 只需要一个loader,不需要useoptions: {cacheDirectory: true, // 开启babel缓存cacheCompression: false, // 不压缩缓存文件}}]},]}]},optimization: { // 不在这里写的话,可以在 plugins 中写minimizer: [new CssMinimizerPlugin(),new terserWebpackPlugin({parallel: threads, // 开启多进程})],},// 插件plugins: [new ESLintPlugin({ // eslint检测的文件都有哪些exclude: "node_modules", // 默认值,排除哪些文件不需要编译context: path.resolve(__dirname, '../src'),cache: true, // 开启缓存cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // 缓存文件存储的路径threads // 开启多进程}),new HtmlWebpackPlugin({ // 使用 html资源引入 插件,并且保留原模版template: path.resolve(__dirname, '../index.html')}),new MiniCssExtractPlugin({ // 将所有的css提取到一个单独的文件中filename: 'static/css/main.css'}),// new CssMinimizerPlugin(),// new terserWebpackPlugin({// parallel: threads, // 开启多进程// })],// 模式mode: 'production',devtool: 'source-map'
}
webpack.dev.js
:
// 引入os模块
const os = require('os')
// 引入path模块
const path = require('path')
// 引入 eslint 插件
const ESLintPlugin = require('eslint-webpack-plugin');
// 引入 html资源引入 插件
const HtmlWebpackPlugin = require('html-webpack-plugin');// 获取cpu的核数
const threads = os.cpus().length;module.exports = {// 入口 - 相对路径(因为编译的时候,命令是从根目录下执行的,所以不用修改)entry: './src/main.js',// 输出output: {// 所有打包的资源输出的目录 - 绝对路径(打包的时候,是根据该文件进行定位的)// path: path.resolve(__dirname, '../dist'), path: undefined, // 开发模式不会输出文件// 入口文件打包输出的名称filename: 'static/js/main.js',// 在打包前,会清空path目录中的所有内容,然后再进行打包 -- webpack5中使用// 如果是webpack4,则需要使用clean-webpack-plugin插件进行清空// clean: true // 开发时不需要},// 加载器module: {rules: [{ // 当规则匹配时,只使用第一个匹配规则oneOf: [{test: /\.css$/i, // 匹配以.css结尾的文件use: ['style-loader', // 将js中的css通过style标签的形式添加到html中'css-loader' // 将css资源编译成commonjs的模块化js], // loader的执行顺序是从后往前的},{test: /\.less$/i,use: ['style-loader','css-loader','less-loader', // 将less文件编译为css文件],},{test: /\.s[ac]ss$/i,use: [// 将 JS 字符串生成为 style 节点'style-loader',// 将 CSS 转化成 CommonJS 模块'css-loader',// 将 Sass 编译成 CSS'sass-loader',],},{test: /\.styl$/i,use: ['style-loader','css-loader','stylus-loader', // 将stylus文件编译为css文件],},{ // webpack4处理图片资源的方式:使用file-loader、url-loader。// file-loader将文件资源编译为webpack识别的资源原封不动的进行输出。// url-loader将图片资源进行优化// webpack5处理图片资源的方式(file-loader、url-loader已经内置):test: /\.(png|jpe?g|svg|webp|gif)$/,type: 'asset',parser: {dataUrlCondition: {maxSize: 30 * 1024 // 小于30kb的图片会转为base64 - 减少请求数量,但是体积会变大}},generator: { // 打包的图片输出的目录// [hash:10]表示只保留名称的前10位filename: 'static/images/[hash:10][ext][query]'}},{ // webpack4处理图片资源的方式:使用file-loader// file-loader将文件资源编译为webpack识别的资源原封不动的进行输出。// webpack5处理图片资源的方式(file-loader已经内置):test: /\.(ttf|woff2?)$/,type: 'asset/resource', // 只会对文件原封不动的输出,不会转为base64generator: { // 打包的字体输出的目录// [hash:10]表示只保留名称的前10位filename: 'static/fonts/[hash:10][ext][query]'}},{ // webpack4处理其它资源的方式:使用file-loader// file-loader将文件资源编译为webpack识别的资源原封不动的进行输出。// webpack5处理其它资源的方式(file-loader已经内置):test: /\.(mp3|MP4|avi|.doc)$/i,type: 'asset/resource', // 只会对文件原封不动的输出,不会转为base64generator: { // 打包的字体输出的目录// [name]表示保留原始文件名filename: 'static/media/[hash:10][ext][query]'}},{test: /\.js$/,// exclude: /(node_modules)/, // 排除哪些文件不需要编译include: path.resolve(__dirname, '../src'), // 哪些文件需要编译// loader: 'babel-loader', // 只需要一个loader,不需要use// // use: {// // loader: 'babel-loader',// // options: { // 智能预设,这个对象可以不写,那么就需要在项目的根目录下新建babel.config.js,添加智能预设// // presets: ['@babel/preset-env'],// // },// // },use: [{loader: 'thread-loader', // 开启多进程options: {// 产生的 worker 的数量,默认是 (cpu 核心数 - 1)workers: threads,}},{loader: 'babel-loader', // 只需要一个loader,不需要useoptions: {cacheDirectory: true, // 开启babel缓存cacheCompression: false, // 不压缩缓存文件}}]}]}]},// 插件plugins: [new ESLintPlugin({ // eslint检测的文件都有哪些exclude: "node_modules", // 默认值,排除哪些文件不需要编译context: path.resolve(__dirname, '../src'),cache: true, // 开启缓存cacheLocation: path.resolve(__dirname, '../node_modules/.cache/eslintcache'), // 缓存文件存储的路径threads // 开启多进程}),new HtmlWebpackPlugin({ // 使用 html资源引入 插件,并且保留原模版template: path.resolve(__dirname, '../index.html')})],// 开发服务器:不会输出资源,在内存中编译打包devServer: {host: 'localhost', // 启动服务器的域名port: '3001', // 启动服务器的端口号open: true, // 是否自动打开浏览器hot: true, // 开启 HMR(默认值)},// 模式mode: 'development',devtool: 'cheap-module-source-map'
}
四、减少代码体积:
1、第三方工具库引用:Tree shaking
① 定义:
用于移除
JavaScript
中的没有使用的代码。
注意:它依赖 ES Module
② 使用:
Webpack
已经默认开启了这个功能,无需其他配置,
2、Babel
编译时代码重复插入:@babel/plugin-transform-runtime
中文文档:https://webpack.docschina.org/loaders/babel-loader/#babel-is-injecting-helpers-into-each-file-and-bloating-my-code
① 定义:
禁用了
Babel
自动对每个文件的runtime
注入。将辅助代码作为一个独立模块,保存在@babel/plugin-transform-runtime
中,需要的时候从@babel/plugin-transform-runtime
进行引入。
② 下载包:
npm install -D @babel/plugin-transform-runtime
③ 使用:
module.exports = {// 加载器module: {rules: [{oneOf: [{test: /\.js$/,// exclude: /(node_modules)/, // 排除哪些文件不需要编译include: path.resolve(__dirname, '../src'), // 哪些文件需要编译use: [{loader: 'thread-loader', // 开启多进程options: {// 产生的 worker 的数量,默认是 (cpu 核心数 - 1)workers: threads,}},{loader: 'babel-loader', // 只需要一个loader,不需要useoptions: {cacheDirectory: true, // 开启babel缓存cacheCompression: false, // 不压缩缓存文件plugins: ['@babel/plugin-transform-runtime'], // 减少代码体积}}]},]}]}
}