Webpack4 配置详解

[[toc]]

Webpack4后包拆成webpack和webpack-cli两个包:

// 安装webpack
npm install --save-dev webpack webpack-cli

主要有以下这些一级配置:

module.exports = {
mode: 'development', // 模式配置,webpack4.0新增
entry: '', // 入口文件
output: {}, // 出口文件
module: {
rules: [/*loader setting*/]
}, // 配置modules,包括loader
plugins: [], // 对应的插件
devServer: {}, // 开发服务器配置
optimization: {}, // 最佳实践
devtool: '',
resolve: { alias: {}},
}

1. mode

Webpack 4 引入了 mode 这个选项。这个选项的值可以是 development 或者 production。

设置了 mode 之后会把 process.env.NODE_ENV 也设置为 development 或者 production。然后在 production 模式下,会默认开启 UglifyJsPlugin 等一堆插件。

  • webpack4支持ES6的方式导入JSON文件,并且支持Tree-shaking (通过工具”摇”我们的JS文件,将其中用不到的代码”摇”掉,是一个性能优化的范畴)

    2. entry & output

let path = require('path');
let HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
// 1.写成数组的方式就可以打出多入口文件,不过这里打包后的文件都合成了一个
// entry: ['./src/index.js', './src/login.js'],
// 2.真正实现多入口和多出口需要写成对象的方式
entry: {
index: path.resolve(__dirname,'./src/index.js'),
login: path.resolve(__dirname,'./src/login.js')
},
output: {
// 1. filename: 'bundle.js', 'bundle.[hash:4].js',
// 2. [name]就可以将出口文件名和入口文件名一一对应
filename: '[name].[hash:8].js', // 打包后会生成index.313eerrd.js和login.dsfcersx.js文件
path: path.resolve(__dirname,'../dist') //打包后的目录
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'./public/index.html'), // 用哪个html作为模板 , react一般在在src目录下public里创建一个index.html页面当做模板来用
filename: 'index.html', // 要打包输出的文件名
chunks: ['manifest','index'] // 对应关系,index.js对应的是index.html
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'./public/login.html'),
filename: 'login.html',
chunks: ['manifest','login'] // 对应关系,login.js对应的是login.html
})
]
}

3. module - rules 配置

webpack4中移除loaders配置,必须使用rules。rules 配置模块的读取和解析规则, 通常用来配置loader, 其类型是一个数组, 数组里每一项都描述了如何去处理部分文件。

配置一项rules大致通过以下方式:

  1. 条件匹配: 通过test、include、exclude三个配置来命中Loader要应用的规则文件。(三个配置都可以是正则,也支持数组)

  2. 应用规则: 对选中后的文件通过use配置项来应用loader,可以应用一个loader或者按照从后往前的顺序应用一组loader。同时还可以分别给loader传入参数。

  3. 重置顺序: 一组loader的执行顺序默认是从右到左执行,通过exforce选项可以让其中一个loader的执行顺序放到最前或者是最后。

    module.exports = {
    //...
    module: {
    rules: [
    {
    test: /\.css$/, // 解析css
    exclude: /node_modules/,
    use: ['style-loader', 'css-loader'] // 从右向左解析
    /*
    也可以这样写,这种方式方便写一些配置参数
    use: [
    {loader: 'style-loader'},
    {
    loader: require.resolve('postcss-loader'), //这里是加上浏览器的前缀
    options: {
    ident: 'postcss',
    plugins: () => [
    require('postcss-flexbugs-fixes'), //修复Flexbugs
    require('postcss-preset-env')({ //postcss-preset-env包括autoprefixer
    autoprefixer: {
    flexbox: 'no-2009',
    },
    stage: 3,
    }),
    ]
    },
    }
    ]
    */
    }
    ]
    }
    }

    4. optimization

    在Webpack4中引入,根据mode(production/development)的不同,配置项默认值不同,具体有以下:

  4. optimization.minimize: 是否自动压缩打包后的代码。mode = production时,为true。
    压缩默认使用terser-webpack-plugin插件(更加兼容ES6),如果想要使用别的压缩插件,可以使用optimization.minimizer设置。

  5. optimization.splitChunks: 根据不同的策略来分割打包出来的bundle。配置基于SplitChunksPlugin,剔除了老的CommonsChunkPlugin(webpack4移除)。

  6. 零配置

    • optimization.nodeEnv: 告诉webpack process.env.NODE_ENV的值,值来自于mode的取值。代替webpack.DefinePlugin
    • optimization.namedModules: 代替webpack.NamedModulesPlugin(webpack4移除) 给模块有意义的名称代替ids
    • optimization.noEmitOnErrors: 代替webpack.NoEmitOnErrorsPlugin(webpack4移除) 编译错误时不写入到输出
    • optimization.concatenateModules: 代替webpack.optimize.ModuleConcatenationPlugin(webpack4移除) 尝试查找模块图中可以安全连接到单个模块中的段。- -

默认配置

5. plugins

  • 常用插件
    1. HtmlWebpackPlugin 自动在html中加载打包后的js文件
    2. DLLPlugin/DllReferencePlugin 提高打包速度
      • DLLPlugin:创建一个只有dll的bundle
      • DllReferencePlugin: 打包生成的dll文件引用到需要的预编译依赖上来
    3. happyPack 多进程打包,加快打包速度。
    4. webpack.DefinePlugin webpack4设置mode会自动使用
    5. uglifyjs-webpack-plugin webpack4 mode = production默认使用
    6. WebpackBar
    7. webpack-bundle-analyzer
    8. clean-webpack-plugin 清除dist文件夹里会残留上次打包的文件
  • 废弃插件
    ExtractTextWebpackPlugin 拆分css样式的插件(webapck4已废弃)。由于webpack4以后对css模块支持的逐步完善和commonchunk插件的移除,
    在处理css文件提取的计算方式上也做了些调整,之前我们首选使用的extract-text-webpack-plugin也完成了其历史使命,
    将让位于mini-css-extract-plugin

6. devServer

webpack的devServer配置基于webpack-dev-server集成的插件。该插件提供了proxy代理配置,基于express中间件 http-proxy-middleware实现,该中间件又基于node http-proxy,

devServer: {
// 提供静态文件目录地址
// 基于express.static实现
contentBase: path.join(__dirname, 'dist'),
// 任意的 404 响应都被替代为 index.html
// 基于node connect-history-api-fallback包实现
historyApiFallback: true,
// 是否一切服务都启用 gzip 压缩
// 基于node compression包实现
compress: true,
// 是否隐藏bundle信息
noInfo: true,
// 发生错误是否覆盖在页面上
overlay: true,
// 是否开启热加载
// 必须搭配webpack.HotModuleReplacementPlugin 才能完全启用 HMR。
// 如果 webpack 或 webpack-dev-server 是通过 --hot 选项启动的,那么这个插件会被自动添加
hot: true,
// 热加载模式
// true代表inline模式,false代表iframe模式
inline: true, // 默认是true
// 是否自动打开
open: true,
// 设置本地url和端口号
host: 'localhost',
port: 8080,
// 代理
// 基于node http-proxy-middleware包实现
proxy: {
// 匹配api前缀时,则代理到3001端口
// 即http://localhost:8080/api/123 = http://localhost:3001/api/123
// 注意:这里是把当前server8080代理到3001,而不是任意端口的api代理到3001
'/api': 'http://localhost:3001',
// 设置为true, 本地就会虚拟一个服务器接收你的请求并代你发送该请求
// 主要解决跨域问题
changeOrigin: true,
// 针对代理https
secure: false,
// 覆写路径:http://localhost:8080/api/123 = http://localhost:3001/123
pathRewrite: {'^/api' : ''}
}
}

举个完整例子:

const path = require('path');
const webpack = require('webpack');
// 插件都是一个类,所以我们命名的时候尽量用大写开头
const HtmlWebpackPlugin = require('html-webpack-plugin'); //打包html
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 提取出来css
const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); // 压缩打包后的js
const HappyPack = require('happypack'); // 多线程构建
const happyThreadPool = HappyPack.ThreadPool({ size: 5 }); // 构造出共享进程池,进程池中包含5个子进程
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')// 最大化压缩css
const Dotenv = require('dotenv-webpack');

console.log('process.env.NODE_ENV------->', process.env.NODE_ENV)
// 解决css 分离后图片引入路径不正确问题
if (process.env.type == 'build') { // 判断package.json里面是build还是dev命令
// 开发
var website ={
publicPath:"/"
}
} else {
// 生产
var website ={
publicPath:"/"
}
}


module.exports = {
// devtool:'eval-source-map',
mode: 'development', // 模式配置
entry: {
main: './src/index.js',
},
output: {
filename: 'bundle.[chunkhash:6].js',
path: path.resolve(__dirname, 'dist'),
publicPath: website.publicPath, // 解决css 分离后图片引入路径不正确问题
},
module: {
rules: [
{
test: /\.css/,
exclude: /node_modules/,
use: ['style-loader', MiniCssExtractPlugin.loader,//拆分css [对应109-112行]
{
loader: require.resolve('postcss-loader'), //这里是加上浏览器的前缀
options: {
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'), //修复Flexbugs
require('postcss-preset-env')({ //postcss-preset-env包括autoprefixer
autoprefixer: {
flexbox: 'no-2009',
},
stage: 3,
}),
]
},
},
'css-loader'],
},
{
test: /\.less$/,
exclude: /node_modules/,
use: ['style-loader', MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'],
},
{
test: /\.(png|jpe?g|gif|svg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
name: '[name]_[hash:7].[ext]',
outputPath:'static/images/'
}
}
]
},
{
test: /\.(htm|html)$/,
use: 'html-withimg-loader'
},
// babel 解析es7 es6 jsx
{
test:/\.(jsx|js)$/,
include: [
path.resolve(__dirname, 'src'),
],
use:['babel-loader'],
/*
如果开启多线程进行构建
use:['happypack/loader?id=js'],
loader这样写 匹配下面注释的插件
*/
exclude:/node_modules/
},
]
},
plugins: [
// 打包html
new HtmlWebpackPlugin({
template: './src/index.html',
hash: true,
minify: {
minifyCSS: true,
minifyJS: true,
removeAttributeQuotes: true
},
}),
new MiniCssExtractPlugin({
filename: "static/css/[name].[chunkhash:8].css",
chunkFilename: "[id].css"
}),
new UglifyJsPlugin({
parallel: true,
}),
new Dotenv(), // 配置 .env 文件
new webpack.DefinePlugin({
NODE_ENV: JSON.stringify('DEV')
}),
// 多线程构建 匹配上面的loader
// new HappyPack({
// id: 'js',
// //threads: 4,
// loaders: ['babel-loader'],
// threadPool: happyThreadPool, // 使用共享进程池中的子进程去处理任务
// }),
'postcss-px-to-viewport': {
viewportWidth: 750, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750
viewportHeight: 1334, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置
unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数
viewportUnit: "vw", //指定需要转换成的视窗单位,建议使用vw
selectorBlackList: ['.ignore'],// 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
mediaQuery: false // 允许在媒体查询中转换`px`
}
],
// 提取公共代码
optimization: {
minimizer: [
// 自定义js优化配置,将会覆盖默认配置 最大化压缩成js
new UglifyJsPlugin({
exclude: /\.min\.js$/, // 过滤掉以".min.js"结尾的文件,我们认为这个后缀本身就是已经压缩好的代码,没必要进行二次压缩
cache: true,
parallel: true, // 开启并行压缩,充分利用cpu
sourceMap: false,
extractComments: false, // 移除注释
uglifyOptions: {
compress: {
unused: true,
warnings: false,
drop_debugger: true
},
output: {
comments: false
}
}
}),
// 用于优化css文件 最大化压缩成css 并且去掉注释掉的css
new OptimizeCssAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessorOptions: {
safe: true,
autoprefixer: { disable: true },
mergeLonghand: false,
discardComments: {
removeAll: true // 移除注释
}
},
canPrint: true
})
],
splitChunks: {
cacheGroups: {
vendor: { // 抽离第三方插件
test: /node_modules/, // 指定是node_modules下的第三方包
chunks: 'initial',
name: 'vendor', // 打包后的文件名,任意命名
// 设置优先级,防止和自定义的公共代码提取时被覆盖,不进行打包
priority: 10
},
// utils: { // 抽离自己写的公共代码,utils这个名字可以随意起 (css/js公用的都会单独抽离出来生成一个单独的文件)
// chunks: 'initial',
// name: 'utils', // 任意命名
// minSize: 0 // 只要超出0字节就生成一个新包
// }
}
}
},
devServer: {
historyApiFallback: true,
inline: true
},
// externals: {
// jquery: "jQuery",
// },
resolve: {
// alias 别名配置,它能够将导入语句里的关键字替换成你需要的路径
alias: {
// 比如我们就可以直接写 import Nav from '@/Nav'
'@': './app/component'
},
// 省略后缀
extensions: ['.js', '.jsx', '.less', '.json', '.css'],
},
performance: {
hints: false // 选项可以控制 webpack 如何通知「资源(asset)和入口起点超过指定文件限制」
}
}

参考文章