共计 5339 个字符,预计需要花费 14 分钟才能阅读完成。
webpack 4 出来也已经一年了,公司的老项目用的还是 webpack3,也是时候该升级一波了。说实话 webpack4 还是有几点挺吸引我的,估计也是感受到了 parcel 的压力,4 这个版本内置了很多默认配置。
迁移的坑
- CommonsChunkPlugin 废除,webpack 4 内置了 optimization.splitChunks 和 runtimeChunk 进行代码拆分
- ExtractTextPlugin 不支持 webpack4,官方推荐了一个 MiniCssExtractPlugin 来拆分 chunk 中的 css 代码
- vue-loader 的升级,vue-loader 版本由 14 升级到了 15, 之前的配置也改变了具体参考从 v14 迁移
基础配置
const package = require('../package.json');
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const utils = require('./utils');
function resolve (dir) {return path.join(__dirname, dir);
}
module.exports = {
entry: {
main: '@/main',
'vender-exten': '@/vendors/vendors.exten.js'
},
output: {path: path.resolve(__dirname, '../dist/' + package.version) // 输出文件的绝对路径
},
module: {
rules: [
{test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
exclude: /node_modules/,
options: {
emitError: true,
emitWarning: false,
failOnError: true
}
},
{
test: /\.vue$/,
exclude: /node_modules/,
loader: 'vue-loader'
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {loader: 'babel-loader'}
},
{test: /\.js[x]?$/,
exclude: /node_modules/,
use: {loader: 'babel-loader'}
},
{test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
}
},
{test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
}
},
{test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
}
]
},
plugins: [new VueLoaderPlugin(),
],
resolve: {extensions: ['.js', '.vue'],
alias: {'@': resolve('../src')
}
},
externals: {
AMap: 'AMap',
vue: 'Vue',
iview: 'iview',
'vue-router': 'VueRouter',
vuex: 'Vuex'
}
};
如上,实现了对 vue,jsx, js, 和资源文件对处理和转译
开发环境配置
const HtmlWebpackPlugin = require("html-webpack-plugin");
const merge = require("webpack-merge");
const webpackBaseConfig = require("./webpack.base.config.js");
const config = require("./config");
const webpack = require("webpack");
module.exports = merge(webpackBaseConfig, {
mode: 'development',
devtool: '#source-map',
output: {
publicPath: '/dist/',
filename: '[name].js',
chunkFilename: '[name].chunk.js'
},
plugins: [
new HtmlWebpackPlugin({
title: 'admin management',
filename: '../index.html',
inject: false
}),
],
module: {
rules: [
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader', 'postcss-loader']
},
{
test: /\.less$/,
use: [
'vue-style-loader',
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
},
// 设置跨域代理
devServer: {
public: 'local.xxxx.net:9000',
port: 9000,
proxy: {
'/login': {
target: config.devApi.passport,
pathRewrite: {'^/login': '/login'},
changeOrigin: true
},
'/v3': {
target: config.devApi.sass,
pathRewrite: {'^/v3': '/v3'},
changeOrigin: true
}
}
}
});
如上,配置了开发环境的配置,注意 webpack4 多了一个 mode,mode 有三个选项可以选择:development, production, 和 none,
可以看官方的这个图,webpack 会根据 mode 自动开启内置插件
生产环境配置
const HtmlWebpackPlugin = require('html-webpack-plugin')
const TerserJSPlugin = require('terser-webpack-plugin');
const webpack = require('webpack')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const cleanWebpackPlugin = require('clean-webpack-plugin')
const merge = require('webpack-merge')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const webpackBaseConfig = require('./webpack.base.config.js')
const path = require('path')
const package = require('../package.json')
module.exports = merge(webpackBaseConfig, {
mode: 'production',
output: {
publicPath: '/' + package.version + '/', // 所有引入的文件资源都加上 publicPath 路径
filename: '[name].[contenthash].js', // 输出 bundle 的名称, 入口 (non-entry)
chunkFilename: '[name].[contenthash].chunk.js' // 非入口 (non-entry) chunk 文件的名称
},
optimization: {
splitChunks: {
cacheGroups: {
// 缓存组
commons: {test: /[\\/]node_modules[\\/]/, // 只抽取引入的 node_modules 文件
name: 'vender-exten', // 要缓存的 分隔出来的 chunk 名称
chunks: 'all' // 对所有的 chunk 都进行缓存
}
}
},
runtimeChunk: {name: 'runtime'},
minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})]
},
module: {
rules: [
{
test: /\.css?$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
},
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'less-loader'
]
}
]
},
plugins: [
new MiniCssExtractPlugin({filename: '[name].[contenthash].css',
chunkFilename: '[name].[contenthash].css'
}),
new cleanWebpackPlugin(['dist/*'], {root: path.resolve(__dirname, '../')
}),
new CopyWebpackPlugin([
{from: path.resolve(__dirname, '../static'),
to: '../static',
ignore: ['.*']
}
]),
new HtmlWebpackPlugin({
title: 'admin',
favicon: './static/favicon.ico',
filename: '../index.html',
template: '!!ejs-loader!./src/template/index.ejs',
inject: false
})
]
});
生产环境
1. 使用 MiniCssExtractPlugin,来拆分 css,我可以可以 options 里面有两个值 filename 和 chunkFilename, filename 指的是 entry 中 key 生成的 css 名称,chunkFilename 是 webpack 中每个 chunk 拆分生成 css 的名称。
2. 使用 OptimizeCSSAssetsPlugin 对打包之后的 css 文件进行压缩,
3. 使用 splitChunks 对公共代码进行抽取缓存,我们先来看一下这个插件的默认配置
module.exports = {
optimization: {
splitChunks: {chunks: 'async', "initial" | "all"( 推荐) | "async" (默认就是 async)
minSize: 30000, 最小尺寸,30000
maxSize: 0, 最小尺寸
minChunks: 1, 最小 chunk,默认 1
maxAsyncRequests: 5, // 最大异步请求数,默认 5
maxInitialRequests: 3, // 最大初始化请求书,默认 3
automaticNameDelimiter: '~', // 打包分隔符
name: true,
cacheGroups: { // 重点,缓存配置
vendors: {test: /[\\/]node_modules[\\/]/, // 只缓存 node_modules 里面的模块
priority: -10 // // 缓存组优先级, 可能有多个
},
}
}
}
};
看下我的配置
splitChunks: {
cacheGroups: {
// 缓存组
commons: {test: /[\\/]node_modules[\\/]/, // 只抽取引入的 node_modules 文件
name: 'vender-exten', // 要缓存的 分隔出来的 chunk 名称
chunks: 'all' // 对所有的 chunk 都进行缓存
}
}
}
通过 webpack-bundle-analyzer 生成的可视化图标分析查看项目中的多个 chunk 共同引入的 lib 包
抽离 5 个依赖的 lib 包,进行缓存 <br/><br/>
4. 使用 optimization.runtimeChunk, 去缓存生成 chunk 过程中生成的 webpack 公用代码 <br/><br/>
5. 使用 TerserJSPlugin 对 js 进行压缩