反应网络组件
该项目旨在连接反应组件和自定义元素之间的联系. 库所采取的策略是创建一个模型,定义DOM如何被翻译为React道具.
由于目标是支持作为自定义元素的 React 组件, 我们并不支持从段落, 选择等已建元素扩展 .
它支持:
- 当属性被更改时自动更新 React 组件
- 支持自动登记和触发事件
- 支持多重渲染目标,自定义元素本身,容器分录或阴影根.
- 允许解析和更新 DOM 的嵌入式结构,例如:
<select>
<option value="something">Else</option>
<option value="certain">Value</option>
</select>
这种DOM结构可以被转化为模型并自动注入回放组件. 你可以变身 任意 DOM结构为模式,将传递给反应组件. 这使得您可以使用大多数 DOM api 并封装 React 作为执行细节.
安装库
npm install @adobe/react-webcomponent
如果你在使用 Babel 6 : 因为我们的目标是自定义元素V1 我们用巴贝尔来转换我们的代码 即时操作自定义元素会有问题. 见 这个问题 讨论. 包含 自定义元素- es5- 适应器 在你装上这个librabry来解决这个问题之前.
你是在用"Babel"7号吗? 问题已经解决了 所以你不需要别的了.
我们正在使用类属性和装饰器,以确保您包括 appropiat bail 插件来使用这个.
定义自定义元素
首先需要的是一个反应组件,作为自定义元素曝光
import React, { Component } from 'react';
import { createCustomElement, DOMModel, byContentVal, byAttrVal, registerEvent } from "@adobe/react-webcomponent";
class ReactButton extends Component {
constructor(props) {
super(props);
}
render() {
return (<div>
<button weight={ this.props.weight }>{ this.props.label }</button>
<p>Text</p>
</div>)
}
}
然后,您需要创建一个模型来定义 DOM 如何解析成反应属性 .
class ButtonModel extends DOMModel {
@byContentVal text = "something";
@byAttrVal weight;
@registerEvent("change") change;
}
您创建自定义元素
const ButtonCustomElement = createCustomElement(ReactButton, ButtonModel, "container");
然后注册
window.customElements.define("test-button", ButtonCustomElement);
确定反应部分将在哪里提供
在定义自定义元素时,您可以通过指定 renderRoot 属性.
可能的价值是:
- 集装箱
这将在自定义元素内产生额外的 div , 反应组件将在那里提供 . 这样做是有用的,因为React会移除它所制成的容器元件的所有儿童.
所以,如果您想要从自定义元素的标记中解析值并修改它们,则元素会在初始渲染后丢失.
例如:
<my-button>
<my-button-label>My Button</my-button-label>
</my-button>
如果我们不在一个容器里做
my-button-label元素会在渲染时被 React 移除 .
-
阴影旋转
这将决定自定义元素"影子"的创建,并将在其中提供"反应"组件 -
元素
React 组件将在自定义元素中直接渲染出.
扩展自定义元素
默认情况下, 我们提供创建自定义元素的功能 createCustomElement。此选项包含默认行为,但不允许元素扩展.
这可以被绕过,而自定义Element可以以新的能力来扩展.
import { CustomElement } from "@adobe/react-webcomponent";
class ButtonCustomElement extends CustomElement {
constructor() {
super();
this._custom = 3;
}
get custom() {
return this._custom;
}
set custom(value) {
this._custom = value;
}
};
ButtonCustomElement.observedAttributes = Model.prototype.attributes;
ButtonCustomElement.domModel = Model;
ButtonCustomElement.ReactComponent = ReactComponent;
ButtonCustomElement.renderRoot = "container"; // optional, defaults to "element"
window.customElements.define("test-button", ButtonCustomElement);
DOM 模式
从将一个 DOM 节点转换为模型,这种功能是可逆的. 该型号由一系列专业的装饰师来装饰. 每个装饰者将解析圆顶并构建模型:
- 由 AttrVal 编辑
- 由 Boolean AttrVal 编辑
- 由 JsonAttrVal 编辑
- ưμ㼯A
- 按内容
- 按儿童状况分类
- 按儿童参考资料
- 按模式
- 按“儿童模式”
- 儿童参考文件
- 由儿童Type阵列
- 注册事件
校对:Soup.
解析元素并设置与元素属性值对应的值
@byAttrVal(attrName:string) - defaults to the name of the property that it decorates.
class Model extends DOMModel {
@byAttrVal() weight;
@byAttrVal("custom-attribute-name") reactPropName;
}
用法 :
<div id="elem" weight="3" custom-attribute-name="some value"/>
const model = new Model().fromDOM(document.getElementById("elem"));
model ~ {
weight: "3",
reactPropName: "some value"
}
由 Boolean AttrVal 编辑
解析元素,并设定元素上属性存在的对应值.
属性的值被忽略,只有属性的存在决定了值
@byBooleanAttrVal(attrName:string) - defaults to the name of the property it decorates
class Model extends DOMModel {
@byBooleanAttrVal() checked;
@byBooleanAttrVal("is-required") required;
}
用法 :
<div id="elem" checked/>
const model = new Model().fromDOM(document.getElementById("elem"));
model ~ {
checked: true,
required: undefined
}
<div id="elem" checked is-required/>
model.fromDOM(document.getElementById("elem"));
model ~ {
checked: true,
required: true
}
由 JsonAttrVal 编辑
通过使用 JSON.parse。 。 。 .
class Model extends DOMModel {
@byJsonAttrVal() obj;
@byJsonAttrVal("alias-attr") anotherObj;
}
<div id="elem" obj='[{"example":1},{"test":2}]' alias-attr='[{"other":"example},{"test":3}]'/>
const model = new Model().fromDOM(document.getElementById("elem"));
model ~ {
obj: [{"example":1},{"test":2}],
anotherObj: [{"other":"example},{"test":3}]
}
ưμ㼯A
解析元素并设定值到 innerText ,该元素.
class Model extends DOMModel {
@byContentVal() label;
}
<div id="elem">My Label</div>
const model = new Model().fromDOM(document.getElementById("elem"));
model ~ {
label: "My Label"
}
按内容
此装饰器允许您捕获一个由 CSS 选择器匹配的 DOM 节点 . 这可用于再现儿童专断的DOM内容,可能不是 与 React 一起转换,输入您的网页组件。 一旦解析完成, 字段 在模型中将包含一个反应组件,代表 DOM 内容 将重新做父母.
当 React 组件被挂起时会移动 DOM 内容 。 还有,内容 如果反应组件后来被卸载,则将被放回原位置.
@byContent(attrName:selector) - the CSS selector that will match the child node.
class Model extends DOMModel {
@byContent('.content') content;
}
<div id="elem">
<div class="content">
This will be reparented
</div>
</div>
<div id="mount-point"/>
const model = new Model().fromDOM(document.getElementById("elem"));
ReactDOM.render(<div>{ model.content }</div>, document.getElementById("mount-point"))
// Once React has rendered the above component, the DOM will look like this
<div id="elem">
<!-- placeholder for DIV -->
</div>
<div id="mount-point">
<div class="content">
This will be reparented
</div>
</div>
按儿童状况分类
分析元素寻找匹配给定选择器并设定值的元素 innerText 内容
class Model extends DOMModel {
@byChildContentVal("custom-label") label;
}
<div id="elem"><custom-label>My Label</custom-label></div>
const model = new Model().fromDOM(document.getElementById("elem"));
model ~ {
label: "My Label"
}
按儿童参考资料
解析元素并寻找一个匹配给定选择器的子元素, 并设定以给定模型解析子元素的结果值
class CustomLabelModel extends DOMModel {
@byContentVal() value;
@byAttrVal() required;
}
class Model extends DOMModel {
@byChildContentVal("custom-label", CustomLabelModel) label;
}
<div id="elem"><custom-label required>My Label</custom-label></div>
const model = new Model().fromDOM(document.getElementById("elem"));
model ~ {
label: {
value: "My Label",
required: true
}
}
按模式
指定在元素上运行一个给定模型的值. 这使得元素模型可以保存在不同的属性上而不是直接保存在模型上.
class CustomModel extends DOMModel {
@byContentVal() value;
@byAttrVal() required;
}
class Model extends DOMModel {
@byModelVal() item;
}
<div id="elem" required>Content</div>
const model = new Model().fromDOM(document.getElementById("elem"));
model ~ {
item: {
value: "Content",
required: true
}
}
按“儿童模式”
通过从自定义 elem 中获取模型值来解析元素并设定值. 此属性仅在有自定义元素解析时返回 .
使用开头定义的按钮 :
window.customElements.define("test-button", ButtonCustomElement);
我们定义一个模型
class Model extends DOMModel {
@byChildModelVal("test-button") button;
}
<div><test-button weight="3">Click me</test-button></div>
const model = new Model().fromDOM(document.getElementById("elem"));
model ~ {
button: {
weight: 3,
text: "Click me"
}
}
这里的根本区别在于模型在另一个自定义元素中被定义,并在这个模型中被再用. 所以它不会被重新定义.
儿童参考文件
解析元素子并选择所有匹配所提供选择器的元素.
对于每个元素,它使用被引用的模型来解析元素的值.
由此而来的所有数值阵列被存储为被装饰的地产上的价值.
class OptionModel extends DOMModel {
@byContentVal() content;
@byAttrVal() value;
@byBooleanAttrVal() selected;
}
class SelectModel extends DOMModel {
@byChildrenRefArray("option", OptionModel) options;
}
<select id="elem">
<option value="1">Amsterdam</option>
<option value="2">Berlin</option>
<option value="3">London</option>
</select>
const model = new Model().fromDOM(document.getElementById("elem"));
model ~ {
options: [{value: 1, content: "Amsterdam"}, {value: 2, content: "Berlin"}, {value: 3, content: "London"}]
}
由儿童Type阵列
如果节点Name与所提供地图中的节点相匹配, 则该节点将对该孩子进行相应的模型分析 .
class Child1Model extends DOMModel {
@byAttrVal() checked;
}
class Child2Model extends DOMModel {
@byAttrVal() selected;
}
class Model extends DOMModel {
@byChildrenTypeArray({
"child-one": Child1Model,
"child-two": Child2Model
}) items;
}
<div id="elem">
<child-one checked/>
<child-two selected/>
</div>
const model = new Model().fromDOM(document.getElementById("elem"));
model ~ {
items: [{checked: true}, {selected: true}]
}
注册事件
注册要注册在 React 组件上的事件, 当它被称作自定义 Event 时, 将会在自定义元素上被触发 .
事件名称会自动转换成骆驼 Case , 并用 on
这种行为发生在自定义元素而不是DOM模型上,DOM模型只登记事件
class Model extends DOMModel {
@registerEvent("change") change;
}
最终将转换为自定义元素 a onChange 属性.