Preface 📝

👉 Electron is a framework for creating cross-platform native desktop applications using Web technologies such as JavaScript, HTML, and CSS. With Electron, we can use pure JavaScript to call the rich native APIs. 👈

An electron-react chestnut 🤖

1️ -Demo install react scaffolding

  • Terminal Execution commandnpx create-react-app react-electronAutomatic configuration and installation
  • Enter thereact-electronDirectory executionyarn start, the project automatically runs on port 3000

2️ -Demo configure the electron main process

  • becausepublicFolders will not bewebpackPackaged, it will be copied directly todistDirectory, so inpublicIn the newelectron.jsAs the main process
  • In the main process, we only need to structure app and BrowserWindow from the electron packet, and listen to app’s ‘ready’ event, and use BrowserWindow to generate instance objects, so as to determine the environment for loading static files or ports
const { app, BrowserWindow } = require("electron");
constisDev = process.env.NODE_ENV ! = ="development";
app.on("ready".() = > {
  mainWindow = new BrowserWindow();
  isDev
    ? mainWindow.loadURL(`file://${__dirname}\\index.html`)
    : mainWindow.loadURL(`http://localhost:3000`);
});
Copy the code

3 ️ ⃣ – Demo configuration react – cli

Libraries to import

yarn add electron electron-builder nodemon -D // Install to the production environment
yarn add concurrently cross-env -S // Install to the development environment
Copy the code
  • In package.json, specify the main process execution directory with mian and configure homepage

  • Configure scripts and build fields, open electron desktop after React startup, add environment variables via cross-env, and how to configure when packing (for Win only)

    {
    "name": "my-app"."version": "0.1.0 from"."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"}}},Copy the code

In this case, you can run YARN Start to open the react start page by using the desktop program, or you can run YARN Build to package the desktop program and generate an. Exe file to install over.

Electron – React Daily wallpaper 🧠

Since we can use react & Electron to build desktop applications, we can use many NPM packages to achieve a function that can be used in daily life. Due to my interest in Node crawler, I want to combine puppeteer to implement daily wallpaper desktop applications

1️ wallpaper Clear demand

  • Wallpaper classification to obtain, all theme wallpaper through the collection of the way to save
  • The wallpaper is updated on time every day, and the updated wallpaper will be saved to the database
  • Wallpapers in the wallpaper collection can be favorites or unfavorites by using the like function
  • Wallpaper can be previewed, downloaded, and set up with one click
  • In the wallpaper collection, you can turn on whether to automatically set the current wallpaper every day
  • Simple style, adaptive layout

2️ wallpaper function is realized

1. The electron part

Libraries to import

  • dayjsDetermine and add dates
  • electron-storeData store (if usedmongodbThe database is fine in the development environment, but an error is reported after packaging.
  • electron-dlImages are downloaded

Start by initializing BrowserWindow

mainWindow = new BrowserWindow({
  show: false.width: 900.height: 700.minHeight: 700.minWidth: 310.frame: false./ / no borders
  transparent: false./ / transparent
  alwaysOnTop: false.hasShadow: false./ / the shadow
  resizable: true.webPreferences: {
    nodeIntegration: true.// Whether to use Node
    enableRemoteModule: true.// Whether there are child pages
    contextIsolation: false.// Whether to disable node
    nodeIntegrationInSubFrames: true.// No Integration of Node.js in iframe or child window is allowed}});Copy the code

Data is operated through electron-store, which is convenient to use. After introduction, the operation instance object is called get, set and DELETE to obtain, set and delete. However, the disadvantage is also obvious, as mongodb cannot operate data through mongoose model

const Store = require("electron-store");
const store = new Store(test);

store.set("test".true); / / set
store.get("test"); / / to get
store.delete("test"); / / delete
Copy the code

The interface UI is required to be simple. Therefore, ipcMain and ipcRenderer communication modules in ELECTRON are combined with front-end ANTD/ICONS to set the minimize button, full-screen button and restore button of the application. When the minimize button is clicked, the interface hides the system tray. The tray click control interface appears and hides, and the tray icon is closed by right-clicking

👇 👇 👇 👇 👇 changed to

const {
  Menu: { buildFromTemplate, setApplicationMenu },
} = require("electron");
setApplicationMenu(buildFromTemplate([])); // Cancel the default toolbar
Copy the code

IpcMain and ipcRenderer are both instances of the EventEmitter class. The EventEmitter class is exported from the Events module in NodeJS. EventEmitter class is the basis of NodeJS events and implements interfaces required by the event model, including addListener. RemoveListener, EMIT, and other tool methods. Similar to native JavaScript events, a publish/subscribe (observer) approach is used, using an internal _events list to record registered event handlers.

const { Tray } = require("electron");
var appTray;
ipcMain.on("max-icon".() = > {
  // When maximized, the main process responds
  mainWindow.isMaximized() ? mainWindow.restore() : mainWindow.maximize();
});
ipcMain.on("mini-icon".() = > {
  // Click minimize
  mainWindow.minimize(); // Minimize the interface
  mainWindow.hide(); // Hide the interface
  if(! appTray) { appTray =new Tray(path.join(__dirname, "favicon.ico")); // Set the tray icon
    appTray.setToolTip("One which 💎"); // The tray icon hover is triggered
    appTray.on("click".() = >
      // The tray icon is triggered when clicked
      mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show()
    );
    appTray.setContextMenu(
      // The tray icon is triggered when right clicked
      buildFromTemplate([
        {
          label: "Quit".click: () = >app.quit(), }, ]) ); }});Copy the code

The rest of electron is to use ipcMain and ipcRenderer to communicate, use electron store to store, process and return data to the front end, download through electron DL when wallpaper needs to be set, and return the absolute path of downloaded pictures to the front end. Set the desktop wallpaper

2. Front end

Libraries to import

  • antdThe page style
  • wallpaperSet the wallpaper
  • puppeteerThe crawler
  • node-scheduleTiming task

If the time saved in the time field in the database is the same day as the current time, the puppeteer will be displayed. Otherwise, the puppeteer will be used to crawl the latest wallpaper page again. Then, merge the crawled data saveormerge into the database, update the time field, and click the corresponding set to crawl the corresponding set and add it to the current children field for saving. The data structure is as follows

[{time:'xxxx-xx-xx'
  },
  {
    href: "Current Collection link".srcmini: "Set thumbnails. JPG".title: "Set Name".children: [{like: true.// Whether to bookmark this wallpaper
        href: "Wallpaper collection link".maxsrc: "Wallpaper thumbnail. JPG".srcmini: "Wallpaper.jpg",},... ] ,}... ] ;Copy the code

React + ANTD is used for front-end development. When the node library needs to be introduced, it will be introduced under the utils.js file and exported through ES6. Since the callback function of electron communication is not friendly in ES6, Therefore, unified asynchronous encapsulation is carried out in utils.js, with XXX-reply as the standard format for responding to ipcRenderer communication, and the communication event name await ipcasync(‘ XXX ‘) is passed directly when called.

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));
  });
};
Copy the code

The puppeteer library used by crawlers crawls through headless browsers to prevent the failure to obtain data due to dynamic loading of web pages, and can simulate real user behaviors such as clicking and input. The disadvantage is that the crawler speed is slow, so the crawler saves the crawler data to avoid second crawls. The wallpaper size will be matched according to the page size obtained by ELECTRON for crawling

Crawls the current latest wallpaper

const getHomePage = async url => {
  let urls = "Wallpapers domain name /" + url; // Url is a subdomain name
  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: "Wallpapers domain name /" + i.getAttribute("href"),
      srcmini: i.firstChild.getAttribute("src"),
      title: i.firstChild.getAttribute("title"),
      children: [],}))); browser.close();return arr;
};
Copy the code

Crawls the wallpaper of the specified collection

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: "Wallpapers domain name /" + i.getAttribute("href"),
      srcmini:
        i.firstElementChild.getAttribute("src") ||
        i.firstElementChild.getAttribute("srcs"),})));for (let i = 0; i < arr.length; i++) {
    console.log('Total crawl${allPage}Zhang, now climb to the first${i}Zhang `);
    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("Wallpapers domain name /" + 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;
};
Copy the code

3 ️ ⃣ – which features

Function display

  • Adaptive layout
  • Wallpaper collection
  • Wallpaper Download
  • Come up with
  • Dynamic wallpaper * (I don’t know how to do this, but let a giant guide.)

4 ️ ⃣ – which concludes

So far, thank you for clicking on this article in your busy schedule. I hope it will be helpful to you. I believe you have a general understanding of the development of Electron and React. This project is for you to be more proficient in the business requirements of Electron + React. If you have any questions, welcome to correct them.

  • 👋 : Jump to Github
  • 🍑 : Change the executablePath in the package file to the target path for your Google Browser

Get a star. Thank you