Meituan Waimai uses a 'Central Routing Base' architecture where the base system manages the loading and unloading of sub-projects. Instead of using heavy frameworks like Single-Spa, they leverage React-Router to minimize migration costs and bundle size.
In React-Router v4+, routing is treated as a component. Dynamic loading is achieved by creating an asynchronous component that:
- Extracts a unique sub-project ID from the URL pathname.
- Fetches the sub-project's resource configuration (URL map).
- Loads the sub-project's JS entry file asynchronously.
- Renders the sub-project component once the resource is loaded and cached.
export const AyncComponent: React.FC<{ hotReload?: number; } & RouteComponentProps> = ({ location, hotReload }) => {
const [ayncLoaded, setAyncLoaded] = useState(false);
const [subAppMapInfoLoaded, setSubAppMapInfoLoaded] = useState(false);
const [ayncComponent, setAyncComponent] = useState(null);
const { pathname } = location;
// Extract sub-project ID from path (e.g., '/subapp/xxx/index' -> 'xxx')
const id = pathname.split('/')[2];
useEffect(() => {
if (!subAppMapInfoLoaded) {
fetchSubappUrlPath(id).then((data) => {
subAppMapInfo = data;
setSubAppMapInfoLoaded(true);
}).catch(() => {
goBackToIndex();
});
return;
}
const subappModule = (subAppMapInfo as any)[id];
if (subappModule) {
if (subappRoutes[id]) {
setAyncLoaded(true);
setAyncComponent(subappRoutes[id]);
return;
}
currentPrefix = id;
setAyncLoaded(false);
const jsUrl = subappModule.js;
loadAsyncSubapp(jsUrl)
.then(() => {
setAyncComponent(subappRoutes[id]);
setAyncLoaded(true);
})
.catch(() => {
setAyncLoaded(false);
console.log('loading failed...');
});
} else {
goBackToIndex();
}
}, [id, subAppMapInfoLoaded, hotReload]);
return ayncLoaded ? ayncComponent : null;
};