如何从远程微前端应用导航至宿主应用路由?
微前端路由导航问题:子应用跳转宿主登录页失败
环境与背景
- 基于Cam Jackson方案实现React微前端,包含3个应用:宿主应用、child1子应用、child2子应用
- 当前问题:子应用1的路由守卫在token过期时调用
navigate('/'),无法跳转到宿主的登录页,而是在子应用路由范围内查找匹配项
核心代码片段
宿主应用路由
const Child1 = ({ history }) => ( <MicroFrontend history={history} host={childapp1} name="child1" /> ); const Child2 = ({ history }) => ( <MicroFrontend history={history} host={childapp2} name="child2" /> ); const App = () => ( <BrowserRouter> <Account> <React.Fragment> <ReactNotifications /> <AppHeader /> <Routes> <Route exact path="/" element={<Signin/>} /> <Route exact path="/about" element={<About/>} /> <Route exact path="/child1/*" element={<Child1/>} /> <Route exact path="/child2/*" element={<Child2/>} /> </Routes> </React.Fragment> </Account> </BrowserRouter> );
子应用1路由
<BrowserRouter> <Account> <React.Fragment> <ReactNotifications /> <Routes> <Route path='child1' element={<ProtectedRoute><Home/></ProtectedRoute>}/> <Route path="child1/dashboard" element={ <ProtectedRoute> <Dashboard/> </ProtectedRoute> } /> <Route path="child1/forbidden" element={<ForbiddenPage/>} /> </Routes> </React.Fragment> </Account> </BrowserRouter>
子应用1的ProtectedRoute(问题代码)
const ProtectedRoute = ({ children }) => { const { authData } = useAuth() const navigate = useNavigate() console.log(authData, 'authData'); if (authData.isAuthenticated && authData.expiryTime > Date.now()) { return children } else{ navigate('/') } }; export default ProtectedRoute;
MicroFrontend组件代码
import React from 'react'; class MicroFrontend extends React.Component { componentDidMount() { const { name, host, document } = this.props; const scriptId = `micro-frontend-script-${name}`; if (document.getElementById(scriptId)) { this.renderMicroFrontend(); return; } var parentUrl = `${window.location.protocol}//${window.location.href.split('/')[2]}` if (name === 'child1'){ fetch(`${parentUrl}/child1/asset-manifest.json`,{ method: "GET", }) .then(res =>res.json()) .then(manifest => { console.log(manifest, 'manifest'); const script = document.createElement('script'); const linkforCSSfile = document.createElement("link") linkforCSSfile.href = `${parentUrl}/child1${manifest['files']['main.css']}` linkforCSSfile.type = 'text/css' linkforCSSfile.rel = 'stylesheet' script.id = scriptId; script.crossOrigin = ''; script.src = `${parentUrl}/child1${manifest['files']['main.js']}`; script.onload = this.renderMicroFrontend; document.head.appendChild(script); document.head.appendChild(linkforCSSfile) }) } else{ fetch(`${parentUrl}/child2/asset-manifest.json`,{ method: "GET", }) .then(res =>res.json()) .then(manifest => { console.log(manifest, 'manifest'); const script = document.createElement('script'); const linkforCSSfile = document.createElement("link") linkforCSSfile.href = `${parentUrl}/child2${manifest['files']['main.css']}` linkforCSSfile.type = 'text/css' linkforCSSfile.rel = 'stylesheet' script.id = scriptId; script.crossOrigin = ''; script.src = `${parentUrl}/child2${manifest['files']['main.js']}`; script.onload = this.renderMicroFrontend; document.head.appendChild(script); document.head.appendChild(linkforCSSfile) }) } } componentWillUnmount() { const { name, window } = this.props; window[`unmount${name}`]?.(`${name}-container`); } renderMicroFrontend = () => { const { name, window, history } = this.props; console.log(history); window[`render${name}`]?.(`${name}-container`, history); }; render() { return <main id={`${this.props.name}-container`} />; } } MicroFrontend.defaultProps = { document, window, }; export default MicroFrontend;
问题分析
子应用内部使用独立的<BrowserRouter>,导致useNavigate()仅在子应用自身的路由上下文生效,无法触发宿主路由的跳转。同时,宿主已将history对象传递给MicroFrontend组件,但子应用未复用该对象,而是创建了新的路由上下文。
解决方案
步骤1:子应用移除独立BrowserRouter,复用宿主传递的history
修改子应用入口文件,接收宿主传递的history,使用Router组件复用该历史对象:
// 子应用1入口文件 import { Router } from 'react-router-dom'; window.renderchild1 = (containerId, history) => { ReactDOM.render( <Router history={history}> <Account> <React.Fragment> <ReactNotifications /> <Routes> <Route path='/' element={<ProtectedRoute><Home/></ProtectedRoute>}/> <Route path="/dashboard" element={<ProtectedRoute><Dashboard/></ProtectedRoute>}/> <Route path="/forbidden" element={<ForbiddenPage/>}/> </Routes> </React.Fragment> </Account> </Router>, document.getElementById(containerId) ); };
同时移除子应用路由中的child1前缀,因为宿主已通过/child1/*匹配到子应用,子应用仅需处理相对路径。
步骤2:修改ProtectedRoute,确保导航生效
复用宿主history后,useNavigate()会自动使用宿主路由上下文,navigate('/')即可正确跳转到宿主登录页。若仍有问题,可使用强制跳转作为备用方案:
const ProtectedRoute = ({ children }) => { const { authData } = useAuth() const navigate = useNavigate() if (authData.isAuthenticated && authData.expiryTime > Date.now()) { return children } else { // 方式1:复用宿主路由上下文导航 navigate('/'); // 方式2:强制跳转(备用) // window.location.href = '/'; } };
步骤3:确保子应用unmount函数正确清理
修改子应用unmount函数,确保移除Router相关渲染:
window.unmountchild1 = (containerId) => { ReactDOM.unmountComponentAtNode(document.getElementById(containerId)); };
预期流程验证
宿主登录页 -> child1首页 -> token过期 -> 自动跳转到宿主登录页
内容的提问来源于stack exchange,提问作者Shashank Ky
相关产品推荐
相关产品推荐

