Web Components中引入外部CSS的 8 種方法

Allan91發表於2021-11-05

開發中,還是會遇到需要引入外部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

總結

各種方法適用場景各不相同,小心食用。

相關文章