Webpack5 (1)基本配置

238 阅读11分钟

概述

本章主要是针对webpack5的基础配置,以基础配置为首开启webpack5的进修

如有疑问可转webpack5官网:webpack.docschina.org/configurati… 或者留下评论

Webpack5配置过程可能出现的问题

  • 相关依赖的安装不兼容,不兼容的情况可以尝试调低版本
  • webpack5的配置与webpack4的配置有些许不同,可能运用到了webpack4的写法
  • plugin、loader的版本与相关依赖有冲突,有的plugin、loader版本与webpack5不兼容
  • 注意文件的相对路径是否正确
  • 有的配置项有明确的参数规定,需要正确地配置,比如devServer
  • plugin在执行的过程中可能会混入不规范的参数到devServer,导致报错,不过这也有可能是webpack相关模块的版本不兼容导致的

相关依赖

//核心依赖如下,可以作为参考,其他的loader、plugin等的依赖自行下载,注意兼容性
  "dependencies": {
    "@webpack-cli/serve": "^2.0.5",
    "webpack-cli": "^5.1.4",
  },
  "devDependencies": {
    "webpack": "^5.39.1",
    "webpack-dev-server": "^5.1.0"
  },

配置项

mode

// production、development
module.exports = {
    mode: 'development',
};
  • development - 开发模式
    • 它会启用更快速的构建速度,因为在开发过程中,开发者通常更关注代码的快速更新和调试,而不是极致的代码优化
    • 它不会对代码进行过度的压缩和混淆,代码基本保持原始的格式,方便开发者阅读和调试,减少了构建过程中的时间消耗
    • Webpack 会生成更详细的错误信息和源代码映射source maps,源代码映射是一种将打包后的代码映射回原始源代码的技术,使得在浏览器调试工具中,能够方便地定位到原始代码中的错误位置。
    • 设置devServer选项中的hot: true,搭配Webpack 开发服务器(webpack-dev-server)启用热加载或者热重载
    • devtool默认值是eval-source-map
  • production - 生产模式
    • 如果不设置mode或者设置为production,Webpack 会默认对代码进行全面的优化,JavaScript 代码可能会被 TerserPlugin 深度压缩,变量名会被缩短,空格和注释会被大量去除,当然也包括提取CSS,devtool默认值是false,不生产source map

devtool

// eval、eval-source-map、cheap-eval-source-map...
module.exports = {
    devtool:'eval-source-map'
};
  • eval
    • 使用eval()函数来执行每个模块,模代码会在运行时被解析和执行,没有完整的源代码映射source-map,因此构建速度极快,没有source-map精度不够,只能定位到eval包裹的代码内部的错误
  • eval-source-map
    • 兼顾速度,也有源代码映射,包括文件,综合性强,更常用
  • cheap-eval-source-map
    • eval-source-map快,生成源代码映射文件时,主要关注行信息,忽略了列信息,精度不够
  • cheap-module-source-map
    • 不使用eval加载模块,生成一个简单的source-map,重点关注模块级别的错误定位和行信息。能快速定位模块级别的错误,相比eval更安全
  • source-map
    • 生成完整、详细的源代码映射文件,能够将打包后的代码精确地映射回原始源代码的文件、行号和列号,由于生成的文件较为复杂,所有构建速度相对较慢
  • hidden-source-map
    • 会生成完整的源代码映射文件,但不会将映射文件作为单独的文件暴露给浏览器,通过特殊的调试工具或方式利用文件定位错误,保护文件信息,比如:浏览器开发者工具
  • nosource-source-map
    • 生成的源代码映射文件只包含错误位置信息,不包含原始源代码内容,可以定位错误在原始文件中的位置,但无法查看原始源代码,安全,最大程度保护源代码

entry

单入口

module.exports = { 
    entry: { 
        main: './src/main.js', 
    }, 
};

多入口

module.exports = { 
    entry: { 
        main: './src/main.js', 
        vendor: './src/vendor.js' //可能用于单独打包第三方库或公共的依赖模块
    }, 
};

动态入口配置

module.exports = { 
    entry: () => { 
        const entryPoints = {}; // 根据某些条件动态添加入口点 
        if (process.env.NODE_ENV === 'development') { 
            entryPoints.dev = './src/dev - entry.js'; 
        }else { 
            entryPoints.prod = './src/prod - entry.js'; 
        } 
        return entryPoints; 
        }, 
    };
};

output

module.exports = { 
        output: {
        // 加上hash值,使用哈希值可以确保在文件内容改变时,
        //文件名也随之改变,方便浏览器缓存更新后的文件
            filename: '[name].[contenthash].js',
        //指定打包文件的目录
            path: path.resolve(__dirname, 'dist'),
        //不使用箭头函数
            environment: {
                arrowFunction: false
            },
        //表示打包后的文件在浏览器中可以通过根路径来访问
            publicPath: '/',
         //用于配置资源模块(如图片、字体等)的输出路径和文件名
            assetModuleFilename: 'assets/[hash][ext]',
    },
};

cache

module.exports = { 
//主要针对整个 Webpack 构建过程进行缓存优化
       cache: {
            //type: 'memory' 
            type: 'filesystem',
            cacheDirectory: path.resolve(__dirname, '.cache/webpack'),
            buildDependencies : {
            // 构建依赖缓存
            // Webpack 配置文件本身
                config: [__filename]
            }
    },
};
  • type: 'memory'
    • 缓存类型将缓存数据存储在内存中,当 Webpack 需要重新使用已经处理过的模块或其他构建资源时,可以直接从内存中获取,而不需要重新计算或加载。它的优点是速度非常快,因为内存访问速度远远高于磁盘访问速度。在构建过程结束后,缓存数据就会丢失,因为内存是临时存储介质,所以这种类型适用于在一次构建过程中需要频繁复用模块的情况,例如在开发过程中的热更新场景下,当文件频繁修改但模块依赖关系变化不大时,可以提高构建效率。
  • type: 'filesystem'
    • 缓存类型将缓存数据存储在文件系统中,在构建过程中,Webpack 会将模块信息、编译后的代码等缓存数据以文件的形式存储在指定的缓存目录(通过cacheDirectory属性指定)。这样,在下次构建时,如果模块没有变化,就可以直接从文件系统中读取缓存数据,而不需要重新处理模块。这种缓存方式的优点是缓存数据可以在多次构建过程中持久保存,适用于长时间的开发周期或者持续集成 / 持续部署(CI/CD)环境。不过,由于涉及文件系统的读写操作,它的速度相对内存缓存会一些。

module

module.exports = { 
 //指定webpack打包时使用的模块
    module: {
        //指定加载的规则
        rules: [
            {
                test: /\.vue$/,
                use: [
                    {
                        loader: 'thread-loader',
                    }, {
                        loader: 'vue-loader',
                        options: {
                            presets: ['@vue/babel-preset-jsx'],
                            cacheDirectory: true
                        }
                    }]
            },
            {
                test: /\.(js|jsx)$/,
                use: [
                    {
                        loader: 'thread-loader',
                    },
                    {
                        loader: 'babel-loader',
                        options: {
                            cacheDirectory: true
                        }
                    }
                ]
            },
            {
                test: /\.(jpg|jpeg)$/,
                loader: 'file-loader',
                options: {
                    name: '[name].[ext]',
                    outputPath: 'images/',
                    publicPath: 'images/'
                }
            }
        ]
    },
};
  • test指定了哪些文件会被当前规则所处理,为了减少重复loader的加载,将js和jsx放一起
  • exclude指定哪些文件或目录不应该应用当前规则
  • include指定哪些文件或目录应该应用当前规则
  • use是一个数组或一个对象,指定处理匹配文件的加载器(loader)或加载器链
    • loader将不同类型的文件转换为 Webpack 能够理解和处理的模块的工具
    • options
      • presets预先指定好的转换或处理规则
      • cacheDirectory对文件进行缓存,下次处理时会先检查缓存

plugins

const { VueLoaderPlugin } = require('vue-loader');
module.exports = { 
    plugins: [
        new VueLoaderPlugin(),
    ],
};
  • 在 Webpack 中,插件(Plugin)是一个具有apply方法的 JavaScript 类对象。可以引入现有的plugin也可以自定义,插件可以用来扩展 Webpack 的功能,它能够在 Webpack 构建过程的各个阶段(如编译开始、模块创建、打包完成等)插入自定义的逻辑,读取compiler信息。

devServer

module.exports = { 
    devServer: {
        static: {
            directory: path.join(__dirname, 'dist'),
            publicPath: '/'
        },
        port: 8080, 端口号
        // liveReload : true,
        hot: true,
        open: true,
        historyApiFallback: true,
    },
};
  • devServer是Webpack 开发服务器相关的配置选项
  • liveReload 热重载 刷新页面
  • hot 热加载or热更新 更新模块
  • historyApiFallback开发服务器会将任何没有匹配到实际文件的请求都重定向到index.html,404的时候跳转到index.html
  • open启动开发服务器后自动打开默认浏览器

resolve

module.exports = { 
    resolve: {
        // 只在node_modules目录下查找模块
        modules: ['node_modules', 'src'],
        // includes:['dist'],
        // 自动补全的文件扩展名
        extensions: [
            '.ts', '.js', '.json', '.vue', '.tsx', '.css', '.sass', '.less'
        ],
        alias: {
            '@': path.resolve(__dirname, 'src')
        }
    },
};
  • alias是一个对象,用于为模块路径创建别名,通过设置别名,可以使用更简短、更清晰的路径来引用模块,用@替代src
  • extensions告诉 Webpack 在解析导入的模块时自动尝试添加的文件扩展名
  • modules告诉 Webpack 在解析模块时应该去哪些目录中查找

optimization

module.exports = { 
 optimization: {
        // 当这个配置应用后,在打包后的代码中,模块 ID 的分配更加稳定。
        moduleIds: 'deterministic',
        // 开启代码压缩
        minimize: true,
        runtimeChunk: 'single',
        // 代码分割
        splitChunks: {
            chunks: 'all',
            minSize: 29.3 * 1024,
            maxSize: 50 * 1024,
            minChunks: 1,
            maxAsyncRequests: 5,
            maxInitialRequests: 3,
            automaticNameDelimiter: '~',
            cacheGroups: {
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        },
        minimizer: [
            new TerserPlugin({
                // 设置需要进行压缩优化的文件范围,这里可以是正则表达式等,示例为匹配所有.js文件
                test: /\.js$/,
                // 设置包含的文件范围,可省略,若设置则需符合特定格式,这里示例为包含src目录下所有文件
                include: /src/,
                // 设置排除的文件范围,可省略,若设置则需符合特定格式,这里示例为排除node_modules目录下所有文件
                exclude: /node_modules/,
                // 设置terser的具体压缩选项,这里可以根据需求详细设置,比如压缩等级等
                terserOptions: {
                    // 去除警告信息
                    warnings: false,
                    compress: {
                        warnings: false,
                        // 去掉console.log
                        drop_console: true,
                        // 去除debug
                        drop_debugger: true,
                    }
                },
                // 是否提取注释,可设置为布尔值或对象以进行更精细的设置,这里示例为不提取注释
                extractComments: false,
                // 是否启用并行压缩,可设置为布尔值,这里示例为启用并行压缩
                parallel: true,
            }),
        ]
    },
};
  • minimize代码压缩,减小代码文件的大小,以提高网页的加载速度
  • minimizer用于配置具体的代码压缩工具和相关选项,有的工具功能差不多,没必要都用,比如TerserPluginTerserWebpackPlugin功能很相似,TerserPluginterserOptions配置了minify函数执行时的压缩选项。compress选项用于控制各种压缩操作,当 Webpack 在优化阶段调用minify函数时,它会根据compress选项对 JavaScript 代码进行压缩,比如删除console语句,删除所有注释,删除debug
  • splitChunks代码分割,将代码分割成多个较小的块(chunks),以便更好地进行缓存和按需加载
  • runtimeChunk用于将运行时代码(如模块加载、解析等相关的代码)提取到一个单独的块中。在 Webpack 构建过程中,会生成一些用于管理模块加载和运行的代码,将这些代码提取出来可以方便地进行缓存和更新
  • moduleIds: 'deterministic'是一种用于控制模块标识符(module ID)生成方式的设置,Webpack 会根据模块的相对路径和一些其他确定的因素(如文件名等),以一种确定性的方式生成模块标识符,模块标识符是 Webpack 用来唯一标识每个模块的字符串或数字,在模块的加载、缓存和其他相关操作中起着关键作用

Webpack5 启动

//--mode development --progress --cache --profile...
"scripts": {
        "buildWebpack": "webpack --config webpack.config.js",
  },

以下参数也能在webpack.config.js文件中配置

  • --mode development
    • 假如命令行--mode development,webpack.config.js文件是mode:production,命令行优先级更大,它会覆盖在webpack.config.js文件中配置的mode选项
  • --config
    • 后面跟webpack文件名,webpack.config.js
  • --progress
    • 启用进度条显示打包进度
  • --watch
    • 监控文件变化并自动重新打包(用于开发环境)
  • -o dist/custom - output或者--output - path dist/custom - output
    • 指定输出目录
  • --cache
    • 控制缓存(提高重新打包速度)
  • --profile
    • 生成性能分析报告(用于优化打包性能)

运行代码:npm run buildWebpack

完整代码

const path = require('path') //拼写路径信息
// 优化代码压缩
const TerserPlugin = require('terser-webpack-plugin');
const webpack = require('webpack');
//webpack 中的所有配置信息都应该写在 module.exports 中
const { VueLoaderPlugin } = require('vue-loader');
const HTMLWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
// 去掉注释 去掉log 
// const TerserWebpackPlugin = require('terser-webpack-plugin');

// 将 CSS 从 JavaScript 文件中提取出来,生成独立的 CSS 文件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// 为模块创建了一个缓存 不兼容
// const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');

// 打包性能监控插件
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// 自制插件查看devServer 变化
//const MyPlugin = require('./MyPlugin')
// 显示进度条
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
// const chalk = require('chalk');
// 不适用element plus 自动插入插件 因为会引起devServer的属性变更
// const AutoImport = require('unplugin-auto-import/webpack')
// const Components = require('unplugin-vue-components/webpack')
// const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')

const webpackConfig = {
    mode: 'development',
    devtool: 'eval-source-map',
    // devtool: false,
    //指定入口文件
    entry: './src/main.ts',
    //指定打包文件所在目录
    output: {
        // 加上hash值,使用哈希值可以确保在文件内容改变时,文件名也随之改变,方便浏览器缓存更新后的文件
        filename: '[name].[contenthash].js',
        //指定打包文件的目录
        path: path.resolve(__dirname, 'dist'),
        //打包后文件的文件
        environment: {
            arrowFunction: false
        },
        publicPath: '/',//表示打包后的文件在浏览器中可以通过根路径来访问
        assetModuleFilename: 'assets/[hash][ext]',//用于配置资源模块(如图片、字体等)的输出路径和文件名
    },
    cache: {
        type: 'filesystem',
        // type: "memory",
        // 选项仅当 cache.type 被设置成 'filesystem' 才可用
        cacheDirectory: path.resolve(__dirname, '.cache/webpack'),
        buildDependencies: {
            // 配置哪些依赖发生变化时需要重新构建缓存
            // webpack.config.js文件本身发生变化时,才会重新构建缓存
            config: [__filename]
        }
    },
    //指定webpack打包时使用的模块
    module: {
        //指定加载的规则
        rules: [
            {
                test: /\.vue$/,
                use: [
                    {
                        loader: 'thread-loader',
                    }, {
                        loader: 'vue-loader',
                        options: {
                            presets: ['@vue/babel-preset-jsx'],
                            cacheDirectory: true
                        }
                    }]
            },
            {
                test: /\.(js|jsx)$/,
                use: [
                    {
                        loader: 'thread-loader',
                    },
                    {
                        loader: 'babel-loader',
                        options: {
                            // 当 Webpack 解析模块之间的依赖关系时,
                            // 会将这些关系存储在cacheDirectory中。
                            // 如果下次构建时模块的依赖关系没有改变,
                            // Webpack 可以直接从缓存中获取这些信息,而不需要重新解析
                            cacheDirectory: true
                        }
                    }
                ]
            },
            {
                test: /\.(ts|tsx)$/,
                use: [
                    // thread-loader 可能与 webpack 版本冲突
                    // {
                    //     loader: 'thread-loader',
                    // },
                    {
                        loader: 'ts-loader',
                    }
                ],
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: 'style-loader',
                    },
                    {
                        loader: 'css-loader',
                    }
                ]
            },
            {
                test: /\.scss$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'sass-loader'
                ]
            },
            {
                test: /\.less$/,
                use: [
                    'style-loader',
                    'css-loader',
                    'less-loader'
                ]
            },
            {
                test: /\.(jpg|jpeg)$/,
                loader: 'file-loader',
                options: {
                    name: '[name].[ext]',
                    outputPath: 'images/',
                    publicPath: 'images/'
                }
            }
        ]
    },
    //配置webpack插件
    plugins: [
        //new MyPlugin(),
        new VueLoaderPlugin(),
        // 在每次构建之前,自动清理之前构建生成的文件。
        // 这可以确保输出目录只包含最新构建的文件,
        // 避免旧文件的积累导致混乱。
        new CleanWebpackPlugin(
            {
                cleanOnceBeforeBuildPatterns: ['dist']
            }
        ),
        new webpack.DefinePlugin({
            '__VUE_PROD_HYDRATION_MISMATCH_DETAILS__': JSON.stringify(false),
        }),
        // 自动生成 HTML 文件,
        // 并将打包后的 JavaScript 和 CSS 文件自动引入到 HTML 中。
        // 它简化了 HTML 文件与 Webpack 打包输出之间的关联,
        // 确保在每次构建后,HTML 文件能够正确引用最新的打包资源。
        new HTMLWebpackPlugin({
            template: 'index.html', //相对路径
            filename: 'index.html'
        }),
        new MiniCssExtractPlugin({
            filename: 'styles.css'
        }),
        // new HardSourceWebpackPlugin()
        // new webpack.HotModuleReplacementPlugin(),
        // 打包性能监控
        // new BundleAnalyzerPlugin(),
        // 自动引入element plus
        // AutoImport({
        //     resolvers: [ElementPlusResolver()],
        // }),
        // Components({
        //     resolvers: [ElementPlusResolver()],
        // }),
        new ProgressBarPlugin({
            format: 'build [:bar] :percent :elapsed seconds :msg',
            customTotal: 100,
            width: 30
        })
    ],
    devServer: {
        static: {
            directory: path.join(__dirname, 'dist'),
            publicPath: '/'
        },
        // port: 8080,
        // liveReload: true,
        hot: true,
        // open: true,
        historyApiFallback: true,
    },

    // devServer: finalDevServerConfig,
    //用来设置引用模块
    resolve: {
        // 只在node_modules目录下查找模块
        modules: ['node_modules', 'src'],
        // includes:['dist'],
        // 自动补全的文件扩展名
        extensions: [
            '.ts', '.js', '.json', '.vue', '.tsx', '.css', '.sass', '.less'
        ],
        alias: {
            '@': path.resolve(__dirname, 'src')
        }
    },
    //...其他配置
    optimization: {
        // 当这个配置应用后,在打包后的代码中,模块 ID 的分配更加稳定。
        moduleIds: 'deterministic',
        runtimeChunk: 'single',
        // 开启代码压缩
        minimize: true,
        // 代码分割
        splitChunks: {
            chunks: 'all',
            minSize: 29.3 * 1024,
            maxSize: 50 * 1024,
            minChunks: 1,
            maxAsyncRequests: 5,
            maxInitialRequests: 3,
            automaticNameDelimiter: '~',
            cacheGroups: {
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                },
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        },
        minimizer: [
            new TerserPlugin({
                // 设置需要进行压缩优化的文件范围,这里可以是正则表达式等,示例为匹配所有.js文件
                test: /\.js$/,
                // 设置包含的文件范围,可省略,若设置则需符合特定格式,这里示例为包含src目录下所有文件
                include: /src/,
                // 设置排除的文件范围,可省略,若设置则需符合特定格式,这里示例为排除node_modules目录下所有文件
                exclude: /node_modules/,
                // 设置terser的具体压缩选项,这里可以根据需求详细设置,比如压缩等级等
                terserOptions: {
                    // 去除警告信息
                    warnings: false,
                    compress: {
                        warnings: false,
                        // 去掉console.log
                        drop_console: true,
                        // 去除debug
                        drop_debugger: true,
                    }
                },
                // 是否提取注释,可设置为布尔值或对象以进行更精细的设置,这里示例为不提取注释
                extractComments: false,
                // 是否启用并行压缩,可设置为布尔值,这里示例为启用并行压缩
                parallel: true,
                // 是否进行压缩操作,可设置为布尔值,这里示例为进行压缩操作
                // minify: (file, sourceMap) => {
                //     // 自定义minify函数
                //     const terser = require('terser');
                //     return terser.minify(file, {
                //         // 这里是传递给terser.minify的参数,如sourceMap等
                //         sourceMap: sourceMap
                //     });
                // },
            }),
            // new TerserWebpackPlugin({
            //     terserOptions: {
            //         compress: {
            //             drop_console: true, // 去掉console.log语句
            //             pure_funcs: ['console.log'], // 另一种去掉console.log的方式,可以和上面的选项配合使用
            //             warnings: false, // 关闭压缩过程中的警告信息
            //             dead_code: true, // 去除无用代码,可用于去掉注释等
            //         },
            //         output: {
            //             comments: false, // 去掉输出文件中的注释
            //         }
            //     }
            // })
        ]
    },
}

module.exports = webpackConfig