WU / 文档通用微前端
中文

SSR 与水合

服务器 render 契约、Declarative Shadow DOM、水合状态及 React/Vue helpers。

版本、变更与要求见 Framework 3.2 与 CLI 1.0。

服务器生成什么

wu-framework/server 将 renderer 结果转换为 WU 容器的 HTML。默认使用 Declarative Shadow DOM 封装内容和样式。浏览器可以在应用 JavaScript 加载前创建该树。

服务器接收具有 render(props) 的模块,而不是客户端挂载生命周期。结果可以是 HTML 字符串,也可以是 { html, styles?, state? }。HTML 生成方必须在加入不可信数据前进行转义。

import { renderApp, escapeHtml, renderStateScript } from 'wu-framework/server';

const result = await renderApp('greeting', {
  props: { name: 'Ada' },
  module: {
    render(props) {
      return {
        html: `<p>Hola, ${escapeHtml(props.name)}</p>`,
        styles: 'p { color: #f97316; }',
        state: props,
      };
    },
  },
});

const html = result.html + renderStateScript({ greeting: result.state });

renderApp 还接受已导入模块、Promise、default 模块或加载模块的函数。宿主决定服务器上可执行哪些模块;不要把用户输入转换为任意 import。

结果与选项

项目 行为约定
result.html 完整容器
result.inner 声明式或平面内部内容
result.containerAttrs ID、data-wu-app、data-wu-ssr、可选 class
result.state renderer 返回的可选状态
containerId 默认为 wu-app-<name>
shadowMode 默认为 open;closed 阻止常规水合
declarative 默认为 true
styles 宿主为该应用添加的 CSS
nonce 此响应样式的 CSP nonce

renderApps 并行处理多个应用。默认将单个应用的故障记录在 errors 中,并保留其他结果;failFast: true 允许提前拒绝。renderer 在 renderApp 中失败会产生错误码为 WU_SSR_RENDER_FAILED 的错误。

保留 DOM 完成水合

客户端 shell 正常注册应用,并挂载到 SSR 容器。sandbox 检测 data-wu-ssr 和 data-wu-root 根节点。如果存在 lifecycle.hydrate,core 会传入服务器 props 和 scoped facade 并调用它。没有该 slot 时调用 mount;原有 HTML 作为初始内容保留,直到 renderer 替换它。

wu.define('greeting', {
  mount(container) {
    container.textContent = 'Montaje cliente';
  },
  hydrate(container, { props }) {
    // Adoptar lo que ya existe. No limpiar container primero.
    const paragraph = container.querySelector('p');
    paragraph?.setAttribute('aria-label', `Saludo para ${props.name}`);
  },
  unmount(container) {
    container.replaceChildren();
  },
});

对于 React,hydrateRoot 必须接收与服务器生成内容相同的树和 props。对于 Vue,adapter 使用 createSSRApp 和 .mount 接管已有 DOM。发布的状态必须保持这种对应;渲染与水合之间改变数据会导致不匹配。

Renderer helpers

server subpath 导出 renderReact、renderPreact、renderVue、renderSvelte、renderSolid 和 renderHtml。这些 helpers 返回符合 render 契约的模块。

import * as React from 'react';
import { renderToString } from 'react-dom/server';
import { renderReact } from 'wu-framework/server';
import App from './App.jsx';

export default renderReact(App, {
  react: React,
  renderToString,
  styles: '.greeting { font-weight: 600; }',
});

注入 renderer 和运行时可避免在服务器 bundle 外解析另一份 React。Vue 支持注入 createSSRApp 和 renderToString,以及 setup。服务器选项名称与客户端 adapter 的 runtime: { React, ReactDOM } 不完全相同。

Astro

---
import WuAppSSR from 'wu-framework/integrations/astro/WuAppSSR.astro';
import greetingServer from '../apps/greeting/server.js';
---
<WuAppSSR
  name="greeting"
  module={greetingServer}
  props={{ name: 'Ada' }}
  fallbackToClient={false}
/>

shell 仍需初始化对应的客户端注册。组件将状态发布为 JSON 以进行水合;hydrate 默认为 true。fallbackToClient 也默认为 true:SSR 失败会留下 slot 供客户端挂载,除非像示例中一样禁用它。

在 Astro 静态构建中,renderer 在编译时运行;这不会让页面按请求生成个性化内容。依赖用户的数据需要合适的服务器策略或客户端加载。

状态与响应头

serializeState 和 renderStateScript 处理 HTML/script 上下文的序列化。escapeHtml 转义文本,不验证任意 HTML 应用。不要将秘密注入水合状态:页面上发布的所有内容对读者可见。

createNonce() 为一个响应生成 nonce。securityHeaders、applySecurityHeaders、wuSecurityMiddleware 和 withSecurityHeaders 帮助安装 CSP 和其他响应头。需要执行的样式与脚本应使用同一响应 nonce,下个响应使用新 nonce;服务器负责协调。

实现参考

导出:src/server/index.js;render:src/server/wu-server-render.js;多应用渲染:src/server/wu-server-render.js;类型:src/server/index.d.ts;DOM 接管:src/core/wu-sandbox.js;hydrate slot:src/core/wu-core.js;Astro:integrations/astro/WuAppSSR.astro。测试:tests/unit/server-render.test.js、tests/unit/server-security-fuzz.test.js、tests/unit/security-headers.test.js。

要隔离每个请求并限制工作,请查阅 SSR 参考中的 createServerRequest、私有 loaders、取消和并发。4.0 响应头 wrappers 拒绝固定 nonce:请使用每次请求调用的函数或默认生成方式。

一起完善 WU

发现了问题?有新的想法?

你的使用体验能让 WU 变得更好。告诉我们遇到的问题,或支持项目的持续开发。