背景
最近新项目的需求是开发桌面应用,因此需要引入Electron。而桌面应用与浏览器web服务的主要区别就在于代码是不是在浏览器中跑的。所以我们仍然沿用web开发时的主流开发框架angular。
于是,新项目前端技术为electron + angular。
Electron + angular初始化
首先,非常重要的一点是:node版本必须高于18.16.1,因为低于这个版本的node无法支持electron。
然后,先初始化一个angular项目(初始化后如图,这个不再赘述)。
接着,我们需要安装electron,在angular项目的根目录下执行
ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/ npm install --save-dev electron
安装成功后可以看到package.json里有了electron的依赖:
为了给我们的angular项目套上electron,我们还需要去重新设置一下入口文件:
然后在根目录下新建main.js,按照electron的方式配置启动路径,指向angular项目。
// 根目录下的main.js文件
const {app, BrowserWindow} = require('electron');
const url = require('url');
const path = require('path');
function onReady () {
win = new BrowserWindow({width: 1600, height: 1000})
loadUrl();
// 打开开发者工具
win.webContents.openDevTools()
// 当 window 被关闭,这个事件会被触发。
win.on('closed', () => {
// 取消引用 window 对象,如果你的应用支持多窗口的话,
// 通常会把多个 window 对象存放在一个数组里面,
// 与此同时,你应该删除相应的元素。
win = null
})
}
function loadUrl() {
win.loadURL(url.format({
pathname: path.join(
__dirname,
'dist/angular-electron-app/index.html'),
protocol: 'file:',
slashes: true
}))
}
//在ready事件里
app.on('ready', async () => {
onReady();
})
// 当全部窗口关闭时退出。
app.on('window-all-closed', () => {
// 在 macOS 上,除非用户用 Cmd + Q 确定地退出,
// 否则绝大部分应用及其菜单栏会保持激活。
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// 在macOS上,当单击dock图标并且没有其他窗口打开时,
// 通常在应用程序中重新创建一个窗口。
if (win === null) {
onReady()
}
})
去到src/index.html,将
改为
根目录下终端运行命令
npm run electron
配置快捷键调起控制台
electron窗口毕竟不是浏览器,该窗口中的控制台是由main.js中的 openDevTools()
方法控制的,前文的main.js中配置了在窗口打开时控制台打开。如果在开发过程中不小心把控制台给关了,是无法通过在electron窗口中的操作调起控制台的。想要再打开就只能去到终端ctrl + c后再执行一次electron启动命令。
搞开发的都知道,没有控制台的开发就像断了一条腿一样难受。
所以我们还需要对控制台的快捷调起进行一番配置。
electron框架提供的与计算机交互的接口中有一个 globalShortcut
,官方对它的描述是:
globalShortcut
可以检测键盘事件,还可以向操作系统注册/注销全局快捷键。有了它,我们就可以实现我们想要的功能了。
首先在main.js中,引入 globalShortcut
:
const {app, BrowserWindow, globalShortcut} = require('electron');
然后将
//在ready事件里
app.on('ready', async () => {
onReady();
})
换成
//在ready事件里
app.on('ready', async () => {
// Ctrl + Shift + i 快捷键调起/关闭控制台
globalShortcut.register('CommandOrControl+Shift+i', function () {
// 判断现在控制台是否处于打开状态
if (win.webContents.isDevToolsOpened()) {
// 如果被打开,则关闭
win.webContents.closeDevTools()
} else {
// 如果没有被打开,则调起
win.webContents.openDevTools()
}
})
onReady();
})
这里使用的是Ctrl + Shift + i 快捷键调起控制台,当然也可以换成别的。
配置热加载
熟悉angular的朋友们都知道,angular的一个很人性化的点就是热加载,当代码有所变动时浏览器中会自动检测到并刷新,而不用手动刷新。因此,使用angular从web开发转为桌面程序开发时自然也是希望开发中保持这个特点。所以还需要配置一下热加载。
我们先回顾一下前文配置:
// main.js中配置的指向angular项目的启动路径
function loadUrl() {
win.loadURL(url.format({
pathname: path.join(
__dirname,
'dist/angular-electron-app/index.html'),
protocol: 'file:',
slashes: true
}))
}
由于指向的路径是ng build后的dist文件夹下的angular项目,所以这样自然是无法实现热加载的,因为我们不可能改一点代码就ng build一次,这不现实。
网上查到的资料来看,大多数的做法及原理都很简单,将electron加载路径由dist/下的angular项目改为 http://localhost:4200
,即将上述配置改为:
function loadUrl() {
win.loadURL('http://localhost:4200');
}
然后在启动electron窗口之前先启动angular,即在一个终端中先 ng s
,再在另一个终端中 npm run electron
。
这样做的确可以实现electron窗口中angular项目的热加载。
但存在一个很重要且必须要注意的问题,就是electron打包时不能就这样打包。如果就这样打包发给用户的话,用户双击启动后只会看到一个空白的窗口,因为angular服务并没有启动但electron却加载了angular服务的端口。所以打包时加载路径仍然应该配置dist/下的angular项目。
还有一点,居然要开两个终端,执行两条启动命令,总觉得很不爽。
综上所述,开发时为了便利,我们可以加载路径指向 http://localhost:4200
以实现热加载;而打包时加载路径仍然应该配置dist/下的angular项目。
完整main.js
const {app, BrowserWindow, globalShortcut} = require('electron');
const url = require('url');
const path = require('path');
function onReady () {
win = new BrowserWindow({width: 1600, height: 1000})
// 打包时成APP时用此路径
// loadUrlWhenPackage();
// 开发时为了实现angular热加载,用此路径
// 打开终端先ng serve启动angular服务
// 打开另一个终端npm run electron启动窗口服务
// 此时可实现热加载
loadUrlWhenDevelop();
// 打开开发者工具
win.webContents.openDevTools()
// 当 window 被关闭,这个事件会被触发。
win.on('closed', () => {
// 取消引用 window 对象,如果你的应用支持多窗口的话,
// 通常会把多个 window 对象存放在一个数组里面,
// 与此同时,你应该删除相应的元素。
win = null
})
}
// 打包时使用的路径
function loadUrlWhenPackage() {
win.loadURL(url.format({
pathname: path.join(
__dirname,
'dist/line-data-record/index.html'),
protocol: 'file:',
slashes: true
}))
}
// 开发时使用的路径(实验angular热加载)
function loadUrlWhenDevelop() {
win.loadURL('http://localhost:4200');
}
//在ready事件里
app.on('ready', async () => {
// Ctrl + Shift + i 快捷键调起/关闭控制台
globalShortcut.register('CommandOrControl+Shift+i', function () {
if (win.webContents.isDevToolsOpened()) {
win.webContents.closeDevTools()
} else {
win.webContents.openDevTools()
}
})
onReady();
})
// 当全部窗口关闭时退出。
app.on('window-all-closed', () => {
// 在 macOS 上,除非用户用 Cmd + Q 确定地退出,
// 否则绝大部分应用及其菜单栏会保持激活。
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
// 在macOS上,当单击dock图标并且没有其他窗口打开时,
// 通常在应用程序中重新创建一个窗口。
if (win === null) {
onReady()
}
})
参考资料
https://www.logicflow.ai/blog/angular-desktop-applications-wi...