開發中,還是會遇到需要引入外部CSS到Shadow DOM情況,那麼如何處理呢?作者就最近遇到的情況給出如下幾種方案。
一、@import
示例程式碼
const template = document.createElement('template');
class WhatsUp extends HTMLElement {
connectedCallback() {
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innderHTML = `
<style>
@import "./index.css"; // 核心程式碼
</style>
<div>Sup</div>
`
}
}
window.customElements.define('whats-up', WhatsUp);
優點:此方法相容性非常好,點選檢視caniuse。
缺點:效能
二、::part
::part
CSS 偽元素表示在陰影樹中任何匹配 part
屬性的元素。
示例程式碼
HTML
<template id="whats-up">
<div part="sup">Sup</div>
<div part="foo">Sup</div>
</template>
<whats-up></whats-up>
CSS
whats-up::part(sup) {
/* 樣式作用於 `sup` 部分 */
}
whats-up::part(foo) {
/* 樣式作用於 `foo` 部分 */
}
優點:簡潔明瞭
缺點:相容性不太好,點選檢視caniuse。
三、var
CSS自定義屬性可以穿透到 Shadow DOM中!
示例程式碼
JS
const template = document.createElement('template');
template.innerHTML = `
<style>
button {
background: var(--background);
color: var(--color);
padding: var(--padding);
font-size: var(--font-size);
border: 0;
}
</style>
<div>Sup</div>`;
CSS
whats-up {
--background: #1E88E5;
--color: white;
--padding: 2rem 4rem;
--font-size: 1.5rem;
}
優點:相容性好
缺點:比較侷限,只能外部定幾個,樣式不能“自由飛翔”
四、通過屬性傳入
示例程式碼
JS
class Whatsup extends HTMLElement {
static get observedAttributes() {return ['css']}
constructor() {
super();
}
get css() {
return this.getAttribute('css');
}
set css(value) {
if (value === null || value === false) {
this.removeAttribute('css');
} else {
this.setAttribute('css', value);
}
}
connectedCallback() {
const shadowRoot = this.attachShadow({
mode: 'open'
});
shadowRoot.innerHTML = `
<style>
:host{
display: flex;
}
${this.css} // 核心程式碼
</style>
<div class="name">Sup</div>
`;
}
}
HTML
<whats-up
css="
.name{
color: red;
}
"
></whats-up>
優點:樣式可以隨意修改
缺點:程式碼不夠優雅
五、自定義元件內部定義修改樣式函式
示例程式碼
JS
class Whatsup extends HTMLElement {
// ...
// 核心程式碼
reStyle(els, styles) {
const elements = Array.isArray(els) ? els : [els];
elements.forEach((element) => Object.assign(element.style, styles));
}
}
HTML
<whats-up></whats-up>
<script>
const myEle = document.querySelector('whats-up')
const title = myEle.shadowRoot.querySelector('.title');
myEle.reStyle(title, {
color: 'red',
width: '200px',
})
</script>
六、通過 slot 外部設定樣式
示例程式碼
JS
class WhatsUp extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<div>
<slot name="header"></slot>
</div>
`;
}
}
customElements.define('whats-up', WhatsUp);
HTML
<style>
.header{
color: red;
}
</style>
<whats-up>
<div slot="header" class="header">
what's up
</div>
</whats-up>
七、fetch獲取
示例程式碼
class WhatsUp extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
// 獲取樣式
fetch('./index.css').then(res => res.text()).then(data => {
let node = document.createElement('style');
node.innerHTML = data;
this.shadowRoot.appendChild(node);
});
// ...
}
}
customElements.define('whats-up', WhatsUp);
優點:優點是相容性不錯,支援Shadow DOM的元素均支援此語法;以及效能還OK
缺點:不優雅
八、CSS module import
此方法使用瀏覽器原生的import語法,但是import的是CSS檔案而不是JS檔案。
也就是把CSS檔案直接作為模組引入。
示例程式碼
import styles from "index.css";
class WhatsUp extends HTMLElement {
constructor() {
// ...
// 核心程式碼
shadow.adoptedStyleSheets = [styles];
}
}
優點:優點是使用方便快捷且是官方推薦方法,或者是import CSS模組就是為了這個場景才支援的;以及效能OK,import本身就是非同步過程。
缺點:相容性不佳,狠狠戳這裡caniuse。
總結
各種方法適用場景各不相同,小心食用。