不是吧?強(qiáng)大的 Vite 居然不支持內(nèi) SVG 轉(zhuǎn) Base64 內(nèi)嵌?
大家好,我是前端西瓜哥。
誒喲喂,SVG 怎么沒內(nèi)嵌?
最近啊,西瓜哥我用 vite 去給一個(gè)項(xiàng)目構(gòu)建(vite build)一個(gè)應(yīng)用。打包結(jié)果是一個(gè) html 和一些加了哈希的資源。
然后打包出來的文件一看,發(fā)現(xiàn)居然有好幾個(gè) 1 Kb 以下的 SVG 文件。
我搜了下源碼,這些 SVG 是這樣被使用的:
<img src="./image/somIcon.svg">
不對呀,理論上小于 4 Kb 的靜態(tài)資源,是會轉(zhuǎn)成 base64 編碼字符串,嵌入到其他資源中。
較小的資源體積小于 assetsInlineLimit 選項(xiàng)值 則會被內(nèi)聯(lián)為 base64 data URL。
build.assetsInlineLimit 默認(rèn)值為 4096 (4kb)。
我發(fā)現(xiàn)使用庫模式(打包成 index.es.js,使用該模式需要設(shè)置 build.lib 配置)時(shí),是不會出現(xiàn) SVG 文件的。
如果你指定了 build.lib,那么 build.assetsInlineLimit 將被忽略,無論文件大小或是否為 Git LFS 占位符,資源都會被內(nèi)聯(lián)。
所以一開始我以為我的配置設(shè)置的有問題,因?yàn)閹炷J經(jīng)]問題了。
我折騰了大半天,檢查配置,查文檔,assetsInlineLimit 給你加到 999999,安裝其他版本的包,給引入的文件末尾加上 ?inline。各種嘗試,都沒用。
后來我用最新版的 vite 構(gòu)建了一個(gè)新的 Vue 項(xiàng)目。
發(fā)現(xiàn)它這個(gè)官方給的 demo 打包出來的文件 SVG 都沒做內(nèi)聯(lián)。
好家伙,我尋思 vite 本身就不支持 SVG 轉(zhuǎn) base64 編碼內(nèi)嵌。
設(shè)計(jì)如此,本就不支持 SVG 轉(zhuǎn) Base64
走,去翻翻 vite 的 issue,然后找到了一個(gè) 3 年前的 issue,編號 1204。
這個(gè) issue 標(biāo)記為 enhancement,即它是一個(gè)增強(qiáng)功能,并不是 bug。
此外可以看到有兩個(gè) PR 是要解決這個(gè) issue 的。
一個(gè) PR 被關(guān)閉了,一個(gè) PR 是打開著。我們?nèi)タ纯础?/p>
先看看被關(guān)閉的那個(gè),PR 編號是 1716,是 vite 成員提的 PR。
他說他不贊成 SVG 轉(zhuǎn)成 Base64 嵌入到 HTML,SVG 是個(gè)文本類的特殊圖片格式,不是二進(jìn)制,沒必要再轉(zhuǎn)一層 Base64,導(dǎo)致體積變大。
因?yàn)?Base64 需要用 4 個(gè)字符表達(dá)原來文本的 3 個(gè)字節(jié),會增大 33~36% 的體積。
即希望結(jié)果是:
<img src='data:image/svg+xml;utf8,<svg ... > ... </svg>'>
而不是:
<img src="data:image/svg+xml;base64,PHN2ZyB4bWxu...c3ZnPg==">
雖然不打算轉(zhuǎn) Base64,但還是可以轉(zhuǎn) utf8 格式的 Data URL 的,一樣可以實(shí)現(xiàn)內(nèi)嵌的效果,而且體積更小,于是提了這個(gè) PR。
這個(gè) PR 是有問題的,有些情況沒處理,比如轉(zhuǎn)義,去掉 svg fragments 等。
然而過了一個(gè)月,這個(gè) PR 還是沒進(jìn)展,說明 優(yōu)先級并不高。
此時(shí)一位路過的野生程序員說他來搭把手。
于是這個(gè) PR 關(guān)閉了,這位老哥創(chuàng)建了一個(gè)新的 PR。
這個(gè) PR 一直就掛在那
加油啊,路過的程序員老哥。你是我們?nèi)迦说南M ?/p>
看看 PR。
兩年前的 PR,至今仍是 Open 狀態(tài)。
哦豁,涼了。
發(fā)生甚么事了?我瞅瞅。
看下 PR 的內(nèi)容。引入了一個(gè) mini-svg-data-uri 第三方包,來做 SVG 轉(zhuǎn) DataUrl,改了一些判斷條件,因?yàn)楹推胀ㄙY源直接走轉(zhuǎn) base64 不同,SVG 是要直接用原來的文本內(nèi)容的。改了了幾個(gè)測試用例。
看著不少 review 和討論,看著應(yīng)該還行,有幾個(gè) vite 成員 approved 了。
最后時(shí)需要有人手動測試是否處理好了 SVG Segment 的情況。此時(shí)提 PR 的老哥不見了。
SVG Segment 是 SVG 的一個(gè)比較特殊的用法,大概是這樣(來自 MDN)。
對于一個(gè) SVG,我們可以用 view 標(biāo)簽指定某種情況下特定的 viewBox 視口范圍。
<svg viewBox="0 0 300 100" width="300" height="100"
xmlns="http://www.w3.org/2000/svg">
<view id="one" viewBox="0 0 100 100" />
<circle cx="50" cy="50" r="40" fill="red" />
<view id="two" viewBox="100 0 100 100" />
<circle cx="150" cy="50" r="40" fill="green" />
<view id="three" viewBox="200 0 100 100" />
<circle cx="250" cy="50" r="40" fill="blue" />
</svg>
然后在使用的時(shí)候通過 icon.svg#<id> 指定 id,來修改 SVG 最終展示的 viewBox。
<!-- 正常視口 -->
<img src="example.svg" alt="three circles" width="300" height="100" />
<!-- 視口切換到綠色圓的位置 -->
<img src="example.svg#three" alt="blue circle" width="100" height="100" />
效果:
回到 PR。
好久,后面有人幫著測試了,發(fā)現(xiàn)有問題。然后就,沒有然后了,此外還有因?yàn)殚L期沒合入出現(xiàn)的合并沖突。
湊合著用吧
啊這。
我 fork 了 vite 項(xiàng)目,把這位老哥的修改應(yīng)用到最新版本上,然后 build 了一下,并拿去構(gòu)建我的一個(gè) demo 項(xiàng)目,結(jié)果 SVG 成功變成了 Base64。
然后我運(yùn)行測試相關(guān)的命令,各種不對。
因?yàn)橛行┰瓉磙D(zhuǎn)換為正常 url 的,現(xiàn)在會轉(zhuǎn)成 base64,就匹配不上了。我還發(fā)現(xiàn) css url 的邏輯還有點(diǎn)問題,拿到了一個(gè)錯(cuò)誤的 none 值。
誒,感覺要提 PR 的話,要修正原來的測試用例,并補(bǔ)充一些新的測試用例,還要處理 css 的情況。行吧,以后再看看。
回到我一開始的需求,行,你不給我轉(zhuǎn) Base64 是吧?我通過 ?raw 直接拿到 SVG 文本內(nèi)容,給你動態(tài)轉(zhuǎn)成 Base64。
import iconSvg from './image/someIcon.svg?raw'; // 這個(gè)會拿到 "<svg ...>...</svg>"
const toSVGDataUrl = (str: string) => {
const base64 = btoa(str);
return `data:image/svg+xml;base64,${base64}`;
};
<img :src="toSVGDataUrl(iconSvg)" />
還行(又不是不能用)。
結(jié)尾
這次經(jīng)歷,我認(rèn)識到 一個(gè)大型的開源項(xiàng)目的維護(hù)者,對單元測試是非??粗氐?,因?yàn)樗绊懼f萬的開發(fā)者,必須保證在絕大多數(shù)情況下能正確運(yùn)行。
突然想起之前 VSCode 我更新了最新版本,結(jié)果運(yùn)行一段時(shí)間就會報(bào)錯(cuò)需要重啟。
然后就是 vite 維護(hù)者 非常注重性能,畢竟是一個(gè)很重要的構(gòu)建工具。SVG 是可以 Base64 的,實(shí)現(xiàn)邏輯也很簡單,和其他圖形走一樣的邏輯。
但 SVG 可以直接用原本的文本數(shù)據(jù),更小,有優(yōu)化空間。因?yàn)轶w積可以優(yōu)化,所以維護(hù)者就寧缺毋濫,寧可丟掉這個(gè)功能。
要是我,我可能就先圖省事,直接支持 Base64 了,然后有機(jī)會再優(yōu)化(通常不了了之)。
然后是優(yōu)先級,優(yōu)先級不高,維護(hù)者就是不會主動去實(shí)現(xiàn)。
此時(shí)就需要社區(qū)的力量了,如果你很需要某個(gè)功能,就要積極提 PR,積極討論并主動推進(jìn),否則可能像這個(gè) PR 一樣,半途而廢。