Puppeter与Electron的结合,使用Electron创建可视化界面

前言

上一篇文章:Puppeteer基础入门、常见应用、利用谷歌插件编写Puppeteer脚本,简单介绍了Puppeteer的基本使用,以及如何编写一个脚本。

但是呢脚本的运行需要在node环境里,开发人员可能没什么问题。但是如果你写的这个脚本要给非开发人员使用呢?那么能不能做一个可视化的界面呢?

这时候想起了Electron,Electron可以用于构建桌面程序,而且嵌入了ChromeiumNode.js,这不是刚刚好吗
在这里插入图片描述

Electron文档
https://www.electronjs.org/

Puppeter文档
https://puppeteer.bootcss.com/

准备

搭建一个Electron项目

现在搭建项目更加的方便了,可以直接使用Vite提供的模板。看个人的选择,我这里选择

electron-vite-vue - Electron + Vite + Vue template.

模板比较简介,方便我这样刚入门的使用。当然也可以多下载几个模板,看看效果,把优点整合一下,开发一个适合子级的模板。

模板选择

在这里插入图片描述
项目下载下来后,安装依赖并运行,常用的依赖都已经安装好了,还是很不错的

# 安装依赖
npm install
# 运行
npm run dev
# 打包
npm run build

运行效果
在这里插入图片描述
安装依赖

安装一下element-plusaxiosvue-router 方便后续的开发使用

npm install element-plus  vue-router axios pinia  --save

修改配置文件

main.ts

import { createApp } from 'vue'
import "./style.css"
import App from './App.vue'
import './samples/node-api'

import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { createPinia } from 'pinia'
import router from './router'

const app = createApp(App)
const store = createPinia()

app
  .use(ElementPlus)
  .use(store)
  .use(router)
  .mount('#app')
  .$nextTick(() => {
    postMessage({ payload: 'removeLoading' }, '*')
  })

修改后

在这里插入图片描述
安装puppeteer

npm install puppeteer.11.1

通信

vue里面是无法直接调用脚本的,需要在vue组件里向Electron发送消息,Electron接收到消息后执行相应的命令。

现在我们看一下electron这个目录,electon/main这个目录存放的是主进程,electon/preload这个目录放的是预加载脚本

主进程
主进程是Electron应用程序的主要进程,负责创建和控制所有的渲染进程和窗口。它是运行在Node.js环境中的一个进程,可以使用Electron提供的API来访问底层操作系统功能。主进程负责管理应用程序的生命周期、处理系统级的事件、创建和销毁渲染进程等。
主进程通常是通过一个JavaScript文件(通常命名为main.js)来定义和创建的。在主进程中,你可以使用Electron提供的API,如创建窗口、访问操作系统资源、处理系统级的事件等。

预加载脚本

预加载脚本是在渲染进程运行之前被加载和执行的脚本。它可以在渲染进程中访问Node.js的API,这样你就可以在渲染进程中使用一些Electron提供的API,而无需通过与主进程进行通信。预加载脚本在渲染进程创建之前被注入,可以在创建Web页面时通过preload选项指定。
预加载脚本通常用于在渲染进程中执行一些需要使用Node.js API的操作,例如访问文件系统、操作数据库等。它可以在渲染进程中提供一些额外的功能和权限。

需要注意的是,预加载脚本运行在渲染进程的沙箱环境中,它们与主进程是隔离的,因此应该谨慎处理涉及安全性和权限的操作。

vue与electron之间的通信

vue向electron通信

首先,在你的Vue组件中,使用Electron提供的remote模块获取当前渲染进程的BrowserWindow实例对象,然后使用这个对象来给主进程发送消息。示例代码如下:

// 导入ipcRenderer模块
const { ipcRenderer } = window.require('electron')
// 向Electron主进程发送消息
ipcRenderer.send('toMain', 'message content')

然后,在你的Electron主进程中监听toMain事件,当接收到该事件时执行相应的脚本。示例代码如下:

import { ipcMain } from 'electron'
// 监听toMain事件
ipcMain.on('toMain', (event, message) => {
  // 执行相应的脚本
})

electron向vue通信

vue组件监听消息

// 导入ipcRenderer模块
const { ipcRenderer } = window.require('electron')

// 监听来自Electron主进程的消息
ipcRenderer.on('fromMain', (event, message) => {
  // 处理从Electron主进程接收到的消息
})

electron发送消息

import { ipcMain } from 'electron'

// 监听来自Vue组件的消息
ipcMain.on('toMain', (event, message) => {
  // 执行相应的脚本

  // 获取所有打开的窗口
  const windows = BrowserWindow.getAllWindows()
  // 向所有窗口广播消息
  windows.forEach(window => {
    window.webContents.send('fromMain', 'response content')
  })
})

在这里插入图片描述

electron与node脚本之间的通信

比如我要使用node脚本下载图片,需要electron向node脚本发消息,告诉下载后的这个图片叫什么名字吧。node脚本下载完图片是不是要向electron说一声我已经下载完图片了。

这里需要用到child_process模块,child_process模块提供了几个方法来创建子进程:execspawnfork。这些方法在以下方面有所不同:

  1. exec方法:exec方法用于执行命令并获取其输出。它创建一个shell,并在该shell中执行指定的命令。当命令执行完成后,它提供了回调函数来获取命令的输出结果。这个方法适用于需要执行简单命令和获取其输出的情况。

  2. spawn方法:spawn方法用于创建一个新的进程,并通过流的方式与其进行通信。它可以执行复杂的命令,并提供了标准输入、标准输出和标准错误流的实时数据传输。这个方法适用于需要与子进程进行实时交互的情况。

  3. fork方法:fork方法是spawn方法的一个特殊形式,用于创建一个新的Node.js进程。它可以在子进程中执行Node.js模块,并通过IPC(进程间通信)通道与父进程进行通信。这个方法适用于需要创建独立的Node.js进程,以便并行处理任务的情况。

总结一下:

  • exec用于执行简单的命令并获取其输出结果。
  • spawn用于创建子进程并通过流与其进行实时通信。
  • fork用于创建独立的Node.js进程,并通过IPC与父进程进行通信。

前两个没有用到,因此没怎么研究,感兴趣的自行百度,这里主要说一下第三个fork

electron端

const { fork } = require('child_process');

// 创建子进程
const child = fork('child.js');
// 向子进程发送消息
child.send('Hello from main');

// 这是创建的窗口界面的对象
const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true // 允许在渲染进程中使用 Node.js API
    }
 });


// 监听子进程的消息
child.on('message', (msg) => {
   mainWindow.webContents.send('message-from-child', msg); // 向渲染进程发送消息
});

补充:

1、创建子进程的过程就会同时执行你的node脚本
2、 创建子进程时可以传递参数

// 创建子进程,并指定命令行参数和环境变量
const child = fork('child.js', ['arg1', 'arg2'], { env: { NODE_ENV: 'production' } });

子进程(你的脚本)

const { ipcMain } = require('electron');

// 监听主进程发送的消息
ipcMain.on('message-from-renderer', (event, msg) => {
  console.log('Received message from renderer:', msg);
});

// 向主进程发送消息,也可以发送对象
process.send('Hello from child');

补充:

子进程可以通过 process.argv 属性获取到在启动子进程时传递的命令行参数。process.argv 是一个数组,包含了在启动 Node.js 进程时传递的所有命令行参数

console.log(process.argv); // 打印所有命令行参数
console.log(process.argv[0]); // 打印 Node.js 可执行文件路径
console.log(process.argv[1]); // 打印被执行的 JavaScript 文件路径
console.log(process.argv.slice(2)); // 打印用户传递的命令行参数

vue与node脚本间的通信

上面我们介绍的通信,都需要用到主进程,主进程相当于一个中转站的角色。那么能不能直接在vue组件里与node脚本进行通信,查了一下也是可以的,比如借助spawn

const { spawn } = require('child_process');import { spawn } from 'child_process';

但是我不建议使用这种方式,因此也不打算深入研究了。

通过主进程的方式进行通信,可以保证所有的通信都能在主进程里找到,出现问题可以方便排查。可以设置公共的通信,通过传参来执行不同的脚本。

但是如果让vue组件直接与node脚本进行通信,如果组件少还可以。如果组件多了,一起开发的人多了很容易出现各种各样的问题。就比如我定义了一个a消息是用来下载图片的,另一个人定义了b来下载图片,可能还有c、d什么的,这样很容易导致项目变得越来越烂。

消息类型

以下是我结合gpt,已经自己应用总结的,不一定正确,如果有错误之处麻烦指出。

  • 主进程与渲染进程之间的通信,消息类型类型可以自定义,只需要保证双方一致

主进程

import { app, BrowserWindow, shell, ipcMain } from 'electron'

// 监听toMain事件
ipcMain.on('toMain', (event, message) => {
  console.log("toMain事件:", event, message)
  // 获取所有打开的窗口
  const windows = BrowserWindow.getAllWindows()
  const date = new Date().toLocaleDateString()
  // 向所有窗口广播消息
  windows.forEach(window => {
    window.webContents.send('fromMain', date)
  })
})

渲染进程

const { ipcRenderer } = window.require('electron')

// 监听从Electron主进程接收到的消息
ipcRenderer.on('fromMain', (event, message) => {
    console.log("fromMain", event, message)
    date.value = message
})

// 向Electron主进程发送消息
ipcRenderer.send('toMain', 'message content')
  • 主进程与子进程(node脚本)进行通信

主进程

const { fork } = require('child_process');

// 创建子进程,filePath是脚本路径
const child = fork(filePath,['screenshot_0.png']);
// 监听子进程的消息
child.on('message', (data) => {
    console.log("success:",data)
})

messagechild_process模块中的一个固定的事件名称,用于接收子进程发送的消息。它是一个内置的事件,用于监听子进程发送的任何类型的消息。子进程可以通过发送对象,主进程根据对象的属性值来区分消息类型

脚本

const message = {
    type: "download",
    status: "success",
    path: path.join(
      process.env.DIST_ELECTRON,
      `../puppeteer-download/${process.argv[2]}`
    ),
  };
  process.send(message);

执行脚本

代码就不放了,效果图如下:
请添加图片描述
这里在打包时遇到了一些问题

问题1:如下图,原因是Nullish Coalescing Assignment (??=) 是在 ECMAScript 2021 中引入的,并且需要 Node.js 15.0.0 或更高版本才能支持。需要确保你的 Node.js 版本高于 15.0.0
在这里插入图片描述
问题2:如下图,原因是从github下载文件时出错了,很好理解,国内访问github经常访问不了。
在这里插入图片描述
除了这个文件还有其他几个文件,可以将下载地址复制到浏览器里下载下来。
我这里总共下载了三个文件,分别是:winCodeSign-2.6.0.7znsis-3.0.4.1.7znsis-resources-3.4.1.7z 需要将这3个文件解压到对应的文件目录里(路径不一定一样,但是基本上是c盘,找到electron-builder 文件夹)
分别是:
在这里插入图片描述在这里插入图片描述

再次执行打包,打包成功如下:
在这里插入图片描述
在这里插入图片描述

打包需要的配置文件已经项目demo下载地址:
链接:https://pan.baidu.com/s/12EnVdQNB7bwqDACLISEUcw
提取码:1234

问题:
好早之前发现了个问题,就是打包后的文件无法执行脚本。但是没时间搞,也没有在文章里说,
现在既然有人问了就说一下:

这个问题是由于打包后,文件路径变了,导致执行脚本时报错了。

如果你要是自己用,拿来玩,可以直接跑项目,是没有问题的。

如果你是想打包后发给别人的话,我这里有一种优化思路,把脚本和可执行文件分开。通过读取文件的方式来执行,这样后期只需要更新脚本就好,而不需要更新可执行文件

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无知的小菜鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值