將Webpack套用至現存的專案
30 Jun 2020網路上有不少手把手一步一步把Webpack專案建構起來的教學,但如果你是想要把手邊亂七八糟的專案導入 Webpack 呢?
0. 前言
webpack 有不少雷來自於版本的差異,如果你上網估狗做法之後剪剪貼貼,很有可能會因為版本更新了而無法使用。所以這件事情寫在最前面,以下分享的內容基於webpack 4.43.0
,並預設您已裝好node.js
以及npm
,且具備基礎知識。
為什麼我要導入 Webpack
簡單提一下我的原始專案,他是一隻單一一個 html 檔的專案,不過他有好幾隻的 js 檔,為了對應不同的業務需求有時候要載入a.js
、b.js
,有時候卻載入a.js
跟c.js
,每次版本切換就要把某幾行註解掉,另外幾行取消註解,html 檔本身也有幾行要修改,久了就覺得我應該要用工具做掉這件事情,於是找上了 Webpack。
如果你是為了類似的需求才找到這篇文章,那應該會有點幫助。
1. 開新專案
先開個新資料夾再慢慢把檔案搬進來吧。
開好之後 cd 進去,然後開始初始化
$ npm init
$ npm i -D webpack webpack-cli
然後在根目錄新增幾個資料夾,以下這些資料夾&檔案命名都隨便,但有些命名習慣可以遵守,不吃虧。
./src/ //放程式碼
./static/ //放靜態資源,例如不想被打包的library,或者圖片檔之類的
./dist/ //放建置完成的結果(可以拿去部署)
接著新增兩個檔案
./index.js //程式的進入點
./webpack.conf.js //webpack的設定檔
然後將建置用的指令放到package.json
裡
"scripts": {
"build": "webpack --config webpack.conf.js",
}
這樣,只要執行npm run build
就會建置了 (但你現在執行會報錯就是,畢竟設定檔是空的)
所以來編輯一下webpack.conf.js
吧
const path = require("path");
module.exports = {
entry: "./index.js",
output: {
filename: "output.js",
path: path.resolve(__dirname, "dist"),
},
};
這時候再建置一次,你的 dist 資料夾內就應該有個output.js
檔,這樣就算是會動了。
2. 撰寫index.js
整理一堆 js 檔成為一個樹狀結構是這邊主要要做的事情。
由於有點複雜,我們先講結論:
- 梳理樹狀結構,找到相依樹的根當成
index.js
- 其他被依賴的
js
檔模組(module
)化 - 掛載會直接被 html 呼叫的程式到全域變數
梳理結構
原始的 html 可能長這樣:
<head>
<script src="a.js"></script>
<script src="b.js"></script>
<script src="c.js"></script>
</head>
你要自行梳理出例如a
才是主程式,並依賴於b
跟c
這樣的關係
這樣的例子中,你就可以把a.js
的內容移植到index.js
內
於是現在變成這樣:
./index.js //原本的a.js
./src/b.js
./src/c.js
模組化
原本的b.js
檔可能是扁平的
const A = 0;
function getA() {
return A;
}
反正這樣掛載到 html 上可以直接呼叫getA()
使用,但透過 webpack 注入的話其他模組是無法呼叫到這些方法與變數的,所以要使用關鍵字export
對外部公開
//依然無法被外部存取
const A = 0;
//可以被外部存取
export function getA() {
return A;
}
這時候可以在index.js
透過以下方法使用
import { getA } from "./src/b.js";
console.log(getA()); //0
掛載全域變數
上述是模組的部分,但如果你的程式碼是會直接被 html 使用的呢?例如這樣
<form onsubmit="start(); return false"></form>
function start() {
// do something...
}
這時候你可以把方法掛載到全域變數解決問題
window.start = function () {
// do something...
};
3. 設定插件
搞定 javascript 的部分,那其他的檔案呢?
使用HtmlWebpackPlugin
導入 html
首先要有一個概念是 webpack 是以打包 js 檔為主的工具,所以你要引入任何不是 js 檔的東西都要額外裝插件,這跟我們單純在寫網頁,是把各種東西引入到 html 檔內的思維不太一樣。
安裝
$ npm i -D html-webpack-plugin
然後在webpack.conf.js
啟用插件
const HtmlWebpackPlugin = require("html-webpack-plugin");
module.exports = {
entry: "./index.js",
output: {
filename: "output.js",
path: path.resolve(__dirname, "dist"),
},
plugins: [
new HtmlWebpackPlugin({
filename: "index.html",
template: "./src/template/index.html",
inject: true,
}),
],
};
其中filename
是輸出時的檔案名稱,而template
則是你的原始 html 檔。
別忘了把你的 html 檔要被動態載入的資源清乾淨,包括js
跟css
,所有動態載入的東西最後會被自動放到body
末端,像這樣:
<html>
<head></head>
<body>
...
<!-- 這行不用自己寫 -->
<script src="output.js"></script>
</body>
</html>
也就是說,不希望動態載入的東西請留在 html 內
使用CopyWebpackPlugin
複製靜態資源
前面提到不希望動態載入的東西要維持原狀,這些東西可以借助這個套件在建置的時候自動複製到/dist
資料夾內
安裝
$ npm i -D copy-webpack-plugin
啟用
const CopyWebpackPlugin = require('copy-webpack-plugin');
//上半省略
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/template/index.html',
inject: true
}),
new CopyWebpackPlugin({
patterns: [
{ from: './static', to: path.resolve(__dirname, 'dist') }
],
})
],
參數應該很好懂,從./static
複製到./dist
使用SCSS
這年頭應該沒有人直接寫 css 檔吧?地圖砲
安裝相依
$ npm i -D node-sass sass-loader style-loader
啟用
module.exports = {
//前面同上省略
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: "style-loader" },
{
loader: "css-loader",
options: { modules: true },
},
{ loader: "sass-loader" },
],
},
{
test: /\.s[ac]ss$/i,
use: ["style-loader", "css-loader", "sass-loader"],
},
],
},
};
就這樣,然後就可以直接在index.js
引入你的scss
檔了
import "./src/style.scss";
4. 其他可以考慮設定的東西
路徑別名
可以隨便指定一個符號當成根目錄,這樣可以解決惱人的相對路徑問題
module.exports = {
resolve: {
extensions: [".js", ".json"],
alias: {
"@": path.join(__dirname, "/src"),
},
},
};
程式碼壓縮
網路上可能會查到UglifyJsPlugin
之類的工具,但這東西已經被 webpack 棄用了,要改用以下方法:
安裝
$ npm i -D terser-webpack-plugin
設定
const TerserPlugin = require("terser-webpack-plugin");
module.exports = {
optimization: {
minimize: true,
minimizer: [new TerserPlugin()],
},
};
詳細設定: TerserWebpackPlugin
開發用伺服器dev-server
如果你不想改一行就要 build 一次,你會需要這東西
安裝
$ npm i -D webpack-dev-server
然後將的指令放到package.json
裡
"scripts": {
"build": "webpack --config webpack.conf.js",
"dev": "webpack-dev-server --inline --progress --config webpack.conf.js",
}
這樣就可以透過npm run dev
開啟一個開發用伺服器預覽效果,並支援 hot reload
後記
上面做了一大堆,其實我的原始需求: 根據業務需求自動載入我需要的某幾隻程式都還沒做 XD
這我最後是透過分別寫兩個webpack.conf.js
檔並分開建置完成的
$ webpack --config webpack.a.conf.js
$ webpack --config webpack.b.conf.js
過去寫過好幾個用vue-cli
建立起來的專案,雖然知道其是建立在 webpack 的基礎上完成,但實際從頭開一個 webpack 的專案才知道原來這麼多東西要設定…
因為我自己也還在一邊寫一邊學,以上內容如果有錯歡迎告知一下,謝謝:D