electron
- Electron是一个由OpenJS基金会和一个活跃的贡献者社区管理的开源项目。
- 桌面应用程序,基于 Chromium 和 Node.js, 让你可以使用 HTML, CSS 和 JavaScript 构建应用。
- Electron 兼容 Mac、Windows 和 Linux,可以构建出三个平台的应用程序。
安装
npm i -D electron@latest
npm init
npm install --save-dev electron
例1
-
建立一个应用,输出标题和内容输出Hello from Electron renderer!
-
根目录下新建index.html文件、main.js,修改package.json
// index.html <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP --> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'" /> <meta http-equiv="X-Content-Security-Policy" content="default-src 'self'; script-src 'self'" /> <title>Hello from Electron renderer!</title> </head> <body> <h1>Hello from Electron renderer!</h1> <p>👋</p> </body> </html>
// package.json "scripts": { "start": "electron .", },
// main.js const { app, BrowserWindow } = require("electron"); const createWindow = () => { const win = new BrowserWindow({ width: 800, height: 600, }); win.loadFile("./index.html"); // win.loadURL("https://www.electronjs.org/zh/docs/latest/api/browser-window"); }; app.whenReady().then(() => { createWindow(); });
-
运行
npm start
-
效果
-
知识点
-
窗口
BrowserWindow
类暴露了各种方法来修改应用窗口的外观和行为new BrowserWindow([options])
options 可以定义窗口的高度、宽度、最大和最小高宽度、是否窗口居中等等,实例方法有:
-
事件
- win.loadFile 返回
Promise<void>
加载一个本地的文件
- win.loadURL 返回
Promise<void>
下载一个远程的网页
- win.loadFile 返回
-
方法
- app.whenReady():返回 Promise - 当Electron 初始化完成。 可用作检查 app.isReady() 的方便选择,假如应用程序尚未就绪,则订阅ready事件。
-
例2
-
输出当前electron的版本号、运行环境版本号
<body> <h1>Hello World!</h1> <p> We are using Node.js <span id="node-version"></span>, Chromium <span id="chrome-version"></span>, and Electron <span id="electron-version"></span>. </p> </body>
//main.js const { app, BrowserWindow } = require("electron"); const path = require("path"); function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, "preload.js"), }, }); win.loadFile("index.html"); } app.whenReady().then(() => { createWindow(); app.on("activate", () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); }); app.on("window-all-closed", () => { if (process.platform !== "darwin") { app.quit(); } });
// preload.js window.addEventListener("DOMContentLoaded", () => { const replaceText = (selector, text) => { const element = document.getElementById(selector); if (element) element.innerText = text; }; for (const type of ["chrome", "node", "electron"]) { replaceText(`${type}-version`, process.versions[type]); } });
-
知识点
- process:处理对象的扩展,Electron's
process
对象继承 Node.jsprocess
object。上述代码使用到的api(process.versions[type])如下:process.versions.chrome
只读,返回一个string
值,表示 Chrome 版本号。process.versions.electron
只读,返回一个string
值,表示 Electron 版本号。process.platform
属性返回字符串,标识Node.js进程运行其上的操作系统平台。值可能为'aix'
'darwin'``'freebsd'
'linux'``'openbsd'``'sunos'``'win32'
- 事件 activate(mac os):首次启动应用程序、尝试在应用程序已运行时或单击应用程序的坞站或任务栏图标时重新激活它。
- 事件 window-all-closed 当所有的窗口都被关闭时触发。强制退出不会触发
- 方法 app.quit 退出应用程序
webPreferences
Object (可选) - 网页功能设置。如preload,设置与加载脚本内容
- process:处理对象的扩展,Electron's
例3
-
修改应用的主题颜色
-
需要创建一个应用窗口、获取当前系统的主题、以及允许设置的主题
-
对应用的操作和应用dom节点的操作应该区分出来管理
-
知识点
- 应用进程和渲染进程之间的通信是异步的,主要是通过
ipcMain
来实现 - 应用进程和渲染进程需要隔离,主要是通过 contextBridge进行隔离的
ipcMain
ipcMain.handle(channel, listener)
为渲染进程添加一个handle,相当于一个回调事件。channel:string 名称 listener:回调
渲染进程调用的方式:
async () => { const result = await ipcRenderer.invoke('my-invokable-ipc', arg1, arg2) // ... }
contextBridge
:在隔离的上下文中创建一个安全的、双向的、同步的桥梁。可以被预加载脚本用来让渲染器访问Node API,最后会挂载在window上,渲染进程可以直接调用window上挂载的方法。contextBridge.exposeInMainWorld
:将nativeTheme.shouldUseDarkColors
当前OS / Chromium是否正处于dark模式,如果想要改变属性,可以修改nativeTheme.themeSource
的值为system
,light
ordark
.ipcRenderer.invoke(channel, ...args)
:通过channel
向主过程发送消息,并异步等待结果。@media (prefers-color-scheme: dark)
媒体查询,prefers-color-scheme
可以监听系统主题模式,然后通过修改body的背景颜色,改成预设的。
// index.html <body> <h1>Hello World!</h1> <p>Current theme source: <strong id="theme-source">System</strong></p> <button id="toggle-dark-mode">Toggle Dark Mode</button> <button id="reset-to-system">Reset to System Theme</button> <button id="say">say</button> <script src="renderer.js"></script> </body>
// preload.js const { contextBridge, ipcRenderer } = require("electron"); contextBridge.exposeInMainWorld("darkMode", { toggle: () => ipcRenderer.invoke("dark-mode:toggle"), system: () => ipcRenderer.invoke("dark-mode:system"), sayHello: () => ipcRenderer.invoke("sayHello"), });
// main.js const { app, BrowserWindow, ipcMain, nativeTheme } = require("electron"); const path = require("path"); function createWindow() { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, "preload.js"), }, }); win.loadFile("index.html"); ipcMain.handle("dark-mode:toggle", () => { if (nativeTheme.shouldUseDarkColors) { nativeTheme.themeSource = "light"; } else { nativeTheme.themeSource = "dark"; } return nativeTheme.shouldUseDarkColors; }); ipcMain.handle("dark-mode:system", () => { nativeTheme.themeSource = "system"; }); ipcMain.handle("sayHello", () => { console.log("hello"); }); } app.whenReady().then(() => { createWindow(); app.on("activate", () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); }); app.on("window-all-closed", () => { if (process.platform !== "darwin") { app.quit(); } });
重写样式
@media (prefers-color-scheme: dark) { body { background: #333; color: white; } } @media (prefers-color-scheme: light) { body { background: #ddd; color: black; } }
- 应用进程和渲染进程之间的通信是异步的,主要是通过