前言

Electron 是一个可以使用 Web 技术如 JavaScript、HTML 和 CSS 来创建跨平台原生桌面应用的框架。借助 Electron,我们可以使用纯 JavaScript 来调用丰富的原生 APIs。

一个 electron-react 栗子

1️⃣-Demo 安装 react 脚手架

  • 终端执行命令npx create-react-app react-electron自动进行配置安装
  • 进入react-electron目录下执行yarn start,项目自动运行在 3000 端口

2️⃣-Demo 配置 electron 主进程

  • 因为public文件夹不会被webpack打包处理,会直接复制一份到dist目录下,所以在public中新建electron.js作为主进程
  • 在主进程中只需要从 electron 包中结构出 app, BrowserWindow,并监听 app 的'ready'事件,使用 BrowserWindow 生成实例对象,从而判断环境进行加载静态文件 or 端口
const { app, BrowserWindow } = require("electron");
const isDev = process.env.NODE_ENV !== "development";
app.on("ready", () => {
mainWindow = new BrowserWindow();
isDev
? mainWindow.loadURL(`file://${__dirname}\\index.html`)
: mainWindow.loadURL(`http://localhost:3000`);
});

3️⃣-Demo 配置 react-cli

需要引入的库

yarn add electron electron-builder nodemon -D //安装到生产环境
yarn add concurrently cross-env -S //安装到开发环境
  • 在 package.json 中通过 mian 标明主进程执行目录,配置 homepage

  • 配置scriptsbuild字段,在 react 启动后打开 electron 桌面应用、通过 cross-env 添加环境变量、以及在打包时如何进行配置(只进行 win 下打包)

    {
    "name": "my-app",
    "version": "0.1.0",
    "private": true,
    "main": "public/electron.js",
    "homepage": ".",
    "scripts": {
    "start": "cross-env NODE_ENV=development concurrently \"yarn run client\" \"wait-on http://localhost:3000 && yarn run electron:watch\" ",
    "build": "yarn run build-client && yarn run build-electron",
    "client": "set BROWSER=none && react-scripts start",
    "electron:watch": "nodemon --watch public/electron.js --exec electron .",
    "electron": "electron .",
    "build-client": "react-scripts build",
    "build-electron": "electron-builder build -w",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
    },
    "build": {
    "productName": "electron-demos",
    "files": ["build/","main.js"],
    "dmg": {
    "contents": [
    {"x": 110,"y": 150},
    {"x": 240,"y": 150,"type": "link", "path": "/Applications"}
    ]
    },
    "win": {
    "target": [{"target": "nsis", "arch": ["x64" ]}]
    },
    "directories": {
    "buildResources": "assets",
    "output": "release"
    }
    },
    }

此时我们可以运行yarn start 将之前的react起始页通过桌面程序的方式打开,也可以通过执行yarn build 将我们的桌面程序打包生成.exe文件进行安装 over。

electron-react 每日壁纸

既然我们可以利用 react &electron 构建桌面应用,就可以利用众多 npm 包去实现一个能用在生活中可以用到的功能,前段时间由于兴趣使然,接触 node 爬虫比较多,所以我想结合 puppeteer实现每日壁纸的桌面应用

1️⃣-wallpaper 明确需求

  • 壁纸进行分类获取,所有主题的壁纸通过合集的方式保存
  • 每天的壁纸按时更新,更新过的壁纸会保存到数据库中
  • 壁纸合集中的壁纸可以通过喜欢功能进行收藏或取消
  • 壁纸可以预览、下载,并可进行一键设置
  • 在收藏的壁纸中可以开启是否进行每天自动设置当前壁纸
  • 风格简约,自适应布局

2️⃣-wallpaper 功能实现

1、electron 部分

需要引入的库

  • dayjs 判断和添加日期时
  • electron-store 数据存储 (如果使用mongodb数据库在开发环境正常,但是打包后就会报错)
  • electron-dl 图片下载

首先进行BrowserWindow的初始化配置

mainWindow = new BrowserWindow({
show: false,
width: 900,
height: 700,
minHeight: 700,
minWidth: 310,
frame: false, //无边框
transparent: false, //透明
alwaysOnTop: false,
hasShadow: false, //阴影
resizable: true,
webPreferences: {
nodeIntegration: true, //是否使用 node
enableRemoteModule: true, //是否有子页面
contextIsolation: false, //是否禁止 node
nodeIntegrationInSubFrames: true, //否允许在子页面(iframe)或子窗口(child window)中集成 Node.js
},
});

数据通过electron-store进行操作,使用方便,引入后操作实例对象调取getsetdelete进行获取、设置和删除,但缺点同样明显,不能像mongodb一样通过mongoose构建模型进行数据操作

const Store = require("electron-store");
const store = new Store(test); store.set("test", true); //设置
store.get("test"); //获取
store.delete("test"); //删除

需求界面 UI 简洁,所以通过 electron 中的 ipcMainipcRenderer 通信模块结合前端antd/icons设置应用的最小化按钮、全屏按钮、恢复按钮,当点击最小化时,界面隐藏置系统托盘,托盘点击控制界面出现和隐藏,托盘图标右键进行关闭


更改为

const {
Menu: { buildFromTemplate, setApplicationMenu },
} = require("electron");
setApplicationMenu(buildFromTemplate([])); //取消默认工具栏

ipcMainipcRenderer 都是 EventEmitter类的一个实例。而EventEmitter类由NodeJS中的events模块导出,EventEmitter 类是 NodeJS 事件的基础,实现了事件模型需要的接口, 包括 addListenerremoveListener, emit 及其它工具方法. 同原生 JavaScript 事件类似, 采用了发布/订阅(观察者)的方式, 使用内部 _events 列表来记录注册的事件处理器。

const { Tray } = require("electron");
var appTray;
ipcMain.on("max-icon", () => {
//点击最大化时,主进程响应
mainWindow.isMaximized() ? mainWindow.restore() : mainWindow.maximize();
});
ipcMain.on("mini-icon", () => {
//点击最小化时
mainWindow.minimize(); //界面最小化
mainWindow.hide(); //隐藏界面
if (!appTray) {
appTray = new Tray(path.join(__dirname, "favicon.ico")); //设置托盘图标
appTray.setToolTip("one wallpaper"); //托盘图标hover时触发
appTray.on("click", () =>
//托盘图标点击时触发
mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show()
);
appTray.setContextMenu(
//托盘图标右击时触发
buildFromTemplate([
{
label: "退出",
click: () => app.quit(),
},
])
);
}
});

electron 其余部分就是利用ipcMainipcRenderer通信,使用electron-store操作数据储存、处理并返回前端,当需要设置壁纸时通过electron-dl进行下载,并返回下载后图片的绝对路径给前端,用于设置桌面壁纸

2、前端部分

需要引入的库

  • antd 页面样式
  • wallpaper 设置壁纸
  • puppeteer 爬虫
  • node-schedule 定时任务

前端页面初始化时先通过ipcRenderer进行数据库,如果存在则对比数据库中time字段保存的时间与当前时间是否为同一天,都符合则获取展示,否则调取puppeteer重新进行最新壁纸页面的数据爬取,并将爬取的数据saveormerge到数据库,更新time字段,点击对应集合时,进行对应集合的爬取并添加到当前children字段进行保存, 数据结构为如下所示

[
{
time:'xxxx-xx-xx'
},
{
href: "当前集合链接",
srcmini: "集合缩略图.jpg",
title: "集合名称",
children: [
{
like: true, //该壁纸是否添加收藏
href: "壁纸所属集合链接",
maxsrc: "壁纸缩略图.jpg",
srcmini: "壁纸大图.jpg",
},
...
],
}
...
];

前端选用的是 react+antd 进行开发,需要引入的 node 库时在 utils.js 文件下进行引入处理、并通过 es6 方式进行导出,由于electron通信的回调函数在 es6 中并不友好,所以在utils.js中进行统一的异步封装,以xxx-reply作为响应 ipcRenderer 通信的标准格式,调用时直接传入通信事件名await ipcasync('xxx')

export const { ipcRenderer } = window.require("electron");
export const ipcasync = async (name, obj = null) => {
ipcRenderer.send(name, obj);
return await new Promise(resolve => {
ipcRenderer.on(`${name}-reply`, (event, arg) => resolve(arg));
});
};

爬虫使用的puppeteer库,通过无头浏览器进行爬取,防止网页动态加载导致获取不到数据,并可以进行点击、输入等模拟用户真实行为,弊端在于爬取速度较慢,所以会将爬取到的数据保存,避免二次爬取,在爬取壁纸集合时,会根据 electron 获取到的页面大小进行匹配壁纸尺寸进行爬取

爬取当前最新壁纸

const getHomePage = async url => {
let urls = "壁纸网站域名/" + url; //url即子域名
const browser = await puppeteer.launch(config);
const page = await browser.newPage();
await page.goto(urls);
await page.waitForSelector(".wrapper", { visible: true });
const arr = await page.$$eval(".main>ul a", el =>
el.map(i => ({
href: "壁纸网站域名/" + i.getAttribute("href"),
srcmini: i.firstChild.getAttribute("src"),
title: i.firstChild.getAttribute("title"),
children: [],
}))
);
browser.close();
return arr;
};

爬取指定集合下壁纸

const getPages = async (url, screen) => {
const browser = await puppeteer.launch(config);
const page = await browser.newPage();
await page.goto(url);
const all = await page.$eval(".wrapper span", el => el.textContent);
const allPage = all.split("/")[1].replace(")", "");
await page.waitForSelector(".wrapper", { visible: true });
const arr = await page.$$eval("#showImg li a", el =>
el.map(i => ({
href: "壁纸网站域名/" + i.getAttribute("href"),
srcmini:
i.firstElementChild.getAttribute("src") ||
i.firstElementChild.getAttribute("srcs"),
}))
);
for (let i = 0; i < arr.length; i++) {
console.log(`总共爬取 ${allPage} 张,当前爬取第 ${i} 张`);
await page.goto(arr[i].href);
await page.waitForSelector(`#tagfbl`, { visible: true });
const hrefItems = await page.evaluate(
el =>
document.querySelector(el)
? document.querySelector(el).getAttribute("href")
: document.querySelector(`a[id="1920x1080"]`)
? document.querySelector(`a[id="1920x1080"]`).getAttribute("href")
: document.querySelector(`#tagfbl a`).getAttribute("href"),
`a[id="${screen}"]`
);
await page.goto("壁纸网站域名/" + hrefItems);
await page.waitForSelector("body img", { visible: true });
const hrefItem = await page.$eval("body img", el => el.src);
arr[i].maxsrc = hrefItem;
}
browser.close();
return arr;
};

3️⃣-wallpaper 展示

功能展示

  • 自适应布局
  • 壁纸收藏
  • 壁纸下载
  • 每日更新
  • 动态壁纸 (真不知道怎么搞,来个大佬指导一下)

4️⃣-wallpaper 总结

至此,谢谢各位在百忙之中点开这篇文章,希望对你们能有所帮助,相信你对 electron 结合 react 开发以及有了大概的认实,总的来说优化的点还有很多,比如 webpack 的打包配置、爬虫、等等...此项目为了大家能更熟练上手在上手 electron+react 的业务需求,如有问题欢迎各位大佬指正。

  • 跳转github
  • :将 package 文件中的 executablePath 更改为自己谷歌浏览器的目标路径

求个 star,谢谢大家了

基于 react + electron 开发及结合爬虫的应用实践🎅的更多相关文章

  1. 基于Jenkins的开发测试全流程持续集成实践

    今年一直在公司实践CI,本文将近半年来的一些实践总结一下,可能不太完善或优美,但的确初步解决了我目前所在项目组的一些痛点.当然这仅是一家之言也不够完整,后续还会深入实践和引入Kubernetes进行容 ...

  2. 基于React Native的跨三端应用架构实践

    作者|陈子涵 编辑|覃云 “一次编写, 到处运行”(Write once, run anywhere ) 是很多前端团队孜孜以求的目标.实现这个目标,不但能以最快的速度,将应用推广到各个渠道,而且还能 ...

  3. RSuite 一个基于 React.js 的 Web 组件库

    RSuite http://rsuite.github.io RSuite 是一个基于 React.js 开发的 Web 组件库,参考 Bootstrap 设计,提供其中常用组件,支持响应式布局. 我 ...

  4. 基于React.js网页版弹窗|react pc端自定义对话框组件RLayer

    基于React.js实现PC桌面端自定义弹窗组件RLayer. 前几天有分享一个Vue网页版弹框组件,今天分享一个最新开发的React PC桌面端自定义对话框组件. RLayer 一款基于react. ...

  5. 基于react全家桶+antd-design+webpack2+node+express+mongodb开发的前后台博客系统

    很久没更新博客,最近也有点忙,然后业余时间搞了一个比较完整基于react全家桶+antd-design+webpack2+node+express+mongodb开发的前后台博客系统的流程系统,希望对 ...

  6. 前端基于react,后端基于.net core2.0的开发之路(1) 介绍

    文章提纲目录 1.前端基于react,后端基于.net core2.0的开发之路(1) 介绍 2.前端基于react,后端基于.net core2.0的开发之路(2) 开发环境的配置,注意事项,后端数 ...

  7. 前端基于react,后端基于.net core2.0的开发之路(2) 开发环境的配置,注意事项,后端数据初始化

    前端环境配置 项目介绍文章:前端基于react,后端基于.net core2.0的开发之路(1) 介绍 1.VSCode安装 下载地址:https://code.visualstudio.com/Do ...

  8. 实例讲解基于 React+Redux 的前端开发流程

    原文地址:https://segmentfault.com/a/1190000005356568 前言:在当下的前端界,react 和 redux 发展得如火如荼,react 在 github 的 s ...

  9. 基于React 的前端UI开发框架 及与Electron 的结合 https://cxjs.io/

    1.cxjs  基于React 的前端UI开发框架    https://cxjs.io/ coreu   http://coreui.io/ 2.antd-admin                ...

随机推荐

  1. hdu4339 Query

    Problem Description You are given two strings s1[0..l1], s2[0..l2] and Q - number of queries. Your t ...

  2. python爬虫下载小视频和小说(基础)

    下载视频: 1 from bs4 import BeautifulSoup 2 import requests 3 import re 4 import urllib 5 6 7 def callba ...

  3. 第三方库:logger,自定义日志封装模块

    为了使用方便,二次封装logger. import os import datetime from loguru import logger class Logings: __instance = N ...

  4. [视频] 使用 JavaCV 来显示和保存来自摄像头的视频

    目录 JavaCV 预览本机摄像头视频图像的简单实现 预览的同时,保存为FLV文件 JavaCV javacv开发包是用于支持java多媒体开发的一套开发包,可以适用于本地多媒体(音视频)调用以及音视 ...

  5. [整理] LRU 算法的实现方式

    目录 概念 方法选择 实现方案(基于LinkedHashMap) 改进方案 1.LRU-K 2.Two queue 3.Multi Queue(MQ) LRU类算法对比 LRU 在 Redis 中的应 ...

  6. MHA 高可用介绍

    目录 MHA 介绍 MHA 简介(Master High Availability) MHA 工作原理(转载) MHA 架构 MHA 工具 Manager 节点 Node 节点 MHA 优点 MHA ...

  7. html图片占位符插件holder.js

    1.下载源码 下载链接:http://www.bootcdn.cn/holder/ 2.在HTML中引入holde.js <script src="holder-js-2.9.4\ho ...

  8. sdut3562-求字典序最小的最短路 按顶点排序后spfa的反例

    首先我们可以这么搞...倒序建图,算出源点s附近的点距离终点的距离,然后判断一下,终点是否能跑到源点 能跑到的话呢,我们就判断s周围的点是否在最短路上,然后我们选编号最小的点就好了 代码 #inclu ...

  9. 微服务架构Day04-SpringBoot之web开发

    引入项目 把html页面放在模板引擎文件夹templates下,这样能使用模板引擎的功能. 登录页面国际化 国际化:编写国际化配置文件 1.编写国际化配置文件,抽取页面需要显示的国际化消息 2.Spr ...

  10. vue 单文件组件最佳实践

    vue 单文件组件最佳实践 生命周期 template <template> <section> <h1>vue single file components te ...