logo
  • 指南
  • API
  • 博客
  • 简体中文
    • 简体中文
    • English
    • @esmx/core
      Esmx
      App
      RenderContext
      ModuleConfig
      PackConfig
      ManifestJson
      @esmx/router
      Router
      Route
      路由配置
      RouterLink
      导航守卫
      动态路由匹配
      嵌套路由
      编程式导航
      滚动行为
      图层路由
      微应用
      错误类型
      类型参考
      @esmx/router-vue
      RouterPlugin
      组合式函数
      组件
      类型增强
      @esmx/router-react
      微应用集成
      Hooks 与上下文
      组件
      SSR
      App
      @esmx/rspack
      @esmx/rspack-vue
      @esmx/rspack-react

      最后更新于: 2026/4/7 02:16:07

      上一页组件下一页@esmx/rspack

      #SSR

      #简介

      使用 @esmx/router 和 React 进行服务端渲染时,通过微应用 apps 回调中的 renderToString() 生命周期钩子实现。路由在服务端以 memory 模式运行,导航到请求的 URL,将应用渲染为 HTML,然后发送给客户端进行水合。

      #服务端入口

      import type { RenderContext } from '@esmx/core';
      import { Router, RouterMode } from '@esmx/router';
      import { createElement } from 'react';
      import { renderToString } from 'react-dom/server';
      import App from './App';
      import { routes } from './routes';
      
      export default async (rc: RenderContext) => {
          const router = new Router({
              mode: RouterMode.memory,
              base: new URL(rc.params.url, 'http://localhost'),
              routes,
              apps: (router) => ({
                  mount(el) { /* 仅客户端 */ },
                  unmount() { /* 仅客户端 */ },
                  async renderToString() {
                      return renderToString(createElement(App, { router }));
                  }
              })
          });
      
          await router.replace(rc.params.url);
          const html = await router.renderToString();
      
          // 收集路由数据用于客户端水合
          const routeData = router.route.data;
      
          rc.html = `<!DOCTYPE html>
      <html lang="zh">
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          ${rc.preload()}
          ${rc.css()}
      </head>
      <body>
          <div id="app">${html}</div>
          ${routeData ? `<script>window.__ROUTE_DATA__ = ${JSON.stringify(routeData)}</script>` : ''}
          ${rc.importmap()}
          ${rc.moduleEntry()}
          ${rc.modulePreload()}
      </body>
      </html>`;
      };

      #客户端水合

      import { Router, RouterMode } from '@esmx/router';
      import { hydrateRoot } from 'react-dom/client';
      import { createElement } from 'react';
      import App from './App';
      import { routes } from './routes';
      
      const router = new Router({
          root: '#app',
          mode: RouterMode.history,
          routes,
          data: (window as any).__ROUTE_DATA__,
          apps: (router) => ({
              mount(el) {
                  hydrateRoot(el, createElement(App, { router }));
              },
              unmount() { /* 处理清理 */ },
              async renderToString() {
                  const { renderToString } = await import('react-dom/server');
                  return renderToString(createElement(App, { router }));
              }
          })
      });