如何将ReactJS+TypeScript组件导出给Vanilla JS和HTML使用?
将React组件导出为Vanilla JS/HTML可用的独立产物(无需引入React)
下面是几种简便可行的方案,无需react-scripts eject:
方案1:打包为Web Components(自定义元素)
Web Components是原生标准,可直接在纯HTML/JS中使用,完全不需要依赖React。步骤如下:
- 安装打包依赖
npm install @babel/core @babel/preset-react @babel/preset-env rollup rollup-plugin-babel rollup-plugin-postcss rollup-plugin-terser --save-dev
- 配置Rollup
在项目根目录创建rollup.config.js:
import babel from 'rollup-plugin-babel'; import postcss from 'rollup-plugin-postcss'; import { terser } from 'rollup-plugin-terser'; export default { input: 'src/YourMainComponent.jsx', // 你的主组件入口 output: { file: 'dist/your-custom-element.js', format: 'iife', // 生成浏览器可直接运行的自执行函数 name: 'YourComponentBundle' }, plugins: [ babel({ presets: ['@babel/preset-react', '@babel/preset-env'], exclude: 'node_modules/**' }), postcss({ inject: true, // 把CSS打包进JS,无需单独引入样式文件 minimize: true }), terser() // 压缩代码 ] };
- 改造组件为自定义元素
修改你的主组件文件,用原生自定义元素API包裹:
import React from 'react'; import ReactDOM from 'react-dom/client'; import YourChildComponent from './YourChildComponent'; import './YourComponent.css'; // 自定义元素类 class YourCustomElement extends HTMLElement { connectedCallback() { // 当元素被添加到DOM时渲染React组件 const root = ReactDOM.createRoot(this); root.render(<YourComponent {...this.getPropsFromAttrs()} />); } // 将HTML属性转为React props getPropsFromAttrs() { return Object.fromEntries( Array.from(this.attributes).map(attr => [attr.name, attr.value]) ); } } // 注册自定义元素 customElements.define('your-component', YourCustomElement);
- 打包并使用
运行打包命令:
npx rollup -c
然后在纯HTML中直接使用:
<script src="dist/your-custom-element.js"></script> <!-- 直接用自定义标签,属性会自动转为组件props --> <your-component title="示例标题" desc="这是一个React转Web组件的示例"></your-component>
方案2:用Vite打包为独立JS库(内置React)
如果不想改造成Web组件,可以用Vite的库模式把React和组件一起打包成单个JS文件,浏览器直接引入即可:
- 配置Vite
创建vite.config.js:
import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; export default defineConfig({ plugins: [react()], build: { lib: { entry: 'src/YourComponent.jsx', name: 'YourComponent', formats: ['iife'], fileName: () => 'your-component.bundle.js' }, rollupOptions: { // 将React和ReactDOM打包进产物,不依赖外部引入 external: [], output: { globals: { react: 'React', 'react-dom': 'ReactDOM' } } } } });
- 打包并使用
运行npm run build,生成的dist/your-component.bundle.js可直接在HTML中使用:
<script src="dist/your-component.bundle.js"></script> <script> // 手动挂载组件到DOM const container = document.getElementById('app'); ReactDOM.createRoot(container).render(React.createElement(YourComponent, { title: 'Hello' })); </script> <div id="app"></div>
方案3:替换React为轻量替代库(如Preact)
Preact体积仅3KB,API和React几乎完全兼容,迁移成本极低,打包后的产物更小:
- 替换依赖
npm uninstall react react-dom npm install preact preact-render-to-string
- 修改组件导入
把所有React相关导入替换为Preact:
// 原代码 import React from 'react'; import ReactDOM from 'react-dom'; // 替换后 import { h } from 'preact'; import { render } from 'preact';
- 打包
用Rollup或Vite按照方案1/2的方式打包,最终产物包含Preact,直接在HTML中引入使用即可。
方案4:Parcel零配置打包
Parcel无需复杂配置,适合快速打包:
- 安装Parcel
npm install -D parcel
- 创建入口文件
新建src/index.jsx,内容为组件挂载逻辑(可参考方案1的自定义元素写法)。 - 打包
npx parcel build src/index.jsx --no-source-maps --dist-dir dist
生成的dist/index.js可直接在HTML中引入使用。
关键注意点
- 确保打包时将所有依赖(包括React/Preact、CSS、子组件)都打包进单个文件,避免外部依赖。
- 如果组件用到状态管理库(如Redux),需将其也加入打包流程。
内容的提问来源于stack exchange,提问作者ibrahimtuna
相关产品推荐
相关产品推荐

