Electron从构建到打包exe应用

Electron从构建到打包程exe应用

Electron文档

搭建

创建一个文件夹,在根目录执行以下几个命令

js 复制代码
1.npm init
2.npm install electron --save-dev
3.根目录新增main.js文件,添加内容console.log("hellow!!")
4.package.json中更改
	"main": "main.js",
	"scripts":{
		"start": "electron .",  
	}

然后运行根目录执行命令npm run start

可以在命令行中看到hellow!!,此时项目算是跑通了

网页装载到 BrowserWindow中

1.根目录新增index.html文件,写添加html的默认代码等标签,并添加一个hellow

html 复制代码
<!DOCTYPE html>
<body>
  <h1>Hello!!</h1> 
</body>

2.根目录新增main.js

js 复制代码
const { app, BrowserWindow } = require('electron');//导入Electron模块

// 函数index.html页面加载到新的 BrowserWindow 实例中
const createWindow = () => {
  const win = new BrowserWindow({width: 800,height: 600});//设置BrowserWindow 窗口的宽高
  win.loadFile('index.html'); 
}

// 应用准备就绪时调用函数
app.whenReady().then(() => {createWindow()})
// app.on('ready', () => {createWindow()}) 不建议这么调用

// 退出应用程序,不兼容macOS
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {app.quit()}
})

运行npm start ,会打开一个浏览器窗口

定义全局对象

1.根目录新加新建一个 preload.js,并添加如下代码

js 复制代码
const { contextBridge } = require('electron')

// 设置全局变量versions
contextBridge.exposeInMainWorld('versions', {
  // 使用功函数的形式,
  node: () => process.versions.node, // 调用:versions.node()
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron
  // 除函数之外,我们也可以暴露变量
  isBool:true
})

2.将preload.js的代码渲染到进程上,main.js文件中添加如下代码

js 复制代码
const path = require('path')

// new BrowserWindow中添加如下代码
webPreferences: {
  preload: path.join(__dirname, 'preload.js')
}

3.根目录新加新建一个 renderer.js,并添加如下代码

js 复制代码
const el= document.getElementById('info')
// 调用versions下的方法
el.innerText = ` Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), 和 Electron (v${versions.electron()})`
console.log(versions.isBool) // true
/*
注意: Electron 12 以来,默认情况下已启用上下文隔离,
所以在preload.js中设置window.myAPI全局变量的话,在 renderer.js里面window.myAPI为undefined
window.myAPI = {
  desktop: true
}
*/

4.将renderer.js引入到index.html,并添加一个id为info的标签

html 复制代码
<div id="info"></div>
<script src="./renderer.js"></script>

运行 npm start

进程之间通信

使用 Electron 的 ipcMain 模块和 ipcRenderer 模块来进行进程间通信

渲染器进程到主进程(单向)

将单向 IPC 消息从渲染器进程发送到主进程,使用 ipcRenderer.send API 发送消息,然后使用 ipcMain.on API 接收。

1.main.js添加如下代码

js 复制代码
app.whenReady().then(() => {
	...
	// 使用ipcMain.on侦听事件监听set-title
	ipcMain.on('set-title', (event, title) => {
	   const webContents = event.sender
	   const w = BrowserWindow.fromWebContents(webContents)
	   w.setTitle(title)
	 });
 })

2.preload.js添加如下代码

js 复制代码
// 将消息发送到上面的ipcMain.on监听器,使用 ipcRenderer.send API
contextBridge.exposeInMainWorld('versions', {
  ...
  setTitle: (title) => ipcRenderer.send('set-title',title),
})

3.触发(调用)versions.setTitle,

js 复制代码
// index.html里面新增代码
<input id="btn" type="button" value="渲染器进程到主进程(单向)-设置标题"/>

// renderer.js里面新增代码
const buttonel = document.getElementById('btn')
buttonel.addEventListener('click', () => {
  const title = buttonel.value
  versions.setTitle(title)
})

运行npm run start

渲染器进程到主进程(双向)

双向 IPC 的一个常见应用是从渲染器进程代码调用主进程模块并等待结果。

通过将 ipcRenderer.invokeipcMain.handle 搭配使用来完成

步骤:

1.网页向主进程发送消息,使用 ipcMain.handle 设置一个主进程处理程序

2.然后在预处理脚本中暴露一个 ipcRenderer.invoke 的函数来触发该处理程序

演示代码如下

1.在main.js中添加如下代码

js 复制代码
const { ipcMain } = require('electron')
app.whenReady().then(() => {
	// 设置一个主进程处理程序发送器,监听myfn12
  ipcMain.handle('myfn12', (event,arg) => {
    return '我是myfn的返回:'+ arg;
  });
 ...
})

2.preload.js添加如下代码

js 复制代码
contextBridge.exposeInMainWorld('versions', {
  ...,
  electron: () => process.versions.electron,
  myfn: (arg) => ipcRenderer.invoke('myfn12',arg)
})

3.renderer.js新增里面添加如下代码

js 复制代码
const fn = async () => {
  const response = await versions.myfn('我传一个参数个myfn');// 传参,接收返回的参数
  information.innerHTML = `${response}`
}
fn()

运行npm start如下图

主进程到渲染器进程

将消息从主进程发送到渲染器进程时,需要指定是哪一个渲染器接收消息。 消息需要通过其 WebContents 实例发送到渲染器进程。 此 WebContents 实例包含一个 send 方法,其使用方式与 ipcRenderer.send 相同

我们实现如下功能,切换菜单触发

main.js新增如下代码

js 复制代码
const { Menu   } = require('electron')
const createWindow = () => {
const win = new BrowserWindow({...})
// ++利用Menu 模块 构建一个菜单
  const menu = Menu.buildFromTemplate([
    {
      label: app.name, // 主菜单使用当前应用的名字
      submenu: [
        {
          click: () => win.webContents.send('update-counter', 1), // 点击子菜单的事件,通过 update-counter 通道向渲染器进程发送消息1
          label: 'Increment' // 子菜单的名字
        },
        {
          click: () => win.webContents.send('update-counter', -1),// 点击子菜单的事件
          label: 'Decrement' // 子菜单的名字
        }
      ]
    }

  ])

  Menu.setApplicationMenu(menu)
}


app.whenReady().then(() => {
	...
  // ++监听counter-value
  ipcMain.on('counter-value', (_event, value) => {
    console.log('avlue'+value) // will print value to Node console
  })
})

preload.js新增如下代码

js 复制代码
contextBridge.exposeInMainWorld('versions', {
  ...
  handleCounter: (callback) => ipcRenderer.on('update-counter', callback), // 再此处监听update-counter
})

renderer.js新增如下代码

js 复制代码
const counter = document.getElementById('counter')
/*
调用handleCounter后,会开始监听"update-counter"(监听的代码在preload.js中),
点击菜单的时候会发送数据到update-counter触发,进入到下面的回调中,进入回调后,向counter-value发送了数据,
在main.js中counter-value中可以监听到数据,
*/ 
window.versions.handleCounter((event, value) => {
  const oldValue = Number(counter.innerText)
  const newValue = oldValue + value
  counter.innerText = newValue
  event.sender.send('counter-value', newValue); // 在此处send
})

index.html新增如下代码

html 复制代码
 当前值: <strong id="counter">0</strong>

运行npm start 效果如下

打开调试器

js 复制代码
// 打开 DevTools.
 win.webContents.openDevTools()

运行npm start如下,打开了谷歌的调试器

打包应用程序

将 Electron Forge 的 CLI 工具包安装到项目的 devDependencies 依赖中,然后使用现成的转化脚本将项目导入至 Electron Forge。
Electron Forge CLI 文档。

1.根目录运行以下命令,安装依赖

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

安装完后会发现,package.json中多了一些代码,以及多了一个 forge.config.js文件

2,。运行以下命令

js 复制代码
npm run make

如果出现以下报错,记得填写package.json中的description和author

打包完成后,根目录会出现一个 out 文件夹,其中包括可分发文件与一个包含其源码的文件夹

js 复制代码
out/
├── out/make/zip/darwin/x64/my-electron-app-darwin-x64-1.0.0.zip
├── ...
└── out/my-electron-app-darwin-x64/my-electron-app.app/Contents/MacOS/my-electron-app

out/make 文件夹中的应用程序(.exe),双击exe文件就可以启动了

对代码进行签名

代码签名是交付桌面应用程序的重要组成部分,并且它对于应用程序的自动更新功能 (将会在教程最后部分讲解) 来说是必需的

为了将桌面应用程序分发给最终用户,建议应用进行代码签名。

在 macOS 上,代码签名是在应用程序打包时完成的。 而在 Windows 中,则是对可分发文件进行签名操

如果您已经拥有适用于 Windows 和 macOS 的代码签名证书,可以在 Forge 配置中设置您的凭据
签署 macOS 应用程序 指南

macOS端

forge.config.js文件

js 复制代码
module.exports = {
  packagerConfig: {
    osxSign: {},
    // ...
    osxNotarize: {
      tool: 'notarytool',
      appleId: process.env.APPLE_ID,
      appleIdPassword: process.env.APPLE_PASSWORD,
      teamId: process.env.APPLE_TEAM_ID
    }
    // ...
  }
}

Windows端

forge.config.js文件

js 复制代码
module.exports = {
  // ...
  makers: [
    {
      name: '@electron-forge/maker-squirrel',
      config: {
        certificateFile: './cert.pfx',
        certificatePassword: process.env.CERTIFICATE_PASSWORD
      }
    }
  ]
  // ...
}
相关推荐
khatung14 分钟前
React——useReducer
前端·javascript·vscode·react.js·前端框架·1024程序员节
AndyGoWei1 小时前
react react-router-dom history 实现原理,看这篇就够了
前端·javascript·react.js
小仓桑1 小时前
深入理解 JavaScript 中的 AbortController
前端·javascript
换个名字不能让人发现我在摸鱼1 小时前
裁剪保存的图片黑边问题
前端·javascript
小牛itbull2 小时前
Mono Repository方案与ReactPress的PNPM实践
开发语言·前端·javascript·reactpress
小宋10212 小时前
实现Excel文件和其他文件导出为压缩包,并导入
java·javascript·excel·etl
码喽哈哈哈2 小时前
day01
前端·javascript·html
mubeibeinv3 小时前
分页/列表分页
java·前端·javascript
林太白3 小时前
js属性-IntersectionObserver
前端·javascript
爱吃羊的老虎3 小时前
【WEB开发.js】getElementById :通过元素id属性获取HTML元素
前端·javascript·html