# @equinor/fusion-framework-react-app 中文文档

规范地址：https://npmcn.com/package/%40equinor/fusion-framework-react-app/

版本：14\.3\.9
npm 元数据更新时间：2026-11-12T10:02:08.551Z
中文译文更新时间：2026-11-13T20:32:23.231Z
软件包许可：ISC

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

原始 npm 页面：https://www.npmjs.com/package/%40equinor/fusion-framework-react-app
原文对照：https://npmcn.com/package/%40equinor/fusion-framework-react-app/?lang=original
源码仓库：https://github.com/equinor/fusion-framework

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

- 包体积（解压后）：1 MB，169 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：9 个
- peer 依赖：rxjs ^7\.0\.0，react ^18\.0\.0 \|\| ^19\.0\.0，react-dom ^18\.0\.0 \|\| ^19\.0\.0，@types/react ^18\.0\.0 \|\| ^19\.0\.0，@equinor/fusion-framework-module-msal ^12\.1\.0，@equinor/fusion-framework-module-roles ^0\.4\.0，@equinor/fusion-framework-module-state ^2\.1\.0，@equinor/fusion-framework-react-router ^2\.4\.8，@equinor/fusion-framework-react-ag-grid ^37\.1\.5，@equinor/fusion-framework-module-ag-grid ^37\.2\.0
- 首次发布：2022-01-14T12:36:26.538Z
- 最新版本发布：2026-11-12T10:02:07.997Z
- 近 30 天下载量：8095（2026-10-14 至 2026-11-12，npm 官方下载统计）
- 已发布版本数：431（近 12 个月 62 个）

## 中文简介

暂无中文简介

## 安装

```sh
npm install @equinor/fusion-framework-react-app
```

也可以使用：`pnpm add @equinor/fusion-framework-react-app`、`yarn add @equinor/fusion-framework-react-app`、`bun add @equinor/fusion-framework-react-app`

## README

# @ equinor/ 组合框架- 反应- app

用于构建模块化Fusion框架应用程序的回放绑定. 为访问框架模块(HTTP, 认证, 上下文, 导航, 书签, 角色, 设置等) 提供渲染辅助工具 , 配置回调, 和 React hooks .

## 特征

-   **单行程序拖带** 通过 `renderApp` —— 创建渲染函数,将组件上载为 React 18's `createRoot`
-   **模块钩** —— 说 `useAppModule` 编号 : `useAppModules` 用于安全进入任何已注册模块
-   **分路径入口** 用于可选能力: MSAL 认证、 HTTP、 上下文、 导航、 书签、 角色、 特征旗帜、 设置、 分析、 AG 网格主题、 帮助中心、 和装入程序
-   **环境变量** 钩- `useAppEnvironmentVariables` 用于运行时访问 app 配置
-   **懒惰装入** ——组件被包入 `React.lazy` + 键 `Suspense` 自动

## 安装

```sh
pnpm add @equinor/fusion-framework-react-app
```

## 使用量

### 启动应用程序

```tsx
// config.ts
import type { AppModuleInitiator } from '@equinor/fusion-framework-react-app';

export const configure: AppModuleInitiator = (configurator) => {
  configurator.configureHttpClient('my-api', {
    baseUri: 'https://api.example.com',
    defaultScopes: ['api://my-api/.default'],
  });
};

// App.tsx
export const App = () => <h1>Hello Fusion</h1>;

// index.ts
import { renderApp } from '@equinor/fusion-framework-react-app';
import { App } from './App';
import { configure } from './config';

export const render = renderApp(App, configure);
export default render;
```

### 访问模块

```tsx
import { useAppModule } from '@equinor/fusion-framework-react-app';

const MyComponent = () => {
  const auth = useAppModule('auth');
  // ...
};
```

### HTTP 请求

```tsx
import { useHttpClient } from '@equinor/fusion-framework-react-app/http';

const MyComponent = () => {
  const client = useHttpClient('my-api');
  // client.fetch$('endpoint').subscribe(...)
  // await client.fetchAsync('endpoint')
};
```

### MSAL 认证

> **说明:** 要求数 `@equinor/fusion-framework-module-msal`. MSAL 模块必须由主机/端口配置——应用程序只应消耗钩.

```tsx
import { useCurrentAccount, useAccessToken } from '@equinor/fusion-framework-react-app/msal';

const UserInfo = () => {
  const user = useCurrentAccount();
  const { token } = useAccessToken({ scopes: ['User.Read'] });
  if (!user) return <p>Not signed in</p>;
  return <p>Welcome, {user.name}</p>;
};
```

### A. 背景情况

```tsx
import { useCurrentContext } from '@equinor/fusion-framework-react-app/context';

const ContextInfo = () => {
  const context = useCurrentContext();
  return <p>Selected: {context?.title ?? 'none'}</p>;
};
```

### 导航/路由

```tsx
import { useRouter } from '@equinor/fusion-framework-react-app/navigation';
import { RouterProvider } from '@equinor/fusion-framework-react-router';

const routes = [{ path: '/', element: <Home /> }];

const App = () => {
  const router = useRouter(routes);
  return <RouterProvider router={router} />;
};
```

## 州管理层

[![](https://img.shields.io/github/package-json/v/equinor/fusion-framework?filename=packages%2Fmodules%2Fstate%2Fpackage.json&label=@equinor/fusion-framework-module-state&style=for-the-badge)](https://github.com/equinor/fusion-framework/tree/main/packages/modules/state)

Fusion框架提供了强大的状态管理解决方案,能够以自动同步的方式持续地,跨组件状态共享. 与传统的React状态在页面刷新中丢失不同,这种状态在应用会话间持续并保持在不同组件间实时同步.

**主要惠益:**

-   🔄 **持久性状态**:幸存页面刷新并重新启动应用程序
-   🔗 **交叉组件同步**: 任何组件之间立即共享状态
-   ⚡ **优化更新**: 自动回滚错误的响应UI
-   🛡️ **类型安全**: 全类型脚本支持和类型推论
-   🎯 **简单 API**: 工作类 `useState` 但坚持不懈

**使用大小写 :**

-   用户偏好和设置
-   表格数据应持续
-   类似过滤、排序或视图模式的 UI 状态
-   需要在多个组成部分之间共享的数据
-   昂贵业务的缓存管理

### 安装

首先,安装状态模块包:

```sh
pnpm install @equinor/fusion-framework-module-state
```

### 设置

启用应用程序配置中的状态模块 。 初始化持续存储并制作 `useAppState` 您可在您的申请中找到:

```typescript
import { enableAppState } from '@equinor/fusion-framework-react-app/state';
export const configure: ModuleInitiator = (appConfigurator) => {
  enableAppState(appConfigurator);
};
```

> \[叹息\] 状态管理模块是一个强大的工具,但是在您的应用中使用它时必须了解潜在的陷阱和局限性. 国家管理是全球性的,如果不仔细使用,可能导致出乎意料的行为.
> 
> **实例1:** 如果有多个依赖于同一状态的组件,更新一个组件中的状态会导致所有使用该状态的组件的重接,可能导致性能问题.
> 
> **实例2:** 用户打开了应用程序的多分页,每个分页都在修改相同的状态. 这会导致出乎意料的行为,因为一个标签中所做的修改会被反映到所有标签中. _(类似于存储选定列的用户首选项)_

### 基本用途

使用 `useAppState` 就像反应一样 `useState`自动坚持 第一个参数是一个独特的密钥,第二个参数是一个带有默认值的选项对象:

```typescript
import { useAppState } from '@equinor/fusion-framework-react-app/state';

const Counter = () => {
  const [count, setCount] = useAppState('counter', { defaultValue: 0 });
  return (
    <div>
      <span>Count: {count}</span>
      <button onClick={() => setCount((prev) => (prev ?? 0) + 1)}>Increment</button>
    </div>
  );
};
```

### 跨组件同步

多个组件可以通过使用相同的密钥来共享相同的状态. 一个组件的更改自动更新所有其他组件 :

```typescript
import { useAppState } from '@equinor/fusion-framework-react-app/state';

const Incrementer = () => {
  const [count, setCount] = useAppState('counter', { defaultValue: 0 });
  return (
    <button onClick={() => setCount((prev) => (prev || 0) + 1)}>
      Increment
    </button>
  );
};

const Display = () => {
  const [count] = useAppState('counter', { defaultValue: 0 });
  return <span>Current count: {count}</span>;
};

// Usage in your app
const App = () => (
  <div>
    <Incrementer />
    <Display />
  </div>
);
```

### 高级使用

**有类型脚本的复杂对象 :**

```typescript
interface UserPreferences {
  theme: 'light' | 'dark';
  language: string;
  notifications: boolean;
}

const SettingsPanel = () => {
  const [settings, setSettings] = useAppState<UserPreferences>('user-settings', {
    defaultValue: { theme: 'light', language: 'en', notifications: true }
  });

  const toggleTheme = () => {
    setSettings(prev => ({
      ...prev!,
      theme: prev!.theme === 'light' ? 'dark' : 'light'
    }));
  };

  return <button onClick={toggleTheme}>Theme: {settings?.theme}</button>;
};
```

**清除状态 :**

```typescript
// Remove from storage completely
const clearSettings = () => setSettings(undefined);
```

### 最佳做法

#### 避免断层关闭

> \[尖叫声\] 在基于当前值更新状态时,始终使用更新器功能来防止同时更新中出现 stale 关闭问题.

```typescript
const [count, setCount] = useAppState('counter', { defaultValue: 0 });

// ❌ Bad: Can use stale value in rapid updates
const increment = () => setCount(count + 1);

// ✅ Good: Always gets the latest value
const increment = () => setCount(prev => (prev || 0) + 1);
```

#### 状态密钥组织

为更好的组织使用分级命名 :

```typescript
// ✅ Good - hierarchical, descriptive
'user.profile.personal'
'user.preferences.theme'
'app.settings.notifications'
'feature.dashboard.filters'

// ❌ Avoid - flat, unclear
'userdata'
'settings'
'stuff'
```

#### 使用强打字

```typescript
// ✅ Good - strong typing
interface UserProfile {
  id: string;
  name: string;
  email: string;
}

const [user, setUser] = useAppState<UserProfile>('user.profile');

// ❌ Avoid - weak typing
const [user, setUser] = useAppState('user.profile');
```

> \[! TIP\] 验证复合型Schemas 使用库像 `zod` 或者说 `yup` 在使用复杂状态计划之前验证它们.
> 
> ```typescript
> const userSchema = z.object({
>   id: z.string().uuid(),
>   name: z.string().min(2).max(100),
>   email: z.string().email(),
> });
> 
> type UserProfile = z.infer<typeof userSchema>;
> 
> // ✅ Good - strong typing with validation
> const useMyUser = () => {
>   const [value, setValue] = useAppState<UserProfile>('user.profile');
>   const setUser = useCallback((user: UserProfile) => {
>     if (userSchema.safeParse(user).success) {
>       setValue(user);
>       return true;
>     } else {
>       console.warn('Provided user is invalid');
>       return false;
>     }
>   }, [setValue]);
>   if(!userSchema.safeParse(value).success) {
>     console.warn('Current user state is invalid');
>     return null;
>   }
>   return value;
> };
> ```

## 特性旗

### 地物标志

> **说明:** 要求数 `@equinor/fusion-framework-module-feature-flag`。 。 。 .

```tsx
import { enableFeatureFlag } from '@equinor/fusion-framework-react-app/feature-flag';

export const configure: AppModuleInitiator = (configurator) => {
  enableFeatureFlag(configurator, [
    { key: 'dark-mode', title: 'Dark mode' },
    { key: 'beta-ui', title: 'Beta UI', allowUrl: true },
  ]);
};
```

```tsx
import { useFeature } from '@equinor/fusion-framework-react-app/feature-flag';

const Toggle = () => {
  const { feature, toggleFeature } = useFeature('dark-mode');
  return <Switch checked={feature?.enabled} onChange={() => toggleFeature()} />;
};
```

### 作用

> **说明:** 要求数 `@equinor/fusion-framework-module-roles` 在申请中或父母关系 框架范围.

```tsx
import { enableRoles } from '@equinor/fusion-framework-module-roles';

export const configure: AppModuleInitiator = (configurator) => {
  enableRoles(configurator);
};
```

```tsx
import { useAccessRole } from '@equinor/fusion-framework-react-app/roles';

const ReportsAccess = ({ claimableRoleId }: { claimableRoleId: string }) => {
  const role = useAccessRole('Reports.Read');

  if (role.isChecking) return <Spinner />;
  if (role.checkError) return <ErrorMessage error={role.checkError} />;
  if (role.hasAccessRole) return <Reports />;

  /** Consumes the event-handler rejection; activationError below owns the visible failure. */
  const handleActivate = (): void => {
    void role
      .activateClaimableRoleAssignment({
        assignmentId: claimableRoleId,
        reason: 'Open reports',
        hours: 2,
      })
      .catch(() => undefined);
  };

  return role.hasClaimableRoleAssignmentForAccessRole ? (
    <>
      {role.activationError ? <p role="alert">{String(role.activationError)}</p> : null}
      <button disabled={role.isActivating} onClick={handleActivate}>
        Claim access
      </button>
    </>
  ) : null;
};
```

`useAccessRole` 安装时检查确切的存取作用名称,并在索赔成功后再次检查. 检查和索赔业务暴露出单独的装载和出错状态. `activateClaimableRoleAssignment` 拒绝失败和设置 `activationError`; 事件处理器必须消耗 拒绝并让突变出错,使用户可以重试.

### 书签

```tsx
import { useCurrentBookmark } from '@equinor/fusion-framework-react-app/bookmark';

const BookmarkView = () => {
  const { currentBookmark } = useCurrentBookmark();
  return <pre>{JSON.stringify(currentBookmark, null, 2)}</pre>;
};
```

### 设置

```tsx
import { useAppSetting } from '@equinor/fusion-framework-react-app/settings';

const ThemePicker = () => {
  const [theme, setTheme] = useAppSetting('theme', 'light');
  return <button onClick={() => setTheme('dark')}>Go dark</button>;
};
```

### 环境变量

```tsx
import { useAppEnvironmentVariables } from '@equinor/fusion-framework-react-app';

const EnvInfo = () => {
  const env = useAppEnvironmentVariables();
  if (!env.complete) return <p>Loading…</p>;
  return <pre>{JSON.stringify(env.value, null, 2)}</pre>;
};
```

### 测试

测试公用事业`renderAppHook`, (中文(简体) ), `renderAppComponent`, (中文(简体) ), `testApp`插件 解析一个应用程序自己的显示/配置为虚拟模块)已移动到自己的软件包, [`@equinor/fusion-framework-vitest-plugin-react-app`](https://npmcn.com/package/vitest-plugin/react-app/README.md) —— 说 查看软件包的 README 用于使用 .

## API 参考

### 核心出口(主要切入点)

| 导出 | 说明 |
| --- | --- |
| `renderApp` | 从组件和可选配置回调创建挂载函数 |
| `makeComponent` | Lazily 初始化模块并在框架提供者中包裹一个组件 |
| `createComponent` | _(折旧)_ 工厂返回a `ComponentRenderer` |
| `createLegacyApp` | _(折旧)_ 遗留的Fusion CLI 应用程序的包装器 |
| `renderComponent` | 下级助手: 挂载 a `ComponentRenderer` 通过 `createRoot` |
| `useAppModule(key)` | 按键返回单个模块实例 |
| `useAppModules()` | 返回所有已关闭的应用程序模块 |
| `useAppEnvironmentVariables()` | 应用环境配置的可观测状态 |

### 分路径入口

| 路径 | 主要出口 |
| --- | --- |
| `/msal` | `useCurrentAccount`, (中文(简体) ), `useAccessToken`, (中文(简体) ), `useToken` |
| `/http` | `useHttpClient` (再出口) |
| `/http/selectors` | 反应选择器公用事业 |
| `/context` | `useCurrentContext`, (中文(简体) ), `useContextProvider`, (中文(简体) ), `useFrameworkCurrentContext` |
| `/navigation` | `useRouter`, (中文(简体) ), `useNavigationModule` |
| `/feature-flag` | `enableFeatureFlag`, (中文(简体) ), `useFeature` |
| `/roles` | `useAccessRole` |
| `/bookmark` | `enableBookmark`, (中文(简体) ), `useCurrentBookmark`, (中文(简体) ), `useBookmark` |
| `/settings` | `useAppSetting`, (中文(简体) ), `useAppSettings` |
| `/analytics` | `useTrackFeature` |
| `/help-center` | `useHelpCenter` |
| `/apploader` | `Apploader`, (中文(简体) ), `useApploader` |
| `/framework` | `useFramework`, (中文(简体) ), `useCurrentUser`, (中文(简体) ), `useFrameworkHttpClient` |
| `/widget` | 部件切入点 |

## 配置

应用程序配置通过调用完成 `renderApp` (或 `makeComponent`) (中文(简体) ). 回调收到一个 `IAppConfigurator` 使用每个模块的构建器:

```ts
const configure: AppModuleInitiator = (configurator) => {
  // HTTP clients
  configurator.configureHttpClient('my-api', { baseUri: '...' });

  // Feature flags
  enableFeatureFlag(configurator, [{ key: 'beta', title: 'Beta' }]);

  // Navigation, context, etc. — see module docs
};
```

> MSAL / auth 模块由主机门户配置并自动提升为应用. 执行 **没有** 在应用程序代码中配置它 .

## 常见问题

### 如何安装 @equinor/fusion-framework-react-app？

在项目目录运行 npm install @equinor/fusion-framework-react-app，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：react（^18\.0\.0 \|\| ^19\.0\.0）、@equinor/fusion-framework-module-msal（^12\.1\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @equinor/fusion-framework-react-app 支持 TypeScript 吗？

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

### @equinor/fusion-framework-react-app 支持 ESM 还是 CommonJS？

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

### @equinor/fusion-framework-react-app 可以商用吗？

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

### @equinor/fusion-framework-react-app 的下载量有多少？

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

### @equinor/fusion-framework-react-app 还在维护吗？

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

