这个框架反对React形式写WebComponents。
框架地址:https://github.com/Silind-Sof...

假如有这样一个web component组件。

<test-component name="jack" age="18" />

direflow的配置如下:

import { DireflowComponent } from "direflow-component";import  App 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 = 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 === 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 === newValue) {    return;  }  if (!factory.componentAttributes.hasOwnProperty(name)) {    return;  }  const propertyName = factory.componentAttributes[name].property;  this.properties[propertyName] = getSerialized(newValue);  this.mountReactApp();}

创立一个React组件

对应下面的this.mountReactApp。

<EventProvider>  {React.createElement(factory.rootComponent, this.reactProps(), anonymousSlot)}<EventProvider>

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(<root.open>{applicationWithPlugins}</root.open>, 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<IShadowComponent> = (props) => {    const shadowedRoot = options.webComponent.shadowRoot      || options.webComponent.attachShadow({ mode: options.mode });    options.shadowChildren.forEach((child) => {      shadowedRoot.appendChild(child);    });    return <Portal targetElement={shadowedRoot}>{props.children}</Portal>;  };  return ShadowRoot;};

获取到shadowRoot,没有的话attachShadow新建一个shadow root。
将子结点增加到shadow root。
返回一个挂载组件到shadow root的Portal,接管children的高阶函数。

残缺构建步骤

一个残缺的direflow web component组件,蕴含以下步骤。

  1. 创立一个web component标签
  2. 创立一个React组件,将attributes转化为properties属性转化并传入React组件(通过Object.defineProperty做劫持,通过attributeChangedCallback做attribute实时刷新)
  3. 将这个React利用,挂载到web component的shadowRoot

<img width="592" alt="direflow" src="https://user-images.githubusercontent.com/19262750/165549797-8cec26b6-2e30-4b67-90db-744b8a1a1331.png">

思考

为什么要每一次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框架在这块还有肯定的优化空间。