@aabuhijleh/electron-remote 中文文档教程
DEPRECATED: electron-remote: an asynchronous 'remote', and more
这个项目不再维护,拉取请求不再被审查或合并,问题也不再得到回应。
electron-remote 提供了 Electron 的 remote 基于 Promises 而不是同步执行的模块。 它还提供了一种将 BrowserWindows 用作“后台进程”的自动方式,可根据使用情况自动缩放,类似于 Grand Central Dispatch 或 .NET TPL Taskpool。
The Quickest of Quick Starts
Calling main process modules from a renderer
import { createProxyForMainProcessModule } from 'electron-remote';
// app is now a proxy for the app module in the main process
const app = createProxyForMainProcessModule('app');
// The difference is all methods return a Promise instead of blocking
const memoryInfo = await app.getAppMemoryInfo();
Calling code in other windows
import { createProxyForRemote } from 'electron-remote';
// myWindowJs is now a proxy object for myWindow's `window` global object
const myWindowJs = createProxyForRemote(myWindow);
// Functions suffixed with _get will read a value
userAgent = await myWindowJs.navigator.userAgent_get()
Renderer Taskpool
import { requireTaskPool } from 'electron-remote';
const myCoolModule = requireTaskPool(require.resolve('./my-cool-module'));
// This method will run synchronously, but in a background BrowserWindow process
// so that your app will not block
let result = await myCoolModule.calculateDigitsOfPi(100000);
But I like Remote!
远程超级方便! 但它也有一些缺点 - 它的主要缺点是它的操作是同步。 这意味着主进程和窗口进程都将等待一个方法完成运行。 即使对于快速方法,过于频繁地调用它也会引入滚动卡顿并通常会导致性能问题。
electron-remote 是 remote 的一个版本,虽然不太符合人体工程学,但保证它不会阻塞调用线程。
Using createProxyForRemote
createProxyForRemote
替代了在 BrowserWindow 或 WebView 实例上使用 Electron 的 executeJavaScript
方法的地方 - 但是,它的工作方式略有不同。 使用 ES2015 中称为 proxy objects 的新功能,我们创建了一个对象它表示远程上下文中的 window
对象,并且所有方法调用都作为消息发送到该远程而不是立即运行,这感觉与 remote
Electron 模块非常相似.
这提供了一些非常重要的优势:
createProxyForRemote
uses asynchronous IPC instead of blocking- Parameters are serialized directly, so you don't have to try to build strings that can be
eval
d, which is a dangerous endeavor at best. - Calling methods on objects is far more convenient than trying to poke at things via a remote eval.
How do I get properties if everything is a Promise tho???
精明的观察者会注意到,获取属性的值始终是一个同步操作 - 为了促进这一点,任何附加有 _get()
的方法都可以让你获取属性的值。
import { createProxyForRemote } from 'electron-remote';
// myWindowJs is now a proxy object for myWindow's `window` global object
const myWindowJs = createProxyForRemote(myWindow);
// Functions suffixed with _get will read a value
myWindowJs.navigator.userAgent_get()
.then((agent) => console.log(`The user agent is ${agent}`));
But do this first!
在使用 createProxyForRemote
之前,您必须在启动时在目标窗口中调用 initializeEvalHandler()
。 这设置了 electron-remote 将使用的监听器。
Bringing it all together
// In my window's main.js
initializeEvalHandler();
window.addNumbers = (a,b) => a + b;
// In my main process
let myWindowProxy = createProxyForRemote(myWindow);
myWindowProxy.addNumbers(5, 5)
.then((x) => console.log(x));
>>> 10
Using createProxyForMainProcessModule
这意味着可以替代您在渲染器进程中使用 remote
的地方。 它几乎与 createProxyForRemote
相同,但不是 eval
ing JavaScript,它只能调用主进程模块上的方法。 它仍然具有所有相同的优点:异步 IPC 而不是 ipc.sendSync
。
Here Be Dragons
electron-remote 相对于 remote 模块有一些你绝对应该知道的重要警告:
- Remote values must be Serializable
你返回到调用进程的对象必须是可序列化的(即你可以调用 JSON.stringify
并且得到一个有效的东西)——这意味着创建类将不起作用,也不会返回像 BrowserWindows 或其他 Electron 对象这样的对象。 例如:
let myWindowProxy = createProxyForRemote(myWindow);
// XXX: BAD - HTML elements aren't serializable
let obj = myWindowProxy.document.createElement('h1');
- Remote event listeners aren't supported
任何涉及事件处理程序的东西都不会起作用:
// XXX: BAD - You can't add event handlers
myWindowProxy.document.addEventListener('onBlur', (e) => console.log("Blur!"));
The Renderer Taskpool
渲染器任务池提供了一种自动方式,可以将 BrowserWindows 用作“后台进程”,根据使用情况自动缩放,类似于 Grand Central Dispatch 或 .NET TPL 任务池。 这是通过允许您提供一个您希望在远程进程中加载的模块来实现的,该模块将根据需要动态加载和卸载。
让我们再看看这个例子:
import { requireTaskPool } from 'electron-remote';
const myCoolModule = requireTaskPool(require.resolve('./my-cool-module'));
// This method will run synchronously, but in a background BrowserWindow process
// so that your app will not block
let result = await myCoolModule.calculateDigitsOfPi(100000);
默认情况下,requireTaskPool
会创建最多四个后台进程来并发运行 JS 代码。 随着这些进程变得繁忙,请求将排队到不同的进程并隐式排队等待。
More Dragons
由于 requireTaskPool
将根据需要创建和销毁进程,这意味着全局变量或其他状态也将被销毁。 您不能依赖于设置一个全局变量并让它持续一段时间而不是一次方法调用。
The remote-ajax module
从主进程中获得的一个非常有用的模块是一种使用 Chromium 的网络堆栈发出网络请求的方法,它可以正确地执行诸如尊重系统代理设置之类的事情。 为此,electron-remote 为 Rx-DOM 的 AJAX 方法提供了一个方便的包装器,称为 remote-ajax
。
import { requireTaskPool } from 'electron-remote';
const remoteAjax = requireTaskPool(require.resolve('electron-remote/remote-ajax'));
// Result is the object that XmlHttpRequest gives you
let result = await remoteAjax.get('https://httpbin.org/get');
console.log(result.url)
>>> 'https://httpbin.org/get'
请参阅 Rx-DOM 的文档了解这些方法有效。
包含的另一种方法是 downloadFileOrUrl
,它允许您将文件下载到目标:
/**
* Downloads a path as either a file path or a HTTP URL to a specific place
*
* @param {string} pathOrUrl Either an HTTP URL or a file path.
* @return {string} The contents as a UTF-8 decoded string.
*/
function downloadFileOrUrl(pathOrUrl, target)
DEPRECATED: electron-remote: an asynchronous 'remote', and more
This project is no longer maintained, pull requests are no longer being reviewed or merged and issues are no longer being responded to.
electron-remote provides an alternative to Electron's remote
module based around Promises instead of synchronous execution. It also provides an automatic way to use BrowserWindows as "background processes" that auto-scales based on usage, similar to Grand Central Dispatch or the .NET TPL Taskpool.
The Quickest of Quick Starts
Calling main process modules from a renderer
import { createProxyForMainProcessModule } from 'electron-remote';
// app is now a proxy for the app module in the main process
const app = createProxyForMainProcessModule('app');
// The difference is all methods return a Promise instead of blocking
const memoryInfo = await app.getAppMemoryInfo();
Calling code in other windows
import { createProxyForRemote } from 'electron-remote';
// myWindowJs is now a proxy object for myWindow's `window` global object
const myWindowJs = createProxyForRemote(myWindow);
// Functions suffixed with _get will read a value
userAgent = await myWindowJs.navigator.userAgent_get()
Renderer Taskpool
import { requireTaskPool } from 'electron-remote';
const myCoolModule = requireTaskPool(require.resolve('./my-cool-module'));
// This method will run synchronously, but in a background BrowserWindow process
// so that your app will not block
let result = await myCoolModule.calculateDigitsOfPi(100000);
But I like Remote!
Remote is super convenient! But it also has some downsides - its main downside is that its action is synchronous. This means that both the main and window processes will wait for a method to finish running. Even for quick methods, calling it too often can introduce scroll jank and generally cause performance problems.
electron-remote is a version of remote that, while less ergonomic, guarantees that it won't block the calling thread.
Using createProxyForRemote
createProxyForRemote
is a replacement for places where you would use Electron's executeJavaScript
method on BrowserWindow or WebView instances - however, it works a little differently. Using a new feature in ES2015 called proxy objects, we create an object which represents the window
object on a remote context, and all method calls get sent as messages to that remote instead of being run immediately, which feels very similar to the remote
Electron module.
This provides a number of very important advantages:
createProxyForRemote
uses asynchronous IPC instead of blocking- Parameters are serialized directly, so you don't have to try to build strings that can be
eval
d, which is a dangerous endeavor at best. - Calling methods on objects is far more convenient than trying to poke at things via a remote eval.
How do I get properties if everything is a Promise tho???
Astute observers will note, that getting the value of a property is always a synchronous operation - to facilitate that, any method with _get()
appended to it will let you fetch the value for the property.
import { createProxyForRemote } from 'electron-remote';
// myWindowJs is now a proxy object for myWindow's `window` global object
const myWindowJs = createProxyForRemote(myWindow);
// Functions suffixed with _get will read a value
myWindowJs.navigator.userAgent_get()
.then((agent) => console.log(`The user agent is ${agent}`));
But do this first!
Before you use createProxyForRemote
, you must call initializeEvalHandler()
in the target window on startup. This sets up the listeners that electron-remote will use.
Bringing it all together
// In my window's main.js
initializeEvalHandler();
window.addNumbers = (a,b) => a + b;
// In my main process
let myWindowProxy = createProxyForRemote(myWindow);
myWindowProxy.addNumbers(5, 5)
.then((x) => console.log(x));
>>> 10
Using createProxyForMainProcessModule
This is meant to be a drop-in replacement for places you would have used remote
in a renderer process. It's almost identical to createProxyForRemote
, but instead of eval
ing JavaScript it can only call methods on main process modules. It still has all the same benefits: asynchronous IPC instead of an ipc.sendSync
.
Here Be Dragons
electron-remote has a number of significant caveats versus the remote module that you should definitely be aware of:
- Remote values must be Serializable
Objects that you return to the calling process must be serializable (i.e. you can call JSON.stringify
on it and get a valid thing)- this means that creating Classes won't work, nor will return objects like BrowserWindows or other Electron objects. For example:
let myWindowProxy = createProxyForRemote(myWindow);
// XXX: BAD - HTML elements aren't serializable
let obj = myWindowProxy.document.createElement('h1');
- Remote event listeners aren't supported
Anything that involves an event handler isn't going to work:
// XXX: BAD - You can't add event handlers
myWindowProxy.document.addEventListener('onBlur', (e) => console.log("Blur!"));
The Renderer Taskpool
Renderer Taskpools provide an automatic way to use BrowserWindows as "background processes" that auto-scales based on usage, similar to Grand Central Dispatch or the .NET TPL Taskpool. This works by allowing you to provide a Module that you'd like to load in the remote processes, which will be loaded and unloaded on the fly according to demand.
Let's look at the example again:
import { requireTaskPool } from 'electron-remote';
const myCoolModule = requireTaskPool(require.resolve('./my-cool-module'));
// This method will run synchronously, but in a background BrowserWindow process
// so that your app will not block
let result = await myCoolModule.calculateDigitsOfPi(100000);
By default, requireTaskPool
will create up to four background processes to concurrently run JS code on. As these processes become busy, requests will be queued to different processes and wait in line implicitly.
More Dragons
Since requireTaskPool
will create and destroy processes as needed, this means that global variables or other state will be destroyed as well. You can't rely on setting a global variable and having it persist for a period of time longer than one method call.
The remote-ajax module
One module that is super useful to have from the main process is a way to make network requests using Chromium's networking stack, which correctly does things such as respecting the system proxy settings. To this end, electron-remote comes with a convenient wrapper around Rx-DOM's AJAX methods called remote-ajax
.
import { requireTaskPool } from 'electron-remote';
const remoteAjax = requireTaskPool(require.resolve('electron-remote/remote-ajax'));
// Result is the object that XmlHttpRequest gives you
let result = await remoteAjax.get('https://httpbin.org/get');
console.log(result.url)
>>> 'https://httpbin.org/get'
See the documentation for Rx-DOM for how these methods work.
Another method that is included is downloadFileOrUrl
, which lets you download a file to a target:
/**
* Downloads a path as either a file path or a HTTP URL to a specific place
*
* @param {string} pathOrUrl Either an HTTP URL or a file path.
* @return {string} The contents as a UTF-8 decoded string.
*/
function downloadFileOrUrl(pathOrUrl, target)