Vue项目升级到Webpack 4.x初步踩坑总结

前言

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 的破坏性变更

由于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配置中全部删除相关代码,因为他们已经没用了(摊手)。

使用config.optimization代替

在Webpack 4.x中,新增了optimization的选项来代替之前版本中的webpack.optimize等。在optimization选项中,我们可以进行详细的设置,以达到分离CSS,分离Chunk,压缩文件等等操作。点我查看官方文档

webpack 4.0

module.exports = {
    //...
    optimization: {}
};

基于webpack 4.0的Vue项目

const webpackConfig = merge(baseWebpackConfig, {
    //...
    optimization: {}
})

压缩JS及CSS文件

由于Webpack 4.x中取消了其内置插件,所以之前的方法都不管用了。这里我推荐大家使用UglifyJsPluginOptimizeCSSPlugin插件来代替之前的内置插件,当然如果你只是想简单的压缩,而不做任何配置的话,可以按照官方文档中给出的方法使用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({})
        ]
    }
}

分离JS文件

在之前版本中,我们使用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)
                    }
                }
            }
        }
    }
}    

提取CSS到单独文件

在之前版本中我们使用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')
    })
}

使用Vue-loader 15.x

如果你升级到了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

你可能感兴趣的:(webpack)