使用 Electron.JS 构建原生桌面应用程序
JavaScript 被浏览器束缚的时代已经一去不复返了。Electron,或者说 Electron.js,是由 OpenJS 基金会维护的一个框架,用于使用 JavaScript、HTML 和 CSS 等 Web 技术构建原生桌面应用程序。
越来越多的原生应用程序,如 Spotify、VSCode 和 Slack,都是使用 Electron 构建的!
这个开源框架使用 Chromium 和 Node.js,可用于创建兼容 Windows、Mac 和 Linux 的跨平台应用程序。在本文中,我们将使用 Electron 构建一个可以在本地运行的简单井字游戏。
Electron 基础知识
Electron 由三个主要组件组成:
- 处理所有网络内容的 Chromium
- 处理与操作系统交互的 Node.js
- 自定义 API,用于附加功能并解决处理操作系统时的常见问题
所有这些组件协同工作以促进桌面应用程序的实现。
此外,Electron 包含两个主要进程。主进程负责窗口管理、所有操作系统交互,渲染进程负责 Web 内容。
渲染进程无法直接与操作系统交互,只能通过主进程进行通信。根据应用程序的不同,一个主进程可以有多个渲染进程。不过,大多数情况下,会使用一个主进程和一个渲染器进程来加载 Web 应用程序。
解释清楚之后,我们就开始构建吧。
设置我们的项目
要使用 Electron,你需要在你的环境中安装 Node.js。我们可以先运行以下命令创建 npm 项目:
npm init
设置好项目后,我们可以使用以下命令安装 Electron:
npm i -D electron@latest
创建 Electron 应用
在我们的 Electron 应用中,我们将使用一个简单的 HTML 页面来运行一场乒乓球游戏。这样,我们只需要关注应用的 Electron 部分,而不是游戏本身。
几周前,我们只用 100 行代码就制作了 Pong,所以我们将重复使用该项目
我们将在项目文件夹中创建两个文件。index.js 将充当我们的主要节点进程,而 pong.html 将充当渲染器进程来加载天气数据。
// Include Electron | |
const { app, BrowserWindow } = require('electron'); | |
// Create the main window for the application | |
function createMainWindow () { | |
// Setup the windows options | |
const win = new BrowserWindow({ | |
width: 800, | |
height: 800, | |
webPreferences: { | |
nodeIntegration: true | |
} | |
}); | |
// load the html file | |
win.loadFile('pong.html'); | |
// Open DevTools - For Debugging (Optional) | |
// win.webContents.openDevTools() | |
} | |
app.whenReady().then(createMainWindow); | |
// Close the application when all windows are closed | |
app.on('window-all-closed', () => { | |
if (process.platform !== 'darwin') { | |
app.quit() | |
} | |
}); | |
// When the application is activated create the main window if not exists | |
app.on('activate', () => { | |
if (BrowserWindow.getAllWindows().length === 0) { | |
createMainWindow() | |
} | |
}); |
// Include Electron | |
const { app, BrowserWindow } = require('electron'); | |
// Create the main window for the application | |
function createMainWindow () { | |
// Setup the windows options | |
const win = new BrowserWindow({ | |
width: 800, | |
height: 800, | |
webPreferences: { | |
nodeIntegration: true | |
} | |
}); | |
// load the html file | |
win.loadFile('pong.html'); | |
// Open DevTools - For Debugging (Optional) | |
// win.webContents.openDevTools() | |
} | |
app.whenReady().then(createMainWindow); | |
// Close the application when all windows are closed | |
app.on('window-all-closed', () => { | |
if (process.platform !== 'darwin') { | |
app.quit() | |
} | |
}); | |
// When the application is activated create the main window if not exists | |
app.on('activate', () => { | |
if (BrowserWindow.getAllWindows().length === 0) { | |
createMainWindow() | |
} | |
}); |
index.js 文件将帮助应用程序创建窗口并加载相应的渲染进程。在本例中,我们的 pong.html 文件如下所示:
<!-- Pull in P5.JS graphics library --> | |
<script | |
src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js" | |
integrity="sha512-WIklPM6qPCIp6d3fSSr90j+1unQHUOoWDS4sdTiR8gxUTnyZ8S2Mr8e10sKKJ/bhJgpAa/qG068RDkg6fIlNFA==" | |
crossorigin="anonymous" | |
></script> | |
<script> | |
const screenDim = 500; | |
let score = [0, 0]; | |
let ball = { x: 250, y: 100, vx: 2, vy: 2 }; | |
let play1 = { x: 30, y: 250, height: 100, width: 20 }; | |
let play2 = { x: screenDim - 50, y: 250, height: 100, width: 20 }; | |
function setup() { // Runs on startup | |
frameRate(100); | |
createCanvas(screenDim, screenDim); | |
} | |
function draw() { // Runs on loop according to frameRate | |
fill("transparent"); | |
rect(0, 0, screenDim, screenDim); // Draw the box around our game | |
// Show Score | |
fill("black"); | |
textSize(32); | |
text(score[0] + " - " + score[1], screenDim / 2 - 40, 100); | |
// Draw ball | |
fill("red"); | |
ellipse(ball.x, ball.y, 10, 10); | |
// Draw paddles | |
fill("black"); | |
rect(play1.x, play1.y, play1.width, play1.height); | |
rect(play2.x, play2.y, play2.width, play2.height); | |
if (ball.y > screenDim - 10 || ball.y < 10) { // Bounce off top and bottom | |
ball.vy *= -1; | |
} | |
if ( // If intersecting with Paddle #1 | |
ball.x < play1.x + play1.width + 10 && | |
ball.y > play1.y && | |
ball.y < play1.y + play1.height | |
) { | |
ball.vx *= -1.1; // Invert and increase velocity by 10% | |
ball.vy = random(8) - 4; // Random y velocity between -4 and 4 | |
} | |
if ( // If intersecting with Paddle #2 | |
ball.x > play2.x - 10 && // If it | |
ball.y > play2.y && | |
ball.y < play2.y + play2.height | |
) { | |
ball.vx *= -1.1; // Invert and increase velocity by 10% | |
ball.vy = random(8) - 4; // Random y velocity between -4 and 4 | |
} | |
// Move Paddles | |
if (keyIsDown(87) && play1.y > 5) { // W is pressed | |
play1.y -= 4; | |
} | |
if (keyIsDown(83) && play1.y < screenDim - play1.height) { // S is pressed | |
play1.y += 4; | |
} | |
if (keyIsDown(UP_ARROW) && play2.y > 5) { | |
play2.y -= 4; | |
} | |
if (keyIsDown(DOWN_ARROW) && play2.y < screenDim - play1.height) { | |
play2.y += 4; | |
} | |
// Update score | |
if (ball.x < 0) { | |
score[1] += 1; | |
ball = { x: 250, y: 100, vx: 2, vy: 2 }; | |
} | |
if (ball.x > screenDim) { | |
score[0] += 1; | |
ball = { x: 250, y: 100, vx: -2, vy: 2 }; | |
} | |
// Move the ball | |
ball.x += ball.vx; | |
ball.y += ball.vy; | |
} | |
</script> |
<!-- Pull in P5.JS graphics library --> | |
<script | |
src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js" | |
integrity="sha512-WIklPM6qPCIp6d3fSSr90j+1unQHUOoWDS4sdTiR8gxUTnyZ8S2Mr8e10sKKJ/bhJgpAa/qG068RDkg6fIlNFA==" | |
crossorigin="anonymous" | |
></script> | |
<script> | |
const screenDim = 500; | |
let score = [0, 0]; | |
let ball = { x: 250, y: 100, vx: 2, vy: 2 }; | |
let play1 = { x: 30, y: 250, height: 100, width: 20 }; | |
let play2 = { x: screenDim - 50, y: 250, height: 100, width: 20 }; | |
function setup() { // Runs on startup | |
frameRate(100); | |
createCanvas(screenDim, screenDim); | |
} | |
function draw() { // Runs on loop according to frameRate | |
fill("transparent"); | |
rect(0, 0, screenDim, screenDim); // Draw the box around our game | |
// Show Score | |
fill("black"); | |
textSize(32); | |
text(score[0] + " - " + score[1], screenDim / 2 - 40, 100); | |
// Draw ball | |
fill("red"); | |
ellipse(ball.x, ball.y, 10, 10); | |
// Draw paddles | |
fill("black"); | |
rect(play1.x, play1.y, play1.width, play1.height); | |
rect(play2.x, play2.y, play2.width, play2.height); | |
if (ball.y > screenDim - 10 || ball.y < 10) { // Bounce off top and bottom | |
ball.vy *= -1; | |
} | |
if ( // If intersecting with Paddle #1 | |
ball.x < play1.x + play1.width + 10 && | |
ball.y > play1.y && | |
ball.y < play1.y + play1.height | |
) { | |
ball.vx *= -1.1; // Invert and increase velocity by 10% | |
ball.vy = random(8) - 4; // Random y velocity between -4 and 4 | |
} | |
if ( // If intersecting with Paddle #2 | |
ball.x > play2.x - 10 && // If it | |
ball.y > play2.y && | |
ball.y < play2.y + play2.height | |
) { | |
ball.vx *= -1.1; // Invert and increase velocity by 10% | |
ball.vy = random(8) - 4; // Random y velocity between -4 and 4 | |
} | |
// Move Paddles | |
if (keyIsDown(87) && play1.y > 5) { // W is pressed | |
play1.y -= 4; | |
} | |
if (keyIsDown(83) && play1.y < screenDim - play1.height) { // S is pressed | |
play1.y += 4; | |
} | |
if (keyIsDown(UP_ARROW) && play2.y > 5) { | |
play2.y -= 4; | |
} | |
if (keyIsDown(DOWN_ARROW) && play2.y < screenDim - play1.height) { | |
play2.y += 4; | |
} | |
// Update score | |
if (ball.x < 0) { | |
score[1] += 1; | |
ball = { x: 250, y: 100, vx: 2, vy: 2 }; | |
} | |
if (ball.x > screenDim) { | |
score[0] += 1; | |
ball = { x: 250, y: 100, vx: -2, vy: 2 }; | |
} | |
// Move the ball | |
ball.x += ball.vx; | |
ball.y += ball.vy; | |
} | |
</script> |
我们现在可以使用以下命令在本地运行我们的应用程序:
electron index.js
就这样!我们的 html 文件已经原生运行了!
结论
Electron 日益流行,使其成为有抱负的开发者必学的语言。更何况,如果你是一名 Web 开发者,想要快速创建网站的原生版本,Electron 可能是最佳选择。
如果您正在构建软件并希望消除瓶颈,那么Codesphere就是您的云服务需求的最佳选择!我们正在将 Codesphere 打造为市场上最直观的云和 DevOps 工具!
编码愉快!
鏂囩珷鏉ユ簮锛�https://dev.to/codesphere/building-native-desktop-apps-with-electronjs-4m8n