项目框架
搭建一个稳健的Electron框架,而不是随便使用别人封装好的electron-vue,想自定义一个更合适自己的项目架构,以便更有利于实现项目发布时的体积尽可能小,并且可以任意选择合适的UI层框架,如React、Vue、Angular甚至是纯html。先看下以下项目架构图:
再看下我们数据通讯流程图如下:
由以上两个图可以清晰的实现代码的高内聚低耦合,并遵循职责分明的设计原则。这样更有利于项目业务拓展和代码高可维护性。下面开始详细讲述我们各个分层的职责吧!
main.js
app 模块
内容
主要是对app的事件进行监听以及做对应的业务操作
使用
// 设置 url scheme (Protocol)协议名称
app.setAsDefaultProtocolClient(CONFIG.protocol);
// 当所有窗口被关闭时,quit整个应用
app.on('window-all-closed', function () {})
// mac 窗口活跃监听
app.on('activate', function () {})
//退出应用之前
app.on('before-quit', () => {
utils.system.quitApp(true);
})
// mac URL唤起
app.on('open-url', () => {})
// 单例判断
app.requestSingleInstanceLock()
// windows端url唤起监听
app.on('second-instance',()=>{})
// ready
app.on('ready',()=>{})
UI层
UI层可以使任意的前端框架搭建的独立的项目,对于Electron有用的其实就是index.html文件,因此我们可以使纯html,也可以是React、Vue、Angular等框架编译后的文件renderView/index.html作为渲染进程的视图url。此处不展开讲各大框架。来个简单的例子:
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>hello world</title>
</head>
<body>
<div>hello world</div>
</body>
</html>
如果是Vue项目,我们可以利用webpack配置编译后输出的文件目录如下:
// vue.config.js
const path = require('path');
let rootPath = path.resolve(__dirname, '..'); // 代码文件的根路径
const webpack = require('webpack');
module.exports = {
...
// 输出文件目录
outputDir: rootPath + '/renderViews',
},
...
};
在window层中创建窗口时的url就可以选择renderView目录作为渲染进程的路径
winUtils.js
const BASE_URL = 'file://' + __dirname + '../../renderViews/index.html#';
...
browserWindow.loadURL(BASE_URL + '/index');
...
Service层
├── ipcService.js # IPC业务处理 可以调用其他模块service
├── loginService.js # 登录服务
├── meetingService.js # 会议服务
├── shareService.js # 共享服务
├── translateService.js # 即时翻译服务
├── systemService.js # 系统模块服务
└── updateService.js # 升级服务
Utils层
├── lang # 国际化
├── api.js # request封装
├── cache.js # 缓存模块类:缓存大小、清楚缓存、store的get\set
├── common.js # 通用函数封装
├── event.js # 事件订阅封装
├── file.js # 文件处理封装
├── log.js # 日志工具类
├── menu.js # 自定义菜单配置
├── system.js # 系统模块类:唤起浏览器等
├── tray.js # 托盘
├── update.js # 升级配置(3.6.0之前的版本是fs替换文件, 3.7.0开始使用electron-updater升级)
├── translate.js # 即时翻译的底层
└── winUtils.js # 业务层的窗口通用配置:create\delete\get
Window层
├── index # 所有窗口的统一出口
├── index-window.js # 主窗口
├── update-window.js # 升级窗口
├── translate-window.js # 即时翻译窗口
…
└── share-window.js # 分享窗口
由于每一个窗口的配置都有相同和不同,因此都继承与winUtils.js的窗口设置。但又独立配置不同大小以及窗口监听事件。如下:
/**
* 即时翻译窗口
* @date 2020/12/8
*/
const { screen } = require('electron');
const utils = require('../utils');
const store = utils.cache.store;
let languageType = store.get('language') ? store.get('language') : 'zh';
function createTranslatorWindow (meetingInfo) {
...
// 窗口相对定位
const translatorWindowConfig = {
width: 80,
height: 80,
x: screen.getPrimaryDisplay().workAreaSize.width / 2 - 300,
y: screen.getPrimaryDisplay().workAreaSize.height - 200,
center: false,
frame: false, //是否显示窗口边框
transparent: true, // 透明
fullscreenable: false, // 允许全屏
resizable: false, // 是否可调整大小
movable: true, // 是否可调整大小
maximizable: false, // 是否可以最大化
minimizable: false, // 是否可以最小化
alwaysOnTop: true, // 一直置顶
title: '',
show: false,
webPreferences: {
nodeIntegration: true, // 开启使用nodejs的配置
},
};
translatorWindow = utils.winUtils.create({ // 继承于基础窗口
windowName: 'translator',
url: '/translator',
config: translatorWindowConfig,
});
translatorWindow.on('close', (e) => {
if (xxx) {
...
e.preventDefault(); // 阻止窗口关闭
}
});
translatorWindow.on('closed', () => {
translatorWindow = null;
});
translatorWindow.webContents.on('did-finish-load', function () {
...
}
module.exports = createTranslatorWindow;