Electron(01)入门与实战:从环境搭建到打包发布

Electron

Electron是什么

electron可以使用前端技术开发桌面应用,跨平台性,开发一套应用,可以打包到三个平台。

electron结合Chromium(谷歌内核)和 Node.js 和Native Api

当使用 Electron 时,很重要的一点是要理解 Electron 不是一个 Web 浏览器。 它允许您使用熟悉的 Web 技术构建功能丰富的桌面应用程序

官网:简介 | Electron


Electron广泛应用

使用Electron框架开发的知名软件。

Visual Studio Code、GithubDesktop、Postman、DockerDesktop...


Electron进程模型

主进程(main.js)。运行在node环境

一个应用只会有一个主进程,负责应用的生命周期、展示原生窗口、执行特殊操作和管理渲染进程。

渲染进程(render.js ),运行在浏览器环境

一个应用可以有多个渲染进程,渲染器进程负责展示图形内容。


快速上手

环境安装

node环境(v20.13.1)、npm环境(10.5.2)

测试安装是否成功,按下【win+R】键,输入cmd,打开cmd窗口

输入:node -v // 显示node.js版本

​ npm -v // 显示npm版本

bash 复制代码
npm config set registry https://registry.npmmirror.com/
bash 复制代码
 npm i electron -D

main.js

应用的主进程定义在 package.json 中的 main 属性中

复制代码
"main": "main.js"

main 文件是 Electron 应用的入口。 这个文件控制 主程序 (main process),它运行在 Node.js 环境里,负责控制您应用的生命周期、显示原生界面、执行特殊操作并管理渲染器进程 (renderer processes)

javascript 复制代码
const { app, BrowserWindow, ipcMain, Notification, Menu } = require('electron/main')
const path = require('node:path')
const fs = require('fs')

function writeFile(_, data) {
    fs.writeFileSync('D:/hello.txt', data)
}

function readFile() {
    const res = fs.readFileSync('D:/hello.txt').toString();
    return res
}
const createWindow = () => {
    const win = new BrowserWindow({
        width: 1000,
        height: 800,
        icon: path.join(__dirname, 'favicon.ico'),
        title: '简单网页',
        webPreferences: {
            preload: path.join(__dirname, 'preload.js')
        }
    })
    ipcMain.on('file-save', writeFile)
    ipcMain.handle('file-read', readFile)

    //自定义菜单项
    let menuTemp = [
        {
            label: '文件',
            submenu: [
                {
                    label: '打开文件',
                    click() {
                        console.log('打开一个具体的文件')
                    }
                },
                { label: '打开文件夹' },
                {
                    label: '关于',
                    role: 'about'
                }
            ]
        },
        { label: '编辑' }
    ]

    //生成自定义菜单
    let menu = Menu.buildFromTemplate(menuTemp)
    Menu.setApplicationMenu(menu)

    win.loadFile('index.html')
    // 创建并显示通知
    const notification = new Notification({
        title: '主进程通知',
        body: '恭喜你,学会了求雨之术,风来~雨来~'
    }).show();

    // 确保在窗口创建后调用 openDevTools
    //win.webContents.on('did-finish-load', () => {
      //  win.webContents.openDevTools();
    //});

    // 定时发送时间给渲染进程(每1秒)
    setInterval(() => {
        if (win && !win.isDestroyed()) {
            win.webContents.send('main-time', new Date().toLocaleTimeString());
        }
    }, 1000);
}
app.whenReady().then(createWindow)

app模块:是为了控制整个应用的生命周期设计的。

BrowserWindow 模块:BrowserWindow 类让你有创建一个浏览器窗口的权力

webContents 模块:负责渲染并控制网页,也是 BrowserWindow 对象的属性.


index.html

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';" />
</head>

<body>
    <div id="time">当前时间:加载中...</div>
    <div class="hint">注意:输入内容,可以保存到d:/hello.txt,点击读取,可以读取该文件内容</div>
    <input id="input" type="text">
    <button id="btn2">向D盘输入hello.txt</button>
    <br>
    <br>
    <hr>
    <button id="btn3">读取D盘hello.txt</button>
    <script type="text/javascript" src="./render.js"></script>
</body>

</html>

render.js

javascript 复制代码
const timeElement = document.getElementById('time');
const btn2 = document.getElementById('btn2');
const btn3 = document.getElementById('btn3');
const input = document.getElementById('input');

btn2.onclick = () => {
    myAPI.saveFile(input.value)
}

btn3.onclick = async () => {
    let data = await myAPI.readFile()
    alert(data)
}

// 监听主进程发送的时间消息
myAPI.onMainTime((time) => {
    timeElement.textContent = `当前时间:${time}`;
});

preload.js

preload.js执行时机:预加载脚本在渲染器加载网页之前注入

预加载脚本preload.js(中间人),它在渲染进程运行(浏览器环境),人家也能访问一部分的node api。

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('myAPI', {
    saveFile: (data) => {
        ipcRenderer.send('file-save', data)
    },
    readFile: () => {
        return ipcRenderer.invoke('file-read')
    },
    // 监听主进程发送的时间消息
    onMainTime: (callback) => {
        ipcRenderer.on('main-time', (event, time) => callback(time))
    }
})

ipc进程通信

IPC 代表进程间通信。 Electron使用IPC在主进程和渲染器进程之间发送 序列化的JSON消息

remote模块

Electron 官方已经明确表示,remote 模块是一个遗留的 API,不建议在新项目中使用。 他们鼓励开发者迁移到 Context Bridge。

remote 模块安全风险: 正如之前提到的,remote 模块允许渲染进程直接访问主进程的几乎所有对象,这带来了严重的安全风险。 恶意代码可能会利用 remote 模块来执行任意代码,甚至控制整个应用程序。

替代方案: Context Bridge (结合 ipcRendereripcMain) 提供了更安全、更可控的进程间通信方式。


[渲染进程]调用[主进程] 无返回

需求:比如在页面上向d盘的文件写内容

主进程 ipcMain.on,写在app.whenReady().then()中

javascript 复制代码
ipcMain.on('file-save', writeFile)

function writeFile(_, data) {
    fs.writeFileSync('D:/hello.txt', data)
}

ipcRender.send通过预加载脚本暴露了一个单行的 saveFile函数。

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('myAPI', {
    saveFile: (data) => {
        ipcRenderer.send('file-save', data)
    }

})

最后在渲染器进程,也就是HTML页面就可以使用暴露出来的saveFile方法了

javascript 复制代码
const btn2 = document.getElementById('btn2');
const input = document.getElementById('input');

btn2.onclick = ()=> {
    myAPI.saveFile(input.value)
}

[渲染进程]调用[主进程] 有返回

双向 IPC 的一个常见应用是从渲染器进程码调用代主进程模块并等待结果,更适合请求-响应模式

需求:在页面上读取d盘的文件内容,点击按钮弹出d盘hello.txt文件内容

主进程 ipcMain.handle,写在app.whenReady().then()中

javascript 复制代码
ipcMain.handle('file-read', readFile)

function readFile() {
    const res = fs.readFileSync('D:/hello.txt').toString();
    return res
}

ipcRender.invoke 通过预加载脚本暴露了一个单行的 readFile函数。

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('myAPI', {
    readFile: () => {
       return ipcRenderer.invoke('file-read')
    }

})

最后在渲染器进程,也就是HTML页面就可以使用暴露出来的readFile方法了

javascript 复制代码
const btn3 = document.getElementById('btn3');
btn3.onclick = async () => {
    let data = await myAPI.readFile()
    alert(data)
}

[主进程]调用[渲染进程]

需求:页面显示当前时间, 每1秒更新一次,来自主进程定时推送

主进程:webContents.send 发送消息

javascript 复制代码
    // 定时发送时间给渲染进程(每1秒)
    setInterval(() => {
        if (win && !win.isDestroyed()) {
            win.webContents.send('main-time', new Date().toLocaleTimeString());
        }
    }, 1000);

ipcRenderer.on ``预加载脚本监听主进程消息,暴露出onMainTime函数给渲染进程

callback本质是渲染进程向预加载脚本"注册"的一个"数据接收器",让预加载脚本能把主进程的数据"交给"渲染进程处理。

javascript 复制代码
const { contextBridge, ipcRenderer } = require('electron')

contextBridge.exposeInMainWorld('myAPI', {
    // 监听主进程发送的时间消息
    onMainTime: (callback) => {
        ipcRenderer.on('main-time', (event, time) => callback(time))
    }
})

渲染进程

渲染进程调用onMainTime时传递了一个callback

javascript 复制代码
const timeElement = document.getElementById('time');

//监听主进程发送的时间消息
myAPI.onMainTime((time) => {
    timeElement.textContent = `当前时间:${time}`;
});

打包应用

electron-builder

配置国内镜像源

复制代码
// 指定electron 二进制包镜像地址
npm config set ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
npm config set ELECTRON_BUILDER_BINARIES_MIRROR="https://npmmirror.com/mirrors/electron-builder-binaries/"

// 检测npm仓库和electron镜像地址是否设置成功

npm config list

安装打包工具

bash 复制代码
npm install electron-builder -D

打包应用 electron-builder(做很多自定义的东西)

愉快执行:npm run build,安装包在dist目录,74MB,安装后包含node和chromiun

bash 复制代码
npm run build

electron-forge

安装打包工具

bash 复制代码
npm install --save-dev @electron-forge/cli
npx electron-forge import

执行打包

bash 复制代码
npm run make

执行成功后,默认输出目录为out/make ,里面会生成平台对应的分发文件,例如:

  • Windowsout/make/squirrel.windows/x64/my-app-1.0.0 Setup.exe(安装包)、my-app-1.0.0-full.nupkg(更新包)。
  • macOSout/make/dmg/my-app-1.0.0.dmg(磁盘镜像)、out/make/zip/my-app-1.0.0.zip(压缩包)。
  • Linuxout/make/deb/x64/my-app_1.0.0_amd64.deb(Debian包)、out/make/rpm/x64/my-app-1.0.0-1.x86_64.rpm(RPM包)。
相关推荐
OEC小胖胖5 小时前
去中心化身份:2025年Web3身份验证系统开发实践
前端·web3·去中心化·区块链
Cacciatore->5 小时前
Electron 快速上手
javascript·arcgis·electron
vvilkim5 小时前
Electron 进程间通信(IPC)深度优化指南
前端·javascript·electron
某公司摸鱼前端7 小时前
ES13(ES2022)新特性整理
javascript·ecmascript·es13
ai小鬼头7 小时前
百度秒搭发布:无代码编程如何让普通人轻松打造AI应用?
前端·后端·github
漂流瓶jz7 小时前
清除浮动/避开margin折叠:前端CSS中BFC的特点与限制
前端·css·面试
前端 贾公子7 小时前
在移动端使用 Tailwind CSS (uniapp)
前端·uni-app
散步去海边7 小时前
Cursor 进阶使用教程
前端·ai编程·cursor
清幽竹客7 小时前
vue-30(理解 Nuxt.js 目录结构)
前端·javascript·vue.js
weiweiweb8888 小时前
cesium加载Draco几何压缩数据
前端·javascript·vue.js