寫在前面
先說問題:同樣一個自定義標籤在 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>
看看會如何列印?
如圖所示,可以看到在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 問題歡迎留言探討~