其實樣式這件事情,是不是有動態載入好像不是那麼重要。除非你像是 AMP 那樣嚴格規範你的樣式使用範圍,不然通常我們都是全部打一包丟上去。然後就大家都用那一包了,你看看精美的 Bootstrap ,你會因為沒有使用到什麼設定而把他拿掉嗎?通常不會。
或者是你可以做一個客製化的 Bootstrap ,然後拿去偽裝成 bootstrap.min.css
,你這樣試試看後續接手的人會不會叫你出來,然後保證不打死你?
樣式區塊
我們在 Vue 裡面其實有支援 CSS Modules 的作法:
所以說,我們這樣就能夠在 Template 裡面使用 $style
的隱式變數來讀取樣式資料。而且,在我們的元件裡面,也能使用 this.$style
來讀取樣式的名稱。不過,這些樣式會被加入一個哈希數值,用以避免碰撞問題。
你可以在元件的生命週期當中,讀取到 this.$style
的設定,但是其實在真實世界中,這些東西是不是對我們有任何好處呢?
對於程式邏輯來說,目前看不出來有什麼好處。
為何說沒有?舉例來說:
// 前略 ...
created () {
console.log(this.$style.red)
},
// 後略 ...
如果以我們上面的例子來看,你會拿到一個叫做 red_1C66Z
的字串。他不能代表什麼,他只能代表說我有這個 設定 ,而這個設定僅會來自於 類別選擇器 以及 ID 選擇器 兩種,舉例來說:
<style module>
.red {
color: red;
}
#myid {
color: blue;
}
div.qq {
color: #666;
}
input.qq[name="test"] {
color: #fff;
}
.red.qq {
color: #666;
}
#myid .red {
color: #fff;
}
</style>
最後你只會拿到三組設定:
然後實際上被產生出來的樣式,會依照命名的邏輯來替換掉原本寫好的部分:
用這種 CSS Modules 有一個絕大的好處:
一般用戶永遠猜不透你的樣式叫什麼名字。而且這樣的樣式跟 DOM 結構樹本身幾乎是深度綁定,換句話說,比較不容易透過產生好的程式碼,來猜出你原始邏輯是什麼。換句話說,我也可以透過一些設定,然後把它變成混淆器來使用。
我們用一樣的例子,然後修改 localIdentName
的設定,就能變出這種輸出:
所以說,如果哪天客戶拖款、欠錢、跑路、老闆跳樓、家裡小狗生病或是小孩考不上大學,除了可以不給原始碼之外,編譯好的檔案就這樣給他就好了。如果覺得不夠亂的話,可以把
hash
加大一點,這樣樣式檔案產出也超大,感覺就是很厲害的樣子!
所以說,這個功能壓根跟動態無關,但是也請不要拿來玩弄你的客戶。
動態樣式
我們之前都是再聊元件或是 App 的事情,那麼樣式到底能不能,或者說,適不適合這樣做?在你的網站有做好簡易的讀取進度畫面的狀況下,可以。如果沒有,那麼你的用戶就會在樣式還沒有載入之前,看到你的網站架構裸奔的狀況。
而我們所做的 Hybrid 模式下,其實都已經先將所有元件所產出的相關樣式檔案,先放到 <head>
裡面去了,所以,在正常情況下,他並不會因為樣式載入問題而讓畫面異常。但是,在網站的讀取流程當中,那些樣式其實對於 當下 的畫面,可能是用不到的。所以,是否能讓那些樣式稍微 延後 一點,或是當我的元件真的載入之前,再將樣式加入畫面當中。
所以,我們要做的事情依序會有:
- 得知元件什麼時候載入。
- 元件載入之前是否有樣式需要預載。
- 預載樣式。
相對來說,知道元件什麼時候需要載入應該是比較容易的。例如說,我同一個骨幹要長出不同的外觀,那麼,我從伺服器端就知道我應該要載入哪些元件,所以,我在伺服器端就已經能夠決定,要輸出給前端的 樣式檔案 有哪些。
那麼,這些 樣式檔案 要怎麼個預載呢?我們可以透過 JavaScript 來完成:
<html>
<head>
<title>CSS Loading</title>
<noscript>
<link rel="stylesheet" href="/static/css/component.css">
</noscript>
</head>
<body>
<script>
(function() {
var s = document.createElement('link');
// 以下略...
// 這種方式太常見,就不在這邊充版面了。
})();
</script>
</body>
</html>
或許你會說,在 <head>
裡面放 <noscript>
好像哪裡怪怪的。不過根據 HTML5 的規範, <noscript>
在 <head>
當中是可以合法使用的。
所以,我們可以把想要動態載入的樣式先放到 <noscript>
裡面,然後等到真的元件被載入後,再次利用 JavaScript 把樣式放進去。這樣做的好處是,你的樣式檔案只有在需要的時候會被載入,但是請記得,當樣式檔案被載入後,瀏覽器會進行重繪的動作,如果你的動態效果很多的話,要小心效能問題。
所以說,我們在元件裡面具體可以怎麼做?其實,只要在 created
的地方確認一下就可以了。但是,這邊有一個前提,
你的元件應該會有一個獨立被打包出來的樣式檔案。當這個檔案存在時,你才有必要做動態載入這件事情,不然,你的元件其實加了這行等於白加。而且,一般的 Webpack 打包時,會使用哈希去計算一個新的檔案名稱,你必須把這件事情 關閉 。不然,你會無法得知最後的檔案叫什麼名字。
不知道人家的名字,還想叫人家,你是不是想(住口)!
// 前略 ...
beforeCreate () {
let style = window.document.querySelector('link[href="/static/css/component.css"]')
if (style === null) {
style = window.document.createElement('link')
// 中略 ...
// 這邊就用傳統的方式把 link 放到 head 裡面去
// 這樣你就可以在建立元件之前,把 css 給載入
}
}
// 後略 ...
小結
動態載入樣式大概就是這樣,主要的目的是可以防止瀏覽器因為 CSS 樣式載入的關係,而去干涉到第一次的渲染過程。至於說是不是每個專案都適合這樣做,也不一定,例如 SPA 就應該非常不適合(都在同一個頁面了,你還不載入也實在說不過去吧)。