自訂元素最佳做法

你可以使用自訂元素,建構自己的 HTML 標記。這份檢查清單涵蓋了建構高品質元素的最佳做法。

自訂元素可讓您擴充 HTML 及定義自己的標記。他們是 功能非常強大,但是等級很低 請務必明確指出導入自己的元素最佳方式

為了協助您創造最佳體驗,我們整理了以下內容 檢查清單。它會詳細列出我們認為 自訂元素的行為良好。

建立用於封裝樣式的陰影根。

為什麼? 在元素的陰影根目錄中封裝樣式,確保樣式可正常運作 以及無論在何處使用如果開發人員 想將元素放置在另一個元素的陰影根目錄中這個 則會套用到核取方塊或圓形按鈕等簡單元素有可能 影子根層級唯一的內容會成為 所發出的呼叫頻率
範例 <howto-checkbox> 元素。

在建構函式中建立陰影根。

為什麼? 建構函式是指您具備元素的專屬知識。 建議您盡快設定您不希望其他導入項目的詳細設定 元素之間的關聯在稍後的回呼中執行這項操作,例如 connectedCallback 意味著您必須面對 因此系統無法將元素卸離,然後再重新附加至文件中。
範例 <howto-checkbox> 元素。

將元素建立的任何子項放入其陰影根目錄中。

為什麼? 由元素建立的子項是其實作的一部分,且應 私人。如果沒有影子根防護機制,JavaScript 以外的位置 無意間幹擾兒童
範例 <howto-tabs> 元素。

使用 <slot>將 Light DOM 子項投影到 shadow DOM 中

為什麼? 允許元件使用者指定元件中的內容,因為 HTML 子項可讓元件變得更加可組合項。如果瀏覽器不支援自訂元素,巢狀內容仍可繼續供使用者存取、顯示和存取。
範例 <howto-tabs> 元素。

設定 :host 顯示樣式 (例如 blockinline-blockflex),除非您偏好使用預設值 inline

為什麼? 自訂元素預設為 display: inline,因此將自訂元素設為 widthheight 不會有任何作用。這經常 是開發人員感到驚訝的現象,且可能導致 版面配置除非你偏好 inline 螢幕,否則你可以 一律設定預設的 display 值。
範例 <howto-checkbox> 元素。

加入符合隱藏屬性的 :host 顯示樣式。

為什麼? 採用預設 display 樣式的自訂元素,例如 :host { display: block } 將覆寫指定值 內建 hidden 屬性。 如果您預期將 hidden 設為 屬性,即可算繪 display: none。此外, 預設的 display 樣式,新增 hidden 的支援 搭配 :host([hidden]) { display: none }
範例 <howto-checkbox> 元素。

屬性和屬性

不覆寫作者設定的全域屬性。

為什麼? 全域屬性是指所有 HTML 元素中顯示的屬性,只有部分通知 範例包括 tabindexrole。自訂元素 建議將初始 tabindex 設為 0,使其成為鍵盤 可聚焦。不過,您應該事先檢查開發人員是否使用 元素已將這個參數設為其他值。舉例來說,假設 tabindex 到 -1,表示他們不想 才能進行互動
範例 <howto-checkbox> 元素。詳細說明請參閱 不要覆寫網頁作者,

一律接受基本資料 (字串、數字、布林值) 做為任一屬性 或屬性。

為什麼? 自訂元素 (例如內建對應元素) 應可供設定。 設定能以宣告方式、透過屬性傳遞,或以命令方式傳遞 透過 JavaScript 屬性在理想情況下,每項屬性都應該連結至 和對應的屬性
範例 <howto-checkbox> 元素。

盡量將原始資料屬性和屬性保持同步,反映 屬性,反之亦然。

為什麼? 您永遠不會知道使用者如何與您的元素互動。例如: 在 JavaScript 中設定屬性,然後預期讀取該值 透過 getAttribute() 等 API如果每項屬性都有 資源與資源相對應 使用者與元素搭配運作也就是呼叫 setAttribute('foo', value) 也應設定對應的 foo 屬性,反之亦然。當然, 此規則。請勿反映頻率高的屬性,例如: currentTime。請善用判斷力。如果是 似乎會與資源或屬性互動 沒辦法反映這一點
範例 <howto-checkbox> 元素。詳細說明請參閱 避免重複性問題

請僅接受多媒體資料 (物件、陣列) 做為屬性。

為什麼? 一般來說,不會包含一些內建 HTML 元素的內建範例。 透過其接受豐富的資料 (純文字 JavaScript 物件和陣列) 屬性。是透過方法呼叫或 資源。接受多媒體資料擁有幾項明顯的缺點 屬性:將大型物件序列化為字串可能非常昂貴。 所有物件參照都會在這項字串化程序中遺失。適用對象 例如,如果您將一個物件對應至另一個物件的參照, 或 DOM 節點,這些參照就會遺失

請勿將豐富的資料屬性反映至屬性。

為什麼? 將豐富的資料屬性反映到屬性不需花太多錢。 需要對相同的 JavaScript 物件進行序列化與取消序列化作業。除非 您的用途只能透過這項功能解決 務必避免。

建議檢查在元素之前已設定的屬性 已升級

為什麼? 使用你的元素的開發人員可能會嘗試在元素上設定屬性 載入其定義之前。如果 開發人員使用的是可處理載入元件的架構, 並將屬性繫結至模型。
範例 <howto-checkbox> 元素。詳細說明請見 將屬性設為延遲

請勿自行套用類別。

為什麼? 需要表示狀態的元素應使用屬性來表示。 class 屬性通常視為 並自行撰寫文字 開發人員類別簡介

活動

分派事件以回應內部元件活動。

為什麼? 您的元件可能有屬性變更,以因應 只有元件知道的知識,例如計時器或動畫 或是資源完成載入有助於調度事件 回應這些變更,並通知主機元件的狀態 也不一樣

不要為回應主機設定屬性 (向下) 分派事件 資料流)。

為什麼? 分派事件以回應主機設定時,作業方式不流暢 (由於主機剛設定,主機就知道目前狀態)。分派事件 回應主機設定,可能會導致資料發生無限迴圈 繫結系統
範例 <howto-checkbox> 元素。

釋疑影片

不要覆寫網頁作者

使用元素的開發人員可能會想覆寫某些值 這項動作會抹除機器的記憶體內容 並將虛擬機器重設為初始狀態例如,將 ARIA role 變更為可聚焦性 tabindex。請確認是否已設定這些和任何其他全域屬性。 才能套用自己的值

connectedCallback() {
  if (!this.hasAttribute('role'))
    this.setAttribute('role', 'checkbox');
  if (!this.hasAttribute('tabindex'))
    this.setAttribute('tabindex', 0);

將屬性設為延遲

在元素開始前,開發人員可能會嘗試先設定元素的屬性 定義已載入如果開發人員使用 以及處理載入元件、將元件插入頁面 將屬性與模型繫結至模型中

在下方範例中,Angular 會以宣告方式繫結模型的 設為核取方塊 checked 屬性的 isChecked 屬性。如果 How to-checkbox 會延遲載入,很有可能讓 Angular 嘗試 在元素升級前勾選的屬性。

<howto-checkbox [checked]="defaults.isChecked"></howto-checkbox>

自訂元素應檢查是否有任何屬性 您可能已經在執行個體中設定<howto-checkbox> 我們用名為 _upgradeProperty() 的方法示範這個模式

connectedCallback() {
  ...
  this._upgradeProperty('checked');
}

_upgradeProperty(prop) {
  if (this.hasOwnProperty(prop)) {
    let value = this[prop];
    delete this[prop];
    this[prop] = value;
  }
}

_upgradeProperty() 會從未升級的執行個體中擷取值並刪除 屬性,這樣就不會覆蓋自訂元素本身的屬性 setter。 這樣一來,元素定義最終完全載入後,就能立即 反映正確的狀態

避免重複發生問題

您可能會很想使用 attributeChangedCallback() 將狀態反映為 基礎屬性,例如:

// When the [checked] attribute changes, set the checked property to match.
attributeChangedCallback(name, oldValue, newValue) {
  if (name === 'checked')
    this.checked = newValue;
}

不過,如果屬性 setter 也反映 屬性。

set checked(value) {
  const isChecked = Boolean(value);
  if (isChecked)
    // OOPS! This will cause an infinite loop because it triggers the
    // attributeChangedCallback() which then sets this property again.
    this.setAttribute('checked', '');
  else
    this.removeAttribute('checked');
}

另一種方式是允許屬性 setter 反映屬性 getter 可以根據屬性決定其值。

set checked(value) {
  const isChecked = Boolean(value);
  if (isChecked)
    this.setAttribute('checked', '');
  else
    this.removeAttribute('checked');
}

get checked() {
  return this.hasAttribute('checked');
}

在此範例中,新增或移除屬性也會設定屬性。

最後,attributeChangedCallback() 可用來處理連帶效果 例如套用 ARIA 狀態

attributeChangedCallback(name, oldValue, newValue) {
  const hasValue = newValue !== null;
  switch (name) {
    case 'checked':
      // Note the attributeChangedCallback is only handling the *side effects*
      // of setting the attribute.
      this.setAttribute('aria-checked', hasValue);
      break;
    ...
  }
}