為 Web Component 載入外部 CSS:Fetch API 與效能優化
上一篇啟用了 Shadow DOM,讓元件的樣式不再洩漏到外部。但 Shadow DOM 同時也意味著外部的 CSS 不會自動套用進來,元件需要自己載入樣式。這篇說明如何用 Fetch API 載入外部 CSS 檔案並注入 Shadow DOM。
為什麼需要另外寫一個函式來載入 CSS
你可能直覺想在 constructor 裡直接用 await fetch(...) 載入 CSS,但 constructor 不能是 async 函式。解決方式是在 constructor 內建立一個獨立的非同步函式並呼叫它:
javascript
class MenuPage extends HTMLElement {
constructor() {
super();
this.root = this.attachShadow({ mode: "open" });
const loadCSS = async () => {
const request = await fetch("components/MenuPage.css");
const css = await request.text();
const styles = document.createElement("style");
styles.textContent = css;
this.root.appendChild(styles);
};
loadCSS();
}
connectedCallback() {
const template = document.getElementById("menu-page-template");
const content = template.content.cloneNode(true);
this.root.appendChild(content);
}
}幾個重點:
fetch回傳的是 HTTP 回應物件,要取得 CSS 文字內容需要呼叫.text(),它同樣回傳 Promise,所以也要await- 不是用
.json(),因為這次讀的是純文字,不是 JSON - 建立一個
<style>元素,將 CSS 文字設定為它的textContent,再把這個<style>附加到 Shadow Root
這樣的方式確實有點囉嗦,但它能讓你把每個元件的 CSS 保持在獨立的外部檔案中,與 JavaScript 分開管理。
每個元件都要重複這段程式碼嗎?
如果有多個 Web Component 都用這個方式載入 CSS,直覺上會讓人覺得重複。這正是建立基礎類別的好時機,讓它封裝這段 CSS 載入邏輯,其他元件繼承它就可以了。這是 OOP 繼承在這個場景下的典型應用。
效能考量:prefetch 和 preload
用 Fetch API 載入 CSS 的問題之一是:CSS 檔案要到元件初始化時才開始下載,可能稍微晚了一些,有時會造成短暫的無樣式閃爍。
如果想提前讓瀏覽器開始下載,可以在 HTML 的 <head> 中加入 prefetch 或 preload 提示:
html
<link rel="prefetch" href="components/DetailsPage.css" as="style">這告訴瀏覽器:「這個檔案之後會用到,有空的時候先下載。」等到 Web Component 真的透過 Fetch 請求它時,檔案已經在快取裡了。這個步驟不是必須的,但在意效能的情況下可以考慮加入。
複習
如何用 JavaScript 為 Web Component 載入外部 CSS 檔案?
使用 Fetch API 取得 CSS 檔案,建立一個 style 元素,將載入的 CSS 設定為它的文字內容,再用 appendChild() 將這個 style 元素附加到 Shadow DOM。
為什麼需要建立獨立的函式來載入 CSS,而不是直接在 constructor 中處理?
因為 constructor 不能是 async 函式,所以需要建立一個獨立的 async 函式,才能在其中使用 await 搭配 Fetch API 載入外部 CSS。
使用 Fetch API 載入 CSS 檔案時,哪個方法用來取得檔案的文字內容?
使用回應物件上的 .text() 方法,它回傳一個帶有檔案文字內容的 Promise。
如何改善 Web Component 載入 CSS 的效能?
使用 link rel 搭配 prefetch 或 preload,讓瀏覽器在 Web Component 實際需要之前就提前開始下載 CSS 檔案。
處理多個 Web Component 的 CSS 載入時,有什麼建議的做法?
建立一個實作了 CSS 載入邏輯的基礎類別,讓其他 Web Component 繼承它,避免在每個元件中重複相同的載入程式碼。
小測驗
如何用 JavaScript 為 Web Component 載入外部 CSS 檔案?
使用 Fetch API 搭配 async 函式取得 CSS 文字內容哪個方法用來將已載入的 CSS 附加到 Web Component 的 Shadow DOM?
this.root.appendChild(styles)為什麼要為 Web Component 的 CSS 載入建立基礎類別?
為了減少各元件之間的重複程式碼如何改善 CSS 檔案的載入效能?
使用 link rel 搭配 prefetch 或 preload在 fetch CSS 檔案時,使用 await 需要什麼前提?
建立一個 async 函式此文章是 FrontendMasters 上的 Vanilla JS: You Might Not Need a Framework 課程筆記
