webpack 4 出来也已经一年了,公司的老项目用的还是webpack3,也是时候该升级一波了。说实话webpack4还是有几点挺吸引我的,估计也是感受到了parcel的压力,4这个版本内置了很多默认配置。

迁移的坑

  1. CommonsChunkPlugin 废除, webpack 4内置了optimization.splitChunks 和 runtimeChunk 进行代码拆分
  2. ExtractTextPlugin 不支持webpack4,官方推荐了一个MiniCssExtractPlugin来拆分chunk中的css代码
  3. 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进行压缩