升級 webpack 至 v2.2.x

本文主要講述如何將 webpack 版本升級至 v2.2.x,若是你還不瞭解 webpack,那麼推薦你先讀一下這篇文章css

今年年初,webpack 2.2.0 版本正式發佈,還記得那時已有不少文章來介紹 webpack 2。 但經歷過以前,先將默認安裝升級至 2.0.x-beta 又退回 1.x 的我來講,吃一塹長一智,決定先觀察看看。html

通過這幾個月的時間,並無什麼大新聞發生,應該不會再鬧烏龍了,便把公司項目和本身博客都試着升級一波看看效果。vue

爲何要升級至 Webpack2

你可能會問 webpack1 用得好好的爲啥要去升級成 webpack 2 哪?有啥好處?是否是又在瞎折騰了?node

固然不是在瞎折騰,由於 webpack2 最大一個好處就是 Tree Shaking(搖樹),這也是年初 webpack2 火了一把的最大緣由。react

使用 webpack2 還有沒有其餘好處哪?固然是有的。webpack

除了 Tree Shaking 以外,webpack2 還支持了 ES6 的模塊語法,單就這一點已經不須要 babel 了,固然若是你要用其餘一些新特性,仍是得加入 babel-loader。git

與此同時,webpack2 還支持使用 System.import 來動態加載模塊。不過,使用此功能時要注意,對不支持 Promise 的瀏覽器須要添加 polyfill。github

再加之,webpack 已經正式棄用 webpack1,也就再也不維護了。從長期的角度考慮,升級到 webpack2 也更加穩妥。web

知道了爲啥升級,接着就來看看如何升級。json

主要變化和注意點

如何從 v1 升級至 v2,webpack 官網的升級手冊已經介紹的很是詳細了,基本先過一遍,而後遇到問題再搜索一下就能搞定。(英文很差的童鞋也不用擔憂,已經有人翻譯了中文版

這裏就再也不一一贅述升級變動點了,主要按經常使用功能整理、分享一下 webpack2 升級的變化。

首先,來看看升級前的配置文件。(因爲篇幅緣由省略了一些重複性的配置,有興趣的能夠到 Github 上查看詳細內容

// base.js
const path = require('path');
const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

const SOURCE_PATH = path.join(__dirname, '../../src');
const DIST_PATH = path.join(__dirname, '../../build/client');

const webpackConfig = {
    // http://mp.weixin.qq.com/s?__biz=MzI3NTE2NjYxNw==&mid=2650600472&idx=1&sn=d4bf85c1bb26a32aff144e81d652582f
    devtool: 'source-map',
    output: {
        path: DIST_PATH,
        publicPath: '/'
    },
    resolve: {
        alias: {
            'vue': 'vue/dist/vue.js',
            'assets': SOURCE_PATH + '/client/assets',
            // 省略其餘 alias...
        },
        extensions: ['', '.js']
    },
    eslint: {
        configFile: '.eslintrc',
        emitWarning: true,
        emitError: true,
        formatter: require('eslint-friendly-formatter')
    },
    postcss: [autoprefixer({browsers: ['last 2 versions']})],
    plugins: [
        new ExtractTextPlugin('style-[contenthash:8].css'),
        new webpack.NoErrorsPlugin()
    ],
    module: {
        preLoaders: [
            {
                test: /[^(\.min)]\.js$/,
                loader: 'eslint-loader',
                exclude: /node_modules/,
                include: SOURCE_PATH
            }
        ],
        loaders: [
            {
                test: /[^(\.min)]\.js$/,
                loaders: ['babel'],
                exclude: /node_modules/,
                include: SOURCE_PATH
            },
            {
                test: /\.html$/,
                loader: 'html',
                query: {interpolate: true},
                exclude: /node_modules/,
                include: SOURCE_PATH
            },
            {
                test: /\.(sc|c)ss$/,
                // extract css file from js file, that will reduce the js file size and optimize page loading.
                // but it will increase the package time, so it should be only used in build file.
                loader: ExtractTextPlugin.extract('style-loader', 'css-loader!postcss-loader!sass-loader')
                // loaders: ['style', 'css', 'postcss', 'sass']
            },
            // 省略其餘 loader...
        ]
    }
};

因爲以前博客已升級成同構應用,webpack 的配置被分爲了客戶端和服務器端兩套,上面這個文件即是兩套配置中共通的部分。除了沒有設置 entry,基本的 webpack 配置就同它差很少,升級 webpack2 也不須要修改 entry,也就正好不用列出了。

接着就一個個來看,這些基本、經常使用的配置屬性,有哪些不要改,有哪些要改的。

一開始就是個好消息,兩個屬性 devtooloutput,它倆同 entry 同樣不用修改。

resolve

接着是 resolveresolve 的變化也不大,主要是其中兩個字段的變化,rootextensions

  • root 改成了 modules,用於設置 webpack 查詢模塊的路徑,默認是 ["node_modules"]
    同時,搜索模塊的優先級與數組的順序有關,越靠前的越先匹配,好比 [path.resolve(__dirname, "src"), "node_modules"],此時,webpack 查找模塊時會優先查找本地 src 下的模塊,查不到再到 node_modules 中查找。

  • extensions,用於設置 webpack 處理的擴展名,默認值爲 [".js", ".json"]
    這裏就牽扯到 2 個變動點:

    1. 升級後就不用像 v1 同樣添加一個空字符串 "" 了;

    2. v2 自帶 json-loader 來處理 json 類型的文件,而不須咱們本身手動引入。

這兩個配置大部分狀況下使用默認值(不配置)就能夠了。但在 react 或 vue 的項目中,可能須要在 extensions 中添加 .jsx.vue

接着兩個是 eslintpostcss,屬於自定義屬性,在 webpack2 中不支持自定義屬性,須要挪到各自的 loader 中進行配置。

既然,遇到了 loader 相關,下一步就先升級 loader

module

在 webpack 中,module 下再也不有 preLoaders, loaderspostLoaders 統一都變成了 rules,如須要替換 preLoaderspostLoaders 則需經過設置 rules.enforce 屬性。

同時,webpack2 再也不支持 loaders,改成 rules.useloader 屬性能夠繼續使用。loader 相關的配置,能夠經過 rules.use.options 設置。

還有一點,在 webpack2 中,已再也不默認給 loader 添加 -loader 後綴,不過還能夠經過將 resolveLoader 設置爲 moduleExtensions: ["-loader"] 來給 loader 添加默認後綴。不過,webpack 官方不推薦這麼作,仍是爲每一個 loader 都加上 -loader 比較好。

這部分能夠算是 webpack2 升級過程當中改動量最多的地方了。但別擔憂,這只是個有點麻煩,細心一點就能解決的問題。

還剩下最後一個部分,plugin 在來看一下它的變化。

plugin

plugin 部分 webpack2 有着一下這些變化:

  • 新增 LoaderOptionsPlugin,用於設置全局的 loaderplugin 屬性

  • 默認引入 OccurrenceOrderPlugin,也就是能夠刪了原先的這個配置

  • DedupePlugin 也被移除

  • NoErrorsPlugin 重命名爲 NoEmitOnErrorsPlugin

  • UglifyJsPlugin 再也不默認壓縮 js,需在 LoaderOptionsPlugin 配置 minimize: true

這樣升級就基本完成了,再來看一下修改後的配置文件。

// base.js
const path = require('path');
const webpack = require('webpack');
const autoprefixer = require('autoprefixer');
const ExtractTextPlugin = require('extract-text-webpack-plugin');

const SOURCE_PATH = path.join(__dirname, '../../src');
const PUBLIC_PATH = '/';
const DIST_PATH = path.join(__dirname, '../../build/client');

const webpackConfig = {
    // http://mp.weixin.qq.com/s?__biz=MzI3NTE2NjYxNw==&mid=2650600472&idx=1&sn=d4bf85c1bb26a32aff144e81d652582f
    devtool: 'source-map',
    output: {
        path: DIST_PATH,
        publicPath: PUBLIC_PATH
    },
    resolve: {
        alias: {
            'vue': 'vue/dist/vue.js',
            'assets': SOURCE_PATH + '/client/assets',
            // 省略其餘 alias...
        }
    },
    plugins: [
        new ExtractTextPlugin('style-[contenthash:8].css'),
        new webpack.NoEmitOnErrorsPlugin()
    ],
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'eslint',
                enforce: 'pre',
                exclude: /node_modules/,
                options: {
                    emitWarning: true,
                    emitError: true,
                    formatter: require('eslint-friendly-formatter')
                }
            },
            {
                test: /\.js$/,
                loader: 'babel',
                exclude: /node_modules/
            },
            {
                test: /\.html$/,
                loader: 'html?interpolate',
                exclude: /node_modules/
            },
            {
                test: /\.(sc|c)ss$/,
                // extract css file from js file, that will reduce the js file size and optimize page loading.
                // but it will increase the package time, so it should be only used in build file.
                use: ExtractTextPlugin.extract({
                    fallback: 'style-loader',
                    use: [
                        'css-loader?sourceMap',
                        {
                            loader: 'postcss-loader?sourceMap',
                            options: {
                                plugins: () => [autoprefixer({browsers: ['last 2 versions']})]
                            }
                        },
                        'sass-loader'
                    ]
                })
            },
            // 省略其餘 loader...
        ]
    }
};

不要覺得這就完了,勿忘初心。這只是成功升級了 webpack,還沒用上 Tree Shaking 哪。

Tree Shaking & Module

想要使用 webpack2 的 tree shaking 就須要讓 webpack 來管理模塊之間的加載,而不是讓 babel-loader 去處理。

不過,這修改起來也很簡單,只需修改 babel 的配置文件 .babelrc,將原先的 es2015 改成 ["es2015", { "modules": false }] 就能夠了。

在公司項目升級 webpack 修改模塊引入方式時,還遇到過 Module build failed: some file... TypeError: Cannot read property '0' of null 這樣一個問題,折騰了半天。最後發現是由於 babel-plugin-antd 報出的問題,babel-plugin-antd 前一陣就升級成 babel-plugin-import 了,項目裏也升級一下問題就解決了.

升級 webpack 後,記得同時升級所用到的 loader 和 plugin。

這樣升級基本完成了,對比一下升級先後的打包結果。

build with webpack1

build with webpack2

能夠看到,app.js 小了不到 8kb,減少了 10%,而 common.js 反而大了 12kb,都沒改代碼啊~[捂臉]

總得來講,將 webpack 從 v1 升級至 v2,主要修改 resolve, moduleplugin 這 3 個屬性,並且基本是一些字段名的修改,總體結構上沒有大的變化,升級仍是比較簡單的,是個耐心活。

至此,博客 2 代成員(vue2 和 koa2)又多了一位 webpack2...?

相關閱讀:Why Webpack 2's Tree Shaking is not as effective as you think

相關文章
相關標籤/搜索