基于Webpack5 Module Federation在Vue3中嵌入React18组件失败求助
Webpack5 Module Federation跨框架集成问题:React组件无法嵌入Vue3应用
问题描述
已创建简单的React HelloComponent,尝试通过Webpack5 Module Federation将其嵌入Vue3应用,但未能成功。React应用运行正常,但Vue无法渲染该组件,尝试多种嵌入方式均无效,寻求解决方案或其他基于Webpack5 Module Federation的跨框架集成方案。
相关配置代码
React Webpack配置(webpack.dev.js)
const { ModuleFederationPlugin } = require('webpack').container; const { merge } = require('webpack-merge'); const commonConfig = require('./webpack.common'); const path = require('path'); const devConfig = { mode: "development", entry: './src/index.jsx', devServer: { port: "5000", }, plugins: [ new ModuleFederationPlugin({ name: 'HelloApp', filename: "remoteEntry.js", exposes: { './HelloComponent': './src/components/HelloComponent', }, shared: { react: { singleton: true, eager: true }, "react-dom": { singleton: true, eager: true } }, }) ], } module.exports = merge(commonConfig, devConfig);
Vue Webpack配置
const HtmlWebPackPlugin = require("html-webpack-plugin"); const { ModuleFederationPlugin } = require('webpack').container; const { VueLoaderPlugin } = require("vue-loader"); const { TransformAsyncModulesPlugin } = require("transform-async-modules-webpack-plugin"); module.exports = { entry: './src/bootstrap.js', output: { publicPath: "http://localhost:3002/", }, resolve: { extensions: [".jsx", ".js", ".json"], }, devServer: { port: 5001, }, externals: { react: 'react', // This tells Vue to use the React instance from the remote container 'react-dom': 'react-dom', }, module: { rules: [ { test: /\.m?js/, type: "javascript/auto", resolve: { fullySpecified: false, }, }, { test: /\.vue$/, loader: "vue-loader", }, { test: /\.css$/i, use: ["style-loader", "css-loader"], }, { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: ['@babel/preset-env'], }, }, }, { test: /\.(jpe?g|png|gif|svg)$/i, loader: 'file-loader', options: { name: 'src/assets/[name].[ext]' } } ], }, plugins: [ new TransformAsyncModulesPlugin(), new VueLoaderPlugin(), new ModuleFederationPlugin({ name: "Vue App", filename: "remoteEntry.js", remotes: { helloApp: 'HelloApp@http://localhost:3000/remoteEntry.js', }, exposes: {}, shared: { react: { singleton: true, eager: true }, "react-dom": { singleton: true, eager: true } }, }), new HtmlWebPackPlugin({ template: "./public/index.html", }), ], };
App.vue
<template> <HelloComponent :name="name" /> </template> <script> // Importing HelloComponent from remote React application const HelloComponent = () => import("HelloApp/HelloComponent"); export default { name: "App", data() { return { name: "World", }; }, components: { HelloComponent, }, }; </script>
React package.json
{ "name": "base-app", "version": "0.1.0", "private": true, "dependencies": { "@babel/core": "^7.24.4", "@babel/preset-env": "^7.24.4", "@babel/preset-react": "^7.24.1", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "axios": "^1.6.8", "babel-loader": "^9.1.3", "html-webpack-plugin": "^5.6.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", "terser-webpack-plugin": "^5.3.10", "web-vitals": "^2.1.4", "webpack": "^5.91.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^4.15.2", "webpack-merge": "^5.10.0" }, "scripts": { "start": "webpack serve --config webpack.dev.js", "build": "webpack build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": [ "react-app", "react-app/jest" ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } }
Vue package.json
{ "name": "vue-app", "version": "0.1.0", "private": true, "scripts": { "start": "webpack-dev-server --open --mode development", "serve": "vue-cli-service serve", "build": "vue-cli-service build", "lint": "vue-cli-service lint" }, "dependencies": { "core-js": "^3.8.3", "vue": "^3.2.13" }, "devDependencies": { "@babel/core": "^7.24.4", "@babel/eslint-parser": "^7.12.16", "@babel/plugin-transform-runtime": "^7.24.3", "@babel/preset-env": "^7.24.4", "@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-eslint": "~5.0.0", "@vue/cli-service": "~5.0.0", "@vue/compiler-sfc": "^3.4.25", "babel-loader": "^9.1.3", "css-loader": "^7.1.1", "eslint": "^7.32.0", "eslint-plugin-vue": "^8.0.3", "file-loader": "^6.2.0", "html-webpack-plugin": "^5.6.0", "react": "^18.3.1", "react-dom": "^18.3.1", "style-loader": "^4.0.0", "transform-async-modules-webpack-plugin": "^1.1.0", "vue-loader": "^17.4.2", "vue-template-compiler": "^2.7.16", "webpack": "^5.91.0", "webpack-cli": "^5.1.4", "webpack-dev-server": "^5.0.4" }, "eslintConfig": { "root": true, "env": { "node": true }, "extends": [ "plugin:vue/vue3-essential", "eslint:recommended" ], "parserOptions": { "parser": "@babel/eslint-parser" }, "rules": {} }, "browserslist": [ "> 1%", "last 2 versions", "not dead", "not ie 11" ] }
报错现象
Vue应用中出现组件渲染失败错误,无法加载并渲染远程的React HelloComponent,控制台提示组件无法识别或渲染失败。
解决方案
1. 修正Vue Webpack配置错误
- 修正远程地址:React应用运行在5000端口,将Vue配置中
remotes的地址改为helloApp: 'HelloApp@http://localhost:5000/remoteEntry.js' - 移除externals配置:已通过
shared共享React和ReactDOM,externals会导致模块查找失败,删除以下代码:externals: { react: 'react', 'react-dom': 'react-dom', }, - 添加React JSX支持:修改babel-loader配置,加入
@babel/preset-react(需先在Vue项目执行npm install @babel/preset-react --save-dev):{ test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: "babel-loader", options: { presets: ['@babel/preset-env', '@babel/preset-react'], }, }, } - 统一publicPath:将
output.publicPath改为http://localhost:5001/,与devServer端口一致
2. 封装React组件为Vue可识别组件
React组件不能直接在Vue模板中使用,需封装成Vue组件。创建src/components/ReactWrapper.vue:
<template> <div ref="reactRoot"></div> </template> <script> import React from 'react'; import ReactDOM from 'react-dom/client'; // 导入远程React组件 import HelloComponent from 'helloApp/HelloComponent'; export default { name: 'ReactWrapper', props: ['name'], mounted() { // 渲染React组件到Vue的DOM节点 const root = ReactDOM.createRoot(this.$refs.reactRoot); root.render(<HelloComponent name={this.name} />); }, // 监听props变化,更新React组件 watch: { name(newVal) { const root = ReactDOM.createRoot(this.$refs.reactRoot); root.render(<HelloComponent name={newVal} />); } }, // 组件卸载时清理React实例 unmounted() { ReactDOM.unmountComponentAtNode(this.$refs.reactRoot); } }; </script>
3. 修改App.vue使用封装后的组件
<template> <ReactWrapper :name="name" /> </template> <script> import ReactWrapper from './components/ReactWrapper.vue'; export default { name: "App", data() { return { name: "World", }; }, components: { ReactWrapper, }, }; </script>
4. 统一React版本
Vue项目中React版本为18.3.1,React项目为18.2.0,版本不一致可能导致冲突。将Vue项目的React版本改为18.2.0,修改package.json后执行npm install:
"react": "^18.2.0", "react-dom": "^18.2.0",
5. 确保React组件默认导出
检查React的HelloComponent是否为默认导出:
const HelloComponent = ({ name }) => { return <h1>Hello, {name}!</h1>; }; export default HelloComponent;
验证步骤
- 启动React应用:
npm start(端口5000) - 启动Vue应用:
npm start(端口5001) - 访问Vue应用,查看是否成功渲染React组件
内容的提问来源于stack exchange,提问作者Magento 2
相关产品推荐
相关产品推荐

