webpack4簡單實用配置解析

Simmzl發表於2018-09-21

雖然webpack已經升級到了4,但網上大量webpack配置的博文依舊是2/3,按照2/3的一些方法去配置webpack4會出現很多問題,非常不利於學習(挫敗感)。
大家調侃webpack配置複雜,有各種像webpack配置工程師這樣的段子。認真梳理起來其實並不難懂。
本文中Webpack4專案工程化構建配置簡單,註釋詳細,且非常實用,附有github原始碼,利於學習。引申到新的概念都有擴充閱讀,不會讓那你一臉懵逼!
github webpack4配置原始碼
希望本文能幫你點亮webpack配置技能點,歡迎評論提問題和star。

建立package.json

npm init
複製程式碼

一路回車就ok了:

package name: (test) test
version: (1.0.0)
description:
entry point: (index.js)
test command:
git repository:
...
複製程式碼

安裝webpack

cnpm i --save-dev webpack
複製程式碼

配置 webpack.config.js

新建檔案,webpack.config.js會最終暴露出一個配置物件。

const config = {};
module.exports = config;
複製程式碼

mode

webpcak4新增了mode,用於根據環境選用對應配置,不設定會warning: The 'mode' option has not been set, webpack will fallback to 'production' for this value...

const config = {
    mode: "production"  // none/development/production 有三個值
}
複製程式碼

entry & output

定義webpack入口和輸出。
entry/output 是一個物件,可以定義多個入口。配置output,使其根據入口加上hash動態生成打包後的名稱:

const path = require("path");

// __dirname為當前絕對路徑
console.log("__dirname ===>", __dirname);
// __dirname ===> D:\github\Personal-site

const config = {
    entry: {
        index: "./src/index.js"
        // 可以新增多個入口
        // app: "./src/app.js"
    },
    output: {
        filename: "[name].[hash].js",
        // 生成絕對路徑
        // D:\github\Personal-site\dist
        path: path.resolve(__dirname, "dist"),
        publicPath: "./"
    }
}
複製程式碼

以上用了path做了路徑處理,pathnode.js內建的package,用來處理路徑。path.resolve(__dirname, "dist")會生成返回一個絕對路徑,以儲存生成的檔案。
publicPath 並不會對生成檔案的路徑造成影響,主要是對頁面裡面引入的資源的路徑做對應的補全。如publicPath: "/"後,生成的頁面引入js的路徑為src="/[name].[hash].js",本地預覽會報錯,設定成publicPath: "./" ===> src="./[name].[hash].js"則可以解決問題。

devtool

指定sourceMap模式。
sourceMap模式有很多種,具體可看:webpack——devtool裡的7種SourceMap模式
vue-cli的webpack.dev.conf.js使用了cheap-module-eval-source-map
生產環境這裡使用hidden-source-map

const config = {
    // cheap-module-eval-source-map is faster for development
    devtool: "cheap-module-eval-source-map"
}
複製程式碼

devServer

  • 在開發模式下,DevServer 提供虛擬伺服器,讓我們進行開發和除錯。
  • 提供實時重新載入,減少開發時間。
  • 它不是 webpack 內建外掛,要安裝
cnpm i --save-dev webpack-dev-server
複製程式碼
const config = {
    devSever: {
        contentBase: './dist',
        hot: true,
        host: 'localhost'
    }
}
複製程式碼

可參考:官方文件segmentfault

這裡我們將開發的devServer單獨分為一個js檔案,並通過node執行檔案從而跑起伺服器。
安裝依賴:

cnpm i --save-dev opn webpack-dev-server
複製程式碼

新建: /build/dev-server.js

const webpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');
// 使用opn開啟瀏覽器(解決devServer.open無效)
const opn = require("opn");

// 引入配置
const config = require('../webpack.config.js');
// devServer配置
const options = {
    contentBase: './dist',
    hot: true,
    host: 'localhost'
};

// 將devServer加入webpack配置
webpackDevServer.addDevServerEntrypoints(config, options);
const compiler = webpack(config);
// 新建devServer
const server = new webpackDevServer(compiler, options);
// 監聽、開啟埠
server.listen(5000, 'localhost', () => {
    console.log('dev server listening on port 5000');
    opn(`http://127.0.0.1:5000`);
});
複製程式碼

package.json中新增指令碼命令:

{
    scripts: {
        "dev": "node ./build/dev-server.js"
    }
}
複製程式碼

註釋掉webpack.config.js中配置的devServer:

const config = {
    // devSever: {
    //     contentBase: './dist',
    //     hot: true,
    //     host: 'localhost'
    // }
}
複製程式碼

執行npm run dev即可開啟服務。(別急著執行,繼續配置)

plugins

plugins 選項用於以各種方式自定義 webpack 構建過程。webpack附帶了各種內建外掛,可以通過webpack.[plugin-name]或者直接引入require([plugin-name]) 訪問這些外掛。 自帶外掛

html-webpack-plugin

自動生成html,插入script。
更多: 官方配置html-minifier配置html-minifier中文文件

const HtmlWebpackPlugin = require("html-webpack-plugin");

const config = {
    entry: {
        index: "./src/index.js"
    },
    plugins: [
        new HtmlWebpackPlugin({
            // html模板檔案(在檔案中寫好title、meta等)
            template: "src/index.html",
            // 輸出的路徑(包含檔名)
            filename: "./index.html",
            //自動插入js指令碼
            // true body head false 預設為true:script標籤位於html檔案的 body 底部
            inject: true,
            // chunks主要用於多入口檔案,當你有多個入口檔案,那就回編譯後生成多個打包後的檔案,那麼chunks 就能選擇你要使用那些js檔案
            chunks: ["index"],
            // 壓縮html
            minify: {
                // 移除註釋
                removeComments: true,
                // 不要留下任何空格
                collapseWhitespace: true,
                // 當值匹配預設值時刪除屬性
                removeRedundantAttributes: true,
                // 使用短的doctype替代doctype
                useShortDoctype: true,
                // 移除空屬性
                removeEmptyAttributes: true,
                // 從style和link標籤中刪除type="text/css"
                removeStyleLinkTypeAttributes: true,
                // 保留單例元素的末尾斜槓。
                keepClosingSlash: true,
                // 在指令碼元素和事件屬性中縮小JavaScript(使用UglifyJS)
                minifyJS: true,
                // 縮小CSS樣式元素和樣式屬性
                minifyCSS: true,
                // 在各種屬性中縮小url
                minifyURLs: true
            }
        })
    ]
}
複製程式碼

mini-css-extract-plugin

自帶外掛,為每個引入 css 的 JS 檔案建立一個 css 檔案,css抽離,並寫入html。

const MiniCssExtractPlugin = require("mini-css-extract-plugin");

const config = {
    plugins: [
        new MiniCssExtractPlugin({
            filename: "[name].[hash].css",
        })
    ]
};
複製程式碼

clean-webpack-plugin

刪除資料夾,避免因為生成的檔案帶hash,而一直存在。

cnpm i --save-dev clean-webpack-plugin
複製程式碼
const CleanWebpackPlugin = require("clean-webpack-plugin");

const config = {
    plugins: [
        new CleanWebpackPlugin(["dist"])
    ]
};
複製程式碼

HotModuleReplacementPlugin

啟用熱替換模式。

NamedModulesPlugin

在控制檯中輸出可讀的模組名。

HashedModuleIdsPlugin

檔案未變動時,保持build出來的檔案hash不變。

// 三種外掛webpack自帶
const config = {
    plugins: [
        // 啟用 HMR
        new webpack.HotModuleReplacementPlugin(),
        // 在控制檯中輸出可讀的模組名
        new webpack.NamedModulesPlugin(),
        // 不做改動hash保持不變
        new webpack.HashedModuleIdsPlugin()
    ]
};
複製程式碼

performance

配置如何展示效能提示。

預設不配置下開啟devServer時,會提示:

WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
  vendor.256a0afe197a0724d634.js (1.67 MiB)

WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
  index (1.8 MiB)
      vendor.256a0afe197a0724d634.js
      index.256a0afe197a0724d634.css
      index.256a0afe197a0724d634.js


WARNING in webpack performance recommendations:
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
複製程式碼

因為限制了檔案大小為250kb,如果超過就會提示
避擴音示設定如下:

const config = {
    performance: {
        // false | "error" | "warning" // 不顯示效能提示 | 以錯誤形式提示 | 以警告...
        hints: "warning",
        // 開發環境設定較大防止警告
        // 根據入口起點的最大體積,控制webpack何時生成效能提示,整數型別,以位元組為單位
        maxEntrypointSize: 5000000, 
        // 最大單個資源體積,預設250000 (bytes)
        maxAssetSize: 3000000
    }
}
複製程式碼

loader

loader 用於對模組的原始碼進行轉換。我們可以使用loader將less/sass/scss/stylus轉為css並壓縮、相容處理等,可以將js es6/7語法轉為es5等等。

處理less

安裝:

cnpm i --save-dev less css-loader postcss-loader less-loader autoprefixer mini-css-extract-plugin
複製程式碼
// css抽離
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 使用mini-css-extract-plugin則不能用style-loader
// 前者是抽離css,後者將所有css插入html,故衝突
const config = {
    module: {
        rules: [
            {
                test: /\.less$/,
                // 只解析改目錄的檔案
                include: path.resolve(__dirname, "src"),
                use: [
                    MiniCssExtractPlugin.loader,
                    // "style-loader",
                    "css-loader",
                    {
                        loader: "postcss-loader",
                        options: {
                            plugins: [
                                require("autoprefixer")({
                                    browsers: [
                                        "ie >= 11",
                                        "ff >= 30",
                                        "chrome >= 34",
                                        "safari >= 7",
                                        "opera >= 23",
                                        "ios >= 7",
                                        "android >= 4.4",
                                        "bb >= 10"
                                    ]
                                })
                            ]
                        }
                    },
                    "less-loader"
                ]
            }
        ]
    }
}
複製程式碼

使用mini-css-extract-plugin會將js中的css抽離出來,打包成單獨的檔案,從而避免預設情況下,打包後,由於css通過js動態插入到html中,導致頁面閃動。
使用css-loder能夠解析js中引入的css:import "main.less"
使用style-loader把載入的css作為style標籤內容插入到html中。
使用postcss-loader autoprefixer能夠將css程式碼自動加相容性字首,配置如上程式碼。
使用less-loader將less程式碼轉為css。

babel-loader

Babel是編寫下一代JavaScript的編譯器,可以將當前執行平臺(瀏覽器、node伺服器)尚不支援的下一代或幾代js語法編譯為當前支援的js語法版本。
使用babel-loader將es6/7語法轉為es5瀏覽器可執行程式碼。
這裡安裝了babel-loader@7.1.5,不注意會有大坑,原因見註釋:

// babel-loader已經升級到了8,需要裝@babel/core,但是還是有問題,所以這裡安裝@7.1.5
cnpm i --save-dev babel-loader@7.1.5 babel-core babel-preset-env babel-polyfill
複製程式碼
const config = {
    module: {
        rules: [{
            test: /\.js$/,
            // 只解析include資料夾內的
            include: path.resolve(__dirname, "src"),
            // 排除node_modules資料夾
            exclude: /node_modules/,
            use: [{
                // cacheDirectory = true 使用快取,提高效能,將 babel-loader 提速至少兩倍
                loader: "babel-loader?cacheDirectory",
                options: {
                    presets: [
                        [
                            "env",
                            {
                                "modules": false
                            }
                        ],
                        // 包含stage-1, stage-2以及stage-3的所有功能,個人開發就直接上最新的了,爽
                        "stage-0"
                    ],
                    plugins: [
                        "transform-es2015-modules-commonjs"
                    ]
                }
            }]
        }]
    }
};
複製程式碼

babel-preset-env 是一個新的 preset,(presets是一系列plugin的集合)可以根據配置的目標執行環境,自動啟用需要的 babel 外掛,由於Preset 的執行順序時從最後一個逆序執行,所以env寫在最前,就當是保底... 但是使用preset依然不會解析Set/Map這樣的,這時候就要用babel-polyfill了。
babel-polyfill簡單描述就是只要引入了babel-polyfill你可以大膽的用ES6,可參考ES6和Babel你不知道的事兒,但是使用後會使程式碼體積增大,視需求而定。在檔案入口引入babel-polyfill即可使用: import "babel-polyfill".
stage-0是對ES7一些提案的支援,Babel通過外掛的方式引入,讓Babel可以編譯ES7程式碼。當然由於ES7沒有定下來,所以這些功能隨時肯能被廢棄掉的。參考:如何區分Babel中的stage-0,stage-1,stage-2以及stage-3,其通過外掛方式引入,所以需要安裝:

cnpm i --save-dev babel-preset-stage-0
複製程式碼

url-loader

使用url-loader而非file-loader,因為前者包含了後者,提供了更為強大的功能。他可以解決css樣式中引入的圖片檔案等打包後路徑指向不正確和將圖片轉為DataURL模式(base64編碼的字串形式,More:DATA URL簡介及DATA URL的利弊)從而提高網站的載入速度。更多參考:file-loader 和 url-loader

cnpm i --save-dev url-loader file-loader
複製程式碼
const config = {
    module: {
        rules: [{
            // 處理引入的圖片視訊字型等檔案的loader
            // 將小於10k的圖片檔案轉為DataURL,並且設定預設的dist中存放方式
            test: /\.(eot|woff|woff2|ttf|svg|png|jpe?g|gif|mp4|webm)(\?\S*)?$/,
            loader: "url-loader?limit=10240&name=assets/img/[name]_[hash].[ext]",
            // 一樣的作用
            // loader: "url-loader?limit=102400&name=[name]_[hash].[ext]&outputPath=assets/img/"
        }]
    }
};
複製程式碼

optimization

優化。

// css優化壓縮
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");

const config = {
    optimization: {
        // 公共程式碼抽取
        // CommonsChunkPlugin 已棄用,使用optimization.splitChunks代替
        // 提取被重複引入的檔案,單獨生成一個或多個檔案,這樣避免在多入口重複打包檔案
        splitChunks: {
            cacheGroups: {
                commons: {
                    // 選擇全部chunk
                    chunks: "all",
                    // 生成的公共程式碼檔名,慣用vendor
                    name: "vendor",
                    // 作用於
                    test: /[\\/]node_modules[\\/]/
                }
            }
        },
        // 壓縮程式碼,預設開啟
        // minimize: true,
        // 壓縮配置
        minimizer: [
            // 優化壓縮css
            new OptimizeCSSAssetsPlugin({}),
            // 壓縮js配置
            new UglifyJsPlugin({
                cache: true,
                parallel: true,
                sourceMap: true
            })
        ]
    }
}
複製程式碼

build

使用webpack命令即可開始構建,也可以自定義命令npm run build

package.json:

{
    "scripts": {
        "build": "webpack"
    }
}
複製程式碼

github原始碼

github webpack4配置原始碼
希望本文能幫你點亮webpack配置技能點,歡迎評論提問題和star。

原文

原文發表於本人blog : 柴犬工作室 CQ STUDIO

相關文章