最後一篇,我們回頭來聊聊 樣式
。
樣式,果然是讓我在學習 Web Component 的期間花最多心力的一個部分了。
你是否覺得奇怪,為什麼樣式我們都一定要寫在 template 或是 class 裡面呢?
難道我們無法將他抽離成獨立的 style.css
再引入嗎?
其實是可以的!為了讓程式碼看起來更乾淨好讀,就讓我們來重構,將 css 抽離出 class 吧。
這個段落將會使用 un-custom-input 元件來做示範。
custom-input.css
。├── custom-input.css
├── custom-input.ts
└── index.ts
<template>
中的 <style>
抽離至 custom-input.css
。template.innerHTML = `
<style> //請將裡面的內容抽離至 custom-input.css </style>
<div class="container" part="container">
<!-- 略... -->
</div>
`;
custom-input.css
。constructor() {
super();
this.internals = this.attachInternals();
const shadowRoot = this.attachShadow({ mode: 'open' });
// 請在此建立樣式的連結,引用抽離的樣式檔。
// 建立 link 並加入 style
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = new URL('./custom-input.css', import.meta.url).href;
shadowRoot.appendChild(link);
const cloneNode = this.render().cloneNode(true);
shadowRoot.appendChild(cloneNode);
this.input = this.shadowRoot!.querySelector('.custom-input');
}
npm run dev
試試看,樣式是否有正確被加入。那麼樣式就成功地被抽離出來囉。
當然還有其他的方式可以實作,這部分就留給各位細細研究了!
開始前,我們先來看看 Paul 的 wc-msc-ai-assistant 關於 CSS.registerProperty()
的原始碼。
可以看見大量出現了 CSS.registerProperty()
這個 API。
這個 API 是什麼呢?
接下來就讓我們來詳細說明一下吧!
CSS.registerProperty() 是 CSS Houdini 的一部分。
Houdini(全稱CSS Houdini)並非一個單一的技術,而是一系列提供給開發者的底層瀏覽器API,旨在讓開發者能夠擴展和修改CSS 的核心渲染過程。
-- MDN
這個 API 讓我們能夠註冊自訂屬性(Custom Properties),也就是我們在 Day 12: Web Component 的樣式 style 介紹的 --card-border-color
這種 CSS 變數。
還記得我們當時定義的方法是一行就完成變數的定義了嗎?
:host {
--card-border-color: #333333;
}
但如果照著上面的方法定義變數,瀏覽器並不知道這個變數是什麼型別,也不知道它能不能被動畫化
。
動畫化
是什麼意思?其實瀏覽器對一般的 CSS 變數沒有型別資訊,就只是把它當成一般字串(像之前定義的那樣)。
所以當我們定義了 100px
是一個字串,200px
也是一個字串。瀏覽器並不會知道這兩者之間是有數值差距的,所以無法計算中間過渡。
所以就算你定義了 default 是 width: 100px
,hover 後要變 width: 200px
,這中間瀏覽器不會幫順順的變寬,而是直接變成 200px。
透過 CSS.registerProperty()
,我們可以將 CSS 變數註冊為瀏覽器認識的 正式屬性
。
我們來看一下 Paul 原始碼中的這一段:
CSS.registerProperty({
name: '--msc-ai-assistant-line-color',
syntax: '<color>',
inherits: true,
initialValue: 'rgba(199 205 210)'
});
註冊成正式屬性會用到下列四種屬性:
name
:自訂 CSS 屬性的名稱。
syntax
:定義該屬性可以接受的值型別。
<length>
: 長度(px, rem, em, %)。<color>
: 顏色 (#333, red, rgb())。<number>
: 純數字。inherits
:決定這個屬性是否會從父元素繼承。
true
:子元素會繼承父元素的值(在 Shadow DOM 中,若想讓外面能控制內部樣式,就要設為 true)。false
:子元素不會繼承,會使用 initialValue(或自己設定的值)。initialValue
:屬性的預設值。
接下來,我們前往 web.dev 可以看到: CSS.registerProperty()
是作用於 JS 當中,如果想要直接定義在 css 樣式檔中,就要使用 @property
來達到一樣的功能。
注意:需要確認瀏覽器的支援度,目前使用上還是以
CSS.registerProperty()
為主。
我們直接將變數名稱定義在外層,內部再進行相關設定。
@property --colorPrimary {
syntax: '<color>';
initial-value: magenta;
inherits: false;
}
我們將會使用最基本的卡片元件來做範例。並使用 CSS.registerProperty()
來做 CSS 變數的註冊。
class CustomCard extends HTMLElement {
constructor() {
super();
// 註冊 CSS 變數
if (CSS?.registerProperty) {
CSS.registerProperty({
name: '--card-color',
syntax: '<color>',
inherits: false,
initialValue: '#abcacf',
});
}
// 建立 Shadow Root
const shadowRoot = this.attachShadow({ mode: 'open' });
const cloneNode = this.render().cloneNode(true);
shadowRoot.appendChild(cloneNode);
}
render() {
const template = document.createElement('template');
template.innerHTML = `
<style>
.card {
width: 100px;
height: 100px;
display: inline-block;
margin: 10px;
cursor: pointer;
}
/* 這裡使用沒註冊的變數 */
.card {
background-color: var(--card1-color, #55a8b5);
transition: var(--card1-color) 0.5s; // 發現沒有動畫
}
.card1:hover {
--card1-color: #cbcacf;
}
/* 這裡使用有註冊的變數 */
.card2 {
background-color: var(--card-color);
transition: --card-color 0.5s; // 有動畫
}
.card2:hover {
--card-color: #55a8b5;
}
</style>
<div class="card card1"></div>
<div class="card card2"></div>
`;
return template.content;
}
}
customElements.define('custom-card', CustomCard);
<body>
<custom-card></custom-card>
</body>
原始碼請看這:https://codepen.io/unlinun/pen/raxmZPg
當你前往 codepen 玩玩看,你會發現,有註冊的元件有動畫的效果,沒有註冊的元件沒有動畫的效果。
當然,動畫是其中一個好處,還有其他好處,像是型別的定義,可以讓瀏覽器更清楚的知道這個 CSS 變數的型別是什麼,其餘的好處就留給大家發掘了!
那麼,最後一篇的主要內容就到這裡,感謝大家的收看。
不得不說,連續 30 天發文章真的是場硬仗,尤其九、十月連假多多(這是太晚開賽沒注意到的事情)。本來想著假日應該會在家裡囤幾篇文章,沒想到幾乎都往外跑。
另一個困難點就是要把想法轉成文字,有時候都開始懷疑自己到底還能說些什麼。但每次開始寫下去後就覺得自己好像講得不夠多 (σ′▽‵)′▽‵)σ
感謝名單當然是先謝謝兩位隊友,三十天幾乎每天晚上都有提醒加油!
另外還有同事的友情協助校稿,幾乎每篇文章都能從我的文章中抓到錯字或是程式碼的問題 XD,沒有各位就沒有我的完賽之旅!
總之,這三十天真的學到了很多,雖然文筆沒變好,但是腦中真的多了很多關於 Web Component
的新知識。
也希望有讀過這系列文章的大家,也能跟我一樣有成長一咪咪囉~(ゝ∀・)b
附錄(參考資料):
webcomponents.org
MDN-Web_components
web.dev-webcomponents
javascript.info-web-components
Web Components 探索之旅,出发!
web-components
lit.dev