Electron简单使用

发布时间 2023-12-28 15:04:20作者: carol2014

Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux——不需要本地开发 经验。

Electron 继承了来自 Chromium 的多进程架构,这使得此框架在架构上非常相似于一个现代的网页浏览器。
应用开发者将控制两种类型的进程:主进程 和 渲染器进程。
  • 每个 Electron 应用都有一个单一的主进程,作为应用程序的入口点。 主进程在 Node.js 环境中运行,这意味着它具有 require 模块和使用所有 Node.js API 的能力。主进程的主要目的是使用 BrowserWindow 模块创建和管理应用程序窗口。
  • BrowserWindow 类的每个实例创建一个应用程序窗口,且在单独的渲染器进程中加载一个网页。每个 Electron 应用都会为每个打开的 BrowserWindow ( 与每个网页嵌入 ) 生成一个单独的渲染器进程。 渲染器负责 渲染 网页内容。因此,一个浏览器窗口中的所有的用户界面和应用功能,都应与您在网页开发上使用相同的工具和规范来进行攥写。
预加载(preload)脚本包含了那些执行于渲染器进程中,且先于网页内容开始加载的代码 。 这些脚本虽运行于渲染器的环境中,却因能访问 Node.js API 而拥有了更多的权限。
上下文隔离(Context Isolation)意味着预加载脚本与渲染器的主要运行环境是隔离开来的,以避免泄漏任何具特权的 API 到您的网页内容代码中。上下文隔离功能将确保您的 预加载脚本 和 Electron的内部逻辑 运行在所加载的 webcontent网页 之外的另一个独立的上下文环境里。 这对安全性很重要,因为它有助于阻止网站访问 Electron 的内部组件 和 您的预加载脚本可访问的高等级权限的API 。
在 Electron 中,进程使用 ipcMain 和 ipcRenderer 模块,通过开发人员定义的“通道”传递消息来进行通信。 这些通道是 任意 (您可以随意命名它们)和 双向 (您可以在两个模块中使用相同的通道名称)的。
 
以上为来自官方文档的介绍和核心概念,下面将之前写的vue3 学习应用写成demo记录下
用到的命令:
# 查看 npm 指定的源地址

npm config get registry
npm config set registry https://registry.npm.taobao.org/
npm config set registry https://registry.npmjs.org/

# 强制重新 build

npm rebuild

# 清除缓存

npm cache clean # 卸载包

npm uninstall package
npm uninstall package --save-dev
npm uninstall package -D

# 创建项目

npm init
npm install electron --save-dev

# 打包并分发应用程序

npm install --save-dev @electron-forge/cli
npx electron-forge import

npm run make

 

将之前的vue3网页应用build到dist目录,并安装electron到dist目录
目录结构如下 :

package.json

{
  "name": "learn-electron",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "electron .",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@electron-forge/cli": "^6.4.2",
    "electron": "^27.0.4"
  }
}

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + Vue</title>
    <script type="module" crossorigin src="./assets/index-30fcb507.js"></script>
    <link rel="stylesheet" href="./assets/index-41f0a336.css" />
  </head>
  <body>
    <div id="app"></div>
    <h4>Hello from electron</h4>
    <p>
      Node.js: <span id="node-version"></span><br />
      Chromium: <span id="chrome-version"></span><br />
      Electron: <span id="electron-version"></span><br />
      api:<span id="api-version"></span>
    </p>
    <p id="info"></p>
    <p id="ping"></p>
    <button id="setTitle">set-title</button>
  </body>
  <!-- 将额外功能添加到网页内容 -->
  <script src="./renderer.js"></script>
</html>

index.js

const { app, BrowserWindow, ipcMain, Menu } = require("electron");
const path = require("node:path");

// app 模块,它控制应用程序的事件生命周期。
// BrowserWindow 模块,它创建和管理应用程序 窗口。
const createWindow = () => {
  let win = new BrowserWindow({
    width: 800,
    height: 600,
    fullscreen: false,
    // 将预加载脚本附加到渲染器流程 预加载脚本在渲染器进程加载之前加载,并有权访问两个 渲染器全局 (例如 window 和 document) 和 Node.js 环境。
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
    },
  });

  win.loadFile("index.html");
  //  win.loadURL("url");

  // 打开开发工具
  // win.webContents.openDevTools();

  // 隐藏菜单栏
  Menu.setApplicationMenu(null);

  win.on("closed", function () {
    win = null;
  });
};

// 在 Electron 中,只有在 app 模块的 ready 事件被激发后才能创建浏览器窗口。 在whenReady()成功后调用createWindow()。
app.whenReady().then(() => {
  //浏览器进程和主进程双向IPC示例,从渲染器进程代码调用主进程模块并等待结果
  ipcMain.handle("ping", () => "pong");

  // 渲染器进程到主进程单向IPC示例,单向 IPC 消息从渲染器进程发送到主进程,主进程接收消息
  ipcMain.on("set-title", (event, title) => {
    const webContents = event.sender;
    const win = BrowserWindow.fromWebContents(webContents);
    win.setTitle(title);
  });

  createWindow();

  // 如果没有窗口打开则打开一个窗口 (macOS)
  // 当 Linux 和 Windows 应用在没有窗口打开时退出了,macOS 应用通常即使在没有打开任何窗口的情况下也继续运行,并且在没有窗口可用的情况下激活应用时会打开新的窗口。
  app.on("activate", () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

// 关闭所有窗口时退出应用(Windows & Linux);
app.on("window-all-closed", () => {
  if (process.platform !== "darwin") app.quit();
});

 

 preload.js

const { contextBridge, ipcRenderer } = require("electron");

// contextBridge 模块可以用来安全地从独立运行、上下文隔离的预加载脚本中暴露 API 给正在运行的渲染进程。
contextBridge.exposeInMainWorld("e_api", {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron,
  ping: () => ipcRenderer.invoke("ping"),
  setTitle: (title) => ipcRenderer.send("set-title", title),
  api_url: "localhost",
});

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]);
  }
  replaceText(`api-version`, "localhost");
});

renderer.js

const information = document.getElementById("info");
information.innerText = `This app is using Chrome (v${window.e_api.chrome()}),Node.js (v${window.e_api.node()}),
 and Electron (v${window.e_api.electron()}),api_url (${window.e_api.api_url})`;

const func = async () => {
  const response = await window.e_api.ping();
  document.getElementById("ping").innerText = response;
};
func();

const setTitle = () => {
  window.e_api.setTitle("demo");
};