Webpack 4区分开发环境和生产环境
官方对mode介绍
先看一下官方对mode介绍:
https://webpack.docschina.org/concepts/mode/#%E7%94%A8%E6%B3%95
只需在配置对象中提供 mode
选项:
module.exports = {
mode: 'production'
};
或者从 CLI 参数中传递:
webpack --mode=production
选项 | 描述 |
---|---|
选项
development |
描述
会将
DefinePlugin 中 process.env.NODE_ENV 的值设置为 development 。启用 NamedChunksPlugin 和 NamedModulesPlugin 。 |
选项
production |
描述
会将
DefinePlugin 中 process.env.NODE_ENV 的值设置为 production 。启用 FlagDependencyUsagePlugin , FlagIncludedChunksPlugin , ModuleConcatenationPlugin , NoEmitOnErrorsPlugin , OccurrenceOrderPlugin , SideEffectsFlagPlugin 和 TerserPlugin 。 |
选项
none |
描述
退出任何默认优化选项
|
但我在package.json中配置了webpack --mode=production,在npm run build时,貌似并没有获取到
如在webpack.base.conf.js中打印process.env.NODE_ENV,取不到值。
先看一下我的目的结构,比较简单,主要就是用webpack区分开发和生产环境,webpack.base.conf.js中写一些公用的配置
因为开发模式不需要压缩 css,将不同的压缩 rule 分别写在 webpack.prod.conf.js 和 webpack.dev.conf.js 文件里:
区分环境分开配置
webpack.dev.conf.js
rules: [
{
test: /\.(sa|sc|c)ss$/,
use: [
'style-loader', //开发模式不使用插件
'css-loader',
'sass-loader',
]
}
]
上面的配置也可以写在webpack.base.conf.js , 然后只需针对prod写不同的配置覆盖base中的配置即可
webpack.prod.conf.js
rules: [
{
test: /\.css|scss$/,
use: [
{
loader: MiniCssExtractPlugin.loader,
options: {
// 设置css中资源链接的路径
// publicPath 默认使用 webpackOptions.output中的publicPath, 如果图片和样式在同一层目录,可以使用默认值
// 由于我上面设置的图片打包路径是 /dist/images, 而css是分块存储到 /dist/css目录中,其中引用的图片路径应为“../images/xxx.[ext]”,固这里设置为'../'
publicPath: '../'
}
},
'css-loader',
'sass-loader',
]
}
]
合并代码到base
但如果想直接在webpack.base.conf.js中直接区分这块代码,比如通常我们写个三目运算进行判断就行了,我想这么写:如果是dev,就加载style-loader, 否则就使用MiniCssExtractPlugin对样式进行抽离
rules: [
{
test: /\.css|scss$/,
use: [
devMode ? 'style-loader' : {
loader: MiniCssExtractPlugin.loader,
options: {
// 设置css中资源链接的路径
// publicPath 默认使用 webpackOptions.output中的publicPath, 如果图片和样式在同一层目录,可以使用默认值
// 由于我上面设置的图片打包路径是 /dist/images, 而css是分块存储到 /dist/css目录中,其中引用的图片路径应为“../images/xxx.[ext]”,固这里设置为'../'
publicPath: '../'
}
},
'css-loader',
'sass-loader',
]
}
]
]
这时我就发现,拿不到process.env.NODE_ENV值,此时就要想想办法怎么拿到process.env.NODE_ENV值
下面说一下在使用中遇到的问题和解决方案,看一下最终完整的webpack配置
webpack.base.conf.js:
'use strict'
const path = require('path');
const webpack = require('webpack');
const config = require('../config')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV !== 'production'
console.log('环境', process.env.NODE_ENV)
module.exports = {
// 入口起点
entry: {
app: './src/main.js',
},
// 输出
// path是webpack所有文件的输出的路径
// 比如:output输出的js,url-loader解析的图片,“path”仅仅告诉Webpack结果存储在哪里,
// HtmlWebpackPlugin生成的html文件,都会存放在以path为基础的目录下
// path即所有输出文件的目标路径;打包后文件在硬盘中的存储位置。
// 如下path配置是将所有打包后生成的文件及目录放到上层路径的“dist”目录
// path.resolve(__dirname, '../dist') 是磁盘绝对路径
// publicPath:输出解析文件的目录,指定资源文件引用的目录 ,打包后浏览器访问服务时的 url 路径中通用的一部分。
// publicPath项则被许多Webpack的插件用于在生产模式下更新内嵌到css、html文件里的url值。
// publicPath设置'./'
// 例:在文件中引用了某张图片,在url-loader设置的图片存储位置是images/[name].[hash].[ext]
// 那最终在生产环境中<img src="./images/xxx.[ext]">的形式
output: {
path: path.resolve(__dirname, '../dist'),
filename: "[name].js"
},
// 解析
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json'],
alias: {
'@': path.join(__dirname, '..', 'src')
}
},
// loader
module: {
rules: [
{
test: /\.js|jsx$/,
exclude: /(node_modules)/,// 屏蔽不需要处理的文件(文件夹)
loader: 'babel-loader'
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 5000, // 最小图片尺寸5kb,5kb以下直接用base64图片
name: 'images/[name].[hash].[ext]' // 加上.[hash],防止各目录中出现相同文件名图片,如果不使用[hash],在汇总到images目录中后,图片会被替换
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 5000,
name: 'medias/[name].[hash].[ext]'
}
},
{
test: /\.(woff|woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 5000,
name: 'fonts/[name].[hash].[ext]'
}
},
{
test: /\.css|scss$/,
use: [
devMode ? 'style-loader' : {
loader: MiniCssExtractPlugin.loader,
options: {
// 设置css中资源链接的路径
// publicPath 默认使用 webpackOptions.output中的publicPath, 如果图片和样式在同一层目录,可以使用默认值
// 由于我上面设置的图片打包路径是 /dist/images, 而css是分块存储到 /dist/css目录中,其中引用的图片路径应为“../images/xxx.[ext]”,固这里设置为'../'
publicPath: '../'
}
},
'css-loader',
'sass-loader',
]
}
]
},
// 目标运行环境
target: "web",
// 插件
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: 'body'
}),
new webpack.DefinePlugin({
'APPID': JSON.stringify(config.APPID),
'COURSE': JSON.stringify(config.COURSE)
})
]
};
webpack.dev.conf.js
'use strict'
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
const config = require('../config/dev.env')
const path = require('path');
const webpack = require('webpack');
module.exports = merge(baseWebpackConfig, {
// 模式
mode: "development",
// 调试工具
devtool: 'inline-source-map',
// 开发服务器
devServer: {
contentBase: false,// 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录
historyApiFallback: true,// 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html
compress: true,// 启用gzip压缩
inline: true,// 设置为true,当源文件改变时会自动刷新页面
hot: true,// 模块热更新,取决于HotModuleReplacementPlugin
host: '127.0.0.1',// 设置默认监听域名,如果省略,默认为“localhost”
port: 3000// 设置默认监听端口,如果省略,默认为“8080”
},
// 插件
plugins: [
// 热更新相关
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin(),
// 这里的设置是给src中用的,在webpack.base.conf如要获取,需在package.json配置cross-env NODE_ENV=development
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(config.mode),
'API_ROOT': JSON.stringify(config.API_ROOT)
}),
],
optimization: {
nodeEnv: config.mode,
}
});
webpack.prod.conf.js
'use strict'
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
const config = require('../config/prod.env')
const path = require('path');
const webpack = require('webpack');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
/**
* 提取css到单独文件
* npm install --save-dev mini-css-extract-plugin
*/
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
/**
* css 压缩 会清除css中注释
* npm install --save-dev optimize-css-assets-webpack-plugin
*/
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
module.exports = merge(baseWebpackConfig, {
// 模式
mode: "production",
// 调试工具
devtool: '#source-map',
// 输出
output: {
path: path.resolve(__dirname, '../dist'),
filename: "js/[name].[chunkhash].js",
publicPath: './'
},
// 插件
plugins: [
// 这里的设置是给src中用的,在webpack.base.conf如要获取,需在package.json配置cross-env NODE_ENV=production
new webpack.DefinePlugin({
'process.env': JSON.stringify(config.mode),
'API_ROOT': JSON.stringify(config.API_ROOT)
}),
// 打包时先删除dist目录,这里与output: path配置的目录同名即可,即:打包到哪个目录,就先删除那个目录,重新生成文件
new CleanWebpackPlugin(['dist'], {
root: path.resolve(__dirname, '../'),
}),
new webpack.HashedModuleIdsPlugin(),
// 配置plugin
new MiniCssExtractPlugin({
// 导出 css
filename: "css/[name].[hash:5].css",
chunkFilename: "css/[id].[hash:5].css"
}),
new UglifyJSPlugin({
uglifyOptions: {
compress: {
drop_debugger: false,
drop_console: true
}
}
}),
new OptimizeCSSAssetsPlugin({
// 默认是全部的CSS都压缩,该字段可以指定某些要处理的文件
assetNameRegExp: /\.(sa|sc|c)ss$/g,
// 指定一个优化css的处理器,默认cssnano
cssProcessor: require('cssnano'),
cssProcessorPluginOptions: {
preset: [ 'default', {
discardComments: { removeAll: true}, //对注释的处理
normalizeUnicode: false // 建议false,否则在使用unicode-range的时候会产生乱码
}]
},
canPrint: true // 是否打印编译过程中的日志
}),
// html打包
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: 'body',
// inject 有4个选项,按最新标准,一般将js文件置于body底部
// true 默认值,script标签位于html文件的 body 底部
// body script标签位于html文件的 body 底部
// head script标签位于html文件的 head中
// false 不插入生成的js文件,这个几乎不会用到的
// 压缩html
minify: {
// 移除注释
removeComments: true,
// 不要留下任何空格
collapseWhitespace: true,
// 当值匹配默认值时删除属性
removeRedundantAttributes: true,
// 使用短的doctype替代doctype
useShortDoctype: true,
// 移除空属性
removeEmptyAttributes: true,
// 从style和link标签中删除type="text/css"
removeStyleLinkTypeAttributes: true,
// 保留单例元素的末尾斜杠。
keepClosingSlash: true,
// 在脚本元素和事件属性中缩小JavaScript(使用UglifyJS)
minifyJS: true,
// 缩小CSS样式元素和样式属性
minifyCSS: true,
// 在各种属性中缩小url
minifyURLs: true
}
}),
// 复制内容到指定目录,一般针对一些静态资源
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../public'),
to: path.resolve(__dirname, '../dist'),
ignore: ['.*']
}
])
],
// 代码分离相关
optimization: {
nodeEnv: config.mode,
minimizer: [],
runtimeChunk: {
name: 'manifest'
},
splitChunks: {
minSize: 30000,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
name: false,
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'initial',
}
}
}
}
});
在 npm run dev 和 npm run build 时, 在base中都获取不到 process.env.NODE_ENV 的值
安装使用cross-env获取env
安装:
npm install cross-env --save-dev
在 NPM 脚本scripts中加上:
cross-env NODE_ENV=production 和 cross-env NODE_ENV=development
package.json
{
"name": "myproject",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": { // cross-env NODE_ENV=development、 cross-env NODE_ENV=production需要用到 cross-env插件
"dev": "cross-env NODE_ENV=development webpack-dev-server --hot --inline --progress --colors --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "cross-env NODE_ENV=production webpack --progress --colors --config build/webpack.prod.conf.js"
},
"author": "",
"homepage": "",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-import": "^1.11.0",
"babel-preset-latest": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-stage-0": "^6.24.1",
"clean-webpack-plugin": "^1.0.0",
"cross-env": "^7.0.0", // 这个插件在上面的,scripts中配置cross-env NODE_ENV=development 或 production,在webpack配置文件中就能拿到NODE_ENV的值了
"css-loader": "^1.0.1",
"file-loader": "^2.0.0",
"html-webpack-plugin": "^3.2.0",
"mini-css-extract-plugin": "^0.9.0",
"node-sass": "^4.10.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"uglifyjs-webpack-plugin": "^2.0.1",
"url-loader": "^1.1.2",
"webpack": "^4.26.1",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10",
"webpack-merge": "^4.1.4"
},
"dependencies": {
"antd": "^3.26.11",
"antd-mobile": "^2.3.1",
"axios": "^0.19.2",
"clipboard": "^2.0.4",
"qs": "^6.9.1",
"react": "^16.6.3",
"react-dom": "^16.6.3",
"react-redux": "^6.0.0",
"react-router-dom": "^4.3.1",
"redux": "^4.0.5"
}
}
测试
再分别运行开发和生产环境 npm run build,和 npm run dev
查看控制台,就能看到正确的输出了