WebComponents框架direflow实现原理
这个框架支持React方式写WebComponents。假设有这样一个web component组件。
框架地址:https://github.com/Silind-Sof...
direflow的配置如下:
import { DireflowComponent } from "direflow-component";
importApp from "./app";
export default DireflowComponent.create({
component: App,
configuration: {
tagname: "test-component",
useShadow: true,
},
});
创建一个Web component
const WebComponent = new WebComponentFactory(
componentProperties,
component,
shadow,
anonymousSlot,
plugins,
callback,
).create();
customElements.define(tagName, WebComponent);
通过customElements.define声明一个web component,tagName为"test-component",WebComponent为集成了渲染react组件能力的的web components工厂函数实例。
web components工厂函数
响应式 劫持所有属性。
public subscribeToProperties() {
const propertyMap = {} as PropertyDescriptorMap;
Object.keys(this.initialProperties).forEach((key: string) => {
propertyMap[key] = {
configurable: true,
enumerable: true,set: (newValue: unknown) => {
const oldValue = https://www.it610.com/article/this.properties.hasOwnProperty(key)
? this.properties[key]
: this.initialProperties[key];
this.propertyChangedCallback(key, oldValue, newValue);
},
};
});
Object.defineProperties(this, propertyMap);
}
首先,将attributes转化为properties。
其次,通过Object.defineProperties劫持properties,在setter中,触发propertyChangedCallback函数。
const componentProperties = {
...componentConfig?.properties,
...component.properties,
...component.defaultProps,
};
上面这段代码中的property变化时,重新挂载React组件。(这里一般是为了开发环境下,获取最新的视图)
/**
* When a property is changed, this callback function is called.
*/
public propertyChangedCallback(name: string, oldValue: unknown, newValue: unknown) {
if (!this.hasConnected) {
return;
}if (oldValue =https://www.it610.com/article/== newValue) {
return;
}this.properties[name] = newValue;
this.mountReactApp();
}
attribute变化时,重新挂载组件,此时触发的是web components原生的attributeChangedCallback。
public attributeChangedCallback(name: string, oldValue: string, newValue: string) {
if (!this.hasConnected) {
return;
}if (oldValue =https://www.it610.com/article/== newValue) {
return;
}if (!factory.componentAttributes.hasOwnProperty(name)) {
return;
}const propertyName = factory.componentAttributes[name].property;
this.properties[propertyName] = getSerialized(newValue);
this.mountReactApp();
}
创建一个React组件 【WebComponents框架direflow实现原理】对应上面的this.mountReactApp。
{React.createElement(factory.rootComponent, this.reactProps(), anonymousSlot)}
EventProvider-创建了一个Event Context包裹组件,用于web components组件与外部通信。
factory.rootComponent-将DireflowComponent.create的component传入,作为根组件,并且通过React.createElement去创建。
this.reactProps()-获得序列化后的属性。为什么要序列化,因为html标签的attribute,只接收string类型。因此需要通过JSON.stringify()序列化传值,工厂函数内部会做JSON.parse。将attribute转化为property
anonymousSlot-匿名slot,插槽。可以直接将内容分发在web component标签内部。
挂载React应用到web component
const root = createProxyRoot(this, shadowChildren);
ReactDOM.render({applicationWithPlugins} , this);
代理组件将React组件作为children,ReactDOM渲染这个代理组件。
web component挂载到DOM时,挂载React App
public connectedCallback() {
this.mountReactApp({ initial: true });
this.hasConnected = true;
factory.connectCallback?.(this);
}
创建一个代理组件
主要是将Web Component化后的React组件,挂载到web component的shadowRoot。
const createProxyComponent = (options: IComponentOptions) => {
const ShadowRoot: FC = (props) => {
const shadowedRoot = options.webComponent.shadowRoot
|| options.webComponent.attachShadow({ mode: options.mode });
options.shadowChildren.forEach((child) => {
shadowedRoot.appendChild(child);
});
return {props.children};
};
return ShadowRoot;
};
获取到shadowRoot,没有的话attachShadow新建一个shadow root。
将子结点添加到shadow root。
返回一个挂载组件到shadow root的Portal,接收children的高阶函数。
完整构建步骤
一个完整的direflow web component组件,包含以下步骤。
- 创建一个web component标签
- 创建一个React组件,将attributes转化为properties属性转化并传入React组件(通过Object.defineProperty做劫持,通过attributeChangedCallback做attribute实时刷新)
- 将这个React应用,挂载到web component的shadowRoot
文章图片
思考
为什么要每一次attribute变化都要重新挂载React App?不能把它看做一个常规的react组件吗,使用react自身的刷新能力?
因为direflow的最终产物,是一个web component组件。
attribute变化,react是无法自动感知到这个变化的,因此需要通过监听attribute变化去重新挂载React App。
但是!React组件内部,是完全可以拥有响应式能力的,因为
direflow是一个什么框架?
其实,direflow本质上,是一个 React组件 + web component +web component属性变化重新挂载React组件的 web component框架。
所以,direflow的响应式其实分为2块:
组件内部响应式(通过React自身响应式流程),组件外部响应式(WebComponents属性变化监听重渲染组件)。
如果外部属性不会经常变化的话,性能这块没有问题,因为组件内部的响应式完全是走了React自身的响应式。
属性外部属性如果会经常变化的话,direflow框架在这块还有一定的优化空间。
推荐阅读
- Android页面路由框架 —— ARouter
- 02 Django框架基础(APP的创建访问)
- 最接近原生APP体验的高性能前端框架——MUI
- 跨平台移动端APP开发---简单高效的MUI框架
- 最接近原生APP体验的高性能前端框架-MUI
- Android编程入门--开源框架Gson
- Android编程入门--开源框架OKHttp
- Android编程入门--开源框架EventBus
- Android插件化框架
- ASP.NET Core(使用Dapper和SwaggerUI来丰富你的系统框架)