这是一个关于Webpack4的文章系列,其中包含以下文章:
- Webpack4学习笔记(一)——基本使用
- Webpack4学习笔记(二)——代码分割(单入口)
- Webpack4学习笔记(三)——代码分割(多入口)
- Webpack4学习笔记(四)——CSS处理
- Webpack4学习笔记(五)——分离production和development环境
- Webpack4学习笔记(六)——Webpack4+VueJS2项目搭建
本系列文章源代码:
git clone https://github.com/davenkin/webpack-learning.git
欢迎访问本文github代码。
在上一篇文章中,我们讲到了CSS的处理,本文我们将讲解开发环境与产品环境的分离配置。
通常来讲,production环境与development的配置有不一样的地方,比如production环境要求更小的代码量以加快网页的加载速度,因此通常都会对源代码进行压缩,比如使用UglifyJsPlugin插件等;而对于development环境来说,我们希望有热加载(HMR)功能等。
Webpack4默认支持mode参数用于区分production环境和development环境。由于2个环境的配置有相同部分又有差异部分,因此通常的做法是提供3个webpack配置文件,一个是公有的配置文件,一个是针对production的配置文件,一个针对development,后两个配置文件通过webpack-merge
引入公有的配置文件。
在默认情况下,Webpack4已经对两种mode分别有不同的配置了,比较明显的不同便是production环境引入了UglifyJsPlugin
插件,而development没有。
具体来讲,webpack在默认情况下对production和development有以下不同配置:
模式 | 默认配置 |
---|---|
production | 通过DefinePlugin设置 process.env.NODE_ENV=production 。启用FlagDependencyUsagePlugin、FlagIncludedChunksPlugin、ModuleConcatenationPlugin、NoEmitOnErrorsPlugin、OccurrenceOrderPlugin、SideEffectsFlagPlugin 和 UglifyJsPlugin插件。 |
development | 通过DefinePlugin设置process.env.NODE_ENV=development 。启用 NamedChunksPlugin和NamedModulesPlugin插件。 |
分离配置文件
多数时候,webpack默认的配置是无法满足我们的需求的,我们希望对于不同的环境进行不同的配置,此时一种常用的做法是维护一个作用于所有环境的公有配置文件,然后针对每个环境再创建单独的配置文件,环境配置文件通过webpack-merge
引用公有配置文件中的内容。
创建3个配置文件:
- webpack.base.conf.js (公有配置文件)
- webpack.prod.conf.js(production环境配置文件)
- webpack.dev.conf.js (development环境配置文件)
创建webpack.base.conf.js如下:
const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
'index': './src/index.js',
},
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'distribution')
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src')],
exclude: [resolve('node_modules')]
},
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true,
modules: true,
localIdentName: '[name]---[local]---[hash:base64:5]'
}
}
]
}
]
},
plugins: [
new CleanWebpackPlugin(['distribution']),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
})
]
};
webpack.prod.conf.js与webpack.dev.conf.js使用webpack-merge引用webpack.base.conf.js,以webpack.dev.conf.js为例:
const merge = require('webpack-merge');
const baseWebpackConfig = require('./webpack.base.conf');
const webpackConfig = merge(baseWebpackConfig, {
mode:'development'
//environment specific config goes here
});
module.exports = webpackConfig;
此时,我们通过配置mode
参数声明当前配置所处的环境。另外,还需要修改一下package.json中的scripts:
...
"scripts": {
"build": "webpack --config webpack.prod.conf.js",
"start": "webpack-dev-server --open --config webpack.dev.conf.js"
},
...
source map
source map有多种风格配置,不同风格各有利弊,对于不同的环境推荐配置如下:
- development:inline-source-map
- production:source-map
配置webpack.dev.conf.js:
...
const webpackConfig = merge(baseWebpackConfig, {
mode:'development',
devtool: 'inline-source-map'
//environment specific config goes here
});
...
配置webpack.prod.config.js如下:
...
const webpackConfig = merge(baseWebpackConfig, {
mode:'production',
devtool:'source-map'
//environment specific config goes here
});
...
本地调试
对于development环境来说,需要配置本地调试服务器,以及HMR。
配置webpack-dev-server
webpack-dev-server用于本地调试,配置webpack.dev.conf.js:
...
const webpackConfig = merge(baseWebpackConfig, {
mode:'development',
devtool: 'inline-source-map',
devServer: {
contentBase: './distribution',
inline:true,//do not use iframe for the page, true is default
open: true,//open browser after dev server starts, true is default
port: 8080,//8080 is default
proxy: {//proxy backend api
'/api': 'http://localhost:3000'
}
}
//environment specific config goes here
});
...
devServer下的很多配置项都可以通过命令行参数的形式替代,具体请参考webpack-dev-server官方文档。
配置HMR
在webpack.dev.conf.js,在其中加入HotModuleReplacementPlugin插件:
...
plugins: [
new webpack.HotModuleReplacementPlugin()
]
...
另外,需要在devServer中配置hot:true
:
devServer: {
contentBase: './distribution',
inline: true,//do not use iframe for the page, true is default
open: true,//open browser after dev server starts
port: 8080,//8080 is default
proxy: {//proxy backend api
'/api': 'http://localhost:3000'
},
hot: true
},
此时报错:
Cannot use [chunkhash] or [contenthash] for chunk in '[name].[contenthash].js' (use [hash] instead)
解决方法:在webpack.dev.conf.js中重新配置output:
...
output: {
filename: '[name].js',
chunkFilename: '[name].js',
},
...
代码分割
production环境通常需要进行代码分割,而development环境则没有这样的需求。
分割javascript代码
对webpack的运行时代码进行抽离:
...
optimization: {
runtimeChunk: {
"name": "manifest"
},
...
另外,所有第三方库单独抽离:
...
splitChunks: {
cacheGroups: {
default: false,
vendors: false,
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
...
为了支持缓存,加入HashedModuleIdsPlugin插件:
...
plugins: [
new webpack.HashedModuleIdsPlugin()
]
...
分割css代码
当前,css代码被style-loader
直接添加到js文件里面了,在production环境中,我们通常希望将css文件单独抽离出来,此时可以采用mini-css-extract-plugin
插件。
不过,当前css的loader配置是放到webpack.base.conf.js中的,因此我们需要将其搬出到每个环境各自的配置文件中,此时webpack.base.conf.js文件如下:
const path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
module.exports = {
entry: {
'index': './src/index.js',
},
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js',
path: path.resolve(__dirname, 'distribution')
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src')],
exclude: [resolve('node_modules')]
}
]
},
plugins: [
new CleanWebpackPlugin(['distribution']),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html'
})
]
};
对于production环境,我们将使用MiniCssExtractPlugin.loader
替换style-loader
:
...
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
modules: true,
localIdentName: '[name]---[local]---[hash:base64:5]'
}
}
]
}
]
}
...
另外需要配置MiniCssExtractPlugin插件:
...
new MiniCssExtractPlugin({
filename: "[name].[contenthash].css",
chunkFilename: "[name].[contenthash].css"
})
...
还需要将css文件分割到单独的css chunk中:
...
splitChunks: {
cacheGroups: {
default: false,
vendors: false,
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
},
styles: {
name: 'styles',
test: /\.css$/,
chunks: 'all',
enforce: true
}
}
}
...
对于development环境,原封不动地将webpack.base.conf.js中的css module配置搬过来,此时的webpack.dev.conf.js文件为:
const merge = require('webpack-merge');
const webpack = require('webpack');
const baseWebpackConfig = require('./webpack.base.conf');
const webpackConfig = merge(baseWebpackConfig, {
//environment specific config goes here
mode: 'development',
output: {
filename: '[name].js',
chunkFilename: '[name].js'
},
devtool: 'inline-source-map',
devServer: {
contentBase: './distribution',
inline: true,//do not use iframe for the page, true is default
open: true,//open browser after dev server starts
port: 8080,//8080 is default
proxy: {//proxy backend api
'/api': 'http://localhost:3000'
},
hot: true
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true,
modules: true,
localIdentName: '[name]---[local]---[hash:base64:5]'
}
}
]
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
});
module.exports = webpackConfig;
在下一篇文章中,我们将讲到使用Webpack4手动搭建VueJS2项目。
网友评论