---
title: Notion Electron 逆向工程：剪贴板 — REA
url: https://rea.tools/zh-CN/showcase/notion/
---

[首页](https://rea.tools/zh-CN/) / [案例展示](https://rea.tools/zh-CN/showcase/) / Notion

案例研究 · Electron

# 如何 Notion 复制 文本和HTML。

从 Notion 的页面 API 出发，经预加载脚本和主进程，一路追踪到 Electron 写入剪贴板的接口。

Notion Desktop 7.6.1 · JavaScript · REA

Notion 桌面包中的桥接代码

1. 01 · 面向页面的API

   `clipboard.write(data)`

   预加载公开了一个可调用的方法。
2. 02 · 预加载包装器

   `ipcRenderer.invoke`

   包装器发送通道和参数。
3. 03 · 主进程处理程序

   `ipcMain.handle`

   接收器检查哪个页面发送它。
4. 04 · 系统剪贴板

   `clipboard.write(data)`

   Electron 写入提供的文本和HTML。

一个通道连接双方: `notion:clipboard:write`

预加载脚本和主进程使用同一个通道。解析包装函数后，就能看出这些名称背后实际调用的 Electron 函数。

## 复制操作去哪里？

页面可以调用名为 `clipboard.write` 的方法。它在预加载脚本中的实现是一个包装函数。关键问题是：它发送了什么，又由哪段代码接收？

你的编程助手

使用REA跟踪Notion如何将文本和HTML复制到系统剪贴板。 在每个步骤显示代码。

一个示例提示，配合本地 Notion 应用包供你的编程助手使用。

### REA查找从哪里开始阅读

用 REA 分析标签页的预加载脚本，可以找到暴露给页面的 API，以及发送 IPC 请求的调用。两个结果都给出了在压缩代码中的位置：

REA 分析结果 → 两个源码位置

公开页面API

```
api_key: "__electronApi"
api_status: "dynamic"
source: preload.js:1:22764–22828
```

渲染器IPC调用

```
operation: "invoke"
channel_expression: "e"
source: preload.js:1:6486–6522
```

这是新一次 `analyze_javascript_application` 分析结果中的部分字段，来自 Notion 标签页的预加载脚本，使用 REA 4.1.0 记录。位置对应原始压缩包中的行号和从零开始的列号。

编程助手打开这些位置，在重建出的 API 中找到剪贴板成员，再读取 `invokerInMain`。调用处传入 `notion:clipboard:write`，让编程助手知道要在主进程代码中搜索哪个名称。下面四段代码展示这条调用链。

### 四段简短代码

从打包的JavaScript简化，具有可读的变量名称。 不相关的成员被省略。

1. #### 从暴露给页面的 API 开始

   tab_browser_view/preload.js · API 成员

   ```
   const clipboardApi = {
     write: invokerInMain("notion:clipboard:write")
   };

   // Included in the API exposed through contextBridge:
   // window.__electronApi.clipboard
   ```

   此成员提供通道名称。`invokerInMain` 的实现告诉我们请求如何离开页面进程。
2. #### 解析预加载的包装器

   tab_browser_view/preload.js · IPC 辅助函数

   ```
   function invokerInMain(channel) {
     return (...args) => invokeInMainAndReturnResult(channel, ...args);
   }

   function invokeInMainAndReturnResult(channel, ...args) {
     return ipcRenderer.invoke(channel, ...args);
   }
   ```

   返回的函数通过 Electron 的请求／响应 IPC 转发参数。接下来可以在主进程中查找 `notion:clipboard:write`。
3. #### 检查接收器是如何注册的

   main/index.js · 处理器辅助函数

   ```
   const handleRequestFromRenderer = {
     addListener(channel, handler) {
       ipcMain.handle(channel, handler);
     }
   };
   ```

   主进程使用另一个包装器。它的 `addListener` 方法为该通道注册一个 `ipcMain.handle` 接收器。
4. #### 查找写入调用及其发送方校验

   main/index.js · 剪贴板接收函数

   ```
   handleRequestFromRenderer.addListener(
     "notion:clipboard:write",
     (event, data) =>
       isNotionWebContents(event.sender)
         ? clipboard.write(data)
         : undefined
   );
   ```

   这就是调用的终点。处理函数检查发送方后，把收到的数据交给 Electron 的剪贴板 API。通道名和两个解析后的包装函数，把暴露给页面的方法连接到了这里。

## 复制的块如何保持其结构？

Notion 的网页编辑器通过 `electronApi.clipboard.write` 发送文本和 HTML。它在本地保留内容块数据，再在复制的 HTML 中放入一个 ID，将二者关联起来。

HTML 关联到已保存的内容块数据

发送到系统剪贴板

```
text: "Project notes"
html: "<p>Project notes</p>"
    + "<!-- notionvc: <copy-id> -->"
```

Notion本地保存

```
id: <copy-id>
mimeTypes:
  text/_notion-blocks-v3-production
    → structured block data
```

1. 读取HTML ID
2. 查找相同的本地ID
3. 恢复Notion块数据

文本和 ID 为示意值。已记录的模块测试显示：ID 匹配时可以取回内容块数据；条目失效时只返回文本和 HTML。

粘贴时，Notion 会查找 HTML 中的 ID，并与保存的副本比对。ID 一致即可恢复内容块数据。

从2026年7月13日缓存的web资产中记录的剪贴板格式。

## 更多示例和来源

另一个例子：Markdown 表格

Notion 将 Markdown 解析为内容块数据。此示例展示一张表格如何变成表头行和数据行。

通过Notion的Markdown解析器的表格

Markdown 输入

```
| A | B |
| - | - |
| 1 | 2 |
```

结构化解析器输出 · 摘录

```
table · headerRow: true
  table_row
    cells: ["A"], ["B"]
  table_row
    cells: ["1"], ["2"]
```

来自抽取出的 Markdown 解析器的记录输出。表格变成行和单元格。


另一条路径：脱机SQLite连接

Notion 的渲染进程通过 IPC 请求 `getSqliteMeta`。主进程创建消息通道，把一个端口交给渲染进程，另一个交给 SQLite 工具进程。后续的语句批次和结果直接通过这个端口传输。

追踪这次交接，就能知道数据库操作在哪个进程执行，也能解释为什么只跟踪最初的 IPC 请求会漏掉后续通信。


资料来源及如何阅读摘录

剪贴板桥接代码来自 Notion Desktop 7.6.1 的 `app.asar`，包括标签页预加载脚本和 `main/index.js`。节选中展开了压缩后的变量名，并省略无关代码；发送方检查辅助函数展示了底层的 `isNotionWebContents` 调用。

虚拟剪贴板和 Markdown 示例来自 2026 年 7 月 13 日缓存的网页资源，包括 `747001`、`58169`、`736810` 和 `422457` 模块。这些资源有独立版本。保存的测试执行提取出的模块和小型测试程序；输出反映的是这些测试，没有记录桌面 UI 操作。

这里的 REA 结果是对保存的标签页预加载脚本进行的专项分析，使用已发布的 REA 4.1.0 记录。它定位了 API 和调用操作。读取包中的包装函数实现后，就能把剪贴板成员与匹配的接收方连接起来。

Markdown 示例使用解析器模块 `422457`。保存的模块测试显示，页面和聊天过滤器都会保留这张表格。粘贴／导入、结构化 Markdown 和 AI／聊天解析器各自有不同入口与中间格式。

- [JavaScript应用指南](https://rea.tools/guides/javascript/)遵循一个小的Electron应用程序的导出功能。
- [DX-Ball 重建](https://rea.tools/zh-CN/showcase/dx-ball/)沿原生指令找到一个经过验证的 C 函数。

[分析你自己的应用程序](https://rea.tools/get-started/)
