天天看点

electron框架学习之浅析IPC使用

一、先介绍一下webContents:

webContents负责渲染和控制一个web页面,它是Browserwindow对象的一个属性。利用它可以做以下的事情,可以emit出很多事件像是完成装载,login等和主web页面相关的一些事件。另外几个比较重要的函数是

1)webContents.loadURL(url[,options])

往窗口中装载url,必须以如下格式:http://或是file://

2)webContents.downloadURL(url)

在URL中初始化一个下载的资源

3)webContents.getURL()

获取当前页面的URL

4)webContents.getTitle()

5)webContents.isLoading()

6)webContents.isWaitingForResponse()

7)webContents.stop()

8)webContents.reload()

9)webContents.reloadIgnoringCache()

10)webContents.canGoBack()

11)webContents.canGoForward()

12)webContents.CanGoToOffser(offset)

13)webContents.clearHistory()

14)webContents.goBack()

15)webContents.goForward()

16)webContents.goToIndex(index)

17)webContents.goToOffset(offset)

18)webContents.isCrashed()

19)webContents.setUserAgent(userAgent)

20)webContents.getUserAgent()

21)webContents.insertCSS(css)

22)webContents.executeJavaScript(code[,userGesture, callback])

23)webContents.setAudioMuted(muted)

24)webContents.isAudioMuted()

25)webContents.undo()

26)webContents.redo()

27)webContents.cut()

28)webContents.copy()

29)webContents.paste()

30)webContents.pasteAndMatchStyle()

31)webContents.delete()

32)webContents.selectAll()

33)webContents.unselect()

34)webContents.replace(text)

35)webContents.replaceMisspelling(text)

36)webContents.insertText(text)

37)webContents.findInPage(text[,options])

...

重点介绍webContents.send(channel[, arg1][, arg2][,...])

发送一个异步消息给渲染进程,也可以发送任意的参数。可以利用ipcRenderer模块来监听channel中的消息。例如:

var window = null;

app.on('ready', function(){

    window = new BrowserWindow({width: 800, height: 600});

    window.loadURL('file://'+__dirname+'/index.html');

    window.webContents.on('did-finish-load', function(){

        window.webContents.send('ping', 'whooooooooooo');

        });

    });

    <html>

        <body>

            <script>

                require('electron').ipcRenderer.on('ping', function(event, message){

                    console.log(message);

                    });

                </script>

        </body>

    </html>

二、再认识下ipcMain模块

ipcMain模块是EventEmitter类的实例,当在主进程中使用的时候,它处理了渲染进程中发过来的同步和异步消息。从渲染进程中发送过来的消息会被emit到这个模块中。同样可以利用webContents.send来发送消息给ipcRenderer,发送异步消息的话,使用的是event.returnValue,发送的是同步消息的话,使用的是event.sender.send(...)

//in main process

const ipcMain = require('electron').ipcMain;

ipcMain.on('asynchronous-message', function(event, arg){

    event.sender.send('asynchronous-reply', 'pong');

    });

ipcMain.on('synchronous-message', function(event, arg){

    event.returnValue = 'pong';

    });

//In renderer process(web page)

const ipcRenderer = require('electron').ipcRenderer;

ipcRenderer.on('asynchronous-reply', function(event, arg) {

    });

ipcRenderer.send('asynchronous-message', 'ping');

//以上部分是Main Process中的模块

//下面介绍下Rendering Process中的模块

三、先说说ipcRenderer

ipcRenderer模块是EventEmitter的实例,提供了一些函数发送同步和异步的消息给主进程。

ipcRenderer.sendToHost(channel[, arg1][, arg2][, ...])

类似ipcRenderer.send但是事件会被发送到<webview>元素中而不是主进程中

下面说说remote

remote模块提供了一个简单的IPC通信方式来实现渲染进程和主进程之间的信息交互。

在electron中GUI相关的模块只能是在主进程中使用,而不是渲染进程。为了在渲染进程中使用,ipc模块必须发送跨进程消息给主进程。通过remote模块,可以调用相应的函数而不需要通过发送显式的消息。类似于Java和RMI,下面是一个在渲染进程中创建一个浏览器窗口的例子:

const remote = require('electron').remote;

const BrowserWindow = remote.BrowserWindow;

var win = new BrowserWindow({ width: 800, height: 600});

win.loadURL('https://github.com');

反过来在主进程中执行渲染进程中的javascript可以通过webContents.executeJavaScript

四、关于Remote对象

每个由remote返回的对象包含函数,都代表的是主进程中的对象。当通过remote对象调用一个remote函数或是创建一个新的对象的时候,实际上就是发送了一个异步的跨进程消息。

在上面的例子中,BrowserWindow和win都是remote对象,在渲染进程中是没法通过new BrowserWindow创建主窗口的。但是却可以通过remote对象在主进程中创建对应的窗口然后返回给win对象。

但是只有enumerable properties可以通过remote对象访问。关于enumerable properties请看:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties;

Remote对象的声明周期

electron保证只要remote对象还在渲染进程中存在那么在主进程中对应的对象就不会被释放。当remote对象被垃圾回收了,在主进程中对应的对象才会被释放。

如果remote对象在渲染进程中泄露比如存储在一个map对象中从不释放,那么在主进程中对应的对象也会产生泄露的情况,所以必须小心使用。

往主进程中传递回调函数

在主进程中的代码可以接收从渲染进程中传递过来的回调函数,比如remote模块,但是必须非常的小心使用。

首先,为了避免死锁,传递给主进程的回调函数必须是异步调用的,主进程是不可能获取到传递过来的回调函数的返回值的。

比如你不能使用在主进程中调用的来自渲染进程的Array.map:

//in main process mapNumbers.js

exports.withRedererCallback = function(mapper){

    return [1,2,3].map(mapper);

    };

exports.withLocalCallback = function(){

    return exports.mapNumbers(function(){

        return x + 1;});

    };

//renderer process

var mapNumbers = require('remote').require("./mapNumbers");

var widtheRendererCb = mapNumbers.withRendererCallback(function(x){

    return x + 1;

    });

var withLocalCb = mapNumbers.withLocalCallback();

正如所见,渲染进程中的回调函数的返回值是获取不到的。其次,传递给主进程的回调函数会保留到主进程被回收为止。

除非显示uninstall,不然callback会一直被主进程引用。如果没有主动uninstall,那么每次reload界面的时候callback又会被重新install一次,每次启动都会泄露callback。

而且更为糟糕的是,由于先前install的callback的context对象已经被释放,所以当主进程发出一个close事件的时候会出现异常。

为了避免这样的问题的发生,要确保清理干净从渲染进程传递给主进程的任何引用,这包含了清理event handler,或者保证主进程显式的解引用这些callbacks

访问主进程中的内建模块

主进程中的内建模块可以通过remote模块的getters来获取,比如:

const  app = remote.app;

以上仅是记录以备忘,后面会结合实际项目,将electron,nodejs配合用typescript更改模块引入方式,以及利用nodejs的package模式灵活加载项目中不同功能的模块。

继续阅读