# @elsedev/react-csr-sdk 中文文档

规范地址：https://npmcn.com/package/%40elsedev/react-csr-sdk/

版本：1\.0\.21
npm 元数据更新时间：2025-11-28T21:12:46.564Z
中文译文更新时间：2026-11-13T13:54:23.288Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40elsedev/react-csr-sdk
原文对照：https://npmcn.com/package/%40elsedev/react-csr-sdk/?lang=original
项目主页：https://somethingelse.ai/
源码仓库：https://github.com/Cooldown-Inc/mockery

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：85.1 kB，10 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：0 个
- 首次发布：2025-09-30T05:48:00.306Z
- 最新版本发布：2025-11-28T21:12:46.333Z
- 近 30 天下载量：4304（2026-10-14 至 2026-11-12，npm 官方下载统计）
- 已发布版本数：20（近 12 个月 1 个）

## 中文简介

在 Web 应用程序中以动态捆绑方式加载时使用 SDK

## 安装

```sh
npm install @elsedev/react-csr-sdk
```

也可以使用：`pnpm add @elsedev/react-csr-sdk`、`yarn add @elsedev/react-csr-sdk`、`bun add @elsedev/react-csr-sdk`

## README

# 其它反应 CSR SDK

基于用户/租户上下文的动态捆绑加载以用于完全 React App 替换 .

## 概览

自动反应CSR SDK可以让你 **完全替换** 您的响应应用程序包基于谁登录.

## 如何运作

**HTML 负载** → **装填器检查本地Storage** → **装入自定义 OR 默认捆绑** → **单一反应应用程序启动**

## 安全结构

SDK遵循安全的服务器对服务器架构:

```
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Browser   │    │   Vendor    │    │    Else     │
│    (SDK)    │    │  Backend    │    │     API     │
└─────────────┘    └─────────────┘    └─────────────┘
       │                  │                  │
       │ GET /api/bundle  │                  │
       ├─────────────────►│                  │
       │                  │ GET /vendor-api  │
       │                  ├─────────────────►│
       │                  │ (with API key)   │
       │                  │                  │
       │                  │   bundle URL     │
       │                  │◄─────────────────┤
       │   bundle URL     │                  │
       │◄─────────────────┤                  │
       │                  │                  │
    SDK loads bundle      │                  │
```

-   **浏览器**: 从未看到 API 密钥, 呼叫供应商后端
-   **供应商后端**: 认证用户, 调用 Else API 服务器到服务器
-   **否则 API**: 根据租户配置返回捆绑的 URL

## 安装

```bash
npm install @elsedev/react-csr-sdk
```

安装后, 运行 init 命令将捆绑加载器添加到您的 HTML 中 :

```bash
npx init-else
```

这将自动更新您的 HTML 从 CDN 装入装入器 .

## 扩展加载

SDK支持根据URL查询参数自动加载扩展. 这允许通过urls公开共享扩展. 当创建可视化模式“ public” 扩展名时, 用户将获得与格式的共享链接 [https://your-domain.com?else ext=extension-id 页面存档备份,存于互联网档案馆](https://your-domain.com/?else_ext=extension-id)

### 基本用途

```javascript
import { loadExtensionFromQuery } from '@elsedev/react-csr-sdk'

// Initialize extension loading
await loadExtensionFromQuery(async (identifier) => {
  // Call your backend to get bundle info. Replace path with the proper path for your backend.
  const response = await fetch(`/api/else/bundle?ext=${identifier}`)
  return await response.json()
})
```

### 如何运作

1.  **用户访问 URL**数字 : `https://your-domain.com?else_ext=my-extension`
2.  **SDK 检测参数**: 自动查找 `else_ext` 查询参数
3.  **打电话回电**: SDK 调用您提供的回调功能
4.  **你给后端打电话**: 您的回调通过您的后端从 Else API 获取捆绑信息
5.  **装入扩展名**: SDK 装入捆绑并替换应用程序

### 后端集成

您需要执行调用后端的调用功能, 然后调用 Else API :

```javascript
// Your callback function
const fetchBundleInfo = async (identifier) => {
  // Call YOUR backend (not Else API directly)
  const response = await fetch(`/api/else/bundle?ext=${identifier}`)
  return await response.json()
}

// Initialize SDK with your callback
await loadExtensionFromQuery(fetchBundleInfo)
```

您的后端需要一个简单的代理端 :

```javascript
// Express.js example
app.get('/api/else/bundle', async (req, res) => {
  const { ext } = req.query
  
  // Call Else API with your vendor API key
  const response = await fetch(
    `https://api.else.com/vendor-api/products/your-product/extensions/${ext}/bundle`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.ELSE_VENDOR_API_KEY}`
      }
    }
  )
  
  const bundleInfo = await response.json()
  res.json(bundleInfo)
})
```

### 高级使用

```javascript
import { loadExtensionFromQuery, loadExtension, setDebugMode } from '@elsedev/react-csr-sdk'

// Enable debug logging
setDebugMode(true)

// Load extension from else_ext query parameter
await loadExtensionFromQuery(async (identifier) => {
  // Your callback: call YOUR backend, which calls Else API
  const response = await fetch(`/api/else/bundle?ext=${identifier}`)
  return await response.json()
})

// Load extension programmatically
await loadExtension('my-extension-slug', async (identifier) => {
  // Your callback: call YOUR backend, which calls Else API
  const response = await fetch(`/api/else/bundle?ext=${identifier}`)
  return await response.json()
})
```

## 快速启动

### 1\. 安装SDK

SDK使用基于CDN的方法,自动更新HTML:

```bash
npm install @elsedev/react-csr-sdk
```

安装后运行 `npx @elsedev/react-csr-sdk` 将装入器 CDN 脚本添加到您的 `index.html`。捆绑装货机的主机位于我们的CDN `https://sdk-cdn.somethingelse.ai` 和您安装的 SDK 版本匹配.

### 2\. 验证 HTML 结构

运行后 `npx @elsedev/react-csr-sdk`,则您的 HTML 将被更新。 应该是这样的

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <!-- Else bundle loader - Automatically added by postinstall script -->
  <script src="https://sdk-cdn.somethingelse.ai/react-csr/else-bundle-loader-v1.0.17.js"></script>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Your App</title>
  
  <!-- Optional: Enable debug logging for bundle loader -->
  <script>
    setTimeout(() => {
      if (window.else) {
        window.else.setDebug(true);
      }
    }, 0);
  </script>
</head>
<body>
  <div id="root"></div>
  
  <!-- IMPORTANT: Add data-else-default-bundle attribute -->
  <script data-else-default-bundle type="module" src="/src/main.tsx"></script>
</body>
</html>
```

**关键细节 :**

-   捆绑加载器脚本自动添加到 `<head>` 以单位命令
-   CDN URL 版本并匹配您已安装的 SDK 版本
-   添加 `data-else-default-bundle` 属性到您主应用程序脚本
-   更新 SDK 后运行 `npx @elsedev/react-csr-sdk` 再次更新 CDN URL

### 3\. 创建供应商后端终点

您的后端应该认证用户并呼叫 Else API :

```javascript
// GET /api/user/bundle
app.get('/api/user/bundle', authenticateUser, async (req, res) => {
  const user = req.user; // Your authenticated user
  
  if (!user.tenantId) {
    return res.json({ bundleUrl: undefined });
  }
  
  try {
    // Call Else API server-to-server with your API key
    const response = await fetch(
      `${NEXUS_API_URL}/vendor-api/products/${PRODUCT_SLUG}/tenants/${user.tenantId}/bundle`,
      {
        headers: {
          'Authorization': `Bearer ${process.env.ELSE_API_KEY}`, // Keep API keys on server
          'Content-Type': 'application/json',
        },
      }
    );
    
    if (response.ok) {
      const data = await response.json();
      res.json({ bundleUrl: data.bundle_url });
    } else {
      res.json({ bundleUrl: undefined });
    }
  } catch (error) {
    console.error('Failed to fetch bundle from Else:', error);
    res.json({ bundleUrl: undefined });
  }
});
```

### 4\. 在前端使用 SDK

```javascript
import { setCustomBundle, clearCustomBundle } from '@elsedev/react-csr-sdk';

// Handle user login - call your own backend
async function handleUserLogin(credentials) {
  try {
    // 1. Authenticate with your backend (your existing auth flow)
    const authResponse = await fetch('/api/auth/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(credentials),
      credentials: 'include'
    });
    
    if (authResponse.ok) {
      // 2. Get bundle URL from your backend
      const bundleResponse = await fetch('/api/user/bundle', {
        credentials: 'include'
      });
      
      if (bundleResponse.ok) {
        const { bundleUrl } = await bundleResponse.json();
        
        // setCustomBundle will exit early if bundleUrl is unchanged
        setCustomBundle(bundleUrl); // Page reloads only if bundle changed
      }
    }
  } catch (error) {
    console.error('Login failed:', error);
  }
}

// Clear custom bundle on logout
async function handleUserLogout() {
  try {
    await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' });
    clearCustomBundle(); // Reloads with default bundle
  } catch (error) {
    console.error('Logout failed:', error);
  }
}
```

## API 参考

### 套装管理

#### `setCustomBundle(bundleUrl, reload?)`

设置自定义捆绑的 URL 并可以选择重新装入页面 。 如果捆绑的 URL 不变, 则提前退出( 不重新加载) .

```javascript
setCustomBundle('https://cdn.example.com/custom-bundle.js', true);
setCustomBundle(undefined); // Clear bundle
```

**参数:**

-   `bundleUrl: string | undefined` - 捆绑或未定义的 URL 清除
-   `reload: boolean` - 是否重新装入页面( 默认: 真)

#### `getCustomBundle()`

获取当前设置的自定义捆绑 URL.

```javascript
const currentBundle = getCustomBundle();
// Returns: string | undefined
```

#### `clearCustomBundle(reload?)`

清除自定义捆绑和可选重装以使用默认值.

```javascript
clearCustomBundle(true);
```

#### `reloadWithBundle(bundleUrl)`

设置捆绑的URL并立即重新装入页面.

```javascript
reloadWithBundle('https://cdn.example.com/new-bundle.js');
reloadWithBundle(undefined); // Clear and reload
```

### 工具函数

#### `setDebugMode(enabled)`

启用/ 禁用调试日志 .

```javascript
setDebugMode(true); // See bundle loading logs
```

## 整合模式

### 用户登录流程

```javascript
async function handleUserLogin(credentials) {
  // 1. Authenticate with your backend
  const user = await authenticateUser(credentials);
  
  // 2. Get bundle URL from your backend
  if (user) {
    try {
      const response = await fetch('/api/user/bundle', {
        credentials: 'include'
      });
      
      if (response.ok) {
        const { bundleUrl } = await response.json();
        
        // Only reloads if bundle URL changed
        setCustomBundle(bundleUrl);
      }
    } catch (error) {
      console.error('Failed to load custom bundle:', error);
      // App continues with default bundle
    }
  }
}
```

### 用户日志流

```javascript
async function handleUserLogout() {
  // 1. Clear session with your backend
  await logoutUser();
  
  // 2. Clear custom bundle and reload with default
  clearCustomBundle();
}
```

### 租户交换

```javascript
async function switchTenant(newTenantId) {
  // 1. Update tenant in your backend
  await updateUserTenant(newTenantId);
  
  // 2. Get new tenant's bundle URL
  const response = await fetch('/api/user/bundle', {
    credentials: 'include'
  });
  
  if (response.ok) {
    const { bundleUrl } = await response.json();
    setCustomBundle(bundleUrl); // Page reloads only if bundle changed
  }
}
```

### 处理错误

```javascript
async function loadUserBundle() {
  try {
    const response = await fetch('/api/user/bundle', {
      credentials: 'include'
    });
    
    if (response.ok) {
      const { bundleUrl } = await response.json();
      setCustomBundle(bundleUrl);
    }
  } catch (error) {
    console.error('Bundle loading failed:', error);
    
    // Optional: Show user notification
    showNotification('Custom features unavailable, using default app');
    
    // App continues with default bundle - no action needed
  }
}
```

## 全球 API (不需要模块导入)

如果无法使用ES模块,捆绑加载器会曝光全局功能:

```javascript
// Available on window.else after bundle-loader.js loads
window.else.setCustomBundle(url);
window.else.getCustomBundle();
window.else.clearCustomBundle();
window.else.reloadWithBundle(url);
window.else.setDebug(enabled);
```

## Bundle 替换工程

1.  **装入器脚本**: 在任何反应代码之前运行
2.  **本地集成检查**: 寻找 `else_bundle_url`
3.  **捆绑决定**数字 :
    -   如果自定义 URL 找到 − 防止默认捆绑, 加载自定义捆绑
    -   如果没有自定义 URL − 允许默认捆绑正常加载
4.  **单一应用程序**: 只有一个反应应用程序开始

## 捆绑要求

您的自定义捆绑应该:

-   完整、独立的反应应用程序
-   包含所有依赖( React, ReactDOM等)
-   挂载到相同的 DOM 元素(`#root`(中文(简体) )
-   处理自己的路线和国家管理

## 后端集成要求

您的后端端点( E)`/api/user/bundle` 或类似)应:

1.  **认证用户** (会议、JWT等)
2.  **确定用户的租户身份** 从您的用户数据库中
3.  **呼叫 API 服务器到服务器** 带有您的 API 密钥
4.  **返回捆绑的 URL** 到前端( 如果没有自定义捆绑, 未定义)

示例响应格式 :

```json
{
  "bundleUrl": "https://cdn.example.com/tenant-123/bundle.js"
}
```

或者如果没有定制捆绑:

```json
{
  "bundleUrl": undefined
}
```

## 安全福利

-   **API 密钥保护**: 从未被浏览器曝光
-   **用户认证**: 您的后端验证身份
-   **授权**: 您的后端确保用户可以访问其租户
-   **审计轨迹**: 您的后端可以日志捆绑请求
-   **限制费率**: 您的后端可以执行限制

## 调试

启用调试模式以查看捆绑加载日志 :

```javascript
import { setDebugMode } from '@elsedev/react-csr-sdk';
setDebugMode(true);

// Or globally:
window.else.setDebug(true);
```

这将日志 :

-   捆绑 URL 分辨率
-   装入尝试
-   后退设想
-   错误细节

## 解决问题

### 默认装入和自定义装入同时

**症状 :** 原始的应用程序和自定义捆绑似乎同时加载,从而产生UI冲突,重复内容,或双重回放挂起.

**根原因 :** 竞速条件,默认捆绑脚本在捆绑装入器能够阻止它之前执行.

**解决方案( 所需步骤 ):**

1.  **校验 CDN 脚本已存在**
    
    -   选中此 CDN 捆绑加载器脚本存在于您的 `index.html`
    -   运行 `npm install` 如果缺少 CDN 脚本
2.  **添加 `data-else-default-bundle` 属性**
    
    ```html
    <script data-else-default-bundle type="module" src="/src/main.tsx"></script>
    ```
    
    -   帮助捆绑者识别和防止默认捆绑
    -   可靠捆绑替换的关键
3.  **校验生产中的装入顺序**
    
    -   构建您的应用程序 : `npm run build`
    -   检查 `dist/index.html` - 捆绑加载器 CDN 脚本应为 FIRST 脚本 `<head>`
    -   如果没有,请确保您的构建进程保留脚本顺序

**调试步骤 :**

1.  启用您的调试模式 `index.html`数字 :
    
    ```html
    <script>
      setTimeout(() => {
        if (window.else) {
          window.else.setDebug(true);
        }
      }, 0);
    </script>
    ```
    
2.  检查浏览器控制台中的装入日志
3.  查找“ Found 默认脚本 ” 和“ 防止默认捆绑负载 ” 消息
4.  如果你看到双倍挂起, 请验证 `data-else-default-bundle` 属性存在

### 生产中未装入

**检查列表 :**

-   XQ 装入器 CDN URL 存在于 `dist/index.html`
-   QQ Bundle 加载器 CDN 脚本是第一个脚本 `<head>` 内 `dist/index.html`
-   主脚本有 `data-else-default-bundle` 属性
-   QQ 自定义捆绑 URL 有效且可访问

**共同问题:**

1.  **Dist/ index.html 中缺少 Bundle 加载器 CDN URL**
    
    -   运行 `npx @elsedev/react-csr-sdk` 再次更新 CDN URL
    -   校验您来源中的脚本标签 `index.html` 已更新
2.  **生产中的脚本顺序错误**
    
    -   检查 `dist/index.html` 手动 - 捆绑装载器应先进入 `<head>`
    -   保证您的构建过程不会删除或重排捆绑加载脚本
3.  **CORS 装入自定义捆绑出错**
    
    -   确保自定义捆绑 URL 有适当的 CORS 头
    -   检查浏览器网络标签失败的请求

### 自定义登录后不应用套装

**检查列表 :**

1.  后端端返回有效 `bundleUrl`
2.  `setCustomBundle(bundleUrl)` 正在被调用
3.  页面正在重新装入( 默认行为)
4.  自定义捆绑 URL 可访问( 无 404 或 CORS 错误)

**调试 :**

```javascript
// Enable debug mode
window.else.setDebug(true);

// Check what's stored
console.log('Current bundle:', window.else.getCustomBundle());

// Test manual load
window.else.reloadWithBundle('https://your-bundle-url.js');
```

**要查找的控制台日志 :**

-   `[Else] Setting custom bundle: <url>` - 确认 SDK 函数
-   `[Else] Custom bundle URL: <url>` - 确认在重新装入时检测到的捆绑
-   `[Else] Loading script: <url>` - 确认装入未遂
-   `[Else] Script loaded successfully: <url>` - 确认装入了捆绑

## 损坏 SDK

SDK在Else工作空间开发扩展时自动被禁用.

要手动禁用 SDK, 请设置一个环境变量 :

**维特:**

```bash
VITE_ELSE_DISABLED=true npm run dev
```

**创建反应 App :**

```bash
REACT_APP_ELSE_DISABLED=true npm start
```

当禁用时,自定义捆绑不会加载,所有的SDK功能都会变成无操作.

## 类型脚本支持

SDK包括了完整的TypeScript定义. 当安装软件包时,全局类型定义 `window.else` 自动可用 :

```typescript
// Global window.else types are automatically available
// No need to manually declare the interface!
window.else?.setCustomBundle('https://example.com/bundle.js')
window.else?.inElseDevEnvironment() // Returns boolean

// Import types for advanced usage
import type { ElseConfig, ElseSDK } from '@elsedev/react-csr-sdk';
```

这个 `window.else` API在全自动完整支持下自动输入.

## 实例

见 `examples/` 完整执行实例目录.

## 支助

关于议题或问题,请参看其他文件或联系支助.

## 常见问题

### @elsedev/react-csr-sdk 是什么？

在 Web 应用程序中以动态捆绑方式加载时使用 SDK

### 如何安装 @elsedev/react-csr-sdk？

在项目目录运行 npm install @elsedev/react-csr-sdk，或使用 pnpm add / yarn add / bun add。它还提供命令行工具 init-else，可以用 npx init-else 直接运行。

### @elsedev/react-csr-sdk 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @elsedev/react-csr-sdk 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @elsedev/react-csr-sdk 可以商用吗？

@elsedev/react-csr-sdk 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @elsedev/react-csr-sdk 的下载量有多少？

根据 npm 官方下载统计，@elsedev/react-csr-sdk 在 2026/10/14 – 2026/11/12 这 30 天内被下载 4,304 次，日均约 143 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @elsedev/react-csr-sdk 还在维护吗？

最新版本 1\.0\.21 发布于 2025/11/29（11 个月前）；至今共发布 20 个版本，其中过去 12 个月发布了 1 个；首次发布于 2025/09/30。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

