You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过CDN在React项目中复用远程React组件?

从CDN加载React组件(含TypeScript/Hooks)的可行方案

问题背景

我有一个名为TEST.TSX的React组件,已编译为.js并上传至GitHub,可通过CDN访问。想在另一个React应用中直接通过该CDN使用这个组件,但尝试过Module Federation(额外托管成本太高)、NextJS打包脚本(失败)、直接加载编译后的JS(本地import可用但CDN加载失败)等方法都没成功。也不能用iframe,本质是想直接从CDN托管的文件(.TSX/.JSX/.TS/.JS)使用React组件,包括那些用TypeScript函数和React Hooks的组件。

当前的webpack配置、导出脚本、组件代码以及尝试的CDN加载代码如下:

当前webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin');
const { ModuleFederationPlugin } = require('webpack').container;
const deps = require("./package.json").dependencies;
const path = require('path');
module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './app/export-component.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'exports.js',
    library: 'MyLibrary', // Set a name for the library
    libraryTarget: 'umd', // Set the module type (Universal Module Definition)
    globalObject: 'this', // To support both browser and Node.js environments
  },
  devServer: {
    port: 3001,
    historyApiFallback: true,
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx|tsx|ts)$/,
        use: [
            {
              loader: 'babel-loader',
              options: {
                presets: ['solid'],
              },
            },
            {
              loader: 'ts-loader',
            },
         ],
          exclude: /node_modules/,
      },
    ],
  },  
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
};

export-component.js

// export-components.js
import DemoClientComponent from './components/DemoClientComponent';
import PayButton from './components/PayButton';
// Option 1: Export individual components
export { DemoClientComponent ,PayButton};

// Option 2: Export as an object (if applicable)
export default function ExportComponents() {
    DemoClientComponent,
    PayButton
};

DemoClientComponent.tsx

import React from 'react';

const DemoClientComponent = () => (
  <>
    <h1>Hello from Braintree</h1>
  </>
);

export default DemoClientComponent;

尝试的CDN加载代码(layout.tsx)

"use client"
import { useEffect, useState } from "react";
const RemoteFetch = () => {
  const [innerHtml, setInnerhtml] = useState(); 
  useEffect(() => {
    const innerHtml = window.MyLibrary.DemoClientComponent();
    setInnerhtml(innerHtml)
  }, []);

return (
  <>
  {innerHtml && innerHtml.outerHTML && <div dangerouslySetInnerHTML={{__html : innerHtml.outerHTML}}>

  </div>}
  </>
);};

export default RemoteFetch;

解决方案

1. 修复webpack配置的核心错误

你的webpack配置里babel用了solid预设(针对SolidJS),而非React的预设,这会导致React组件编译错误。同时需要把React设为外部依赖,避免打包到输出文件中(由宿主应用提供React)。修改后的webpack.config.js:

const path = require('path');
module.exports = {
  mode: 'production',
  devtool: 'source-map',
  entry: './app/export-component.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'exports.js',
    library: 'MyLibrary',
    libraryTarget: 'umd',
    globalObject: 'this',
    umdNamedDefine: true, // 确保UMD导出能被浏览器和模块系统正确识别
  },
  externals: {
    // 声明React为外部依赖,由宿主应用提供
    react: 'react',
    'react-dom': 'react-dom'
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx|tsx|ts)$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              // 替换为React的babel预设
              presets: ['@babel/preset-react', '@babel/preset-typescript'],
            },
          },
          'ts-loader',
        ],
        exclude: /node_modules/,
      },
    ],
  },  
  resolve: {
    extensions: ['.tsx', '.ts', '.js', '.jsx'],
  },
};

2. 修正组件导出脚本

确保导出的是React组件本身,而非无效的默认函数。修改export-component.js:

// export-components.js
import DemoClientComponent from './components/DemoClientComponent';
import PayButton from './components/PayButton';

// 直接导出组件,确保UMD能正确暴露它们
export { DemoClientComponent, PayButton };

// 可选:默认导出包含组件的对象
export default {
  DemoClientComponent,
  PayButton
};

3. 正确加载CDN组件(支持Hooks)

不要用dangerouslySetInnerHTML,React组件返回的是React元素而非DOM节点,直接渲染即可。同时要确保CDN脚本加载完成后再访问window.MyLibrary:

"use client"
import { useEffect, useState } from "react";

const RemoteFetch = () => {
  const [Components, setComponents] = useState<{
    DemoClientComponent?: React.FC;
    PayButton?: React.FC;
  }>({});

  useEffect(() => {
    // 动态加载CDN脚本
    const script = document.createElement('script');
    script.src = '你的CDN脚本地址';
    script.async = true;
    script.onload = () => {
      // 脚本加载完成后,获取导出的组件
      setComponents(window.MyLibrary);
    };
    document.body.appendChild(script);

    return () => {
      document.body.removeChild(script);
    };
  }, []);

  // 直接渲染React组件,Hooks会正常工作
  const DemoClientComponent = Components.DemoClientComponent;
  return (
    <>
      {DemoClientComponent && <DemoClientComponent />}
    </>
  );
};

export default RemoteFetch;

4. 处理TypeScript类型(可选)

如果需要类型支持,可以把组件的类型定义单独打包成.d.ts文件上传到CDN,然后在宿主应用中通过declare module引入:

// 在宿主应用中创建类型声明文件
declare module 'your-cdn-library' {
  import React from 'react';
  export const DemoClientComponent: React.FC;
  export const PayButton: React.FC;
}

关键说明

  • 必须确保宿主应用和组件使用的React版本兼容,避免双React实例导致的问题
  • 组件中使用的Hooks会正常工作,因为组件是在宿主应用的React环境中渲染的
  • 打包时把React设为外部依赖,能大幅减小CDN脚本体积,同时避免版本冲突

内容的提问来源于stack exchange,提问作者Pradeep Yadav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 15:40:20