Webpack 4.x发布已经有很长时间,听说其性能又有较大的提升。所以在5月中的时候手贱的升级到了最新版本,果不其然出现大量报错。经过一段时间的折腾,解决了很多升级时遇到的坑。原本那时候就打算出个博文的,结果工作一直忙到现在,写文几乎不可能。今天总算是有了时间,所以就分享一下在升级过程中遇到的坑以及我的解决方法。以下将会对涉及到的坑进行逐步说明,其中大部分以Vue项目为例。
由于Webpack的更新,很多Loader也必须更新才能适应Webpack的最新版本,在Vue项目中以下Loader必须更新(可能会有遗漏)!!
必须更新的loader | 最低要求版本 | 功能说明 |
---|---|---|
babel-loader | 7.1.3 | 对最新的ES语法进行转换 |
eslint-loader | 2.0.0 | 代码检查 |
file-loader | 1.1.10 | 批量修改文件路径,或者指定编译后文件存储路径 |
vue-loader | 15.0.0 | 解析、编译vue单文件组件 |
vue-style-loader | 4.1.0 | 解析、编译vue单文件组件中的样式 |
更新以上Loader后,个人建议使用npm-check
再次校验一下依赖库中有哪些不是最新的,一些破坏性或重大变更请自行在github上查找,以保证不会出现重大错误。
由于Webpack 4.x的重大变更,导致其不再支持部分插件和Loader,其中最闹心的是Chunk分离及文件分离。
失效的插件或loader | 类型 | 功能说明 |
---|---|---|
webpack.optimize.CommonsChunkPlugin | 内置插件 | 分离Chunk。将多次出现的代码统一打包到一个文件中。Vue工程中用来打包vendor及mainfest |
webpack.optimize.UglifyJsPlugin | 内置插件 | 压缩JS |
extract-text-webpack-plugin | Loader | 分离文件。将部分代码或文件提取到单独文件中。Vue工程里用来分离CSS到指定文件 |
如果你之前的项目中使用的是webpack 3.x版本,那么恭喜你,升级到webpack 4.x后,以上列举的这些请在Webpack配置中全部删除相关代码,因为他们已经没用了(摊手)。
在Webpack 4.x中,新增了optimization的选项来代替之前版本中的webpack.optimize
等。在optimization选项中,我们可以进行详细的设置,以达到分离CSS,分离Chunk,压缩文件等等操作。点我查看官方文档
webpack 4.0
module.exports = {
//...
optimization: {}
};
基于webpack 4.0的Vue项目
const webpackConfig = merge(baseWebpackConfig, {
//...
optimization: {}
})
由于Webpack 4.x中取消了其内置插件,所以之前的方法都不管用了。这里我推荐大家使用UglifyJsPlugin
及OptimizeCSSPlugin
插件来代替之前的内置插件,当然如果你只是想简单的压缩,而不做任何配置的话,可以按照官方文档中给出的方法使用optimization.minimizer: true
即可。
const UglifyJsPlugin = require("uglifyjs-webpack-plugin")
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
module.exports = {
//...
optimization: {
minimizer: [
// js mini
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: false // set to true if you want JS source maps
}),
// css mini
new OptimizeCSSPlugin({})
]
}
}
在之前版本中,我们使用CommonsChunkPlugin
进行文件分离,而在Webpack 4.x中,我们则借助于config.optimization
来实现。
webpack 3.0
module.exports = {
//...
plugins:[
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function(module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
]
}
webpack 4.0
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
// 大于30KB才单独分离成chunk
minSize: 30000,
maxAsyncRequests: 5,
maxInitialRequests: 3,
name: true,
cacheGroups: {
default: {
priority: -20,
reuseExistingChunk: true,
},
vendors: {
name: 'vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: "all"
},
echarts: {
name: 'echarts',
chunks: 'all',
// 对echarts进行单独优化,优先级较高
priority: 20,
test: function(module){
var context = module.context;
return context && (context.indexOf('echarts') >= 0 || context.indexOf('zrender') >= 0)
}
}
}
}
}
}
在之前版本中我们使用extract-text-webpack-plugin
来提取CSS文件,不过在webpack 4.x中则应该使用mini-css-extract-plugin
来提取CSS到单独文件中
基于webpack 3.0的Vue项目
const utils = require('./utils')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
//...
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash:7].css')
})
}
基于webpack 4.0的Vue项目
const utils = require('./utils')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
//...
new MiniCssExtractPlugin({
filename: utils.assetsPath('css/[name].[contenthash:7].css')
})
}
如果你升级到了Webpack 4.x,那么Vue项目的vue-loader
也必须更新到15.0以上版本。在Webpack 3.0时,我们不需要对Vue-loader做任何复杂的设置即可达到项目要求,然而如果你是从Webpack 3.0升级上来的旧Vue项目,并且未使用vue-cli
来重新设置Webpack配置文件的话,那这个改动量可是很大的。点我查看官方文档
首先,在Webpack 4.x中我们必须显性的引入vue-loader
插件
// webpack.base.conf.js
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = {
//...
plugins: [
new VueLoaderPlugin()
]
}
其次,我们仍需要修改utils.js
文件,满足全新的webpack 4.x语法,防止其出现错误。这里我只放当前修改的代码,其实有些细节还是有待商榷。
//...
exports.cssLoaders = function(options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap,
importLoaders: 1
}
}
// generate loader string to be used with extract text plugin
function generateLoaders(loader, loaderOptions) {
let loaders = []
if (loader) {
loaders = [{
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
}]
}
if (options.extract) {
let extractLoader = {
loader: MiniCssExtractPlugin.loader,
options: {}
}
// 不清楚先后顺序是否影响编译,但当前顺序是正确的
return [extractLoader, 'css-loader'].concat(['postcss-loader'], loaders)
} else {
// 不清楚先后顺序是否影响编译,但当前顺序是正确的
return ['vue-style-loader', 'css-loader'].concat(['postcss-loader'], loaders)
}
}
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function(options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
以上就是我在探索Webpack 4.x时遇到的问题及其解决方法。在最开始探索的时候真的是什么文档也没有,vue-loader
的文档中细节也没有现在这么全面,现在文档逐步全面了,大家可能看看官方文档就能知道怎么搞了。不管这篇文章对大家是否起到一定作用,我最后都要说一句:“Webpack的文档真的是烂透了”。
于2018年6月26日 21:15:00