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