持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情
上一篇搭建了基本的react脚手架,可以支持react,less的基本使用,但是没有考虑到项目中的实际情况,例如生产环境的代码混淆,代码压缩,开发环境的热启动等。 下面我们就针对开发与生产环境的不同需求,对webpack进行下一步的配置 生产环境的配置与开发环境的配置有很多的不同点,所以我们需要分别建对应的配置文件,在根目录下分别创建 webpack.prod.js(生产配置) 与webpack.dev.js(开发配置),同时如果你熟悉cli的开发的话,应该其还有build和dev的两个指令
配置开发环境
在根目录创建webpack.prod.js文件
在package.json中新增开发时启动
"dev":"webpack-dev-server --open --config webpack.dev.js --mode=development",
在开发环境中,很明显我们是需要对错误进行定位的所以我们要开启devtool的inline-source-map,其中css的详细跟踪,我们在之前的配置中已经完成了。
同时我们上次为了浏览器自动打开,同时在我们修改文件的内容的时候会热更新,所以我们配置了devServer,注意如果你使用的webpack4的时候只有在修改主要的文件的时候才会帮我们进行刷新,但是在一些复杂的场景下,当依赖的模块发生改变的时候并不能做到更新,webpack中有自带的HotModuleReplacementPlugin插件,我们只要引入就可以解决这些问题,我们这里用的是webpack5所以直接hot:true就可以了。
同时devServer应该开启gzip压缩,对对应出错的地方进行更新输出,如果要查看文件重新编译加载的速度还应该开启进度条progress,设计跨域处理的话我们可以配置代理服务器,同时对于文件的监控我们要减少不必要的文件监控,如node_modules中的文件基本不会改变所以可以排除在外,同时有时候会进行连续更改如果每次一更改就进行更新性能又有所消耗,所以欧美通过watchOptions进行配置一个防抖时间。最后整个配置如下:
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const webpack = require("webpack");
const HTMLWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/main.js",
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
chunkFilename: "[name].bundle.js",
},
module: {
rules: [
{
test: /\.(le|c)ss$/,
use: [
// 解析规则为从右向左,即 less-loader, postcss-loader, css-loader, style-loader
"style-loader",
{
loader: "css-loader",
options: {
sourceMap: true, //是否打开样式查找
},
},
{
loader: "postcss-loader",
//配置参数
options: {
postcssOptions: {
//添加插件autoprefixer,能加前缀
plugins: [require("autoprefixer")],
},
},
},
{
loader: "less-loader", // 解析样式文件
options: {
sourceMap: true,
},
},
],
},
{
test: /\.(js)x?$/,
use: ["babel-loader"],
exclude: /node-modules/,
},
],
},
plugins: [
new HTMLWebpackPlugin({
inject: true, // 所有js脚本放于body之后
hash: true, // 为静态资源生成hash,用于清楚缓存
cache: true, // 仅在文件被更改时发出文件
title: "react admin",
filename: "index.html",
template: path.resolve(__dirname, "./public/index.html"),
minify: {
collapseWhitespace: true, // 折叠空白
removeComments: true, // 删除注释
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
},
}),
new CleanWebpackPlugin(),
],
devServer: {
static: {
directory: path.resolve(__dirname, "dist"), // 静态文件目录,用于浏览器显示
publicPath: "/", // 浏览器访问路径
},
hot: true, // 启动热更新
compress: true, // 启用gzip压缩
port: 9000,
open: true, // 自动调起浏览器
client: {
overlay: {
// 出现错误或警告是否覆盖页面线上错误信息
warnings: true,
errors: true,
},
progress: true,
},
proxy: {
// 代理
},
},
watchOptions: {
// 监控文件相关配置
poll: true,
ignored: /node_modules/,
aggregateTimeout: 300, // 默认值, 当你连续改动时候, webpack可以设置构建延迟时间(防抖)
},
};
配置生产环境
同样我们先在项目根路径创建webpack.pro.dev.js文件
在package.json中新增生产启动
在生产环境中为了提高速度和代码的隐藏性,我们会开启代码压缩,同时为了能使得缓存正常,我们的打包文件命名不能再是[name].js因为这样会使得名字和入口文件一直一样,使得即使我们改变了文件的内容,在缓存看来依旧没变,所以我们给其加上对应的chunkhash变成[name][chunkhash].js这样只要对应的一个板块中有东西进行了更改chunkhash就会进行更改,缓存就会失效重新请求了。
对于js的压缩我们需要安装对应的插件teret-webpack-plugin后进行对应的配置Webpack4.0默认是使用terser-webpack-plugin这个压缩插件,在此之前是使用uglifyjs-webpack-plugin,它们的区别是对ES6的压缩不是很好,同时我们可以开启parallel参数,使用多进行压缩,加快压缩。
对于css的压缩打包我们之前都是直接hejs打包在一起的,但是这样是不合理的,为了抽离出来我们需要这个插件mini-css-extract-plugin,注意这里要换掉原来的style-loader用它的loader。
涉及到压缩那我们就要用到另一个插件:css-minimizer-webpack-plugin,然后直接new实例就行。
到此我们的生成环境也配置好了,全部配置如下
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const TerserWebpackPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
module.exports = {
entry: path.resolve(__dirname,"./src/main.js"),
output: {
filename: "[name].[chunkhash].js",
path: path.resolve(__dirname, "dist"),
chunkFilename: "[name].[chunkhash].js",
},
module: {
rules: [
{
test: /\.(le|c)ss$/,
use: [
// 解析规则为从右向左,即 less-loader, postcss-loader, css-loader, style-loader
MiniCssExtractPlugin.loader, // 替换了原来的style-loader
{
loader: "css-loader",
options: {
sourceMap: true, //是否打开样式查找
},
},
{
loader: "postcss-loader",
//配置参数
options: {
postcssOptions: {
//添加插件autoprefixer,能加前缀
plugins: [require("autoprefixer")],
},
},
},
{
loader: "less-loader", // 解析样式文件
options: {
sourceMap: true,
},
},
],
},
{
test: /\.(js)x?$/,
use: ["babel-loader"],
exclude: /node-modules/,
},
],
},
plugins: [
new CssMinimizerPlugin(),
new MiniCssExtractPlugin({// 将css打包成单独的css文件
filename: '[name].[hash:5].css',//hash限制为5位
chunkFilename: '[id].[hash:5].css'
}),
new HTMLWebpackPlugin({
inject: true, // 所有js脚本放于body之后
hash: true, // 为静态资源生成hash,用于清楚缓存
cache: true, // 仅在文件被更改时发出文件
title: "react admin",
filename: "index.html",
template: path.resolve(__dirname, "./public/index.html"),
minify: {
collapseWhitespace: true, // 折叠空白
removeComments: true, // 删除注释
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
},
}),
new CleanWebpackPlugin(),
],
devServer: {
static: {
directory: path.resolve(__dirname, "dist"), // 静态文件目录,用于浏览器显示
publicPath:"/" // 浏览器访问路径
},
hot: true, // 启动热更新
compress: true, // 启用gzip压缩
port: 9000,
open: true, // 自动调起浏览器
client: {
overlay: {
// 出现错误或警告是否覆盖页面线上错误信息
warnings: true,
errors: true,
},
progress:true
},
proxy: {
// 代理
},
},
watchOptions: {
// 监控文件相关配置
poll: true,
ignored: /node_modules/,
aggregateTimeout: 300, // 默认值, 当你连续改动时候, webpack可以设置构建延迟时间(防抖)
},
devtool:"source-map",
optimization: {
minimizer: [
// js压缩
new TerserWebpackPlugin({
parallel: true, // 多线程调用
}),
]
}
};
公共配置抽离
我们的开发和生成配置其实有很多地方是可以抽离公共的,所以我们通过安装一个插件webpack-merge来进行抽离,以下是公共部分
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HTMLWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./src/main.js",
module: {
rules: [
{
test: /\.(js)x?$/,
use: ["babel-loader"],
exclude: /node-modules/,
},
],
},
plugins: [
new HTMLWebpackPlugin({
inject: true, // 所有js脚本放于body之后
hash: true, // 为静态资源生成hash,用于清楚缓存
cache: true, // 仅在文件被更改时发出文件
title: "react admin",
filename: "index.html",
template: path.resolve(__dirname, "./public/index.html"),
minify: {
collapseWhitespace: true, // 折叠空白
removeComments: true, // 删除注释
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
},
}),
new CleanWebpackPlugin(),
],
optimization: {
// 公共代码抽离
splitChunks: {
//启动代码分割,有默认配置项
chunks: "all",
},
},
};
我们再来配置生产环境。在webpack.dev.js中引入webpack-merge和common的部分
操作完我们的webpack.dev.js如下,运行结果和原来一样
const path = require("path");
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
module.exports = merge(common, {
mode: "development",
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
chunkFilename: "[name].bundle.js",
},
module: {
rules: [
{
test: /\.(le|c)ss$/,
use: [
// 解析规则为从右向左,即 less-loader, postcss-loader, css-loader, style-loader
"style-loader",
{
loader: "css-loader",
options: {
sourceMap: true, //是否打开样式查找
},
},
{
loader: "postcss-loader",
//配置参数
options: {
postcssOptions: {
//添加插件autoprefixer,能加前缀
plugins: [require("autoprefixer")],
},
},
},
{
loader: "less-loader", // 解析样式文件
options: {
sourceMap: true,
},
},
],
},
],
},
devServer: {
static: {
directory: path.resolve(__dirname, "dist"), // 静态文件目录,用于浏览器显示
publicPath: "/", // 浏览器访问路径
},
hot: true, // 启动热更新
compress: true, // 启用gzip压缩
port: 9000,
open: true, // 自动调起浏览器
client: {
overlay: {
// 出现错误或警告是否覆盖页面线上错误信息
warnings: true,
errors: true,
},
progress: true,
},
proxy: {
// 代理
},
},
devtool: "eval-cheap-module-source-map",
watchOptions: {
// 监控文件相关配置
poll: true,
ignored: /node_modules/,
aggregateTimeout: 300, // 默认值, 当你连续改动时候, webpack可以设置构建延迟时间(防抖)
},
});
生产环境我们也进行一样的操作
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
module.exports = merge(common, {
mode: "production",
output: {
filename: "[name].[chunkhash].js",
path: path.resolve(__dirname, "dist"),
chunkFilename: "[name].[chunkhash].js",
},
module: {
rules: [
{
test: /\.(le|c)ss$/,
use: [
// 解析规则为从右向左,即 less-loader, postcss-loader, css-loader, style-loader
MiniCssExtractPlugin.loader, // 替换了原来的style-loader
{
loader: "css-loader",
options: {
sourceMap: true, //是否打开样式查找
},
},
{
loader: "postcss-loader",
//配置参数
options: {
postcssOptions: {
//添加插件autoprefixer,能加前缀
plugins: [require("autoprefixer")],
},
},
},
{
loader: "less-loader", // 解析样式文件
options: {
sourceMap: true,
},
},
],
},
],
},
plugins: [
new CssMinimizerPlugin(),
new MiniCssExtractPlugin({
// 将css打包成单独的css文件
filename: "[name].[hash:5].css", //hash限制为5位
chunkFilename: "[id].[hash:5].css",
})
],
optimization: {
minimizer: [
// js压缩
new TerserWebpackPlugin({
parallel: true, // 多线程调用
}),
],
},
});
上面就完成了生产与开发环境的配置,但是之前的配置还有部分遗漏,一个是在开发环境没有配置css的抽离规则,一个是没有配置react开发中的图片资源加载跟字体库与icon下面我们就先针对开发环境的css进行一个处理,之前我们在运行webpack的时候是没办法区分开发与生产环境的,所以我们现在在webpack的命令中设置环境变量,用以区分开发还是生产环境,我们先引用cross-env的包,用于各平台设置环境变量的兼容,命令为 npm i cross-env -D然后修改package.json中的启动命令为
"dev": "cross-env NODE_ENV=development webpack server --open --config webpack.dev.js",
"build": "cross-env NODE_ENV=production webpack --config webpack.prod.js"
然后在webpack.common.js中打印process.env.NODE_ENV,然后可以观察到在npm run dev的命令下process.env.NODE_ENV是'development', npm run build 的命令下 process.env.NODE_ENV是'production',由此,我们来配置css的抽离规则,先将之前配置在webpack.prod.js中的css打包配置删除,然后在webpack.common.js中新增如下配置
接下来到图片的处理,我们知道在开发的时候我们的图片多数情况都是相对路径引入的,但是打包又是基于入口文件所以就会图片引入失败。
在webpack5中我们已经不需要再通过url-loader来配置了,我们通过资源模块实现,具体这里我就不展开解释了,点击去官网自己看。
module: {
rules: [
{
test: /\.(jpg|png|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 200 * 1024,
},
},
},
....
....
同理我们把字体图标的也进行同样的配置最后配置完成如下:
webpack.common.js
const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HTMLWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV === "development"; // 命令行设置的值
module.exports = {
entry: path.resolve(__dirname, "./src/main.js"),
module: {
rules: [
{
test: /\.(jpg|png|gif|webp)$/,
type: "asset",
parser: {
dataUrlCondition: {
maxSize: 200 * 1024,
},
},
},
{
test: /\.(js)x?$/,
use: ["babel-loader"],
exclude: /node-modules/,
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
type: "asset"
}
],
},
plugins: [
new HTMLWebpackPlugin({
inject: true, // 所有js脚本放于body之后
hash: true, // 为静态资源生成hash,用于清楚缓存
cache: true, // 仅在文件被更改时发出文件
title: "react admin",
filename: "index.html",
template: path.resolve(__dirname, "./public/index.html"),
minify: {
collapseWhitespace: true, // 折叠空白
removeComments: true, // 删除注释
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
},
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
// 将css打包成单独的css文件
filename: devMode ? "[name].css" : "[name].[hash:5].css",
chunkFilename: devMode ? "[id].css" : "[id].[hash:5].css",
}),
],
optimization: {
// 公共代码抽离
splitChunks: {
//启动代码分割,有默认配置项
chunks: "all",
},
},
};
webpack.prod.js
const path = require("path");
const TerserWebpackPlugin = require("terser-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
module.exports = merge(common, {
mode: "production",
output: {
filename: "[name].[chunkhash].js",
path: path.resolve(__dirname, "dist"),
chunkFilename: "[name].[chunkhash].js",
},
module: {
rules: [
{
test: /\.(le|c)ss$/,
use: [
// 解析规则为从右向左,即 less-loader, postcss-loader, css-loader, style-loader
MiniCssExtractPlugin.loader, // 替换了原来的style-loader
{
loader: "css-loader",
options: {
sourceMap: true, //是否打开样式查找
},
},
{
loader: "postcss-loader",
//配置参数
options: {
postcssOptions: {
//添加插件autoprefixer,能加前缀
plugins: [require("autoprefixer")],
},
},
},
{
loader: "less-loader", // 解析样式文件
options: {
sourceMap: true,
},
},
],
},
],
},
plugins: [
new CssMinimizerPlugin()
],
optimization: {
minimizer: [
// js压缩
new TerserWebpackPlugin({
parallel: true, // 多线程调用
}),
],
},
});
webpack.dev.js
const path = require("path");
const { merge } = require("webpack-merge");
const common = require("./webpack.common.js");
module.exports = merge(common, {
mode: "development",
output: {
filename: "[name].js",
path: path.resolve(__dirname, "dist"),
chunkFilename: "[name].bundle.js",
},
module: {
rules: [
{
test: /\.(le|c)ss$/,
use: [
// 解析规则为从右向左,即 less-loader, postcss-loader, css-loader, style-loader
"style-loader",
{
loader: "css-loader",
options: {
sourceMap: true, //是否打开样式查找
},
},
{
loader: "postcss-loader",
//配置参数
options: {
postcssOptions: {
//添加插件autoprefixer,能加前缀
plugins: [require("autoprefixer")],
},
},
},
{
loader: "less-loader", // 解析样式文件
options: {
sourceMap: true,
},
},
],
},
],
},
devServer: {
static: {
directory: path.resolve(__dirname, "dist"), // 静态文件目录,用于浏览器显示
publicPath: "/", // 浏览器访问路径
},
hot: true, // 启动热更新
compress: true, // 启用gzip压缩
port: 9000,
open: true, // 自动调起浏览器
client: {
overlay: {
// 出现错误或警告是否覆盖页面线上错误信息
warnings: true,
errors: true,
},
progress: true,
},
proxy: {
// 代理
},
},
devtool: "eval-cheap-module-source-map",
watchOptions: {
// 监控文件相关配置
poll: true,
ignored: /node_modules/,
aggregateTimeout: 300, // 默认值, 当你连续改动时候, webpack可以设置构建延迟时间(防抖)
},
});