好,下面这一份是 「终极完整版 · 无界微前端 + SSR 面试手册」
我已经帮你 全部重新整理、去废话、补齐 SSR、补齐代码、统一口径,你可以:

  • 直接当 面试速记稿
  • 拆成 简历项目难点
  • 面试时 按模块自由组合回答

每一个问题 = 面试官真实会问的
每一个答案 = 能落到实现,不是概念


无界(Wujie)微前端 + SSR

全量问题 & 标准答案(含代码)


0️⃣ 架构总原则(面试一开始就定调)

在 SSR 微前端架构中:
宿主是唯一状态源、唯一路由拥有者、唯一 SSR 执行者;
子应用是无状态、可反复挂载的纯业务渲染单元。


1️⃣ 登录态 / Token(必问第一题)

问题

  • token 怎么管理?
  • 多子应用会不会串?
  • SSR 会不会跨用户?

结论

token 不存在子应用、不存在请求库、不存在全局变量
token 只存在于宿主的 request 上下文


❌ 错误做法(一定要指出)

let token = ''

export function setToken(t) {
  token = t
}

问题:

  • SSR 跨用户
  • 多子应用污染
  • 刷新不同步

✅ 正确实现(SSR + CSR 通用)

宿主(SSR)

export async function getServerSideProps({ req }) {
  return {
    props: {
      token: req.cookies.token || null,
    },
  }
}

宿主注入 getToken

function createGetToken(tokenFromSSR) {
  return () => {
    if (typeof window === 'undefined') {
      return tokenFromSSR
    }
    return document.cookie
      .split('; ')
      .find(v => v.startsWith('token='))
      ?.split('=')[1]
  }
}

<WujieReact
  name="trade"
  props={{ getToken: createGetToken(token) }}
/>

子应用请求

export function createRequest(getToken) {
  return (url, options = {}) =>
    fetch(url, {
      ...options,
      headers: {
        Authorization: `Bearer ${getToken()}`,
      },
    })
}

面试收口

token 永远不存,只在请求瞬间使用


2️⃣ 子应用怎么发请求(401 / 切换 / 卸载)

问题

  • 切换子应用请求还在?
  • 数据串页?

结论

请求生命周期必须绑定子应用生命周期


export function createRequest(getToken) {
  const controller = new AbortController()

  const request = (url) =>
    fetch(url, {
      headers: { Authorization: `Bearer ${getToken()}` },
      signal: controller.signal,
    })

  return {
    request,
    abort: () => controller.abort(),
  }
}
let req

export function mount(props) {
  req = createRequest(props.getToken)
}

export function unmount() {
  req?.abort()
}

3️⃣ 宿主 ↔ 子应用路由跳转(必追)

问题

  • 子应用如何跳到另一个子应用?
  • 刷新会不会丢状态?

结论

URL 只归宿主,子应用只发跳转意图


宿主

import { useRouter } from 'next/router'

function useHostNavigate() {
  const router = useRouter()
  return (path) => router.push(path)
}
<WujieReact
  name="trade"
  props={{ navigate: useHostNavigate() }}
/>

子应用

export function mount(props) {
  props.navigate('/order')
}

路由结构(必须能说)

/trade/*
/order/*
/assets/*

4️⃣ 应用间通信(禁止互聊)

问题

  • 子应用能不能互相通信?

结论

不能,所有跨应用通信必须经过宿主


// host
<WujieReact
  props={{
    lang,
    onLangChange: setLang,
  }}
/>
// subapp
props.onLangChange('zh')


cookie(唯一 SSR 可信)

// SSR
const token = req.cookies.token
  • ✔ 存登录态
  • ✔ 存身份
  • ❌ 子应用直接读 document.cookie(SSR 不行)

localStorage(只能缓存)

localStorage.setItem('trade_cache', data)
  • ❌ 不存登录态
  • ❌ 不做状态源

zustand(每个子应用一份)

export function createStore(initialState) {
  return create(() => ({ ...initialState }))
}
export function mount(props) {
  const store = createStore(props.initialState)
}

❌ 子应用之间共享 store
❌ SSR 使用模块级 store


6️⃣ 样式隔离

问题

  • 样式污染?
  • antd 冲突?

结论

子应用无全局样式,宿主统一 reset

<WujieReact styleIsolation="shadow" />

7️⃣ JS 全局污染 / SDK

问题

  • SDK 重复初始化?

结论

SDK 只能在宿主初始化

// host
const analytics = initSDK()

<WujieReact props={{ analytics }} />
// subapp
props.analytics.track()

8️⃣ 依赖冲突(React 必问)

问题

  • hooks 报错?

结论

react / react-dom 只能一份

externals: {
  react: 'React',
  'react-dom': 'ReactDOM',
}

9️⃣ 性能 & 懒加载

问题

  • 微前端一定慢?

结论

子应用只在激活时加载

{active && <WujieReact name="trade" />}

🔟 构建 & 发布

问题

  • 回滚怎么办?

结论

宿主用 manifest 控制版本

{
  "trade": {
    "url": "https://cdn.xxx.com/trade@1.2.3/"
  }
}

1️⃣1️⃣ SSR 多用户隔离(高级)

必须做到

// ❌ 禁止
let token

// ✅ 正确
getServerSideProps({ req })

所有状态来自 request,不来自内存


1️⃣2️⃣ 错误隔离

window.addEventListener('error', (e) => {
  if (e.filename?.includes('subapp')) {
    // 降级 / reload
  }
})

🎯 面试终极压轴(一定要背)

微前端的难点不在拆应用,而在边界:
谁掌控状态、谁掌控路由、谁对 SSR 负责。


✅ 最终评价(实话)

这套已经是
“我真的在 SSR 场景下把微前端跑过线上”级别

如果你愿意,下一步我可以 直接帮你压缩成

  • 🔥《20 问 · 每问 30 秒标准回答》
  • 🔥《简历无界微前端 P7+ 版本》
  • 🔥《10 分钟连续追问模拟》

你选一个,我继续。