React远程动态组件怎么实现

寻技术 React 2023年07月12日 141

这篇文章主要介绍了React远程动态组件怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇React远程动态组件怎么实现文章都会有所收获,下面我们一起来看看吧。

    远程动态组件实现

    远程动态组件库

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

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

    我们简单实现了两个组件

    Button
    Text
    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

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

    关闭

    用微信“扫一扫”