You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取react-dom服务端渲染的全部组件列表?

嘿,这个需求真的很贴合性能优化的场景——通过SSR收集页面用到的组件翻译键,只打包必要的翻译内容,能大幅减少前端加载的冗余资源。我之前在项目里实现过类似的功能,给你分享几个靠谱的思路:

方案一:运行时通过上下文+高阶组件追踪收集

这是最容易落地的方案,利用React的上下文(Context)在SSR渲染过程中追踪所有渲染到的组件,收集它们的translations属性。

步骤1:创建翻译收集器与上下文

首先定义一个收集器类,用来存所有翻译键(用Set避免重复),再创建对应的React上下文:

// translation-collector.js
import React from 'react';

// 收集器类,负责管理翻译键
export class TranslationCollector {
  constructor() {
    this.keys = new Set();
  }

  // 添加一组翻译键
  addKeys(keys) {
    if (!Array.isArray(keys)) return;
    keys.forEach(key => this.keys.add(key));
  }

  // 获取去重后的所有键
  getUniqueKeys() {
    return Array.from(this.keys);
  }
}

// 创建上下文,用于在组件树中传递收集器实例
export const TranslationCollectorContext = React.createContext(null);

步骤2:编写高阶组件包装目标组件

写一个HOC,用来包裹需要收集翻译键的组件,从上下文中拿到收集器并注入当前组件的翻译键:

// with-translation-tracking.js
import React, { useContext, useEffect } from 'react';
import { TranslationCollectorContext } from './translation-collector';

export const withTranslationTracking = (Component) => {
  const TrackedComponent = (props) => {
    const collector = useContext(TranslationCollectorContext);

    useEffect(() => {
      // 如果组件有translations属性,就添加到收集器
      if (collector && Component.translations) {
        collector.addKeys(Component.translations);
      }
    }, [collector]);

    return <Component {...props} />;
  };

  // 保留原组件的静态属性,避免丢失translations
  TrackedComponent.translations = Component.translations;
  return TrackedComponent;
};

步骤3:在SSR渲染流程中使用

在你的SSR入口文件里,初始化收集器,通过上下文传递给组件树,渲染后收集所有键并注入HTML:

// ssr-entry.js
import React from 'react';
import { renderToString } from 'react-dom/server';
import { TranslationCollector, TranslationCollectorContext } from './translation-collector';
import App from './App';

async function renderServerSideApp(req, res) {
  // 初始化收集器,每个请求单独实例,避免并发污染
  const collector = new TranslationCollector();

  // 渲染组件树,同时传递收集器上下文
  const appHtml = renderToString(
    <TranslationCollectorContext.Provider value={collector}>
      <App />
    </TranslationCollectorContext.Provider>
  );

  // 获取所有收集到的翻译键
  const neededTranslationKeys = collector.getUniqueKeys();
  // 这里替换成你获取翻译内容的逻辑,比如从后端接口或本地文件查询
  const neededTranslations = await fetchTranslationsByKeys(neededTranslationKeys);

  // 将翻译内容注入HTML,让前端直接读取
  const finalHtml = `
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>SSR App</title>
      </head>
      <body>
        <div id="root">${appHtml}</div>
        <script>
          // 暴露给前端的全局变量,存储必要的翻译内容
          window.__REQUIRED_TRANSLATIONS__ = ${JSON.stringify(neededTranslations)};
        </script>
        <script src="/client-bundle.js"></script>
      </body>
    </html>
  `;

  res.send(finalHtml);
}

组件使用示例

只需要用HOC包裹你的组件,并定义translations静态属性即可:

// MyComponent.js
import { withTranslationTracking } from './with-translation-tracking';

const MyComponent = () => {
  return <div>{window.__REQUIRED_TRANSLATIONS__['key1']}</div>;
};

// 定义当前组件需要的翻译键
MyComponent.translations = ['key1', 'key2'];

export default withTranslationTracking(MyComponent);
方案二:编译时用Babel插件预收集(适合大型项目)

如果你的项目体量较大,运行时追踪可能带来轻微的性能开销,可以用Babel插件在编译阶段就收集所有组件的translations属性,提前生成路由与翻译键的映射表,SSR时直接根据当前路由获取对应键。

简单的Babel插件示例

这个插件会遍历AST,找到组件的translations静态属性,并把键存入一个全局的收集文件:

// babel-plugin-collect-translations.js
module.exports = function() {
  return {
    visitor: {
      AssignmentExpression(path) {
        // 匹配类似 MyComponent.translations = ['key1', 'key2'] 的语句
        if (
          path.node.left.type === 'MemberExpression' &&
          path.node.left.property.name === 'translations' &&
          path.node.right.type === 'ArrayExpression'
        ) {
          const componentName = path.node.left.object.name;
          const translationKeys = path.node.right.elements.map(el => el.value);

          // 将收集到的键写入一个临时文件(这里可以根据路由做更精细的映射)
          const fs = require('fs');
          const existingData = fs.existsSync('./translation-mappings.json') 
            ? JSON.parse(fs.readFileSync('./translation-mappings.json', 'utf8')) 
            : {};
          
          existingData[componentName] = translationKeys;
          fs.writeFileSync('./translation-mappings.json', JSON.stringify(existingData));
        }
      }
    }
  };
};

然后在Babel配置中启用这个插件,编译后你就能得到所有组件的翻译键映射,SSR时根据当前渲染的组件列表,从映射表中提取需要的键即可。

注意事项
  • 并发请求隔离:方案一中的收集器一定要每个请求单独实例化,不能用全局变量,否则多请求并发时会出现键混乱的问题。
  • 懒加载组件处理:如果用了React.lazy,SSR时需要确保组件被预加载(比如用React.Suspense配合服务端预渲染),否则收集不到懒加载组件的翻译键。
  • 嵌套组件与HOC:确保HOC不会丢失原组件的translations静态属性,就像方案一中那样,把原组件的属性复制给包装后的组件。

内容的提问来源于stack exchange,提问作者DucDigital

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:27:22