【Web Components】關於自定義元件屬性在 Vue 和 React 中不同表現的探討

Allan91發表於2021-11-30

寫在前面

先說問題:同樣一個自定義標籤在 Vue、React中,傳入的屬性會有不一樣的結果

為方便下面講述,先自定義一個標籤 <my-cell />,程式碼如下:

class MyCell extends HTMLElement {
  static get observedAttributes() {
    return ['desc'];
  }
  constructor() {
    super();
  }
  get desc() {
    console.log('get');
    return this.getAttribute('desc');
  }
  set desc(value) {
    console.log('set', typeof value);
    this.setAttribute('desc', value);
  }
  attributeChangedCallback(name, oldValue, newValue) {
    console.log('attributeChangedCallback!', name, 1);
  }
  connectedCallback() {
    console.log('connectedCallback!', typeof this.desc);
    const shadowRoot = this.attachShadow({
      mode: 'open'
    });
    shadowRoot.innerHTML = `
    <style>
      :host{
        background: red;
      }
    </style>
    <slot></slot>
    ${this.desc}
    `;
  }
}
customElements.define('my-cell', MyCell);

然後在Vue和React中使用

<my-cell desc="false">Cell</my-cell>

看看會如何列印?
image.png
如圖所示,可以看到在Vue專案中會走到 set,而React專案中沒有。

此外,經查證 Vue 傳遞 DOM Property 中:

// 屬性傳遞是 DOM property!
<my-cell desc="false">Cell</my-cell> // 字串

// 屬性傳遞是 DOM property!
<my-cell :desc="false">Cell</my-cell> // 布林值

總結:

  • vue 中屬性作為 DOM property 傳入,會觸發 set
  • react 中屬性作為 DOM attribute 傳入,不會觸發 set

如何解決 Vue 和 React 中一致表現?

DOM property、DOM attribute 區別導致自定義標籤在 Vue、React 中使用時表現不一,核心思路是做好對映關係。

在 WCs 內部執行:

connectedCallback() {
+  this.desc = this.desc; // attribute -> property 對映
}

等號左邊 this.desc 進行賦值動作,會觸發執行 set

set desc(value) {
    this.setAttribute('desc', value); // 設定為 attribute
}

等號右邊 this.desc 是一個獲取動作,會呼叫 get

get desc() {
    return this.getAttribute('desc'); // 獲取 attribute
}

最終,我們將外出傳入的屬性統一轉為 Dom Attribute

總結:不管外面傳入的是啥屬性,用 this.xx = this.xx 統一將外部傳入屬性設定為 DOM attribute

寫在最後

關於 Web Components 問題歡迎留言探討~

相關文章