做者:鍾離,酷家樂PC客戶端負責人
原文地址:https://webfe.kujiale.com/electron-ku-jia-le-ke-hu-duan-kai-fa-shi-jian-fen-xiang-jin-cheng-tong-xin/
酷家樂客戶端:下載地址 https://www.kujiale.com/activity/136
文章背景:在酷家樂客戶端在V12改版成功後,咱們積累了許多的寶貴的經驗和最佳實踐。前端社區裏關於Electron知識相對較少,所以但願將這些內容以系列文章的形式分享出來。
系列文章:css
Electron中的進程,其實就是計算機中的進程,咱們先來看看什麼是進程通訊html
進程間通訊(IPC,Inter-Process Communication),指至少兩個進程或線程間傳送數據或信號的一些技術或方法
每一個進程都有本身的一部分獨立的系統資源,彼此是隔離的。爲了能使不一樣的進程互相訪問資源並進行協調工做,纔有了進程間通訊。前端
一個Electron應用有一個主進程和多個渲染進程,渲染進程還可能內嵌多個webview。兩兩之間均可能須要進行通訊,狀況仍是比較複雜的。node
主進程: 使用ipcMain
進行通訊
渲染進程: 使用ipcRenderer
和remote
模塊進行通訊
webview: 通常會禁用webview的node集成,而後使用preload的方式拿到ipcRenderer
來作進程通訊。web
// preload.js const electron = require('electron'); const { ipcRenderer } = electron; // 把ipcRenderer掛載到window上,webview內部的js能夠拿到這個模塊 window.ElectronIpcRenderer = ipcRenderer;
主進程和渲染進程通訊方式,擰出來單獨說一下。先來看一個簡單例子的:api
點擊建立按鈕,建立一個新的窗口。點擊關閉按鈕,關掉這個新窗口。
瀏覽器
左側代碼使用ipcRenderer/ipcMain
進行通訊,右側代碼使用remote進行通訊。實現的功能都是同樣的。從這個例子中能夠發現:安全
ipcMain/ipcRenderer
通訊,業務邏輯同時存在於主進程和渲染進程的代碼中。同時爲了通訊,會產生很是多的event & event handler
。主進程能夠視做爲模塊提供者,而渲染進程是模塊的消費者,渲染進程經過remote來獲取主進程的模塊,實現業務邏輯。這樣作有如下好處:app
介紹了一下前置知識,如今來看看不一樣狀況下,Electron進程通訊的實現方法。dom
主進程發消息、渲染進程收消息:主進程使用窗口的webContents
發消息,渲染進程內使用ipcRenderer
收消息
// main.js const win = new BrowserWindow(); win.load('index.html'); win.webContents.send('hello', {a: 1}); // index.html 中的js const { ipcRenderer } = require('electron'); ipcRenderer.on('hello', (e, data) => { console.log(data); // 打印出 {a: 1} })
渲染進程發消息、主進程收消息: 渲染進程使用ipcRenderer
發消息,主進程使用ipcMain
收消息。
// main.js const { ipcMain } = require('electron'); ipcMain.on('hello', (e, data) => { console.log(data); // 打印出 {a: 1} }); // index.html 中的js const { ipcRenderer } = require('electron'); ipcRenderer.send('hello', {a: 1});
通常遇到主進程和渲染進程通訊的狀況,大部分都是渲染進程來須要獲取主進程的模塊,此時推薦使用remote
來作通訊。
// main.js // 主進程無需添加任何代碼 // index.html 中的js,獲取主進程模塊 const { remote } = require('electron'); const {app, BrowserWindow, dialog, ...} = remote;
渲染進程之間也是會頻繁通訊的,具體場景舉例:在設置窗口點擊更換皮膚,須要通知全部窗口進行顏色、背景的更新。
最佳實踐:渲染進程A經過remote
模塊,獲取到須要目標窗口的webContents
對象,而後經過webContents
向目標窗口的發送消息。目標窗口使用ipcRenderer
監聽事件。
const { remote } = require('electron') const allWindows = remote.BrowserWindow.getAllWindows(); // 窗口A中的邏輯 // 一、第一步,獲取到目標窗口的webContents // 能夠根據id,title來找到目標窗口,也能夠用其餘辦法 const targetId = 1; const targetTitle = '目標窗口'; // let targetWindow = allWindows.find(w => w.id === targetId); let targetWindow = allWindows.find(w => w.title === targetTitle); // 二、第二步,使用目標窗口的webContents發送消息 targetWindow.webContents.send('theme-change', 'gray'); // 目標窗口內的邏輯,使用ipcRenderer監聽事件 // 窗口收到theme-change事件,改變窗口顏色。不須要關注事件從哪裏發出,只須要關注接收到該事件後作什麼 ipcRenderer.on('theme-change', (e, theme) => { console.log(theme); // gray });
還有一種傳統的辦法,不用remote
,改用ipcMain
作通訊,可是會在主進程冗餘不少事件代碼。所以仍是推薦使用remote
,理由同上。小例子:
// mian.js // 用於事件轉發,沒有實際的邏輯 ipcMain.on('send-event-to-window', (e, id, eventName, ...args) => { BrowserWindow.getAllWindows() .find(w = > w.id === id) .webContents .send(eventName, ...args); }); // 窗口A內部,向主進程發事件 const targetId = 1; ipcRenderer.send('send-event-to-window', id, 'theme-change', 'gray'); // 目標窗口 ipcRenderer.on('theme-change', (e, theme) => { console.log(theme); // gray });
內嵌的web頁面運行在客戶端中,也能夠獲取本地化的能力。此時,webview就須要與渲染進程通訊了。
在文章開頭講到了,爲了應用的安全性webview是須要禁用node集成的,經過preload的方式,注入了一個ipcRenderer
並掛載到window上。
webview發消息,渲染進程收消息:webview內部使用ipcRenderer.sendToHost
來發消息。渲染進程獲取到webview的dom元素,監聽dom元素的ipc-message事件接收消息
// 渲染進程拿到webview的dom,接收事件 const webview = document.querySelector('webview') webview.addEventListener('ipc-message', (event) => { console.log(event.channel); // hello }); // webview頁面內,僞裝點了一個按鈕,發送事件 btn.onclick = () => { window.ElectronIpcRenderer.sendToHost('hello') }
渲染進程發消息,webview收消息:渲染進程使用webview.send
發消息。webview使用內置的ipcRenderer
收消息。
// webview內部 window.ElectronIpcRenderer.on('event-from-renderer', (e, data) => { console.log(e, data); // {a: 1} }); // 渲染進程內部 const webview = document.querySelector('webview') webview.send('event-from-renderer', {a: 1})
這三種通訊方式是最基礎的,在此之上進行排列組合也是很常見的,這個由開發者自行拓展便可。
舉一個小例子:webview內部觸發更換皮膚功能 -> 通知渲染進程同步更新皮膚 -> 渲染進程收到消息,向其餘渲染進程通訊 -> 同步更新皮膚完成。
歡迎你們在評論區討論,技術交流 & 內推 -> zhongli@qunhemail.com