完成electron-bridge

electron-bridge

github链接 求star

Motivition

  • 假如想一套代码同时能跑在web环境和electron环境中,就须要在代码中先推断环境,再离别写对应的逻辑。每次写到electron环境下的逻辑,又要辨别衬着历程和主历程,由于有些事只能衬着历程做,有些事只能主历程做。所以,我愿望能将这些笼统出来,某个要领,只能在electron环境下被挪用,而且不须要体贴在什么历程下,web只需推断环境,调差别的要领就行,不须要体贴和electron的交互。

  • 假如,我须要疾速的开启另一个electron的项目,我愿望我web里的代码能随意马虎的猎取到electron的才能,而不是重新最先编写,这个时刻,我愿望有一层对electron才能的封装。

  • 团队内有些成员对web很熟悉,然则对electron不是很相识,假如到场项目,就须要去进修electron的学问,这个时刻,假如能有一个库列出了一切electron能做的事,你只须要挪用,无需体贴它是怎样完成的,能很大水平进步开辟效力。

Goals

  1. 给web注入恰当的环境变量,让web晓得本身的环境

  2. 给web注入一个对象,包含一切electron能做的事(包含主历程、衬着历程)

How to do

在load web页面的时刻,有个webPreferences设置,我们在这里预加载一个js文件,就是electron-bridge.js

这个文件具有node的才能,而且它是属于衬着历程的,所以它能做衬着历程里的事, 也能跟主历程通信。

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

Let’s do it

给web注入恰当的环境变量

加载bridge.js

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

经由过程bridge.js 来挪用主历程的要领

我们经由过程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
  }
});
    原文作者:xiguadada
    原文地址: https://segmentfault.com/a/1190000009586423
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞