React 远程动态组件实践示例详解

 更新时间:2023年03月27日 13:00:34   作者:Aaaaaaaaaaayou  
这篇文章主要为大家介绍了React 远程动态组件实践示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

背景

想象有这样一个场景:A 团队开发了一套组件库,B 和 C 团队都在各自的业务项目中使用了该组件库。现在 A 团队需要对某个组件进行更新(比如修改颜色),按照以往的做法,A 团队需要先发布一个新的版本,然后其他两个团队各自更新业务项目中所依赖的组件库的版本后发布上线。

有没有更快速的方法呢?比如能否做到只更新组件库,其他依赖它的项目可以自动获取到其最新的版本,即实现远程动态组件。答案是有的,Webpack 5 新增的 Module Federation 就可以实现这个需求,但是今天我们要讨论的是另外一种方法。

远程动态组件实现

远程动态组件库

远程动态组件库项目结构如下所示:

.
├── babel.config.js
├── package.json
├── rollup.config.js
└── src
    ├── Button.js
    ├── Text.js

我们简单实现了两个组件 ButtonText

import React from 'react'
export default ({children}) => {
  return <button style={{color: 'blue'}}>{children}</button>
}
import React from 'react'
export default ({children}) => {
  return <span style={{color: 'blue'}}>{children}</span>
}

我们使用 rollup 对其进行打包,之所以用 rollup 是因为其打包出来的代码非常简洁,方便我们查看,rollup 配置为:

import babel from 'rollup-plugin-babel'
import fs from 'fs'
const components = fs.readdirSync('./src')
export default components.map((filename) => {
  return {
    input: `./src/${filename}`,
    output: {
      file: `dist/${filename}`,
      format: 'cjs',
    },
    plugins: [babel()],
  }
})

打包后的结果如下所示:

.
├── dist
│   └── Button.js
│   └── Text.js

其中 Button.js 如下所示:

'use strict'
var React = require('react')
function _interopDefaultLegacy(e) {
  return e && typeof e === 'object' && 'default' in e ? e : {default: e}
}
var React__default = /*#__PURE__*/ _interopDefaultLegacy(React)
var Button = function (_ref) {
  var children = _ref.children
  return /*#__PURE__*/ React__default['default'].createElement(
    'button',
    {
      style: {
        color: 'blue',
      },
    },
    children
  )
}
module.exports = Button

然后我们使用 http-server 在 dist 目录下开启一个静态文件服务,则可以通过 http://localhost:8080/Button.js 获取到打包后的代码。

远程组件库介绍完毕,接下来介绍业务项目中如何使用。

接入远程组件库

我们使用 create-react-app 创建一个 React 应用,并新增一个 DynamicComponent 组件:

const DynamicComponent = ({name, children, ...props}) => {
  const Component = useMemo(() => {
    return React.lazy(async () => fetchComponent(name))
  }, [name])
  return (
    <Suspense
      fallback={
        <div style={{alignItems: 'center', justifyContent: 'center', flex: 1}}>
          <span style={{fontSize: 50}}>Loading...</span>
        </div>
      }>
      <Component {...props}>{children}</Component>
    </Suspense>
  )
}
export default React.memo(DynamicComponent)

这里使用到了 React 中的 Suspense 组件和 React.lazy 方法,关于他们的用法这里不做过多解释,整个 DynamicComponent 组件的含义是远程加载目标组件,这个过程该组件会渲染传入 Suspense 参数 fallback 之中的内容,最后会使用加载成功的组件进行替换。接下来看看 fetchComponent 是如何实现的:

const fetchComponent = async (name) => {
  const text = await fetch(
    `http://127.0.0.1:8080/${name}.js?ts=${Date.now()}`
  ).then((a) => {
    if (!a.ok) {
      throw new Error('Network response was not ok')
    }
    return a.text()
  })
  const module = getParsedModule(text, name)
  return {default: module.exports}
}

该方法会发起网络请求得到组件的代码,并交给 getParsedModule 去解析,最后得到模块返回。我们来看一下 getParsedModule 是怎么实现的:

const packages = {
  react: React,
}
const getParsedModule = (code) => {
  let module = {
    exports: {},
  }
  const require = (name) => {
    return packages[name]
  }
  Function('require, exports, module', code)(require, module.exports, module)
  return module
}

这里使用 Function 来运行传入的代码,因为打包远程组件的时候并没有将 react 库打包进去,所以这里需要实现 require 这个方法。

我们结合之前打包得到的 Button.js 来看这段代码,它其实同下面这个代码是等价的:

const packages = {
  react: React,
}
const getParsedModule = (code, moduleName) => {
  let module = {
    exports: {},
  }
  const require = (name) => {
    return packages[name]
  }
  ;((require, exports, module) => {
    'use strict'
    var React = require('react')
    function _interopDefaultLegacy(e) {
      return e && typeof e === 'object' && 'default' in e ? e : {default: e}
    }
    var React__default = /*#__PURE__*/ _interopDefaultLegacy(React)
    var Button = function (_ref) {
      var children = _ref.children
      return /*#__PURE__*/ React__default['default'].createElement(
        'button',
        {
          style: {
            color: 'blue',
          },
        },
        children
      )
    }
    module.exports = Button
  })(require, module.exports, module)
  return module
}

最后我们可以按照如下方式来使用 DynamicComponent 组件:

import DynamicComponent from './DynamicComponent'
function App() {
  return (
    <div>
      <DynamicComponent name='Button'>Click Me</DynamicComponent>
      <DynamicComponent name='Text'>Remote Component</DynamicComponent>
    </div>
  )
}
export default App

现在我们尝试修改远程动态组件库中的组件,重新打包后就可以马上看到修改后的效果了。

总结

本文介绍了一种实现远程动态组件的方式,不过比较简陋,事实上我们还有很多优化的空间。比如按照现在的实现方式,如果页面上面使用了两个 Button,会发起两次请求,这显然不合理。针对这个问题,我们可以通过提前加载以及模块缓存的方式来解决。

以上就是React 远程动态组件实践示例详解的详细内容,更多关于React 远程动态组件的资料请关注脚本之家其它相关文章!

相关文章

  • React拖拽调整大小的组件

    React拖拽调整大小的组件

    这篇文章主要为大家详细介绍了React拖拽调整大小的组件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • React 状态管理工具优劣势示例分析

    React 状态管理工具优劣势示例分析

    这篇文章主要为大家介绍了React 状态管理工具优劣势示例分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • React SSR样式及SEO的实践

    React SSR样式及SEO的实践

    这篇文章主要介绍了React SSR样式及SEO的实践,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10
  • React+Mobx基本使用、模块化操作

    React+Mobx基本使用、模块化操作

    React 和 MobX 是一对强力组合,React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染,而MobX提供机制来存储和更新应用状态供 React 使用,这篇文章主要介绍了React+Mobx基本使用、模块化,需要的朋友可以参考下
    2022-09-09
  • 基于webpack4.X从零搭建React脚手架的方法步骤

    基于webpack4.X从零搭建React脚手架的方法步骤

    这篇文章主要介绍了基于webpack4.X从零搭建React脚手架的方法步骤,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • React父组件调用子组件中的方法实例详解

    React父组件调用子组件中的方法实例详解

    最近做一个React项目,所有组件都使用了函数式组件,遇到一个父组件调用子组件方法的问题,下面这篇文章主要给大家介绍了关于React父组件调用子组件中方法的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • React中的useState和setState的执行机制详解

    React中的useState和setState的执行机制详解

    这篇文章主要介绍了React中的useState和setState的执行机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • React中引入less、less-loader问题

    React中引入less、less-loader问题

    这篇文章主要介绍了React中引入less、less-loader问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • 使用React+SpringBoot开发一个协同编辑的表格文档实现步骤

    使用React+SpringBoot开发一个协同编辑的表格文档实现步骤

    随着云计算和团队协作的兴起,协同编辑成为了许多企业和组织中必不可少的需求,本文小编就将为大家介绍如何使用React+SpringBoot简单的开发一个协同编辑的表格文档,感兴趣的朋友一起看看吧
    2023-11-11
  • ReactNative实现弧形拖动条的代码案例

    ReactNative实现弧形拖动条的代码案例

    本文介绍了ReactNative实现弧形拖动条,本组件使用到了react-native-svg和PanResponder,结合示例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2024-02-02

最新评论