webpack4 系列教程(十六):開發模式和生產模式·實戰
作者按:這是
webpack4
系列最後一篇教程了。這篇文章在之前所有教程的基礎上,做了一個真正意義上的 webpack 專案!我花費了三個月整理了這份教程,並且完善了相關示例程式碼,也更熟悉 webpack 的理論和應用,當然,也感謝大家的支援。好了,感慨完畢,開始正題 ?
0. 課程介紹和資料
本節課的程式碼目錄如下:
1. 如何分離開發環境和生產環境?
熟悉 Vuejs 或者 ReactJs 的腳手架的朋友應該都知道:在根目錄下有一個/build/
資料夾,專門放置webpack
配置檔案的相關程式碼。
不像我們前 15 節課的 demo (只有一個配置檔案webpack.config.js
webpack
配置程式碼。
毫無疑問,有一些外掛和配置是兩種環境共用的,所以應該提煉出來,避免重複勞動。如前文目錄截圖,build/webpack.common.conf.js
就儲存了兩種環境都通用的配置檔案。而build/webpack.dev.conf.js
和build/webpack.prod.conf.js
分別是開發和生產環境需要的特殊配置。
2. 編寫package.json
類似上一節講的,為了讓命令更好呼叫,需要配置scripts
選項。模仿vue-cli
的命令格式,編寫如下package.json
:
{
"scripts": {
"dev": "webpack-dev-server --env development --open --config build/webpack.common.conf.js",
"build": "webpack --env production --config build/webpack.common.conf.js"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-transform-runtime": "^6.23.0" ,
"babel-preset-env": "^1.7.0",
"clean-webpack-plugin": "^0.1.19",
"css-loader": "^1.0.0",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"html-webpack-plugin": "^3.2.0",
"jquery": "^3.3.1",
"style-loader": "^0.21.0",
"webpack": "^4.16.1",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.4",
"webpack-merge": "^4.1.3"
},
"dependencies": {
"babel-polyfill": "^6.26.0",
"babel-runtime": "^6.26.0"
}
}
按照配置,執行:
npm run dev
: 進入開發除錯模式npm run build
: 生成打包檔案
還可以看出來,build/webpack.common.conf.js
不僅僅是存放著兩種環境的公共程式碼,還是webpack
命令的入口檔案。
3. 如何合併 webpack 的不同配置?
根據前面所講,我們有 3 個配置檔案。那麼如何在build/webpack.common.conf.js
中引入開發或者生產環境的配置,並且正確合併呢?
此時需要藉助webpack-merge
這個第三方庫。下面是個示例程式碼:
const merge = require("webpack-merge");
const productionConfig = require("./webpack.prod.conf");
const developmentConfig = require("./webpack.dev.conf");
const commonConfig = {}; // ... 省略
module.exports = env => {
let config = env === "production" ? productionConfig : developmentConfig;
return merge(commonConfig, config); // 合併 公共配置 和 環境配置
};
4. 如何在程式碼中區分不同環境?
4.1 配置檔案
如果這個 js 檔案是 webpack 命令的入口檔案,例如build/webpack.common.conf.js
,那麼mode
的值(production 或者 development)會被自動傳入module.exports
的第一個引數,開發者可以直接使用。
如下面的程式碼,先判斷是什麼環境,然後再決定使用什麼配置,最後 return 給 webpack:
module.exports = env => {
let config = env === "production" ? productionConfig : developmentConfig;
return merge(commonConfig, config); // 合併 公共配置 和 環境配置
};
4.2 專案檔案
如果這個 js 檔案是專案中的指令碼檔案,那麼可以訪問process.env.NODE_ENV
這個變數來判斷環境:
if (process.env.NODE_ENV === "development") {
console.log("開發環境");
} else {
console.log("生產環境");
}
5. 編寫配置檔案
5.1 編寫公共配置檔案
// /build/webpack.common.conf.js
const webpack = require("webpack");
const merge = require("webpack-merge");
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const path = require("path");
const productionConfig = require("./webpack.prod.conf.js"); // 引入生產環境配置檔案
const developmentConfig = require("./webpack.dev.conf.js"); // 引入開發環境配置檔案
/**
* 根據不同的環境,生成不同的配置
* @param {String} env "development" or "production"
*/
const generateConfig = env => {
// 將需要的Loader和Plugin單獨宣告
let scriptLoader = [
{
loader: "babel-loader"
}
];
let cssLoader = [
{
loader: "css-loader",
options: {
minimize: true,
sourceMap: env === "development" ? true : false // 開發環境:開啟source-map
}
}
];
let styleLoader =
env === "production"
? ExtractTextPlugin.extract({
// 生產環境:分離、提煉樣式檔案
fallback: {
loader: "style-loader"
},
use: cssLoader
})
: // 開發環境:頁內樣式嵌入
cssLoader;
return {
entry: { app: "./src/app.js" },
output: {
publicPath: env === "development" ? "/" : __dirname + "/../dist/",
path: path.resolve(__dirname, "..", "dist"),
filename: "[name]-[hash:5].bundle.js",
chunkFilename: "[name]-[hash:5].chunk.js"
},
module: {
rules: [
{ test: /\.js$/, exclude: /(node_modules)/, use: scriptLoader },
{ test: /\.css$/, use: styleLoader }
]
},
plugins: [
// 開發環境和生產環境二者均需要的外掛
new HtmlWebpackPlugin({
filename: "index.html",
template: path.resolve(__dirname, "..", "index.html"),
chunks: ["app"],
minify: {
collapseWhitespace: true
}
}),
new webpack.ProvidePlugin({ $: "jquery" })
]
};
};
module.exports = env => {
let config = env === "production" ? productionConfig : developmentConfig;
return merge(generateConfig(env), config);
};
5.2 編寫開發環境配置檔案
// /build/webpack.dev.conf.js
const webpack = require("webpack");
const path = require("path");
module.exports = {
mode: "development",
devtool: "source-map",
devServer: {
contentBase: path.join(__dirname, "../dist/"),
port: 8000,
hot: true,
overlay: true,
proxy: {
"/comments": {
target: "https://m.weibo.cn",
changeOrigin: true,
logLevel: "debug",
headers: {
Cookie: ""
}
}
},
historyApiFallback: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin()
]
};
5.3 編寫生產環境配置檔案
// /build/webpack.comm.conf.js
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const path = require("path");
module.exports = {
mode: "production",
plugins: [
new ExtractTextPlugin({
filename: "[name].min.css",
allChunks: false // 只包括初始化css, 不包括非同步載入的CSS
}),
new CleanWebpackPlugin(["dist"], {
root: path.resolve(__dirname, "../"),
verbose: true
})
]
};
6. 其他檔案
在專案目錄截圖中展示的樣式檔案,vendor 下的檔案還有 app.js,程式碼就不一一列出了。完全可以根據自己的需要,寫一些簡單的程式碼,然後執行一下。畢竟前面的配置檔案的架構和講解才是最重要的。
這裡僅僅給出原始碼地址(歡迎 Star 哦):
7. 執行效果和測試
鼓搗這麼半天,肯定要測試下,要不怎麼才能知道正確性(這才是另人激動的一步啦啦啦)。
7.1 跑起來:開發模式
進入專案目錄,執行npm run dev
:
成功跑起來,沒出錯(廢話,都是被除錯了好多次了哈哈哈)。
開啟瀏覽器的控制檯看一下:
很好,都是按照編寫的app.js
的邏輯輸出的。
7.2 跑起來:生產模式
按Ctrl+C
退出開發模式後,執行npm run build
,如下圖打包成功:
打包後的檔案也放在了指定的位置:
直接點選index.html
,並且開啟瀏覽器控制檯:
ok, 符合app.js
的輸出:成功辨識了是否是開發環境!!!
8. 最終
完結撒花 ✿✿ ヽ(°▽°)ノ ✿