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 } });