2022.10.10 更新:新增多檔案編譯功能。
本篇要解決的問題
最近開始用 Vue3 在開發,也看了幾個 Youtube 上有關 Vue3 的教學,雖然官網很推薦 SFC(Single-File Component)的開發方式,但前端工程師們應該都會有個狀況:不是每個案子來,都是可以讓我們自由的開始下一個 npm init vue@latest
後就快快樂樂地開發的,有些時候我們是接手之前的人寫的案子,這些案子如果沒意外,大部份是後端渲染的方式,也就是我們可能會收到 .php、.cshtml、.aspx …… etc.
這時用 SFC 就有點不切實際,但如果我們又要用 Vue 來開發呢?就得用 CDN 的方式,或是把 Vue 給 import 進一個 JS 檔裡。
這時候又會產生另一個問題,就是當我們 import 了 Vue、Pinia,再加上其它我們需要的 JS 檔後,編譯工具的編譯速度會愈來愈慢。
這陣子 August 得用 Windows 開發,改用 Prepros 這套工具開發時,編譯 JS 檔需要到十幾秒,暈,那我整個專案改完,等待的編譯時間加起來都夠我看完一本哈利波特第一集了。
所以囉,有問題就要去解決問題,而不是去解決之前的工程師 XD,就決定做一個可以加快開發速度的初始檔,剛好前陣子看到了 esbuild 這套速度快到飛高高的編譯工具,就拿它來開發時使用。
本篇最後完成的初始檔,共有以下設定:
- Framework:Vue 3、Pinia、Tailwind CSS 3。
- 開發:esbuild
- 打包:rollup.js
之所以打包時不用 esbuild 而用 rollup.js,是因為 esbuild 還不支援將 JS 編譯成 ES5 的版本,為了讓專案可以支援大部份瀏覽器,在打包時還是改用 rollup.js。
之所以開發時不直接用 rollup.js,是因為經 August 人體實驗證明,rollup.js 裡的 rollup-plugin-esbuild 這個套件使用時,編譯速度是可以縮到一秒多,但直接用 esbuild 卻可以不到一秒,在這個超高音速導彈都已經被製作出來的時代,還是愈快愈好。
本篇的初始檔有放上 GitHub,取用前麻煩分享本篇,或是 GitHub 上打個星星,你的小小動作對本站都是大大的鼓勵。
https://github.com/letswritetw/vue3_tailwind3_esbuild_rollup
安裝、使用
從 GitHub 下載整包檔案後,必備的檔案如下:
- dist/
- src/
- .babelrc
- esbuild.config.js
- package.json
- rollup.config.js
- tailwind.config.js
除了以上的資料夾及檔案,其它的都可以刪掉沒關係。
esbuild.config.js、rollup.config.js 這二個檔案裡有寫了輸入、輸出的 JS 檔案路徑,預設如下:
- 輸入:./src/main.js
- 輸出:./dist/main.min.js
Tailwind 的路徑是寫在 package.json 的 dev-tailwind
、build-tailwind
二行裡,預設如下:
- 輸入:./src/tailwind.css
- 輸出:./dist/tailwind.min.css
準備使用時,先輸入指令安裝 package:
$ npm install
或
$ yarn
package.json 裡的 scripts
部份有寫了指令碼。
要 開發 時,編譯 main.js 的指令為:
$ npm run dev-js
或
$ yarn dev-js
編譯 tailwind.css 的指令為:
$ npm run dev-tailwind
或
$ yarn dev-tailwind
要 打包 時,編譯 main.js 的指令為:
$ npm run build-js
或
$ yarn build-js
編譯 tailwind.css 的指令為:
$ npm run build-tailwind
或
$ yarn build-tailwind
也有寫上一次打包 JS 跟 Tailwind 的指令:
$ npm run build
或
$ yarn build
同時打包這邊,有多寫了一個 remove-dist-js
,會先刪掉 ./dist/main.min.js、./dist/main.min.js.map 這二個檔案,不過寫的 command-line 是用 macOS 的,如果你是用 Windows 的電腦就會失敗,把 yarn remove-dist-js
這行刪掉就行。
另外同時打包的指令用的是 yarn
,沒安裝的朋友需事先裝好 Yarn,或是把 build
裡的 yarn
都改成 npm run xxxxx
。
以上,祝大家使用愉快~~
esbuild 開發時通知
開發時使用的 esbuild 裡,August 有加上通知,分別是執行 esbuild 時、有錯誤時。
執行 yarn dev-js
時,會出現一個通知,像這樣:
對,麻煩讓本站偷偷打個小廣告這樣 XD。
如果想要修改通知,就在 esbuild.config.js
這個檔案裡。
除了會跳出訊息通知,在終端機上也可以看到完整的錯誤資訊:
Vue 3、Pinia
本專案用的是 Vue 3 + Pinia,裡面附的程式碼是 Composition API 的寫法,對,因為想在同事們之間炫技就做了這個微錯誤的決定(未來有機會會說明 XD),有時間會加上 Options API 用的範例檔。
多檔案編譯
有時候我們無法像 SPA 一樣只產一支 JS 檔就打遍天下,比如接手的專案很舊了,每個頁面的檔案散落在世界各地。
把四散的 JS 併成一支會很花時間,又想到就算完成也沒辦法算進績效裡時,就會睜隻眼閉隻眼的針對每個頁面去寫一支 JS 檔來進行修改。
對,不用掙扎了,實務上案子會一直來一直來,來到天荒地老,如果公司大大大主管沒有想翻新,整個打掉重練程式的話,需求能怎麼解決就怎麼解決。
即便真的花了時間整併 JS,還不一定每個功能都能正常運作,而且真的也無法寫進績裡,所以能用最簡單的方式解決,就用最簡單的方式吧。
現在假設我們有二個頁面:A.html、B.html,裡面各自引用了 a.js、b.js,總不能在改頁面時,改 A 就手動改一次 entryPoints 的路徑,改 B 時再去手動改一次路徑,一天之內又改 A 又改 B,我跳進去了我又跳出來了,可以這樣嗎?而且這還只是改二個頁面而已。
所以囉,我們的實際需求,esbuild 跟 rollup.js 的神人相信也經歷過,看了二邊的文件,有提供同時編譯多個檔案的說明。
esbuild
esbuild 的多檔編譯比較簡單,因為它的 entryPoints 可以寫成陣列,官方文件 範例:
require('esbuild').buildSync({
entryPoints: ['a.js', 'b.js'],
bundle: true,
write: true,
outdir: 'out',
})
entryPoints
裡寫要編譯的檔案有哪些。
outdir
寫要輸出的資料夾名稱。比方範例寫的是 out
,那最後編譯出的檔案就會是:
./outdir/a.js、./outdir/b.js。
如果想要改變輸出的檔名,那就將 entryPoints
從陣列改為物件:
entryPoints: {
out1: 'a.js',
out2: 'b.js',
},
輸出的檔案就會變成:
./out/out1.js、./out/out2.js
然後,你們知道的,工程師會有工程師的堅持,一般來說,壓縮過的檔案 August 會習慣加上 *.min.js
的檔名,所以要再加工一下,在原本的 outdir
後再加入一行:
outExtension: { '.js': '.min.js' }
這樣輸出的 .js
就會變成 .min.js
了。
最後在 esbuild 這邊要提的是,官方文件的範例用的是 buildSync
,實際開發時會遇到一個錯誤:
Cannot use "watch" with a synchronous build
watch
模式下不能用 buildSync
,因此本專案寫的時候是用 build
。
rollup.js
rollup 的方式稍微麻煩一些些,每編譯一個檔案,就要寫進一個物件裡,所以當有多個檔案要編譯,就要寫多個物件出來,官方文件 範例:
export default [{
input: 'a.js',
output: {
file: 'dist/a.js', format: 'cjs'
}
},
{
input: 'b.js',
output: {
file: 'dist/b2.js', format: 'es'
}
}];
如果有用到 plugins,就每一個物件裡都要寫,所以在寫的時候可以把 plugins 命成一個變數,就不必相同的東西一直重複寫。
.env
本篇最後的檔案,開發時用 esbuild,打包時用 rollup.js,為了避免相同的 input、output 分開在二個檔案裡,就引入了 .env 的方式,把 input、output 寫在 .env 檔中,然後二個檔案再各自引用。
.env 用的是 dotenv 這個 package。
以下是 August 在本專案裡的寫法,要編譯的檔案有二個:
- ./src/main.js
- ./src/main2.js
目標輸出的檔案是:
- ./dist/main.min.js
- ./dist/main2.min.js
.env
ENTRY=./src/main.js,./src/main2.js
OUTPUT=./dist/main.min.js,./dist/main2.min.js
esbuild
require('dotenv').config();
const entry = process.env.ENTRY.split(',');
require('esbuild').build({
entryPoints: entry,
bundle: true,
write: true,
outdir: './dist/',
outExtension: { '.js': '.min.js' }
})
rollup.js
require('dotenv').config();
const plugins = [ ... ];
const entry = process.env.ENTRY.split(',');
const output = process.env.OUTPUT.split(',');
const resultArray = [];
for(let i in entry) {
const item = {
input: entry[i],
plugins,
output: {
file: output[i],
format: 'iife'
}
}
resultArray.push(item);
}
export default resultArray;
Top comments (0)