写在前面

先说问题:同样一个自定义标签在 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 问题欢迎留言探讨~

03-05 14:06