github連接 求starjavascript
若是想一套代碼同時能跑在web環境和electron環境中,就須要在代碼中先判斷環境,再分別寫對應的邏輯。每次寫到electron環境下的邏輯,又要區分渲染進程和主進程,由於有些事只能渲染進程作,有些事只能主進程作。因此,我但願能將這些抽象出來,某個方法,只能在electron環境下被調用,而且不須要關心在什麼進程下,web只要判斷環境,調不一樣的方法就行,不須要關心和electron的交互。java
若是,我須要快速的開啓另外一個electron的項目,我但願我web裏的代碼能輕易的獲取到electron的能力,而不是從新開始編寫,這個時候,我但願有一層對electron能力的封裝。node
團隊內有些成員對web很熟悉,可是對electron不是很瞭解,若是加入項目,就須要去學習electron的知識,這個時候,若是能有一個庫列出了全部electron能作的事,你只須要調用,無需關心它是怎麼實現的,能很大程度提升開發效率。git
給web注入適當的環境變量,讓web知道本身的環境github
給web注入一個對象,包含全部electron能作的事(包括主進程、渲染進程)web
在load web頁面的時候,有個webPreferences配置,咱們在這裏預加載一個js文件,就是electron-bridge.jspromise
這個文件擁有node的能力,而且它是屬於渲染進程的,因此它能作渲染進程裏的事, 也能跟主進程通信。app
st=>start: start op0=>operation: index.js去調用bridge.js暴露出來的方法, ElectronBridge.setFullScreen() op1=>operation: bridge.js經過ipcRender告訴ipacMain作什麼,並把回調暫存起來 op2=>operation: 主進程作完告訴bridge.js作完了,發送數據 op4=>operation: bridge.js帶上收到的數據,執行暫存的回調 op3=>operation: bridge.js直接作完,觸發回調 cond=>condition: bridge.js判斷是否是主進程作的事? e=>end: end st->op0->cond cond(yes)->op1->op2->op4->e cond(no)->op3->e
加載bridge.jselectron
win = new BrowserWindow({ width: 800, height: 600, show: false, webPreferences: { preload: path.join(__dirname, '../bridge/bridge.js'), plugins: true } });
當咱們啓動electron的時候,主進程開始通知這個渲染進程,給渲染進程注入主進程的環境變量,再有渲染進程掛載到window對象上,這樣web就能獲取本身的環境信息函數
//bridge.js const {ipcRenderer} = require('electron'); //監聽主進程,設置環境變量 ipcRenderer.on('set-env', (event, msg) => { for (const key in msg) { window[key] = msg[key]; } });
//main.js const {BrowserWindow, ipcMain} = require('electron'); const win = new BrowserWindow({...}); //獲取建立好的window對象發送消息 win.webContents.on('did-finish-load', function() { win.webContents.send('set-env', { //設置web環境變量 __ELECTRON__: true, __DEV__: true, __PRO__: false, __SERVER__: false, windowLoaded: true }); });
咱們經過ipcRender給主進程發送一系列消息,包括作什麼事情(eventName), 根據哪些參數(params),對外根據不一樣的事件暴露不一樣的方法,接受參數,和回調函數。
先將回調函數放在 eventsMap上暫存起來,由於ipcRender不能發送函數,全部的信息會被序列化後再發送給主進程,因此,咱們先生成一個時間戳,讓 eventsMap[時間戳] = cb 並把時間戳一同發送過去,等一下子,主進程通知渲染進程調用哪一個時間戳函數
經過'resist-event'頻道, 發送參數,包括 eventName、params、timeStamp
//bridge.js const {ipcRenderer} = require('electron'); const eventsMap = {}; //調用原生事件 function registEvent(eventName, params, cb) { //容許只傳兩個數據 if (!cb) { cb = params; params = {}; } //若是win還未ready if (!windowLoaded) { cb(new Error('window not ready')); return; } const stamp = String(new Date().getTime()); const opts = Object.assign({eventName}, params, {stamp}); eventsMap[stamp] = cb; //註冊惟一函數 ipcRenderer.send('regist-event', opts); //發送事件 } //進入全屏 function setFullScreen(cb) { registEvent(SET_FULL_SCREEN, cb); } window.ElectronBridge = { setFullScreen };
主進程監聽‘resist-event’頻道,作對應的事。咱們會將全部主進程能作的事,放在eventsList對象下,當接受到渲染進程的通知,去eventsList找有沒有對應的事能作,有,作完經過promise,或者經過回調函數,去在‘fire-event’頻道通知,渲染進程,事情已經作完,並把數據傳回去,包括 stamp(以前渲染進程傳過來的,如今傳回去,告訴渲染進程執行哪一個回調函數) 、 payload(返回數據) 、err (錯誤信息)
//main.js const {ipcMain} = require('electron'); //監聽對原生的調用 ipcMain.on('regist-event', (event, arg) => { const nativeEvent = eventsList[arg.eventName]; if (nativeEvent) { const result = nativeEvent(app, win, arg.params); if (isPromise(result)) { result.then(res => { event.sender.send('fire-event', { stamp: arg.stamp, payload: res }); }).catch(err => { event.sender.send('fire-event', { stamp: arg.stamp, err }); }); } else { event.sender.send('fire-event', { stamp: arg.stamp, payload: result }); } } else { event.sender.send('fire-event', { stamp: arg.stamp, err: new Error('event not support') }); } });
渲染進程監聽‘fire-event’執行對應時間戳回調函數,並把主進程傳過來的數據傳給回調函數。觸發完成後,刪掉該回調函數。
//bridge.js //觸發事件回調 ipcRenderer.on('fire-event', (event, arg) => { const cb = eventsMap[arg.stamp]; if (cb) { if (arg.err) { cb(arg.err, arg.payload); } else { cb(false, arg.payload); } delete eventsMap[arg.stamp]; } });
若是是渲染進程能作的事,就不須要再和主進程通信,能夠直接完成觸發回調
//bridge.js const {webFrame} = require('electron'); //設置縮放比,只能在渲染進程中實現 function setZoomFactor(params, cb) { webFrame.setZoomFactor(params); cb && cb(); } window.ElectronBridge = { setZoomFactor };
最終web中的js代碼去調用bridge.js暴露出來的方法
// ../web/index.js $btn1.addEventListener('click', function() { if (__ELECTRON__ && ElectronBridge) { //electron 環境 ElectronBridge.setFullScreen((err) => { if (err) return; console.log('done'); }); } else { //web 環境 alert('不能設置全屏') //do something else } });