# @dynamic-labs/iframe-setup 中文文档

规范地址：https://npmcn.com/package/%40dynamic-labs/iframe-setup/

版本：5\.16\.1
npm 元数据更新时间：2026-11-13T08:39:30.992Z
中文译文更新时间：2026-11-13T02:25:42.834Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40dynamic-labs/iframe-setup
原文对照：https://npmcn.com/package/%40dynamic-labs/iframe-setup/?lang=original
项目主页：https://www.dynamic.xyz/

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

- 包体积（解压后）：609 kB，13 个文件
- 模块格式：ESM + CJS
- TypeScript 类型：包内自带
- 运行依赖：1 个
- 首次发布：2025-03-06T23:50:56.015Z
- 最新版本发布：2026-11-13T08:39:30.795Z
- 近 30 天下载量：3029（2026-10-13 至 2026-11-11，npm 官方下载统计）
- 已发布版本数：329（近 12 个月 179 个）

## 中文简介

收集在 Iframe 中使用动态 SDK 的公用设施

## 安装

```sh
npm install @dynamic-labs/iframe-setup
```

也可以使用：`pnpm add @dynamic-labs/iframe-setup`、`yarn add @dynamic-labs/iframe-setup`、`bun add @dynamic-labs/iframe-setup`

## README

# @ liframe- setup

一个能使动态SDK(@distract-labs/sdk-react-core)在iframe环境中无缝工作的通用包.

## 安装

```bash
npm install @dynamic-labs/iframe-setup
```

## 特征

这个包提供了用于设置iframe环境的设置Iframe方法.

## 实施指南

### 步骤1: 父页面设置

配置包含 iframe 的母页 .

要设置 Iframe, 您需要使用 `initial-parent-url` 查询参数

```tsx
import { setupIframe } from '@dynamic-labs/iframe-setup';

const iframeURL = new URL('https://iframe.com');

if (typeof window !== 'undefined') {
  iframeURL.searchParams.set('initial-parent-url', window.location.href);
}

const App = () => {
  const iframeRef = useRef<HTMLIFrameElement>(null);

  useEffect(() => {
    const cleanUp = setupIframe(iframeRef.current!, iframeURL.origin);

    return () => {
      cleanUp();
    };
  }, []);

  return (
    <div>
      <iframe ref={iframeRef} src={iframeURL.toString()} />
    </div>
  );
};
```

### 步骤2: Iframe 应用程序设置

在您的 Iframe 应用程序中, 初始化 SDK :

```tsx
import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';
import { setupInsideIframe } from '@dynamic-labs/utils';

// Initialize as early as possible in your iframe application
setupInsideIframe();

const App = () => {
  return (
    <DynamicContextProvider>{/* Your application */}</DynamicContextProvider>
  );
};
```

一旦两个步骤完成,动态SDK将在您iframe环境中全面运行.

## 促成社会调整

要在您的 iframe 应用程序中实现社会重定向功能, 您需要配置 `redirectUrl` 设置 `DynamicContextProvider`。 。 。 .

```tsx
import { DynamicContextProvider } from '@dynamic-labs/sdk-react-core';
import { PlatformService } from '@dynamic-labs/utils';
import { setupInsideIframe } from '@dynamic-labs/utils';

// Initialize as early as possible in your iframe application
setupInsideIframe();

const App = () => {
  return (
    <DynamicContextProvider
      settings={{
        redirectUrl: PlatformService.getUrl().toString(),
        // other settings
      }}
    >
      {/* Your application */}
    </DynamicContextProvider>
  );
};
```

这个配置确保了在社交认证后,用户会被正确重定向回您的应用程序,而不是iframe URL.

* * *

## 备选实施

### 手动设置( 在父应用程序中不安装软件包)

如果您无法在父应用程序中安装软件包, 您可以直接执行 `setupIframe` 通过复制以下代码实现功能:

```ts
/**
 * Setup an iframe to communicate with the Dynamic SDK
 * running inside the iframe
 *
 * This file is self contained and should not import or depend on any
 * other file or package.
 *
 * @see https://www.npmjs.com/package/@dynamic-labs/iframe-setup
 */
/* eslint no-restricted-syntax: ["error", "ImportDeclaration"] */

/**
 * The same implementation was added to the utils package
 * and they should be kept in sync
 * The type is duplicated here to avoid a dependency on the utils package
 * @see packages/utils/src/setupInsideIframe/types.ts
 */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type MESSAGE_HANDLER = (...params: any[]) => Promise<any> | void;
type IFRAME_EVENTS = {
  OPEN_URL: (args: {
    url: string;
    target?: 'self' | 'blank';
    features?: string;
  }) => void;
  UPDATE_PARENT_URL: (args: { url: string }) => void;
  APP_FOCUS: () => void;
};

/**
 * Setup an iframe to comunicate with the Dynamic SDK
 * running inside the iframe
 *
 * @returns a function to teardown the iframe setup
 */
export const setupIframe = (
  iframe: HTMLIFrameElement,
  origin: string,
): VoidFunction => {
  if (typeof window === 'undefined') return () => {};

  const messageHandler = createMessageHandler<IFRAME_EVENTS>(
    window,
    origin,
    iframe,
  );
  const messageSender = createMessageSender<IFRAME_EVENTS>(iframe);

  // Add event listener to handle deeplink
  const setupOpenUrlHandler = () =>
    messageHandler('OPEN_URL', ({ url, target = 'self', features }) => {
      // This navigates the host top window, so never honor a URL whose scheme
      // can execute script in the host origin (iframe->host XSS).
      if (hasDangerousScheme(url)) return;

      if (target === 'blank') {
        window.open(url, '_blank', features);
      } else {
        window.location.assign(url);
      }
    });

  // Watch for url changes and update Dynamic SDK in iframe
  const setupUrlUpdateHandler = (): VoidFunction => {
    const notifyUrlChange = () => {
      try {
        messageSender('UPDATE_PARENT_URL', { url: window.location.href });
      } catch {
        // Never let a notification failure surface to the host application
        // (e.g. an SPA router calling history.pushState)
      }
    };

    patchHistoryOnce();

    // popstate fires on browser back/forward navigation; the custom event
    // fires on programmatic pushState/replaceState (which never emit popstate)
    window.addEventListener('popstate', notifyUrlChange);
    window.addEventListener(URL_CHANGE_EVENT, notifyUrlChange);

    return () => {
      window.removeEventListener('popstate', notifyUrlChange);
      window.removeEventListener(URL_CHANGE_EVENT, notifyUrlChange);
    };
  };

  /**
   * Watch for app focus and send event to Dynamic SDK in iframe
   */
  const watchForAppVisibilityChange = (): VoidFunction => {
    let lastFired = false;

    const onVisibilityOrFocus = () => {
      if (!lastFired && document.visibilityState === 'visible') {
        lastFired = true;
        messageSender('APP_FOCUS');

        // Reset flag after a short delay to allow next valid trigger
        setTimeout(() => (lastFired = false), 100);
      }
    };

    document.addEventListener('visibilitychange', onVisibilityOrFocus);
    window.addEventListener('focus', onVisibilityOrFocus);

    return () => {
      document.removeEventListener('visibilitychange', onVisibilityOrFocus);
      window.removeEventListener('focus', onVisibilityOrFocus);
    };
  };

  // Holds all the cleanup handlers
  const cleanupHandlers: VoidFunction[] = [];

  cleanupHandlers.push(setupUrlUpdateHandler());
  cleanupHandlers.push(setupOpenUrlHandler());
  cleanupHandlers.push(watchForAppVisibilityChange());

  return () => cleanupHandlers.forEach((offHandler) => offHandler());
};

// Utils

/**
 * URL schemes that can execute script in the host origin. Navigating the host
 * top window to any of these turns an OPEN_URL message into arbitrary code
 * execution in the integrator's origin, so they are blocked.
 *
 * A deny-list (rather than an http/https allow-list) is used deliberately:
 * wallet flows legitimately open custom-scheme deep links, and allow-listing
 * http(s) would break them.
 */
const DANGEROUS_SCHEMES = [
  'javascript:',
  'data:',
  'blob:',
  'vbscript:',
  'file:',
];

/**
 * Leading characters the browser ignores when resolving a URL scheme: spaces,
 * tabs, newlines and other C0 control chars (U+0000–U+0020). Stripping them
 * defeats obfuscation such as "\tjavascript:alert(1)".
 */
// eslint-disable-next-line no-control-regex
const LEADING_IGNORED_CHARS = /^[\u0000-\u0020]+/;

/**
 * Returns true when the URL begins with a script-executing scheme.
 *
 * Non-string input is treated as dangerous, leading ignored chars are stripped
 * first, and the comparison is case-insensitive ("JavaScript:" is blocked).
 */
const hasDangerousScheme = (url: unknown): boolean => {
  if (typeof url !== 'string') return true;

  const normalized = url.replace(LEADING_IGNORED_CHARS, '').toLowerCase();

  return DANGEROUS_SCHEMES.some((scheme) => normalized.startsWith(scheme));
};

/**
 * Custom event dispatched when the parent URL changes via the History API.
 * Used instead of re-patching history per setupIframe call so that multiple
 * instances can observe changes without clobbering each other's patches.
 */
const URL_CHANGE_EVENT = 'dynamic-labs:url-change';

const HISTORY_PATCHED_FLAG = '__dynamicLabsHistoryPatched';

/**
 * Patch history.pushState/replaceState to emit URL_CHANGE_EVENT.
 *
 * popstate does not fire for programmatic navigation, so we patch the History
 * API to broadcast a custom event instead. The patch is installed only once,
 * retains no references, and is intentionally never restored — it merely
 * dispatches an event, which keeps it safe to leave in place and composable
 * across multiple setupIframe instances.
 */
const patchHistoryOnce = (): void => {
  const history = window.history as History & Record<string, unknown>;

  if (history[HISTORY_PATCHED_FLAG]) return;
  history[HISTORY_PATCHED_FLAG] = true;

  const originalPushState = history.pushState.bind(history);
  const originalReplaceState = history.replaceState.bind(history);

  history.pushState = (...args: Parameters<History['pushState']>) => {
    originalPushState(...args);
    window.dispatchEvent(new Event(URL_CHANGE_EVENT));
  };

  history.replaceState = (...args: Parameters<History['replaceState']>) => {
    originalReplaceState(...args);
    window.dispatchEvent(new Event(URL_CHANGE_EVENT));
  };
};

/**
 * Create a message handler for the iframe
 */
const createMessageHandler =
  <T extends Record<string, MESSAGE_HANDLER>>(
    window: Window,
    origin: string,
    iframe: HTMLIFrameElement,
  ) =>
  <EVENT_NAME extends Extract<keyof T, string>>(
    eventName: EVENT_NAME,
    handler: T[EVENT_NAME],
  ): VoidFunction => {
    const messageHandler = (event: MessageEvent) => {
      if (event.origin !== origin) return;

      // Only trust messages coming from this iframe's own window. Checking the
      // origin alone lets any frame/tab at the SDK-iframe origin forge events.
      if (event.source !== iframe.contentWindow) return;

      const { eventName: incomingEventName, args } = event.data;

      if (eventName === incomingEventName) {
        handler(...args);
      }
    };

    window.addEventListener('message', messageHandler);

    return () => {
      window.removeEventListener('message', messageHandler);
    };
  };

/**
 * Create a function to send messages to the iframe
 */
const createMessageSender =
  <T extends Record<string, MESSAGE_HANDLER>>(iframe: HTMLIFrameElement) =>
  <EVENT_NAME extends Extract<keyof T, string>>(
    eventName: EVENT_NAME,
    ...args: Parameters<T[EVENT_NAME]>
  ) => {
    iframe.contentWindow?.postMessage(
      { args, eventName },
      new URL(iframe.src).origin,
    );
  };

```

现在你可以导入并使用 `setupIframe` 函数

```tsx
import { setupIframe } from './iframe-setup';

const iframeURL = new URL('https://iframe.com');

if (typeof window !== 'undefined') {
  iframeURL.searchParams.set('initial-parent-url', window.location.href);
}

const App = () => {
  const iframeRef = useRef<HTMLIFrameElement>(null);

  useEffect(() => {
    const cleanUp = setupIframe(iframeRef.current!, iframeURL.origin);

    return () => {
      cleanUp();
    };
  }, []);

  return (
    <div>
      <iframe ref={iframeRef} src={iframeURL.toString()} />
    </div>
  );
};
```

## 常见问题

### @dynamic-labs/iframe-setup 是什么？

收集在 Iframe 中使用动态 SDK 的公用设施

### 如何安装 @dynamic-labs/iframe-setup？

在项目目录运行 npm install @dynamic-labs/iframe-setup，或使用 pnpm add / yarn add / bun add。

### @dynamic-labs/iframe-setup 支持 TypeScript 吗？

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

### @dynamic-labs/iframe-setup 支持 ESM 还是 CommonJS？

同时提供 ES Module 与 CommonJS 入口，import 和 require\(\) 都可以使用，打包工具会自动选择合适的版本。

### @dynamic-labs/iframe-setup 可以商用吗？

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

### @dynamic-labs/iframe-setup 的下载量有多少？

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

### @dynamic-labs/iframe-setup 还在维护吗？

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

