js-rpc2 2.4.1 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/doc/chrome-extensions.md +132 -132
- package/doc/electron.md +137 -137
- package/doc/worker_threads.md +186 -186
- package/package.json +1 -1
- package/src/lib.js +4 -4
package/README.md
CHANGED
|
@@ -338,7 +338,7 @@ ipcMain.on('port', (event) => {
|
|
|
338
338
|
import { ipcRenderer } from 'electron/renderer'
|
|
339
339
|
|
|
340
340
|
window.onmessage = (/** @type {MessageEvent} */ event) => {
|
|
341
|
-
if (event.
|
|
341
|
+
if (event.isTrusted && event.data == 'port') {
|
|
342
342
|
ipcRenderer.postMessage('port', null, [event.ports[0]])
|
|
343
343
|
}
|
|
344
344
|
}
|
package/doc/chrome-extensions.md
CHANGED
|
@@ -1,133 +1,133 @@
|
|
|
1
|
-
# Chrome Extensions RPC 通信使用说明
|
|
2
|
-
|
|
3
|
-
## 概述
|
|
4
|
-
|
|
5
|
-
本项目使用 `js-rpc2` 库实现在 Chrome 扩展各组件间的远程过程调用(RPC)通信,支持在 popup、content scripts 和 background 之间进行双向通信。
|
|
6
|
-
|
|
7
|
-
## 项目结构
|
|
8
|
-
|
|
9
|
-
### 组件说明
|
|
10
|
-
|
|
11
|
-
- **background.js**: 后台脚本,提供核心服务接口
|
|
12
|
-
- **content_scripts.js**: 内容脚本,运行在网页上下文中,可作为通信中继
|
|
13
|
-
- **popup.js**: 扩展弹窗界面,用户交互入口
|
|
14
|
-
|
|
15
|
-
## 核心实现
|
|
16
|
-
|
|
17
|
-
### Background 脚本
|
|
18
|
-
|
|
19
|
-
```javascript
|
|
20
|
-
import {createRpcServerChromeExtensions} from 'js-rpc2/src/lib.js'
|
|
21
|
-
|
|
22
|
-
export class RpcBackgroundApi {
|
|
23
|
-
/**
|
|
24
|
-
* 带回调的示例方法
|
|
25
|
-
* @param {(progress: string, date: Date) => Promise<void>} cb - 回调函数
|
|
26
|
-
*/
|
|
27
|
-
async callback(cb) {
|
|
28
|
-
for (let i = 0; i < 10; i++) {
|
|
29
|
-
await sleep(1000)
|
|
30
|
-
await cb(`from background index is ${i}`, new Date())
|
|
31
|
-
}
|
|
32
|
-
return 'over!'
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// 创建RPC服务端点
|
|
37
|
-
createRpcServerChromeExtensions({
|
|
38
|
-
chrome,
|
|
39
|
-
key: 'rpc-popup->background',
|
|
40
|
-
extension: new RpcBackgroundApi()
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
createRpcServerChromeExtensions({
|
|
44
|
-
chrome,
|
|
45
|
-
key: 'rpc-content-scripts->background',
|
|
46
|
-
extension: new RpcBackgroundApi()
|
|
47
|
-
})
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Content Scripts 脚本
|
|
51
|
-
|
|
52
|
-
```javascript
|
|
53
|
-
import {createRpcServerChromeExtensions, createRpcClientChromeExtensions} from 'js-rpc2/src/lib.js'
|
|
54
|
-
|
|
55
|
-
export class RpcContentScriptsApi {
|
|
56
|
-
async callBackgroundWithCallback(name, callback) {
|
|
57
|
-
return await rpcContentScriptBackground.callback(callback)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// 创建RPC服务端点供popup调用
|
|
62
|
-
createRpcServerChromeExtensions({
|
|
63
|
-
chrome,
|
|
64
|
-
key: 'rpc-popup->content-script',
|
|
65
|
-
extension: new RpcContentScriptsApi()
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
// 创建RPC客户端连接到background
|
|
69
|
-
export const rpcContentScriptBackground = createRpcClientChromeExtensions({
|
|
70
|
-
chrome,
|
|
71
|
-
key: 'rpc-content-scripts->background'
|
|
72
|
-
})
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### Popup 脚本
|
|
76
|
-
|
|
77
|
-
```javascript
|
|
78
|
-
import {createRpcClientChromeExtensions} from 'js-rpc2/src/lib.js'
|
|
79
|
-
|
|
80
|
-
// 获取当前活动标签页
|
|
81
|
-
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
|
|
82
|
-
|
|
83
|
-
// 创建RPC客户端
|
|
84
|
-
export const rpcPopupContentScripts = createRpcClientChromeExtensions({
|
|
85
|
-
chrome,
|
|
86
|
-
key: 'rpc-popup->content-script',
|
|
87
|
-
tabId: tab.id
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
export const rpcPopupBackground = createRpcClientChromeExtensions({
|
|
91
|
-
chrome,
|
|
92
|
-
key: 'rpc-popup->background'
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
// 使用示例
|
|
96
|
-
let resp = await rpcPopupContentScripts.callBackgroundWithCallback("name", async (progress, date) => {
|
|
97
|
-
console.info('at popup callback', progress, date)
|
|
98
|
-
})
|
|
99
|
-
console.info("over!", resp)
|
|
100
|
-
|
|
101
|
-
let resp2 = await rpcPopupBackground.callback(async (progress, date) => {
|
|
102
|
-
console.info('at popup callback', progress, date)
|
|
103
|
-
})
|
|
104
|
-
console.info("over!", resp2)
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## 通信模式
|
|
108
|
-
|
|
109
|
-
### 直接通信
|
|
110
|
-
Popup → Background
|
|
111
|
-
```javascript
|
|
112
|
-
rpcPopupBackground.callback(callbackFunction)
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### 间接通信
|
|
116
|
-
Popup → Content Script → Background
|
|
117
|
-
```javascript
|
|
118
|
-
rpcPopupContentScripts.callBackgroundWithCallback(name, callbackFunction)
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
## 使用方法
|
|
122
|
-
|
|
123
|
-
1. 在各组件中导入相应的 RPC 创建函数
|
|
124
|
-
2. 定义服务接口类
|
|
125
|
-
3. 使用 `createRpcServerChromeExtensions` 创建服务端点
|
|
126
|
-
4. 使用 `createRpcClientChromeExtensions` 创建客户端连接
|
|
127
|
-
5. 通过客户端实例调用远程方法
|
|
128
|
-
|
|
129
|
-
## 注意事项
|
|
130
|
-
|
|
131
|
-
- 确保 manifest.json 中正确配置了各组件的权限和通信策略
|
|
132
|
-
- 注意处理异步回调的生命周期管理
|
|
1
|
+
# Chrome Extensions RPC 通信使用说明
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
本项目使用 `js-rpc2` 库实现在 Chrome 扩展各组件间的远程过程调用(RPC)通信,支持在 popup、content scripts 和 background 之间进行双向通信。
|
|
6
|
+
|
|
7
|
+
## 项目结构
|
|
8
|
+
|
|
9
|
+
### 组件说明
|
|
10
|
+
|
|
11
|
+
- **background.js**: 后台脚本,提供核心服务接口
|
|
12
|
+
- **content_scripts.js**: 内容脚本,运行在网页上下文中,可作为通信中继
|
|
13
|
+
- **popup.js**: 扩展弹窗界面,用户交互入口
|
|
14
|
+
|
|
15
|
+
## 核心实现
|
|
16
|
+
|
|
17
|
+
### Background 脚本
|
|
18
|
+
|
|
19
|
+
```javascript
|
|
20
|
+
import {createRpcServerChromeExtensions} from 'js-rpc2/src/lib.js'
|
|
21
|
+
|
|
22
|
+
export class RpcBackgroundApi {
|
|
23
|
+
/**
|
|
24
|
+
* 带回调的示例方法
|
|
25
|
+
* @param {(progress: string, date: Date) => Promise<void>} cb - 回调函数
|
|
26
|
+
*/
|
|
27
|
+
async callback(cb) {
|
|
28
|
+
for (let i = 0; i < 10; i++) {
|
|
29
|
+
await sleep(1000)
|
|
30
|
+
await cb(`from background index is ${i}`, new Date())
|
|
31
|
+
}
|
|
32
|
+
return 'over!'
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 创建RPC服务端点
|
|
37
|
+
createRpcServerChromeExtensions({
|
|
38
|
+
chrome,
|
|
39
|
+
key: 'rpc-popup->background',
|
|
40
|
+
extension: new RpcBackgroundApi()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
createRpcServerChromeExtensions({
|
|
44
|
+
chrome,
|
|
45
|
+
key: 'rpc-content-scripts->background',
|
|
46
|
+
extension: new RpcBackgroundApi()
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Content Scripts 脚本
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
import {createRpcServerChromeExtensions, createRpcClientChromeExtensions} from 'js-rpc2/src/lib.js'
|
|
54
|
+
|
|
55
|
+
export class RpcContentScriptsApi {
|
|
56
|
+
async callBackgroundWithCallback(name, callback) {
|
|
57
|
+
return await rpcContentScriptBackground.callback(callback)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 创建RPC服务端点供popup调用
|
|
62
|
+
createRpcServerChromeExtensions({
|
|
63
|
+
chrome,
|
|
64
|
+
key: 'rpc-popup->content-script',
|
|
65
|
+
extension: new RpcContentScriptsApi()
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
// 创建RPC客户端连接到background
|
|
69
|
+
export const rpcContentScriptBackground = createRpcClientChromeExtensions({
|
|
70
|
+
chrome,
|
|
71
|
+
key: 'rpc-content-scripts->background'
|
|
72
|
+
})
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Popup 脚本
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
import {createRpcClientChromeExtensions} from 'js-rpc2/src/lib.js'
|
|
79
|
+
|
|
80
|
+
// 获取当前活动标签页
|
|
81
|
+
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
|
|
82
|
+
|
|
83
|
+
// 创建RPC客户端
|
|
84
|
+
export const rpcPopupContentScripts = createRpcClientChromeExtensions({
|
|
85
|
+
chrome,
|
|
86
|
+
key: 'rpc-popup->content-script',
|
|
87
|
+
tabId: tab.id
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
export const rpcPopupBackground = createRpcClientChromeExtensions({
|
|
91
|
+
chrome,
|
|
92
|
+
key: 'rpc-popup->background'
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// 使用示例
|
|
96
|
+
let resp = await rpcPopupContentScripts.callBackgroundWithCallback("name", async (progress, date) => {
|
|
97
|
+
console.info('at popup callback', progress, date)
|
|
98
|
+
})
|
|
99
|
+
console.info("over!", resp)
|
|
100
|
+
|
|
101
|
+
let resp2 = await rpcPopupBackground.callback(async (progress, date) => {
|
|
102
|
+
console.info('at popup callback', progress, date)
|
|
103
|
+
})
|
|
104
|
+
console.info("over!", resp2)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 通信模式
|
|
108
|
+
|
|
109
|
+
### 直接通信
|
|
110
|
+
Popup → Background
|
|
111
|
+
```javascript
|
|
112
|
+
rpcPopupBackground.callback(callbackFunction)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 间接通信
|
|
116
|
+
Popup → Content Script → Background
|
|
117
|
+
```javascript
|
|
118
|
+
rpcPopupContentScripts.callBackgroundWithCallback(name, callbackFunction)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## 使用方法
|
|
122
|
+
|
|
123
|
+
1. 在各组件中导入相应的 RPC 创建函数
|
|
124
|
+
2. 定义服务接口类
|
|
125
|
+
3. 使用 `createRpcServerChromeExtensions` 创建服务端点
|
|
126
|
+
4. 使用 `createRpcClientChromeExtensions` 创建客户端连接
|
|
127
|
+
5. 通过客户端实例调用远程方法
|
|
128
|
+
|
|
129
|
+
## 注意事项
|
|
130
|
+
|
|
131
|
+
- 确保 manifest.json 中正确配置了各组件的权限和通信策略
|
|
132
|
+
- 注意处理异步回调的生命周期管理
|
|
133
133
|
- 建议为所有 RPC 接口添加适当的错误处理机制
|
package/doc/electron.md
CHANGED
|
@@ -1,138 +1,138 @@
|
|
|
1
|
-
# Electron RPC 通信使用说明
|
|
2
|
-
|
|
3
|
-
## 概述
|
|
4
|
-
|
|
5
|
-
本文档介绍如何在 Electron 应用中使用 `js-rpc2` 库实现主进程与渲染进程之间的 RPC 通信。通过 MessagePort 机制,实现跨进程的远程方法调用。
|
|
6
|
-
|
|
7
|
-
## 项目结构
|
|
8
|
-
|
|
9
|
-
### 组件说明
|
|
10
|
-
|
|
11
|
-
- **main.js**: Electron 主进程,提供核心服务接口
|
|
12
|
-
- **preload.js**: 预加载脚本,建立主进程与渲染进程的通信桥梁
|
|
13
|
-
- **renderer.js**: 渲染进程,运行在浏览器环境中
|
|
14
|
-
- **usage.js**: 使用示例,展示如何调用 RPC 方法
|
|
15
|
-
|
|
16
|
-
## 核心实现
|
|
17
|
-
|
|
18
|
-
### 主进程 (main.js)
|
|
19
|
-
|
|
20
|
-
```javascript
|
|
21
|
-
import { ipcMain } from 'electron'
|
|
22
|
-
import { createRpcServerElectronMessagePort } from 'js-rpc2/src/lib.js'
|
|
23
|
-
|
|
24
|
-
class AppApi {
|
|
25
|
-
asyncLocalStorage = new AsyncLocalStorage()
|
|
26
|
-
|
|
27
|
-
async hello(param) {
|
|
28
|
-
return 'wertyuioiuytre ' + param
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// 监听来自渲染进程的 port 消息
|
|
33
|
-
ipcMain.on('port', (event) => {
|
|
34
|
-
const port = event.ports[0]
|
|
35
|
-
// 创建 RPC 服务端点
|
|
36
|
-
createRpcServerElectronMessagePort({
|
|
37
|
-
port,
|
|
38
|
-
rpcKey: '',
|
|
39
|
-
extension: new AppApi()
|
|
40
|
-
})
|
|
41
|
-
port.start()
|
|
42
|
-
})
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
### 预加载脚本 (preload.js)
|
|
46
|
-
|
|
47
|
-
```javascript
|
|
48
|
-
import { ipcRenderer } from 'electron/renderer'
|
|
49
|
-
|
|
50
|
-
// 监听来自渲染进程的消息
|
|
51
|
-
window.onmessage = (/** @type {MessageEvent} */ event) => {
|
|
52
|
-
// 验证消息来源并转发给主进程
|
|
53
|
-
if (event.
|
|
54
|
-
ipcRenderer.postMessage('port', null, [event.ports[0]])
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
### 渲染进程 (renderer.js)
|
|
60
|
-
|
|
61
|
-
```javascript
|
|
62
|
-
import { createRpcClientMessagePort } from 'js-rpc2/src/lib.js'
|
|
63
|
-
|
|
64
|
-
// 创建消息通道
|
|
65
|
-
const channel = new MessageChannel();
|
|
66
|
-
|
|
67
|
-
// 向主进程发送端口
|
|
68
|
-
window.postMessage("port", location.origin, [channel.port1]);
|
|
69
|
-
|
|
70
|
-
// 创建 RPC 客户端
|
|
71
|
-
let rpc = createRpcClientMessagePort({
|
|
72
|
-
port: channel.port2,
|
|
73
|
-
rpcKey: ""
|
|
74
|
-
});
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### 使用示例 (usage.js)
|
|
78
|
-
|
|
79
|
-
```javascript
|
|
80
|
-
async function postPortMessage() {
|
|
81
|
-
// 调用远程方法
|
|
82
|
-
let ret = await rpc.hello('6667');
|
|
83
|
-
console.info("ret from rpc:", ret);
|
|
84
|
-
}
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
## 通信流程
|
|
88
|
-
|
|
89
|
-
1. 渲染进程创建 `MessageChannel` 并通过 `postMessage` 将 `port1` 发送到预加载脚本
|
|
90
|
-
2. 预加载脚本验证消息来源并将端口转发给主进程
|
|
91
|
-
3. 主进程接收端口并创建 RPC 服务端点
|
|
92
|
-
4. 渲染进程使用 `port2` 创建 RPC 客户端
|
|
93
|
-
5. 客户端可以调用主进程中定义的服务方法
|
|
94
|
-
|
|
95
|
-
## 使用方法
|
|
96
|
-
|
|
97
|
-
### 1. 主进程设置
|
|
98
|
-
|
|
99
|
-
在主进程中定义服务类并创建 RPC 服务:
|
|
100
|
-
|
|
101
|
-
```javascript
|
|
102
|
-
class AppApi {
|
|
103
|
-
async yourMethod(params) {
|
|
104
|
-
// 实现业务逻辑
|
|
105
|
-
return result;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
ipcMain.on('port', (event) => {
|
|
110
|
-
const port = event.ports[0]
|
|
111
|
-
createRpcServerElectronMessagePort({
|
|
112
|
-
port,
|
|
113
|
-
rpcKey: '',
|
|
114
|
-
extension: new AppApi()
|
|
115
|
-
})
|
|
116
|
-
port.start()
|
|
117
|
-
})
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### 2. 渲染进程调用
|
|
121
|
-
|
|
122
|
-
在渲染进程中建立连接并调用远程方法:
|
|
123
|
-
|
|
124
|
-
```javascript
|
|
125
|
-
const channel = new MessageChannel();
|
|
126
|
-
window.postMessage("port", location.origin, [channel.port1]);
|
|
127
|
-
let rpc = createRpcClientMessagePort({ port: channel.port2, rpcKey: "" });
|
|
128
|
-
|
|
129
|
-
// 调用远程方法
|
|
130
|
-
let result = await rpc.yourMethod(params);
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
## 注意事项
|
|
134
|
-
|
|
135
|
-
- 确保在 Electron 的安全策略下正确配置 `contextIsolation` 和 `sandbox` 选项
|
|
136
|
-
- 预加载脚本中的消息验证是安全通信的关键
|
|
137
|
-
- MessagePort 需要在两端都启动后才能正常通信
|
|
1
|
+
# Electron RPC 通信使用说明
|
|
2
|
+
|
|
3
|
+
## 概述
|
|
4
|
+
|
|
5
|
+
本文档介绍如何在 Electron 应用中使用 `js-rpc2` 库实现主进程与渲染进程之间的 RPC 通信。通过 MessagePort 机制,实现跨进程的远程方法调用。
|
|
6
|
+
|
|
7
|
+
## 项目结构
|
|
8
|
+
|
|
9
|
+
### 组件说明
|
|
10
|
+
|
|
11
|
+
- **main.js**: Electron 主进程,提供核心服务接口
|
|
12
|
+
- **preload.js**: 预加载脚本,建立主进程与渲染进程的通信桥梁
|
|
13
|
+
- **renderer.js**: 渲染进程,运行在浏览器环境中
|
|
14
|
+
- **usage.js**: 使用示例,展示如何调用 RPC 方法
|
|
15
|
+
|
|
16
|
+
## 核心实现
|
|
17
|
+
|
|
18
|
+
### 主进程 (main.js)
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
import { ipcMain } from 'electron'
|
|
22
|
+
import { createRpcServerElectronMessagePort } from 'js-rpc2/src/lib.js'
|
|
23
|
+
|
|
24
|
+
class AppApi {
|
|
25
|
+
asyncLocalStorage = new AsyncLocalStorage()
|
|
26
|
+
|
|
27
|
+
async hello(param) {
|
|
28
|
+
return 'wertyuioiuytre ' + param
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 监听来自渲染进程的 port 消息
|
|
33
|
+
ipcMain.on('port', (event) => {
|
|
34
|
+
const port = event.ports[0]
|
|
35
|
+
// 创建 RPC 服务端点
|
|
36
|
+
createRpcServerElectronMessagePort({
|
|
37
|
+
port,
|
|
38
|
+
rpcKey: '',
|
|
39
|
+
extension: new AppApi()
|
|
40
|
+
})
|
|
41
|
+
port.start()
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 预加载脚本 (preload.js)
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
import { ipcRenderer } from 'electron/renderer'
|
|
49
|
+
|
|
50
|
+
// 监听来自渲染进程的消息
|
|
51
|
+
window.onmessage = (/** @type {MessageEvent} */ event) => {
|
|
52
|
+
// 验证消息来源并转发给主进程
|
|
53
|
+
if (event.isTrusted && event.data == 'port') {
|
|
54
|
+
ipcRenderer.postMessage('port', null, [event.ports[0]])
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 渲染进程 (renderer.js)
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
import { createRpcClientMessagePort } from 'js-rpc2/src/lib.js'
|
|
63
|
+
|
|
64
|
+
// 创建消息通道
|
|
65
|
+
const channel = new MessageChannel();
|
|
66
|
+
|
|
67
|
+
// 向主进程发送端口
|
|
68
|
+
window.postMessage("port", location.origin, [channel.port1]);
|
|
69
|
+
|
|
70
|
+
// 创建 RPC 客户端
|
|
71
|
+
let rpc = createRpcClientMessagePort({
|
|
72
|
+
port: channel.port2,
|
|
73
|
+
rpcKey: ""
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 使用示例 (usage.js)
|
|
78
|
+
|
|
79
|
+
```javascript
|
|
80
|
+
async function postPortMessage() {
|
|
81
|
+
// 调用远程方法
|
|
82
|
+
let ret = await rpc.hello('6667');
|
|
83
|
+
console.info("ret from rpc:", ret);
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 通信流程
|
|
88
|
+
|
|
89
|
+
1. 渲染进程创建 `MessageChannel` 并通过 `postMessage` 将 `port1` 发送到预加载脚本
|
|
90
|
+
2. 预加载脚本验证消息来源并将端口转发给主进程
|
|
91
|
+
3. 主进程接收端口并创建 RPC 服务端点
|
|
92
|
+
4. 渲染进程使用 `port2` 创建 RPC 客户端
|
|
93
|
+
5. 客户端可以调用主进程中定义的服务方法
|
|
94
|
+
|
|
95
|
+
## 使用方法
|
|
96
|
+
|
|
97
|
+
### 1. 主进程设置
|
|
98
|
+
|
|
99
|
+
在主进程中定义服务类并创建 RPC 服务:
|
|
100
|
+
|
|
101
|
+
```javascript
|
|
102
|
+
class AppApi {
|
|
103
|
+
async yourMethod(params) {
|
|
104
|
+
// 实现业务逻辑
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
ipcMain.on('port', (event) => {
|
|
110
|
+
const port = event.ports[0]
|
|
111
|
+
createRpcServerElectronMessagePort({
|
|
112
|
+
port,
|
|
113
|
+
rpcKey: '',
|
|
114
|
+
extension: new AppApi()
|
|
115
|
+
})
|
|
116
|
+
port.start()
|
|
117
|
+
})
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 2. 渲染进程调用
|
|
121
|
+
|
|
122
|
+
在渲染进程中建立连接并调用远程方法:
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
const channel = new MessageChannel();
|
|
126
|
+
window.postMessage("port", location.origin, [channel.port1]);
|
|
127
|
+
let rpc = createRpcClientMessagePort({ port: channel.port2, rpcKey: "" });
|
|
128
|
+
|
|
129
|
+
// 调用远程方法
|
|
130
|
+
let result = await rpc.yourMethod(params);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## 注意事项
|
|
134
|
+
|
|
135
|
+
- 确保在 Electron 的安全策略下正确配置 `contextIsolation` 和 `sandbox` 选项
|
|
136
|
+
- 预加载脚本中的消息验证是安全通信的关键
|
|
137
|
+
- MessagePort 需要在两端都启动后才能正常通信
|
|
138
138
|
- 异步方法调用需要正确处理 Promise 返回值
|
package/doc/worker_threads.md
CHANGED
|
@@ -1,187 +1,187 @@
|
|
|
1
|
-
# 在 Node.js Worker Threads 中使用 js-rpc
|
|
2
|
-
|
|
3
|
-
Node.js Worker Threads 是一种在 Node.js 中实现并行处理的机制,js-rpc 提供了在主线程和 Worker 线程之间进行 RPC 调用的能力。
|
|
4
|
-
|
|
5
|
-
## 概述
|
|
6
|
-
|
|
7
|
-
js-rpc 在 Node.js Worker Threads 中的使用主要包括两个部分:
|
|
8
|
-
1. 在 Worker 线程中创建 RPC 服务器:使用 [createRpcServerNodeJSWorker](../src/lib.js#L993)
|
|
9
|
-
2. 在主线程中创建 RPC 客户端:使用 [createRpcClientNodeJSWorker](../src/lib.js#L1013)
|
|
10
|
-
|
|
11
|
-
## 完整示例
|
|
12
|
-
|
|
13
|
-
### 1. Worker 线程代码 (worker.js)
|
|
14
|
-
|
|
15
|
-
```js
|
|
16
|
-
// 导入必要的模块
|
|
17
|
-
import { parentPort } from 'worker_threads'
|
|
18
|
-
import { createRpcServerNodeJSWorker } from 'js-rpc2/src/lib.js'
|
|
19
|
-
|
|
20
|
-
// 定义可被远程调用的函数
|
|
21
|
-
async function readClipboard() {
|
|
22
|
-
// 实现读取剪贴板的逻辑
|
|
23
|
-
// 这里只是示例,实际实现取决于你的需求
|
|
24
|
-
return 'clipboard content'
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function writeClipboard(data) {
|
|
28
|
-
// 实现写入剪贴板的逻辑
|
|
29
|
-
console.log('Writing to clipboard:', data)
|
|
30
|
-
return true
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function writeClipboardText(text) {
|
|
34
|
-
// 实现写入文本到剪贴板的逻辑
|
|
35
|
-
console.log('Writing text to clipboard:', text)
|
|
36
|
-
return true
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function readClipboardText() {
|
|
40
|
-
// 实现读取剪贴板文本的逻辑
|
|
41
|
-
return 'clipboard text'
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function readClipboardHtml() {
|
|
45
|
-
// 实现读取剪贴板 HTML 内容的逻辑
|
|
46
|
-
return '<p>clipboard html</p>'
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function readClipboardImage() {
|
|
50
|
-
// 实现读取剪贴板图片的逻辑
|
|
51
|
-
return new Uint8Array([1, 2, 3, 4]) // 示例二进制数据
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// 将所有函数导出为一个 API 对象
|
|
55
|
-
export const ExtensionApi = {
|
|
56
|
-
readClipboard,
|
|
57
|
-
writeClipboard,
|
|
58
|
-
writeClipboardText,
|
|
59
|
-
readClipboardText,
|
|
60
|
-
readClipboardHtml,
|
|
61
|
-
readClipboardImage,
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 创建 RPC 服务器
|
|
65
|
-
createRpcServerNodeJSWorker({
|
|
66
|
-
parentPort,
|
|
67
|
-
extension: ExtensionApi,
|
|
68
|
-
logger: (msg) => console.log('[Worker]', msg) // 可选的日志记录器
|
|
69
|
-
})
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### 2. 主线程代码 (main.js)
|
|
73
|
-
|
|
74
|
-
```js
|
|
75
|
-
// 导入必要的模块
|
|
76
|
-
import { createRpcClientNodeJSWorker } from 'js-rpc2/src/lib.js'
|
|
77
|
-
import { Worker } from 'worker_threads'
|
|
78
|
-
|
|
79
|
-
// 创建 Worker 实例
|
|
80
|
-
export const worker = new Worker(new URL('./worker.js', import.meta.url))
|
|
81
|
-
|
|
82
|
-
// 创建 RPC 客户端
|
|
83
|
-
/** @type{ExtensionApi} */
|
|
84
|
-
const rpc = createRpcClientNodeJSWorker({ worker: worker })
|
|
85
|
-
|
|
86
|
-
// 取消 Worker 的引用,允许程序在没有其他任务时退出
|
|
87
|
-
worker.unref()
|
|
88
|
-
|
|
89
|
-
// 包装远程调用函数
|
|
90
|
-
export async function readClipboard() {
|
|
91
|
-
return await rpc.readClipboard()
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* 写入剪贴板内容
|
|
96
|
-
* @param {'text'|'richtext'|'image'} type - 数据类型
|
|
97
|
-
* @param {string|Buffer} data - 要写入的数据
|
|
98
|
-
*/
|
|
99
|
-
export async function writeClipboard(type, data) {
|
|
100
|
-
return await rpc.writeClipboard(type, data)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* 写入文本到剪贴板
|
|
105
|
-
* @param {string} text - 要写入的文本
|
|
106
|
-
*/
|
|
107
|
-
export async function writeClipboardText(text) {
|
|
108
|
-
return rpc.writeClipboardText(text)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export async function readClipboardText() {
|
|
112
|
-
return rpc.readClipboardText()
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export async function readClipboardHtml() {
|
|
116
|
-
return rpc.readClipboardHtml()
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export async function readClipboardImage() {
|
|
120
|
-
return rpc.readClipboardImage()
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// 使用示例
|
|
124
|
-
async function example() {
|
|
125
|
-
// 调用远程函数
|
|
126
|
-
const text = await readClipboardText()
|
|
127
|
-
console.log('Clipboard text:', text)
|
|
128
|
-
|
|
129
|
-
await writeClipboardText('Hello from main thread!')
|
|
130
|
-
|
|
131
|
-
const image = await readClipboardImage()
|
|
132
|
-
console.log('Clipboard image size:', image.length)
|
|
133
|
-
}
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## API 说明
|
|
137
|
-
|
|
138
|
-
### createRpcServerNodeJSWorker
|
|
139
|
-
|
|
140
|
-
在 Worker 线程中创建 RPC 服务器。
|
|
141
|
-
|
|
142
|
-
```js
|
|
143
|
-
/**
|
|
144
|
-
* @param {{
|
|
145
|
-
* parentPort: NodeJSMessagePort;
|
|
146
|
-
* extension: Object;
|
|
147
|
-
* logger?:(msg:string)=>void;
|
|
148
|
-
* }} param
|
|
149
|
-
*/
|
|
150
|
-
export function createRpcServerNodeJSWorker(param)
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
参数说明:
|
|
154
|
-
- `parentPort`: Worker 线程的 parentPort 对象,用于与主线程通信
|
|
155
|
-
- `extension`: 包含可被远程调用函数的对象
|
|
156
|
-
- `logger`: 可选的日志记录函数
|
|
157
|
-
|
|
158
|
-
### createRpcClientNodeJSWorker
|
|
159
|
-
|
|
160
|
-
在主线程中创建 RPC 客户端。
|
|
161
|
-
|
|
162
|
-
```js
|
|
163
|
-
/**
|
|
164
|
-
* @param {{
|
|
165
|
-
* worker:NodeJSWorker;
|
|
166
|
-
* }} param
|
|
167
|
-
*/
|
|
168
|
-
export function createRpcClientNodeJSWorker(param)
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
参数说明:
|
|
172
|
-
- `worker`: Worker 实例
|
|
173
|
-
|
|
174
|
-
## 特性
|
|
175
|
-
|
|
176
|
-
1. **透明的远程调用**:在主线程中调用 Worker 中的函数就像调用本地函数一样简单
|
|
177
|
-
2. **支持异步函数**:所有远程函数都可以是异步的
|
|
178
|
-
3. **参数和返回值序列化**:自动处理参数和返回值的序列化/反序列化
|
|
179
|
-
4. **类型安全**:通过 JSDoc 注解提供完整的类型支持
|
|
180
|
-
5. **错误处理**:远程函数抛出的错误会正确传播到调用方
|
|
181
|
-
|
|
182
|
-
## 注意事项
|
|
183
|
-
|
|
184
|
-
1. 确保 Worker 线程中的函数是可序列化的
|
|
185
|
-
2. 大数据传输可能影响性能,考虑使用流式传输
|
|
186
|
-
3. 避免在远程函数中使用闭包引用 Worker 外部的变量
|
|
1
|
+
# 在 Node.js Worker Threads 中使用 js-rpc
|
|
2
|
+
|
|
3
|
+
Node.js Worker Threads 是一种在 Node.js 中实现并行处理的机制,js-rpc 提供了在主线程和 Worker 线程之间进行 RPC 调用的能力。
|
|
4
|
+
|
|
5
|
+
## 概述
|
|
6
|
+
|
|
7
|
+
js-rpc 在 Node.js Worker Threads 中的使用主要包括两个部分:
|
|
8
|
+
1. 在 Worker 线程中创建 RPC 服务器:使用 [createRpcServerNodeJSWorker](../src/lib.js#L993)
|
|
9
|
+
2. 在主线程中创建 RPC 客户端:使用 [createRpcClientNodeJSWorker](../src/lib.js#L1013)
|
|
10
|
+
|
|
11
|
+
## 完整示例
|
|
12
|
+
|
|
13
|
+
### 1. Worker 线程代码 (worker.js)
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
// 导入必要的模块
|
|
17
|
+
import { parentPort } from 'worker_threads'
|
|
18
|
+
import { createRpcServerNodeJSWorker } from 'js-rpc2/src/lib.js'
|
|
19
|
+
|
|
20
|
+
// 定义可被远程调用的函数
|
|
21
|
+
async function readClipboard() {
|
|
22
|
+
// 实现读取剪贴板的逻辑
|
|
23
|
+
// 这里只是示例,实际实现取决于你的需求
|
|
24
|
+
return 'clipboard content'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function writeClipboard(data) {
|
|
28
|
+
// 实现写入剪贴板的逻辑
|
|
29
|
+
console.log('Writing to clipboard:', data)
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function writeClipboardText(text) {
|
|
34
|
+
// 实现写入文本到剪贴板的逻辑
|
|
35
|
+
console.log('Writing text to clipboard:', text)
|
|
36
|
+
return true
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function readClipboardText() {
|
|
40
|
+
// 实现读取剪贴板文本的逻辑
|
|
41
|
+
return 'clipboard text'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function readClipboardHtml() {
|
|
45
|
+
// 实现读取剪贴板 HTML 内容的逻辑
|
|
46
|
+
return '<p>clipboard html</p>'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function readClipboardImage() {
|
|
50
|
+
// 实现读取剪贴板图片的逻辑
|
|
51
|
+
return new Uint8Array([1, 2, 3, 4]) // 示例二进制数据
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 将所有函数导出为一个 API 对象
|
|
55
|
+
export const ExtensionApi = {
|
|
56
|
+
readClipboard,
|
|
57
|
+
writeClipboard,
|
|
58
|
+
writeClipboardText,
|
|
59
|
+
readClipboardText,
|
|
60
|
+
readClipboardHtml,
|
|
61
|
+
readClipboardImage,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 创建 RPC 服务器
|
|
65
|
+
createRpcServerNodeJSWorker({
|
|
66
|
+
parentPort,
|
|
67
|
+
extension: ExtensionApi,
|
|
68
|
+
logger: (msg) => console.log('[Worker]', msg) // 可选的日志记录器
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 2. 主线程代码 (main.js)
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
// 导入必要的模块
|
|
76
|
+
import { createRpcClientNodeJSWorker } from 'js-rpc2/src/lib.js'
|
|
77
|
+
import { Worker } from 'worker_threads'
|
|
78
|
+
|
|
79
|
+
// 创建 Worker 实例
|
|
80
|
+
export const worker = new Worker(new URL('./worker.js', import.meta.url))
|
|
81
|
+
|
|
82
|
+
// 创建 RPC 客户端
|
|
83
|
+
/** @type{ExtensionApi} */
|
|
84
|
+
const rpc = createRpcClientNodeJSWorker({ worker: worker })
|
|
85
|
+
|
|
86
|
+
// 取消 Worker 的引用,允许程序在没有其他任务时退出
|
|
87
|
+
worker.unref()
|
|
88
|
+
|
|
89
|
+
// 包装远程调用函数
|
|
90
|
+
export async function readClipboard() {
|
|
91
|
+
return await rpc.readClipboard()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 写入剪贴板内容
|
|
96
|
+
* @param {'text'|'richtext'|'image'} type - 数据类型
|
|
97
|
+
* @param {string|Buffer} data - 要写入的数据
|
|
98
|
+
*/
|
|
99
|
+
export async function writeClipboard(type, data) {
|
|
100
|
+
return await rpc.writeClipboard(type, data)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 写入文本到剪贴板
|
|
105
|
+
* @param {string} text - 要写入的文本
|
|
106
|
+
*/
|
|
107
|
+
export async function writeClipboardText(text) {
|
|
108
|
+
return rpc.writeClipboardText(text)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function readClipboardText() {
|
|
112
|
+
return rpc.readClipboardText()
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export async function readClipboardHtml() {
|
|
116
|
+
return rpc.readClipboardHtml()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function readClipboardImage() {
|
|
120
|
+
return rpc.readClipboardImage()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 使用示例
|
|
124
|
+
async function example() {
|
|
125
|
+
// 调用远程函数
|
|
126
|
+
const text = await readClipboardText()
|
|
127
|
+
console.log('Clipboard text:', text)
|
|
128
|
+
|
|
129
|
+
await writeClipboardText('Hello from main thread!')
|
|
130
|
+
|
|
131
|
+
const image = await readClipboardImage()
|
|
132
|
+
console.log('Clipboard image size:', image.length)
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## API 说明
|
|
137
|
+
|
|
138
|
+
### createRpcServerNodeJSWorker
|
|
139
|
+
|
|
140
|
+
在 Worker 线程中创建 RPC 服务器。
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
/**
|
|
144
|
+
* @param {{
|
|
145
|
+
* parentPort: NodeJSMessagePort;
|
|
146
|
+
* extension: Object;
|
|
147
|
+
* logger?:(msg:string)=>void;
|
|
148
|
+
* }} param
|
|
149
|
+
*/
|
|
150
|
+
export function createRpcServerNodeJSWorker(param)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
参数说明:
|
|
154
|
+
- `parentPort`: Worker 线程的 parentPort 对象,用于与主线程通信
|
|
155
|
+
- `extension`: 包含可被远程调用函数的对象
|
|
156
|
+
- `logger`: 可选的日志记录函数
|
|
157
|
+
|
|
158
|
+
### createRpcClientNodeJSWorker
|
|
159
|
+
|
|
160
|
+
在主线程中创建 RPC 客户端。
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
/**
|
|
164
|
+
* @param {{
|
|
165
|
+
* worker:NodeJSWorker;
|
|
166
|
+
* }} param
|
|
167
|
+
*/
|
|
168
|
+
export function createRpcClientNodeJSWorker(param)
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
参数说明:
|
|
172
|
+
- `worker`: Worker 实例
|
|
173
|
+
|
|
174
|
+
## 特性
|
|
175
|
+
|
|
176
|
+
1. **透明的远程调用**:在主线程中调用 Worker 中的函数就像调用本地函数一样简单
|
|
177
|
+
2. **支持异步函数**:所有远程函数都可以是异步的
|
|
178
|
+
3. **参数和返回值序列化**:自动处理参数和返回值的序列化/反序列化
|
|
179
|
+
4. **类型安全**:通过 JSDoc 注解提供完整的类型支持
|
|
180
|
+
5. **错误处理**:远程函数抛出的错误会正确传播到调用方
|
|
181
|
+
|
|
182
|
+
## 注意事项
|
|
183
|
+
|
|
184
|
+
1. 确保 Worker 线程中的函数是可序列化的
|
|
185
|
+
2. 大数据传输可能影响性能,考虑使用流式传输
|
|
186
|
+
3. 避免在远程函数中使用闭包引用 Worker 外部的变量
|
|
187
187
|
4. Worker 线程在没有任务时会自动退出,如需保持运行请适当管理引用
|
package/package.json
CHANGED
package/src/lib.js
CHANGED
|
@@ -696,13 +696,13 @@ export function createRpcClientWebSocket(param) {
|
|
|
696
696
|
signal.resolve()
|
|
697
697
|
})
|
|
698
698
|
ws.addEventListener('error', (e) => {
|
|
699
|
-
if (param.intercept) {
|
|
700
|
-
param.intercept(e)
|
|
701
|
-
}
|
|
702
699
|
console.error('createRpcClientWebSocket createWebSocket ws error', e)
|
|
703
700
|
promise.resolve()
|
|
704
701
|
})
|
|
705
|
-
ws.addEventListener('close', () => {
|
|
702
|
+
ws.addEventListener('close', (e) => {
|
|
703
|
+
if (param.intercept) {
|
|
704
|
+
param.intercept(e)
|
|
705
|
+
}
|
|
706
706
|
console.error('createRpcClientWebSocket createWebSocket ws close')
|
|
707
707
|
promise.resolve()
|
|
708
708
|
})
|