iT邦幫忙

2025 iThome 鐵人賽

DAY 6
0
Modern Web

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

Day 6: Web Component 的生命週期 Life cycle

  • 分享至 

  • xImage
  •  

截至目前為止,我們一直都還沒有提到 生命週期
但...是的,沒錯!
跟各大框架一樣,Web Component 也有生命週期。
其實光看到這四個字,心裡就已經開始抗拒了 (ι´Дン)ノ
但生命週期,其實是學習 Web Component 的關鍵,他定義了元件在出生更新消失 等不同階段下的行為,也可以讓我們更有效的控制元件。

Web Component 的生命週期

life cycle

  • constructor() 當元件實例化時。
  • connectedCallback() : 當元件首次被加入 DOM 時。
  • disconnectedCallback() : 當元件從 DOM 移除時。
  • attributeChangedCallback(name, oldValue, newValue) : 當元件的屬性有改變時。
  • adoptedCallback() : 當元件被移到新的文件時。

constructor

在元件實例化時被呼叫,通常使用於初始化元件的狀態或設定預設值

在前幾篇文章中其實已經使用了很多次,我們一直在 constructor() 中初始化元件,像是建立 Shadow DOM 或套用 template。
要注意的是,這時候的元件還沒加入 DOM,所以無法操作尺寸或讀取外部屬性。

// 建構函數
constructor() {
  super();

  const shadowRoot = this.attachShadow({ mode: 'open' }); // 初始化 shadowRoot
  const cloneNode = spinnerTemplate.content.cloneNode(true);
  shadowRoot.appendChild(cloneNode); // 初始化內容,加入 template
}

connectedCallback

類似 Angular: ngAfterViewInitReact: componentDidMount

當元件首次被加入 DOM 時,通常使用於初始化操作,像是綁定事件、呼叫 API 等等...

這個時候,元件已經被添加到 DOM 了,並且可以訪問到其他 DOM 元素。

connectedCallback() {
  this.timer = setInterval(() => console.log('timer'), 1000); // 加入計時器
  this.shadowRoot.querySelector('.spinner')
    .addEventListener('click', () => {console.log('click')}) // 綁定事件
}

disconnectedCallback

類似 Angular: ngOnDestroyReact: componentWillUnmount

當元件從DOM 移除時觸發。通常用於清理定時器或取消事件監聽。

disconnectedCallback() {
  clearInterval(this.timer); // 清理計時器
  this.shadowRoot.querySelector('.spinner')
    .removeEventListener('click', () => {console.log('click')}) // 取消事件監聽
}

attributeChangedCallback

attributeCahngedCallback 會在下一篇文章中詳細解釋

當元件的屬性改變時觸發。可以監聽屬性發生的變化,並取得舊值與新值做後續運用。

  • name:屬性的名稱('rounede', 'size', 'disabled', 'loading'...)
  • oldValue:屬性原本的值(第一次設置時是 null)
  • newValue:屬性最新的值(移除屬性時會變成 null)
attributeChangedCallback(name, oldValue, newValue) {
  console.log(`${name} 屬性,原本的值是 ${oldValue},變成了 ${newValue}`);
}

adoptedCallback

當一個 Custom Element 被從一個 document 移動到另一個 document 時,通常運用在跨 iframe 移動,或是跨 window.open 出來的新視窗移動。

這個其實是 Web Component 四大生命週期裡最少人用到的一個。

adoptedCallback(oldDocument, newDocument) {
  console.log(oldDocument, newDocument);
}

範例:

const spinner = document.createElement('cat-spinner');
document.body.appendChild(spinner);

// 打開一個新視窗
const newWindow = window.open('', '', 'width=300,height=200');

// 把 spinner 加入到新視窗中
newWindow.document.body.appendChild(spinner);

在這個過程中,adoptedCallback 就會被呼叫,因為 spinner 從舊 document 中被領養到新的 document。

connectedCallback 及 disconnectedCallback 的應用

接下來我們將 cat-spinner 升級一下,利用生命週期,變成可以加入 progress 的元件。
一樣先在 constructor 時初始化影子樹、template。

  1. 定義一個 setLoadingProgress 函式
setLoadingProgress = function() {
  const spinner = this.shadowRoot.querySelector('.spinner-container');
  const label = spinner.querySelector('.label');

  this.progress = 0;

  // 每秒 +10%
  this.timer = setInterval(() => {
    if (this.progress < 100) {
      this.progress += 10;
      label.innerText = `${this.progress}%`;
    } else {
      clearInterval(this.timer);
      label.textContent = "完成!";
    }
  }, 1000);
}
  1. connectedCallback,初始化 timer
  connectedCallback() {
    const cloneNode = spinnerTemplate.content.cloneNode(true);
    this.shadowRoot.appendChild(cloneNode);
    this.setLoadingProgress(); // 初始化 timer
  }
  1. disconnectedCallback,加上清除 timer 機制,當元件被清除時要清除 timer,避免記憶體洩漏。
  disconnectedCallback() {
    // 清除計時器,避免記憶體洩漏
    clearInterval(this.timer);
  }

life cycle

今天就到這啦~
接下來我們要深入探討一下,到底怎麼使用 attributeChangedCallback 來監聽屬性變更!


上一篇
Day 5: Web Component 中的模板 Template
系列文
原生元件養成計畫:Web Component6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言