放弃船ð¢
好了,经过一些成功的博客文章,我们终于遇到了第一个大问题,这是一个表演者。我的意思是什么?
当我第一次设计我们的应用程序时,我沿着PWA路线走了,以为浏览器拥有我们应用程序正常运行所需的一切。
但是我错了。我忽略了我们的SpeedRun计时器,全局快捷方式的最关键功能之一。
全局快捷方式是键或键组合,您可以在键盘上输入,以触发应用程序或操作系统上的某种功能(例如,Windows键打开应用程序启动器)。
如果您熟悉LivesPlit,并且您已经在很大的时间内一直是速度跑步者,那么您会知道全球快捷键是 LifeSaver 在跟踪分裂时。确保您的计时器在速度运行游戏时保持前景是不切实际的。您常常会意外地将另一个应用程序打开,并完全弄乱您的分裂时间或整个运行!
那么,您知道 不能执行全局快捷方式吗?浏览器。
从PWA迁移到电子
哦,男孩,我们该怎么办?幸运的是,我们没有任何选择。实际上,我一开始就考虑了这一点。
我选择在VUE/JavaScript中编写该项目的前端的原因之一是,如果需要,我可以轻松地迁移到Electron应用程序。 Electron.js为我们提供了一种在JavaScript中构建跨平台桌面应用程序的方法。因此,通过此迁移,我们可以利用全局快捷方式并在所有主要桌面平台上运行我们的应用!
代码更改
我不会再改变单个代码,因为这会导致一个疯狂的帖子。此外,我们的代码将在很大程度上保持不变。 将更改是我们的项目结构和添加的全局快捷方式功能。
我们的项目结构将分为3个部分:
- 在我们的应用程序和操作系统之间通信的node.js代码。
- 渲染器代码本质上只是我们的前端。
- preloader代码,它是Node.js和前端之间的桥梁。
node.js代码
我们的node.js代码默认情况下,默认情况下是给我们的。许多评论也是从中产生的。我们要更改的唯一代码是,一旦应用程序准备就绪,我们要注册global shortcuts。我们使用node.js处理此操作,因为我们需要在操作系统级别上收听键盘输入,而不仅仅是在应用程序本身上。触发回调函数后,我们将命令发送到mainWindow
与我们的计时器进行交互。
重要的是要注意,一旦绑定键盘快捷键(如此处显示的数字“ 1”),您将无法在任何其他应用程序上输入或使用该键。这种限制是电子团队的design choice,以防止钥匙记录员的发展。可能有某种工作能力,但我不会在这里参与其中。如果您知道另一种解决方案,请随时发表评论!
// main.js
const { app, BrowserWindow, globalShortcut } = require('electron');
const path = require('path');
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) {
app.quit();
}
let mainWindow;
const createWindow = () => {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
},
});
// and load the index.html of the app.
if (MAIN_WINDOW_VITE_DEV_SERVER_URL) {
mainWindow.loadURL(MAIN_WINDOW_VITE_DEV_SERVER_URL);
} else {
mainWindow.loadFile(path.join(__dirname, `../renderer/${MAIN_WINDOW_VITE_NAME}/index.html`));
}
// Open the DevTools.
mainWindow.webContents.openDevTools();
};
// This method will be used to setup our global shortcuts code.
// https://electronjs.org/docs/latest/api/global-shortcut
app.whenReady().then(() => {
globalShortcut.register('1', () => {
mainWindow.webContents.send("global-timer", 0);
})
globalShortcut.register('2', () => {
mainWindow.webContents.send("global-timer", 1);
})
globalShortcut.register('3', () => {
mainWindow.webContents.send("global-timer", 2);
})
}).then(createWindow);
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.
预加载器
如前所述,此代码将用作Node.js代码(操作系统通信)和渲染器代码(FRONTERD)之间的桥梁。当前,我们要做的就是公开ipcRenderer
API,以允许渲染器与节点进行通信。
// preloader.js
// See the Electron documentation for details on how to use preload scripts:
// https://www.electronjs.org/docs/latest/tutorial/process-model#preload-scripts
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electronAPI', {
listenForTimerCommands: (listener) => ipcRenderer.on('global-timer', listener)
});
渲染器
最后,我们有我们的渲染器。此代码只是我们的PWA之前的输入文件(旧的main.js)。
// renderer.js
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
createApp(App).mount("#app");
让我们继续并在前端添加代码以对全局快捷键输入做出反应。
// Stopwatch.vue
<script setup>
import { onMounted, onUnmounted } from 'vue';
import { useStopwatch } from '../composables/stopwatch';
import { WorkerCommands } from '../helpers/timer-worker-helper';
const {
timerTxt,
onTimerStart,
onTimerStop,
onTimerReset,
onTimerInit,
onTimerTeardown
} = useStopwatch();
onMounted(() => {
onTimerInit();
window.electronAPI.listenForTimerCommands((_, data) => {
switch(data) {
case WorkerCommands.START:
onTimerStart();
break;
case WorkerCommands.STOP:
onTimerStop();
break;
case WorkerCommands.RESET:
onTimerReset();
break;
}
});
});
onUnmounted(() => {
onTimerTeardown();
});
</script>
<template>
<div>
<p>{{ timerTxt }}</p>
<button @mousedown="onTimerStart">Start</button>
<button @mousedown="onTimerStop">Stop</button>
<button @mousedown="onTimerReset">Reset</button>
</div>
</template>
<style scoped>
button {
margin: 0 5px;
}
</style>
结论
那里有!现在,我们已经成功地从PWA迁移到电子。我真的很高兴看到桌面开发将为我们提供什么。我已经学到了很多!
如果您想查看或希望我要去任何其他代码更改,请随时用您的问题对帖子发表评论。您也可以在GitHub repo中跟随。