輕前端筆記 - Vue3 SFC(.vue) 元件打包
3 |
昨天分享透過 vue3-sfc-loader 載入 .vue 檔案的解決方案,讓輕前端也能輕鬆應用 SFC 元件實現氣刀體一致,但仍有美中不足之處。
首先,vue3-sfc-loader 的原理是把 Vue CLI 開發階段用的 SFC 編譯程式搬到瀏覽器端( vue3-sfc-loader.js = Webpack( @vue/compiler-sfc + @babel ) ),因此 vue3-sfc.loader.js 高達 1.37MB,以當代 JavaScript 力求輕薄的標準有點肥大。
第二,.vue 編譯動作將重複在每個瀏覽器每次載入執行一次(所以 vue3-sfc-loader 設計了簡單的 compiledCache 機制),不利網頁效能。
在我的想法裡,開發測試階段以簡單方便優先,上線時則需追求最佳化,才是良好的系統架構。於是我開始研究,有沒有不需要建立 Vue 專案,將單一 .vue 打包成 .js 的做法。
前後花了二十幾個小時爬文跟反覆實驗,大半個勞動節連假跟它拼到底,從中午搞到凌晨,睡沒幾小時想到點子又跳下床開始測試,用了點 Hakcing 手段,終於試出滿意的解法,興奮到尖叫。(好久沒有這麼熱血了)
身為前端菜鳥對 node.js 所知有限,研究到 Vue CLI 內部運作根本在「越級打怪」。幸好,靠著累積多年的實戰經驗及 Debug 技巧,終究還是讓我破解難題。
經過一番搜索,我先找到一個看似完美的 vue-cli-service build --target lib 指令。
以全域方式裝好 @vue/cli-service,只需要 vue-cli-service build --target lib '.\timer.vue'
,Vue CLI 會將 .vue 編譯成 .js 及 .css,還有一個 demo.html,展示在網頁載入 vue.js、timer.umd.js 及 timer.css,並建立 Vue Model 使用元件,這是標準的輕前端做法沒錯,好感動呀。(安裝及執行步驟可參考這篇)
dmeo.html 還可直接執行測試,驗證包出來的 js 跟 css 沒問題,很酷吧!
只是,我馬上發現問題,雖然元件可以動,但 demo.html 載入的是 //unpkg.com/vue@2,不是 Vue3。另一個線索是,必須要安裝 Vue2 專用的 vue-template-compiler 才能產生 .js,所以元件是被編譯成 Vue2 版本。
vue-cli-service 沒提供參數指定 Vue2 或 Vue3,我只能追進原始碼。結果讓我憂喜參半,好消息是 vue-cli-service 可同時支援 Vue2 跟 Vue3,壞消息是採用 Vue2 或 Vue3 是依專案環境自動判斷,單靠一個 .vue 檔無法判別。
再追到上游,cli-service 的 Vue 版本判斷由 @vue\cli-service\lib\util\getVueMajor.js
這段程式決定:
* Get the major Vue version that the user project uses
* @param {string} cwd the user project root
* @returns {2|3}
*/
module.exports = function getVueMajor (cwd) {
const vue = loadModule('vue', cwd)
// TODO: make Vue 3 the default version
const vueMajor = vue ? semver.major(vue.version) : 2
return vueMajor
}
當只有單一 .vue 檔沒有專案相關檔案,取不到 vue 模組時將預設為 2。(有 TODO 註解,所以未來版本預設值將改為 3)
用 vue create hello
建立一個 Vue3 專案,把 .vue 擺進去,再執行 vue-cli-service build --target lib,確實就能判斷為 Vue3,但會變成找不到 vue/compiler-sfc 模組錯誤。
感覺問題出在 Vue CLI 版本更新速度偏慢,對 Vue3 元件編譯的支援還沒到位,故用起來種種不順,不像 Vue2 般行雲流水,理論再等一陣子就會改善。
如果你跟我一樣任性「不管,拎杯現在就要」,可參考以下我找到的 Workaround 解法:(註:包含非正規手法,請自行斟酌)
- 安裝 @vue/cli-service、@vue/compiler-sfc
npm i -g @vue/cli-service npm i -g @vue/compiler-sfc
- 找到
C:\Users\使用者名稱\AppData\Roaming\npm\node_modules\@vue\cli-service\lib\util\getVueMajor.js
,第 11 行原本為 2,改為 3,把 TODO 變成 JUST DO IT (假設該環境以 Vue3 為主)const vueMajor = vue ? semver.major(vue.version) : 3
- vue crate dummy 新建一個 Vue3 專案,找到其中的 node_modules\vue 資料夾
將其複製到C:\Users\使用者名稱\AppData\Roaming\npm\node_modules
目錄下
經過這番魔改,就能用 Vue CLI 將 .vue 快速轉成 Vue3 版 js + css 了,開心!!
[2022-05-02 更新] 讀者 Shu Huan Huang 分享一招,在 .vue 所在目錄執行 npm i vue 產生 node_modules 目錄讓 vue-cli-service 將 .vue 視為 Vue3 版,取代第 2 步修改 getVueMajor.js 的動作,而第 3 步建立 Vue3 專案動作也可用 npm i vue 取代,一樣可得到 node_modules\vue。
There is a issue of Vue3 SFC library building on Vue CLI, this article provide a workaround.
Comments
# by Arthur Chen
我用這種方式打包出來的元件無法使用 props and emit, 只好用ref 存取元件內部function 取代 props. 用mitt 取代 emit.
# by Guest
You cannot use this method to generate .js under latest Vue 3 cli.
# by 小黑
過度熱血