direflow落地分享-以React方式写WebComponents

简介: direflow落地分享-以React方式写WebComponents
React大家都很熟悉,WebComponents估计也有所耳闻。那么React+WebComponents会碰撞出怎样的火花呢?

其实有这样一个开源框架,支持React方式写组件,最终打包后的产物为WebComponents。

它就是direflow,这个框架支持React方式写WebComponents。

框架地址:https://github.com/Silind-Sof...



为什么选择direflow


开源社区有很多WebComponents框架,比如stencil、lit等等,这些框架社区活跃度高、落地实践多,但是它们都存在一些不符合我们场景的问题:

  • 都具有自己的一套DSL,具有一定的学习成本
  • 缺少基于AntD等React技术栈的最佳实践

而基于React实现的direflow,可以完美避免上面的问题。

  • React方式写组件,零学习成本增加
  • 基于Webpack打包,AntD等React技术栈可以直接使用


落地分析


落地WebComponents有一段时间了,目前我们落地了几个场景,对开发效率提升是非常大的,极大程度地降低了业务线接入成本,因此阶段性地做一个总结。



image.png


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

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


原理分析


完整构建步骤


一个完整的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://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7062464f9cfb469f9b92f63c471108c3~tplv-k3u1fbpfcp-zoom-1.image">


下面再来详细分析一下:


假设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) {
  this.properties[name] = newValue;
  this.mountReactApp();
}


attribute变化时,重新挂载组件,此时触发的是web components原生的attributeChangedCallback。

public attributeChangedCallback(name: string, oldValue: string, newValue: string) {
  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化后的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的高阶函数。


思考


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



相关文章
|
前端开发 JavaScript 安全
从 React Hooks 理解 Web components 的生命周期
从 React Hooks 理解 Web components 的生命周期
504 0
|
2月前
|
前端开发 JavaScript 开发者
深入理解React Hooks:提升前端开发效率的关键
【10月更文挑战第5天】深入理解React Hooks:提升前端开发效率的关键
|
22天前
|
前端开发 JavaScript 开发者
颠覆传统:React框架如何引领前端开发的革命性变革
【10月更文挑战第32天】本文以问答形式探讨了React框架的特性和应用。React是一款由Facebook推出的JavaScript库,以其虚拟DOM机制和组件化设计,成为构建高性能单页面应用的理想选择。文章介绍了如何开始一个React项目、组件化思想的体现、性能优化方法、表单处理及路由实现等内容,帮助开发者更好地理解和使用React。
59 9
|
2月前
|
前端开发
深入解析React Hooks:构建高效且可维护的前端应用
本文将带你走进React Hooks的世界,探索这一革新特性如何改变我们构建React组件的方式。通过分析Hooks的核心概念、使用方法和最佳实践,文章旨在帮助你充分利用Hooks来提高开发效率,编写更简洁、更可维护的前端代码。我们将通过实际代码示例,深入了解useState、useEffect等常用Hooks的内部工作原理,并探讨如何自定义Hooks以复用逻辑。
|
2月前
|
前端开发 JavaScript API
探索React Hooks:前端开发的革命性工具
【10月更文挑战第5天】探索React Hooks:前端开发的革命性工具
|
2月前
|
前端开发 数据管理 编译器
引领前端未来:React 19的重大更新与实战指南🚀
React 19 即将发布,带来一系列革命性的新功能,旨在简化开发过程并显著提升性能。本文介绍了 React 19 的核心功能,如自动优化重新渲染的 React 编译器、加速初始加载的服务器组件、简化表单处理的 Actions、无缝集成的 Web 组件,以及文档元数据的直接管理。这些新功能通过自动化、优化和增强用户体验,帮助开发者构建更高效的 Web 应用程序。
133 1
引领前端未来:React 19的重大更新与实战指南🚀
|
27天前
|
前端开发 JavaScript Android开发
前端框架趋势:React Native在跨平台开发中的优势与挑战
【10月更文挑战第27天】React Native 是跨平台开发领域的佼佼者,凭借其独特的跨平台能力和高效的开发体验,成为许多开发者的首选。本文探讨了 React Native 的优势与挑战,包括跨平台开发能力、原生组件渲染、性能优化及调试复杂性等问题,并通过代码示例展示了其实际应用。
53 2
|
29天前
|
前端开发 JavaScript 开发者
React与Vue:前端框架的巅峰对决与选择策略
【10月更文挑战第23天】React与Vue:前端框架的巅峰对决与选择策略
|
29天前
|
前端开发 JavaScript 开发者
“揭秘React Hooks的神秘面纱:如何掌握这些改变游戏规则的超能力以打造无敌前端应用”
【10月更文挑战第25天】React Hooks 自 2018 年推出以来,已成为 React 功能组件的重要组成部分。本文全面解析了 React Hooks 的核心概念,包括 `useState` 和 `useEffect` 的使用方法,并提供了最佳实践,如避免过度使用 Hooks、保持 Hooks 调用顺序一致、使用 `useReducer` 管理复杂状态逻辑、自定义 Hooks 封装复用逻辑等,帮助开发者更高效地使用 Hooks,构建健壮且易于维护的 React 应用。
32 2
|
29天前
|
前端开发 JavaScript 数据管理
React与Vue:两大前端框架的较量与选择策略
【10月更文挑战第23天】React与Vue:两大前端框架的较量与选择策略