iT邦幫忙

2025 iThome 鐵人賽

DAY 7
2
Modern Web

原生元件養成計畫:Web Component系列 第 7

Day 7: Web Component 的屬性監聽 Attribute

  • 分享至 

  • xImage
  •  

在昨天,我們已經學會了 Web Component 的 生命週期
接下來要深入了解 Web Component 生命週期中的 attributeChangedCallback

如果今天我們想要控制的不是內容,而是這個元件的屬性 像是外觀或行為,你就會使用到這個重要的生命週期!
所以接下來我們就來深入的討論如何更改元件的 屬性(attribute)吧!

元件的屬性


在設計元件時,屬性是外部開發者能直接設定在標籤上的值,用來影響元件的行為與外觀。
所以在開始前可以先思考:這個元件要提供哪些屬性,才能讓使用者更方便的調整?

回到我們現在在設計的元件 cat-spinner,你認為它會有什麼樣的屬性?

外觀來看,可能有:

  1. 顏色樣式:primary | secondary | danger
  2. 外型樣式:rounded-none | rounded-sm | rounded-md | rounded-lg
  3. 大小:small | medium | large
  4. label:載入提示文字,例如 Loading...、處理中...

狀態來看,可能有:

  1. 是否正在載入:loading
  2. 是否錯誤:error(可以顯示錯誤圖示或文字)
  3. 是否暫停 / 停用:disabled(spinner 停止轉動)

來替我們的元件增加屬性吧


我們先提供 4 個核心屬性給 cat-spinner:

  1. label : 顯示在 spinner 旁邊的文字
  2. size:small | medium | large
  3. color:控制 spinner 顏色
  4. loading:true | false

核心重點:宣告屬性監聽屬性變化

attribute

先在 template 中加入對應的樣式

const spinnerTemplate = document.createElement('template');

spinnerTemplate.innerHTML = `
  <style>
    .spinner-container {
      display: flex;
      align-items: center;
      gap: 8px;
    }

    /***** 先提供預設屬性 *****/
    .spinner {
      border: 3px solid #ddd;
      border-top-color: #4a32e3;
      border-radius: 50%;
      width: 16px;
      height: 16px;
      animation: spin 1s linear infinite;
    }
    
    .label {
      font-size: 14px;
      font-weight: 600;
    }

    /***** SIZE *****/
    .small { 
      width: 16px; 
      height: 16px; 
    }
    
    .medium { 
      width: 24px; 
      height: 24px; 
    }
    .large { 
      width: 40px; 
      height: 40px; 
    }

    @keyframes spin {
      to { 
        transform: rotate(360deg); 
      }
    }
  </style>

  <div class="spinner-container">
    <!-- spinner-->
    <div class="spinner"></div>

    <!-- 預設的 label -->
    <span class="label">Loading...</span>
  </div>
`;

宣告監聽屬性

其實我們可以透過 this.getAttribute("屬性") 來取得標籤的內容。但它只是一次性的抓取當前值,不會自動通知你屬性變化。
要知道變化的話就要透過靜態方法 static get observedAttributes(),回傳一個陣列,列出所有需要監聽的屬性名稱。
現在我們想要監聽的屬性有:label, size, color, loading

static get observedAttributes() {
  return ['label', 'size', 'color', 'loading'];
}

監聽屬性變化,當宣告的屬性有改變時要觸發的動作

在 Web Component 中,如果我們希望元件能對外部傳入的屬性做出反應,就必須讓元件監聽屬性變化
透過 attributeChangedCallback(name, oldValue, newValue),你就可以在屬性發生變化時執行你要觸發的後續動作。

  • name:屬性的名稱('rounede', 'size', 'disabled', 'loading'...)
  • oldValue:屬性原本的值(第一次設置時是 null)
  • newValue:屬性最新的值(移除屬性時會變成 null)
attributeChangedCallback(name, oldValue, newValue) {
  const spinner = this.shadowRoot.querySelector('.spinner');
  const label = this.shadowRoot.querySelector('.label');

  switch (name) {
    case 'label':
      label.textContent = newValue || 'Loading...';
      break;

    case 'size':
      spinner.className = `spinner ${newValue || 'small'}`;
      break;

    case 'color':
      spinner.style.borderTopColor = newValue || '#4a32e3';
      break;

    case 'loading':
      console.log(this.hasAttribute('loading'), oldValue, newValue)
      spinner.style.display = (this.hasAttribute('loading') && newValue === 'false') 
        ? 'none' : 'flex';
      break;
  }
}

在外部使用屬性

一般的 html 相信大家已經很熟悉了,在使用原生的元件時,我們可以在 <button disabled>送出</button> 標籤上加入 disabled 就可以控制元件的禁用狀態,而自定義元件的使用方式也是一樣的。

我們一樣在標籤上加入屬性,然後在元件內部 監聽 屬性變化。

依據上方定義的幾種屬性,你可以寫成以下的程式碼:

  <body>
    <div style="display: flex; align-items: center; gap: 16px; padding: 20px">
      <h2>Web Component</h2>

      <cat-spinner></cat-spinner>
      <cat-spinner label="等待奴才中..." size="large" color="#e34a4a" loading></cat-spinner>
      <cat-spinner label="處理中飼料中..." size="small" color="#000000" loading></cat-spinner>
      <cat-spinner label="主子來啦~🐱🐱🐱" size="medium" loading="false"></cat-spinner>
    </div>
    
    <script src="spinner.js"></script>
  </body>

得到的結果:
spinner-attribute
attribute
(由於 gif 畫質太差了~補一張靜態的!)


上一篇
Day 6: Web Component 的生命週期 Life cycle
下一篇
Day 8: Web Component 的事件傳遞 CustomEvent
系列文
原生元件養成計畫:Web Component12
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言