課程資源:
線上課程講義


webpack 的介紹

webpack 將前端多套工具整合

早期的網頁前端以 JS, CSS, HTML 三大部份再加上 jQuery 為主,雖著時間正技術不斷的進步,現在的前端發展出許多的開發工具與技術。

但在這些的開發工具與技術無法直接於瀏覽器使用,瀏覽器也還是以 JS, CSS, HTML 三大部份為主。

這時就需要透過 webpack 自動化工具來處理相關的前端技術,將相關的前端資源例如框架、typescript、scss、等等….,最後處理成 HTML, CSS, JS, 給瀏覽器使用。
另外就是會有許多的獨立工具做出單一的編譯處理,例如 scss 就一套、react 就又另外一套、壓圖片時又使用另外的工具,要開發一個專案就可能用到多套的工具處理編譯或轉換。
而以 webpack 來說就是直接一套重頭到尾處理完成。

多支 .js 連結檔所產生的問題

此外 webpack 也很合用於多人團隊開發,如果一個專案中會用到許多的前端套件,像是下方的圖片以一套工具或是一個 JavaScript 的模塊功能,就開出一支 .js 檔。

.js 做拆分出一個連結檔,如果在大型的項目中會很難維護,另外也可能其他的產生的問題,例如:

  1. 在 JavaScript 的全域下因為使用過多的套件產生衝突。
  2. 戴入 .js 多支檔多連結點,在戴入時是非同步處理,不是以 HTML 的結構一筆筆的載下,像是如果要先執行 index.js 中的程式碼,但因為非同步載入的問是還沒將 index.js 載下後,就先將下方以載完的 .js 的 JS 程式碼先做運行,因此在瀏灠器中可能就會回報錯誤訊息。

依多筆的連結處理拆分 .js 的部份,會很難確任 JavaScript 的處理流程是如何執行,比較好的做法是將所有的 JavaScript 程式碼處理裡進一支 .js 檔中,確保運作的結構流程是正確的。

透過 webpack 模組化 .js 檔

而透過 webpack 處理後,就只會留下 vendor.jsindex.js 二支的 .js 檔。

vendor.js 是將所用到的前端第三方插件整合在一起。
index.js 是用來將模塊化或是自行撰寫的 JavaScript 程式碼,整合到一支檔案中成為一個進入點,就可以準確控制程式的流程。


現在前端與 webpack 的流程

webpack 在 node.js 的開發環境

webpack 本身就是在 node.js 的環境下所執行的自動化工具,所以在開發前端的話也都需要用到 node.js

NVM 管理 node.js 版本

在 node.js 的官網有提供 LTS (稱定版) 與 Current (先行版) 的特定版號,如果在專案中所使用的 node.js 是較舊版 (低版) 就需要使用到特定版本號的 node.js,透過 NVM 管理 node.js 就可以處理管理、安裝、在同一個專案或是同一台電腦中快速切換 node.js 的版本。

NVN 如果是 windows 系統的話要前往 GitHub – coreybutler / nvm-windows > Release 頁面找到 nvm-setup.zip 下載檔案,將安裝檔下載後直接安裝。

更多PC 安裝 NVM 說明可觀看 youtube-SASS超入門與RWD 影片

nvm (version 1.1.9. for windows 10 以後版本) 指定安裝版本的命令已經改為。

<version> 為 node.js 版本
[arch] 為填入 64 或 32 (位元)

使用 nvm install latest 64 可安裝 node.js 最新版本,使用 nvm install 12.0 32 安裝 node.js 12.0 32-bits 版本。

nvm 指令時最好使用系統管理員身份下指令,在命令提示字元上按 shift 鍵可選。

在 windows OS 裡開啟命令提示視窗,輸入 nvm 後 Enter 執行,此時會回應 nvm 的版本號,另外還有其他 nvm 相關使用指令,裡面可以查到 available 指令。

輸入 nvm list available 指令,會列出目前線上的可下載的 node.js 版本列表。

要下載 node.js 的穩定版本 (LTS),例如下戴的是 8.12.0 的版本後,使用指令 nvm install 8.12.0 輸入 Enter 後,就會自動下戴 node.js 對應的版本。

透過 NVM 下戴 node.js 要的版本完成後,此時還沒有指定使用 node.js,輸入指令 nvm list 查看在本機端有的 node.js 版本號列出,其中前面的 * 星號就是目前本機使用的版本。

切換到指定的 node.js 版本,使用 nvm use 8.12.0 指定切換使用的 node.js 版本,在使用 nvm list 就可以看到星號換過去了。

npm 前端套件管理

以第三方插件、工具、或是 webpack 都是透過 npm 進行下載,處理已被模組化的第三方套件與工具為主。

透過 webpack 將專案中的相關開發工具或是語言進行編譯,例如 scss, vue, pug, typescript …等等,處理成瀏覽器可取的檔案。


webpack 自動化工具使用 babel 處理 JavaScript

webpack 設定檔的建立

進入到 webpack 英文官網 https://webpack.js.org/ 查看文件。

目前官方已發表 webpack 5 的版本,所連過去的文件也是 5 版的,如果要看 webpack 4 的文件 英文點此中文點按此

在官網入口的下方有提到 Bundle It (使用 webpack 打包),其中有說到使用 webpack.config.js 的設定檔來撰寫 webpack 的設定。

在專案資料夾中的根目錄下建立一支 webpack.config.js 的 webpack 設定檔,將官網上所列的下方程式碼貼入設定檔後存檔。

在現在前端開發多使用模組化開發,webpack 也是如此處理,在 module.exports 的部份就是將 webpack 的設定模組,以模組匯出來使用設定檔 webpack.config.js

npm 安裝 webpack 模組檔進入專案

看到 webpack 文件中的 Get Started > Basic Setup立即开始 > 基本安装,裡面有提到下面的的安裝指令。

npm init -y 指的是建新 npm 的設定檔記錄下 webpack 工具套件的相關設定。
npm install webpack webpack-cli --save-dev 是將 webpackwebpack-cli 存於開發模式下使用。

在 VSCode 中開啟終端機面版,輸入 npm init -y 在專案中建立 npm 的記錄檔,也就是會多出 package.json 的檔案出來。

接著輸入 npm install webpack webpack-cli --save-dev 指令安裝 webpack 與 webpack-cli 的工具套件。
webpack-cli 是在 webpack 4 才新增的相依套件,如果升級時一定要安裝 webpack-cli。

NPM 的官網查看 webpack 的版本號列表,影片中所使用的是 4.22.0 版的 webpack 與 3.1.2 的 webpack-cli。

--save-dev 的部份會在 package.json 的檔案裡,加入 "devDependencies": {} 並將 webpack 與 webpack-cli 的版號列入。
在下戴完 npm 的工具套件後,也會在專案資料夾中出現 node_modules 資料夾,會將所有相關的相依套件或是套件內容存於此資料夾中。

設定 webpack 的專案資料夾中的 entry (進入點) 與 output (輸出目地)

entry 指的是一開始的進入點,讓所有的 .js 相關檔案都是由進入點 index.js 這支檔所進入,而這支檔中寫的就是原始碼或是高版本的 JS,例如 ES6, ES7 … 等等。

在專案資料夾中建立 src 資料夾與在裡面建立出一支 index.js 檔。

設定輸出檔的部份可以到 filename: 指定檔路與路徑,影片是將 'bundle.js' 改成 'index.bundle.js'

針對 output (輸出) 中的 path: path.resolve(__dirname, 'dist'),,是透過 node.js 的模組透過語法 require() 來使用,其中有一個 'path' 功能,都是在處理路徑轉換的工具,用來處理路徑轉換。

node.js 中的 require('path') 說明文件

.resolve() 是用將相對路徑轉成絕對路徑的方法。

執行 webpack

在影片的範例中是直接在終端機輸入指令 webpack 就可以在專案中執行 webpack 自動化編譯處理,另外在專案資料夾中多了一個 dest 的資料夾。

新版本的 webpack 無法在終端機上直接輸入指令 webpack 直接運行,需要過透後面的自定指令加上 npm run xxx 執行 webpack 自動化腳本。

dest 資料夾中有新產生出一支 index.bundle.js 檔,比照 webpack 的設定檔中的檔案命名。

打開 index.bundle.js 檔可以看到 webpack 把 JavaScript 處理,主要處理的部份約有變數名稱重覆、全域變數、相容性…等等,就算是注入點的 src/index.js 沒有寫相關的程式碼,webpack 預設就會做以上的處理,以第三方套件或是模組化來說也是如此。

直接在 webpack 的 index.js 注入點,撰寫 JavaScript ES6 不會做相容性降轉處理

接著在 index.js 輸入 arr 的陣列資料,並透過 ES6 的陣列方法一筆筆的印出。

加入程式碼內容後,接著到終端機上輸入指令 webpack 執行 webpack 編譯處理。
執行後的 index.bundle.js 檔,開啟後看到陣列方法也還是以 ES6 的 .map() 中的函式也還是箭頭函式,並沒有將輸出的部份做相容性降轉處理。

這樣的處理,可以了解到 webpack 本身只會做編譯處理而並不會處理 JavaScript 的相容降轉。

掛載 babel-loader 到 webpack,處理 JavaScript 相容降轉

GitHub babel-loader 官方頁面。
babel-loader 主要是讓 webpack 用來識別所寫的內容後,將 JavaScript 進行相容轉換的處理。

在 Babel Loader 文件中提到對應的版本是 webpack 4.x ,所以直接將裡面的 npm 安裝指令輸入到終端機中。

指令中的 -D 參數指的是 --save-dev 的縮寫,指令中如果沒有帶這個參數設定,就不會將前端套件寫入到 npm 的 package.json 檔中存下設定。

babel 的介紹與使用於 webpack

babel 的官方頁面
bable 主要是用來將高版的 JavaScript 做相容性降轉處理,可以透過 babel 官網的入口動畫了解。

在 babel-loder 下戴完成到 webpack 專案中,接著就可以將 GitHub 文件中將設定檔,貼入到 webpack.config.js 中,在 output: {} 的下方加入以下設定內容,並用 , 隔開。

加入內容並存檔後,在終端機在次下指令 webpack 執行 JavaScript 的編譯,執行後會將 index.bundle.js 檔做重新編譯,再次查看到檔案中的 .map() 陣列方法,裡面原本的 箭頭函式就會轉處理成傳統函式,箭頭函式自帶 return 的方式也加入到傳統函式中。

在專案資料夾的根目錄下,加入一支 index.html 的檔案,並在裡面加入 HTML 的結構,其中在 <body></body> 的中間,加上 <script src="./dist/index.bundle.js"></script>


NPM 設定 webpack 指令

NPM 的 package.json 設定檔中, "scripts": {} 裡面加入 webpack 的使用指令,讓 NPM 指令可以直接下達執行 webpack 環境指令。

自定 NPM 行指令讓 webpack 運作不同的開發環境與編譯動作

"watch""start""deploy" 後方的值前面都有 webpack,透過 NPM 指令來達行 webpack。
"watch":不中斷執行。
"start":單純執行一次。
"deploy":上線前的打包編譯執行。

–mode development 與 –mode production

開發時主要有分 development 與 production 版本,
production 版本主要是針對上線的壓縮與去除註解等等的處理。
development 版本不處理壓縮與去除註解,方便用於開發時。

直接在終端機上輸入 webpack 的執行指令,裡面會出現黃色的字,裡面是說明沒有指定針對運行模式 (就是 --mode 後方接的 developmentproduction)。
如果沒有指定就會出現黃色提示的內容,並將編譯的轉出以 production 的編譯模式進行輸出。

如果直接在終端機上輸入 webpack --mode development 指令,執行後就不會在出現黃色的提示內容。

打開 index.bundle.js 檔,就可以看到所編譯出來的檔案並沒處理成壓縮單行。

webpack --mode development --watch 指令是針前先前只執行一次編譯進行監視。
在透過 webpack 的 --watch 方法就可以讓執行緒不中斷,

將原本的 const arr 陣列資料做出修改的動作,修改後存檔的同時就會在進行一次編譯動作。

如果要中斷執行透過組合鍵 ctrl + c 就可中斷監視。

"webpack --mode development --watch" 為何不直接以 "webpack --mode production --watch" 的方式運行?
如果將編譯模式都段 production 的方式處理,期中會有處理壓縮與過濾的動作,又由其是處理圖片會變的更久,這會讓效能與處理效率降低。

執行 NPM 自定名稱指令後,指定運行 webpack 編譯模式與動作

NPM 有針對 webpack 設定執行名稱 "watch""start""deploy"
如果要執行 webpack 的編譯模式與動作,只要輸入 npm run << webpack 編譯模式自定名稱 >>
例如輸入 npm run watch,當下指令後也會執行 webpack --mode development --watch 的指令。


什麼是模組化

像是先前提的 HTML 下的 .js 檔的結構來說,有 index.jslogin.jsmenu.js 的 JS 拆分檔。

專案中原本就有 index.js 檔,接著在加入 menu.jslogin.js 檔。

見到 webpack 的官網,在中間的地方分別有 src/index.jssrcbar.js 檔。
右方的 src/bar.js 的 JavaScript 語法 export default function bar() {} 就是模組化輸出。

export default 以模組匯出程式碼,包成一個模塊檔案

export default 是 JavaScript 模組輸出的語法,後面接的是字串所包出去的就會是字串,但如果後方接著是一支具句函式,那匯出就會是以一支函式。
menu.js 中先帶入一支 function menu() {} 匯出模組,而裡面的函式沒有內容。

import 匯入外部模塊檔,指定於一個變數使用

接著到 index.js 裡的前方加入 import Menu from "。/menu.js",將外部的 menu.js 檔案引入 index.js 中使用,會先以 Menu 的變數名稱在 index.js 中使用,直接透過 console.log() 查看 Menu 的內容。

export default 模塊檔案,所帶出的資料或內容是什麼,import 匯入輸出就會相對呈現內容

使用瀏覽器查看後,就可以看到 index.html 中所引入的 ./dist/index.bundle.js 檔,裡面出現的是 Menu(){} 一支函式。

如果將 export default 後方接的函式,改成一個字串輸出,最後所呈現的也會是字串。

export default 的函式執行內容,將 index.js 查看 Menu 變數內容,改成 Menu(); 執直接行函式 (調用函式)。

像是如果是在執行事件 window.onload 觸發函式,就會使用到匯入的模塊函式 Menu,瀏覽器最後就會帶出模塊函式的運行結果。

相對的 login.js 也比照上面的處理方式,以模塊匯出後由 index.js 匯入使用。


透過模組化的處理,就可以避免 .js 多支載入非同步的所產生錯誤問題。


其他參考資料