关于 Electron 进程间通信的一个小小实践
Electron 是一个跨平台桌面框架,它集成了 node.js 和 chromium,所以我们可以借助 node.js 实现桌面客户端访问操作系统资源的功能(出于安全,浏览器是不可以访问操作系统的),而 chromium 允许我们使用前端工具HTML、JS、CSS 甚至结合 React、Vue 等渲染出用户界面。
用进程间通信模拟实现 B/S 架构
B/S 即 ‘浏览器/服务器’ 架构。既然 Electron 由 node.js 和 chromium 集成, 把 Electron 的渲染进程直接当作客户端浏览器进程,主进程当作服务器进程,把访问操作系统资源、读写文件等都放在主进程里,而渲染进程专心用于渲染页面和向主进程‘请求’操作资源,主进程和渲染进程之间可以实现进程间通信,可以模拟浏览器和服务器之间的 ajax 请求。
一个结合 Electron 的项目架构:
下面以一个获取 github 用户信息和远程仓库的 Electron APP 为例,用具体的代码来展示如何实现
渲染进程里使用 ipcRenderer 封装一个类 api 请求:
// render/api.js
var electron = window.electron
var { ipcRenderer } = electron
import ipc_channel from '../const/ipc_channel'
export default {
loginGithub: ({username, password, cb}) => { // 登录 github 以创建一个 token
// 向主进程请求登录 GitHub
ipcRenderer.send(ipc_channel.LOGIN_GITHUB, {username, password})
// 主进程完成登录后返回登录响应(比如一个token)
ipcRenderer.on(ipc_channel.LOGIN_GITHUB, (e, res) => {
cb(res)
})
},
logoutGithub: ({username, password, id, cb}) => { // 退出登录
ipcRenderer.send(ipc_channel.LOGOUT_GITHUB, {username, password, id})
ipcRenderer.on(ipc_channel.LOGOUT_GITHUB, (e, res) => {
cb(res)
})
}
}
复制代码
主进程使用 ipcMain 将渲染进程的各个请求路由到相应的路由处理器,并且传递一个回调函数方便路由处理器向渲染进程发送响应:
//main.js
const routes = require('./server-routes/routes')
for(let routeName in routes) {
ipcMain.on(routeName, (e, params) => {// 监听渲染进程各个 ‘请求’
routes[routeName]({ // 路由
params, // 请求参数
cb: (respond) => {// 回调函数用于向渲染进程发送响应
e.sender.send(routeName, respond)}
})
})
}
复制代码
路由处理器:
// server-routes/routes.js
var github = require('octonode')// octonode 是一个 GitHub API 封装库
var ipc_channel = require('../../const/ipc_channel')
const fs = require('fs')
const {getABranchContent, readLocalDir_OR_FileWithPath, readAFile} = require('./middleweres')
module.exports = {
[ipc_channel.LOGIN_GITHUB]: ({params, cb}) => {
let {username, password} = params
var scopes = {
'scopes': ['user', 'repo', 'gist'],
'note': 'admin script'
}
github.auth.config({
username,
password
}).login(scopes, function (err, id, token, headers) {
console.log(id, token)
if(!err) cb({id, token})
})
},
[ipc_channel.LOGOUT_GITHUB]: ({params, cb}) => {
let {username, password, id} = params
github.auth.config({
username,
password
}).revoke(id, function (err) {
if (err) throw err
else cb({msg: '已经退出登录'})
})
}
}
复制代码
用一个单独的文件导出各个 ipc channel:
// ipc_channel.js
module.exports = {
LOGIN_GITHUB: 'login-github-for-token', // 登录 GitHub 以获取 token
LOGOUT_GITHUB: 'logout-and-revoke-token', // 退出登录并销毁 token
GET_REPO: 'get-a-repo', // 获取 github 上某个仓库的内容
READ_LOCAL_PATH: 'read-local-path', // 读取本地目录
READ_LOCAL_FILE: 'read-local-file', // 读取本地文件
READ_LOCAL_REPO_INFO_FILE: 'read-repo-info-file' // 读取存储在本地的仓库的信息
}
复制代码
这样借鉴了 web 中前后端分离的思想,个人认为可以很清晰的分离应用的逻辑。
本文的素材源自个人的 Electron 小项目:github-repo-viewer