用react构建微前端

方德宇
2023-12-01
  • Do you have an application using an old version of Angular or an HTML templating engine that you want to add React components to?

    您是否有要使用React组件的旧版本的Angular或HTML模板引擎的应用程序?
  • How about working with multiple dev teams that prefer different UI frameworks, but are working on the same UI project?

    与多个喜欢使用不同UI框架但在同一个UI项目上工作的开发团队一起工作如何?
  • Have you ever wanted to share dynamic content with a third party partner that can be updated in real time without requiring the partner to make changes?

    您是否曾经想过与第三方合作伙伴共享动态内容,这些内容可以实时更新而无需合作伙伴进行更改?
  • Do you want a way to allow the marketing team to build and maintain static websites, using React components, themselves without needing to rely on a dev team for every change?

    您是否想让营销团队自己使用React组件来构建和维护静态网站,而无需依赖开发团队进行任何更改?

If any of those interest you or you just want to know how to easily bundle existing React components so they can be used in any application than keep reading.

如果您有任何兴趣,或者您只是想知道如何轻松捆绑现有的React组件,那么它们可以在任何应用程序中使用,而不是继续阅读。

TL; DR: (TL;DR:)

This micro frontend pattern takes any existing react component and exposes a wrapper around it that allows the component to be bundled, shared and used in any other app. Using this pattern allows for incremental upgrades, decoupled codebases, independent deployments, and autonomous teams which are some of the great benefits of micro frontends that the article by Cam Jackson on Martin Fowlers Blog discusses in more detail.

这个微型前端模式采用了任何现有的react组件,并在其周围暴露了一个包装器,该包装器可以将该组件捆绑,共享并在任何其他应用程序中使用。 使用这种模式可以进行增量升级,解耦代码库,独立部署和自治团队,这是微型前端的一些巨大好处,Cam Jackson在Martin Fowlers Blog上的文章对此进行了详细讨论。

高级步骤: (High Level Steps:)

  1. Create a new file for each component you want to share that wraps the component with a `ReactDOM.render()` call

    为您要共享的每个组件创建一个新文件,并使用`ReactDOM.render()`调用包装该组件
  2. Utilize rollup’s ability of taking in multiple inputs to iterate over each component to bundle them into their own separate JS files.

    利用汇总功能,可以接收多个输入以迭代每个组件,以将它们捆绑到各自独立的JS文件中。

  3. Upload the bundled JS files to any object storage solution like S3.

    将捆绑的JS文件上传到任何对象存储解决方案(如S3)。
  4. Add a div and script HTML element referencing the JS file to add the React component (see examples below)

    添加引用JS文件的div和脚本HTML元素以添加React组件(请参见下面的示例)
  5. Add unpkg.com script tags for react and react-dom to the head of your main “index.html” file (not needed if adding to an existing React app)

    将用于react和react-dom的unpkg.com脚本标签添加到主“ index.html ”文件的头部(如果添加到现有的React应用程序则不需要)

Check out the CodeSandbox links at the end of this article to see live code examples using this pattern within Angular, Vue.JS, and React apps.

请查看本文结尾处的CodeSandbox链接,以在AngularVue.JSReact应用程序中使用此模式查看实时代码示例。

将元素添加到纯HTML应用程序的示例代码片段(第4步): (Example snippet to add an element to a pure HTML app (step 4):)

<div
id="ReactDynamicCard"
data-card-image="https://reactjs.org/logo-og.png"
data-card-header="React"
data-card-sub-text="My preferred JS library for building UIs"
data-card-link="https://reactjs.org/"
></div><script
data-id="ReactDynamicCard"
src="https://universal-component-library.s3.amazonaws.com/DynamicCard.js"
></script>

For frameworks that do not allow script HTML tags (React, Angular and Vue), you can add a pure JS code snippet like below instead of the script tag:

对于不允许脚本HTML标签(React,Angular和Vue)的框架,您可以添加如下所示的纯JS代码片段,而不是script标签:

const script = document.createElement('script');
script.setAttribute('data-id', 'ReactDynamicCard');
script.src = 'https://universal-component-library.s3.amazonaws.com/CustomAppBar.js';
document.body.appendChild(script);

示例unpkg.com脚本元素(步骤5): (Example unpkg.com script elements (step 5):)

<script
src="https://unpkg.com/react@16.11.0/umd/react.production.min.js">
</script>
<script
src="https://unpkg.com/react-dom@16.11.0/umd/react-dom.production.min.js">
</script>

完整的演练 (Full Walk Through)

This walkthrough references code examples from my universal-components GitHub repo. It shows this pattern in use and how the components are structured, built, uploaded, and demoed/documented using Storybook.

本演练参考了我的通用组件GitHub repo中的代码示例。 它显示了正在使用的这种模式,以及如何使用Storybook构造,构建,上载和演示/记录组件。

Prerequisite: - Have existing React component(s) or page(s) that you want to share- Use rollup as the build tool. Parcel or Webpack bundled apps can be converted to use Rollup, but this article is written assuming that is done.

前提条件: -具有要共享的现有React组件或页面-将汇总用作构建工具。 可以将Parcel或Webpack捆绑的应用程序转换为使用汇总,但是本文是在假定已完成的情况下编写的。

Note: Rollup is used since it has the ability to pass multiple entry points into the configuration, which is required, if wanting to bundle multiple components into their own separate JS file.

注意:使用汇总是因为它具有将多个入口点传递到配置中的能力,如果要将多个组件捆绑到它们自己的单独JS文件中,则必须使用汇总。

现有仓库中需要添加的内容 (Additions needed within your existing repo)

1.) Create a new file called “widget.jsx” in the existing component’s directory.

1.)在现有组件的目录中创建一个名为“ widget.jsx ”的新文件。

Note: The name of the file is the naming convention utilized by rollup in this example. When rollup iterates over each components folder it looks for a specific file name to exist, but that name can be anything instead of “widget”, if desired.

注意:在此示例中,文件名是汇总使用的命名约定。 当汇总遍历每个组件文件夹时,它会寻找一个特定的文件名,但是如果需要,该名称可以是任何东西,而不是“ widget”。

2.) Add import statements for your existing component, React, and ReactDOM.

2.)为现有组件React和ReactDOM添加import语句。

3.) Create a variable that pulls the “data-id” property from current script element.

3.)创建一个变量,从当前脚本元素中提取“ data-id ”属性。

4.) Create a variable that gets an element by id using the “data-id” value in the previous step.

4.)创建一个变量,该变量在上一步中使用“ data-id ”值通过id获取元素。

Note: The built components will be referenced in other applications via a script element with the “data-id” attribute that maps to the “id” attribute of the div element where the React component should be rendered.

注意:生成的组件将在其他应用程序中通过脚本元素被引用,该脚本元素具有“ data-id ”属性,该属性映射到应渲染React组件的div元素的“ id ”属性。

5.) Add a call to the “ReactDOM.render()” function passing in your existing component and the element retrieved in the previous step.

5.)将调用添加到“ ReactDOM.render() ”函数中,以传入您现有的组件以及在上一步中检索到的元素。

6.) Optional: if your component has any props that should be passed then spread the div elements “dataset” field within your components JSX syntax.

6.)可选:如果您的组件具有任何应传递的道具,则在组件JSX语法内散布div元素的“数据集”字段。

Note: The dataset property on an element maps all “data-*” attributes from that element to a DOMStringMap which is very similar to a JSON object except only strings can be used for values. For example, a value of false will get converted to a string of the word false. This piece allows the consuming application to pass properties into a div element that the React component will consume as props during rendering.

注意:元素上的数据集属性将所有“ data- * ”属性从该元素映射到DOMStringMap ,该对象与JSON对象非常相似,只不过字符串可用于值。 例如,false值将转换为单词false的字符串。 这段代码允许使用中的应用程序将属性传递到div元素中,在渲染过程中,React组件将这些属性用作道具。

7.) Repeat all of the previous steps for each component within your repo.

7.)对仓库中的每个组件重复上述所有步骤。

Example “widget.jsx” file after completing steps 1–6:

完成步骤1-6后,示例“ widget.jsx”文件:

import React from 'react';
import ReactDOM from 'react-dom';
import DynamicCard from './DynamicCard';const widgetId = document.currentScript.dataset.id;
const widget = document.getElementById(widgetId);ReactDOM.render(
(<DynamicCard {...widget.dataset} />),
widget,
);

This one new file per component is all that is needed as far as modifications go to the existing source code.

只要对现有源代码进行修改,就只需要每个组件一个新文件。

汇总捆绑器更新: (Rollup bundler updates:)

View the rollup.config.js file in the universal-components-library example repo to see a full example of all the changes discussed in this section.

查看通用组件库示例存储库中的rollup.config.js文件,以查看本节中讨论的所有更改的完整示例。

There are four changes that are needed for the rollup config to create multiple output files compared to a config that produces one bundle of the entire app or library.

与产生整个应用程序或库的捆绑包的配置相比,汇总配置需要创建四个输出文件来进行四个更改。

  1. Create a new variable that contains an array of all the component names you want to bundle separately.

    创建一个新变量,该变量包含要单独捆绑的所有组件名称的数组。

    Create a new variable that contains an array of all the component names you want to bundle separately. Note: This could be automated by using the file system packages and pulling directory names, but this way it allows adding one component at a time instead of an all or nothing approach.

    创建一个新变量,该变量包含要单独捆绑的所有组件名称的数组。 注意:这可以通过使用文件系统软件包并提取目录名称来自动执行,但是通过这种方式,它允许一次添加一个组件,而不是全有或全无的方法。

  2. Change the existing configuration object to be wrapped by a map call that will iterate over every name in the component names array. This allows rollup to iterate its build process over your configuration X amount of times, where X is the number of components you have. In the end producing X amount of separate JS files, one for each component.

    将现有配置对象更改为由映射调用包装,该调用将迭代组件名称数组中的每个名称。 这使汇总可以在您的配置X遍历其构建过程的次数,其中X是您拥有的组件数。 最后产生X个单独的JS文件,每个组件一个。
  3. Change the input and output file names to be dynamic by utilizing the component name variable passed in by the map during each iteration. This tells rollup the directory to look for the “widget.jsx” file as well as the name of the output file.

    通过在每次迭代期间利用映射传递的组件名称变量,将输入和输出文件名更改为动态。 这告诉汇总目录查找“ widget.jsx ”文件以及输出文件的名称。

  4. Add the replace rollup plugin to set the “NODE_ENV” environment variable to production. This variable override is needed to ensure that the React components are bundled in production mode to properly minimize the size of the built files.

    添加replace rollup插件以将“ NODE_ENV”环境变量设置为生产环境。 需要使用此变量覆盖来确保React组件在生产模式下捆绑在一起,以正确最小化构建文件的大小。

将组件添加到公共对象存储工具 (Add components to a public object storage tool)

To allow other applications to utilize the bundled components they need to be accessible to the public. Since the files are bundled into static JS files, they can be stored in any object storage service of your liking.

为了允许其他应用程序使用捆绑的组件,需要公众对其进行访问。 由于文件捆绑在静态JS文件中,因此可以将它们存储在您喜欢的任何对象存储服务中。

For this example, I utilized AWS’s S3 and created an uploadWidget.js script to upload all files that were created during the rollup build process. This script requires four environment variables which are provided when creating a bucket with S3.

在此示例中,我利用了AWS的S3并创建了一个uploadWidget.js脚本来上传在汇总构建过程中创建的所有文件。 该脚本需要使用S3创建存储桶时提供的四个环境变量。

Here is a good medium article walking through the steps to create a S3 bucket and retrieving the needed environment variables.

这是一个很好的中等技巧,它逐步完成了创建S3存储桶并检索所需环境变量的步骤。

在现有应用程序中使用组件 (Use Component in existing application)

Now that your components are built and uploaded to an object storage location you can start consuming them. There is one prerequisite needed prior to your app using the shared components. If you are adding these components to a React app then the prerequisite is already met and can be skipped.

现在,您的组件已构建并上传到对象存储位置,您可以开始使用它们了。 在您的应用使用共享组件之前,有一个先决条件。 如果您要将这些组件添加到React应用程序,则前提条件已经满足,可以跳过。

Prerequisite: Add the react and react-dom script elements (see snippet below) into the head element within the top level index.html file of your app. By adding both dependencies one time in the root HTML page, several of these micro frontend components can now be added anywhere throughout the app.

先决条件:将react和react-dom脚本元素(请参见下面的代码段)添加到应用程序顶级index.html文件中的head元素中。 通过添加依赖关系都在根HTML页面一次,这几条微前端组件现在可以在整个应用程序的任何地方添加。

<script
src="https://unpkg.com/react@16.11.0/umd/react.production.min.js">
</script>
<script
src="https://unpkg.com/react-dom@16.11.0/umd/react-dom.production.min.js">
</script>

Why these scripts are needed: The rollup config in this example sets react and react-dom as peer dependencies. This means that each component requires the consuming application to have both of those dependencies available in order to render on the page. Existing React applications already have them built in, but all other applications need them manually added. Instead of bundling within each component adding substantially to the minified size, just adding it once in the consuming app allows for several components to be added.

为什么需要这些脚本:本示例中的汇总配置将react和react-dom设置为对等依赖项。 这意味着每个组件都要求使用方应用程序同时具有这两个依赖项才能在页面上呈现。 现有的React应用程序已经内置了它们,但是所有其他应用程序都需要手动添加它们。 与其在每个组件中捆绑在一起几乎不会增加最小的大小,只需在使用的应用中将其添加一次就可以添加多个组件。

Step 1:Add a div HTML element (similar to the snippet below) with an id and optional “data-*” attributes in the code where you want it displayed. The “data-*” attributes get mapped to the React component properties as we discussed previously within the “widget.jsx” file that was used as the entry point for bundling the component.

步骤1:在您要显示的代码中添加一个具有id和可选的“ data- * ”属性的div HTML元素(类似于以下代码段)。 正如我们之前在“ widget.jsx ”文件中所讨论的那样,“ data- * ”属性已映射到React组件属性,该文件用作捆绑组件的入口点。

<div
id="widgetId"
data-card-image="https://reactjs.org/logo-og.png"
data-card-header="React"
data-card-sub-text="My preferred JavaScript library for building user interfaces"
data-card-link="https://reactjs.org/"
>
</div>

Step 2:Add script HTML element that will hydrate the div with the shared component within your UI. The script needs a “data-id” attribute that should be set to the same id of the div element added from step 1. It also requires a “src” attribute that should link to the object storage location of the component you are adding. Most UI frameworks do not allow script tags within the code of the UI. If you are using one of those frameworks then reference Snippet B instead of A.

步骤2:添加脚本HTML元素,该元素将div与UI中的共享组件结合在一起。 该脚本需要一个“ data-id ”属性,该属性应设置为与步骤1中添加的div元素相同的id。它还需要一个“ src”属性,该属性应链接到您要添加的组件的对象存储位置。 大多数UI框架不允许UI代码中包含脚本标签。 如果您使用的是这些框架之一,请参考代码段B而不是A。

Snippet A (for Pure HTML apps):

片段A (适用于Pure HTML应用):

<script
data-id="widgetId"
src="https://universal-component-library.s3.amazonaws.com/DynamicCard.js"
>
</script>

Snippet B (for apps using a framework):

片段B (适用于使用框架的应用):

const script = document.createElement('script');
script.setAttribute('data-id', 'widgetId');
script.src = 'https://universal-component-library.s3.amazonaws.com/DynamicCard.js';
document.body.appendChild(script);

Step 3:Start your web application and you will now see your React component running within your existing app.

第三步: 启动您的Web应用程序,您现在将看到React组件在现有应用程序中运行。

实时组件文档 (Live Component Documentation)

Every component library should have some kind of UI documentation. With this pattern you can utilize storybook to view the components when running locally as well as produce the exact code snippet someone would need to copy and use in their consuming application. Check out the live storybook doc created for this example repo as a Github page. You will see both the HTML only snippet and the JS snippet shown in the notes section.

每个组件库都应具有某种UI文档。 通过这种模式,您可以利用故事书在本地运行时查看组件,并生成确切的代码片段,以供有人在其使用的应用程序中复制和使用。 以Github页面的形式查看为该示例存储库创建的实时故事书文档。 您将在注释部分中看到仅HTML片段和JS片段。

翻译自: https://medium.com/@franklsm1/building-micro-frontends-with-react-5c79f6f91bea

 类似资料: