面试题答案
一键面试1. 路由设计
在 Next.js 主应用中,利用动态路由实现 /content/[moduleName]/[contentId]
这种形式的路由。
// pages/content/[moduleName]/[contentId].js
import React from'react';
const ContentPage = ({ moduleName, contentId }) => {
return (
<div>
<h1>展示来自 {moduleName} 模块的 {contentId} 内容</h1>
</div>
);
};
export async function getStaticPaths() {
// 这里可以从 API 或者其他数据源获取所有可能的 moduleName 和 contentId 组合
const paths = [];
// 示例数据获取
const modules = await fetch('/api/modules').then(res => res.json());
for (const module of modules) {
const contents = await fetch(`/api/modules/${module.name}/contents`).then(res => res.json());
for (const content of contents) {
paths.push({ params: { moduleName: module.name, contentId: content.id.toString() } });
}
}
return { paths, fallback: false };
}
export async function getStaticProps({ params }) {
const { moduleName, contentId } = params;
// 这里可以根据 moduleName 和 contentId 从 API 获取具体内容数据
const contentData = await fetch(`/api/modules/${moduleName}/contents/${contentId}`).then(res => res.json());
return { props: { moduleName, contentId, contentData }, revalidate: 60 };
}
export default ContentPage;
2. 微前端通信处理
- 事件总线:可以使用类似于
mitt
这样的轻量级事件总线库。在主应用和各个微前端模块初始化时,都引入事件总线实例。例如,主应用向微前端模块请求内容时,可以发送一个事件,携带moduleName
和contentId
信息。
import mitt from'mitt';
const emitter = mitt();
// 主应用发送事件
emitter.emit('request - content', { moduleName, contentId });
// 微前端模块监听事件
emitter.on('request - content', ({ moduleName, contentId }) => {
// 根据 moduleName 判断是否是自己模块的内容请求
if (currentModuleName === moduleName) {
// 获取并处理内容
const content = getContentById(contentId);
// 可以再通过事件总线将内容返回给主应用
emitter.emit('content - response', { moduleName, contentId, content });
}
});
- 共享状态管理:使用
Redux
或MobX
等状态管理库。主应用和微前端模块共享一个状态仓库,主应用将请求的moduleName
和contentId
存入状态中,微前端模块监听状态变化,当检测到相关请求且是自己模块的内容时,获取并更新状态中的内容数据。
3. 动态加载模块
- Webpack 的动态导入:在 Next.js 项目中,可以使用
import()
语法动态导入微前端模块。
const loadModule = async (moduleName) => {
try {
// 假设微前端模块打包后有一个统一的引入路径
const module = await import(`/micro - frontends/${moduleName}`);
return module;
} catch (error) {
console.error(`加载 ${moduleName} 模块失败`, error);
return null;
}
};
- Next.js 的
next/dynamic
:结合next/dynamic
实现按需加载微前端模块,并处理加载状态。
import dynamic from 'next/dynamic';
const DynamicModule = dynamic(() => loadModule(moduleName), {
loading: () => <div>加载中...</div>,
ssr: false
});
4. 性能和可维护性
- 代码拆分:确保每个微前端模块都进行了合理的代码拆分,只加载当前需要的代码,减少初始加载体积。例如,在微前端模块内部,使用
Webpack
的splitChunks
配置,将公共代码提取出来。 - 缓存策略:对于获取的内容数据以及加载的微前端模块,实现缓存机制。比如在客户端使用
localStorage
或sessionStorage
缓存数据,在服务器端利用Redis
等缓存中间件,减少重复请求。 - 模块化和组件化:在主应用和微前端模块中,都遵循良好的模块化和组件化原则。每个微前端模块负责自己独立的业务逻辑,接口清晰,易于维护和替换。主应用只负责路由和整体协调,不耦合过多具体业务代码。
- 错误处理:在动态加载模块、获取内容数据以及微前端通信过程中,都要进行全面的错误处理。例如,在加载微前端模块失败时,给出友好的提示信息,在获取内容数据失败时,重试或显示缓存数据。