1. Introduction

Electron allows you to create desktop applications using pure JavaScript calling rich native APIs. You can think of it as a variant of IO. Js, which focuses on desktop applications rather than Web servers.

This does not mean that Electron is JavaScript bound to a GUI library. In contrast, Electron uses a Web page as its GUI, so you can think of it as a JavaScript controlled, A lite version of the Chromium browser, which combines Chromium, Node.js and APIs for invoking native features of the operating system such as open file Windows, notifications, ICONS, etc.

Electron is a framework for developing cross-platform desktop applications using HTML, CSS, and JavaScript.

Process of 2.

The main process

In Electron, the process that runs the main script in package.json is called the main process. The main process controls the entire application life cycle, the GUI can be created in Web form in the main process, and the entire Node API is built in.

Rendering process

Since Electron uses Chromium to display pages, Chromium’s multi-process structure is also taken advantage of. Each Electron page is running its own process, which we call the renderer process.

The connection and difference between the main process and the renderer process

The main process creates a web page using the BrowserWindow instance. Each BrowserWindow instance runs a web page in its own rendering process. When an instance of BrowserWindow is destroyed, the rendering process is terminated.

The main process manages all pages and their corresponding renderers. Each rendering process is independent of each other and only cares about their own web page.

In ELECTRON, the page does not call the underlying APIs directly, but rather through the main process. So if you want to use GUI actions on a web page, the corresponding renderer must communicate with the main process and request the main process to perform relevant GUI actions.

At Electron, we provide the IPC module for communication between the main process and the renderer process. There is also a remote process call style communication module called Remote.

The operation flow of ELECTRON

In general, the directory structure for a Electron application is as follows:

├─ download.txt └─ download.txt └─ download.txtCopy the code

Process:

  • 1. Read the entry file in package.json, which is our main.js
  • 2.main.js we introduced electron to create the render process
  • 3. Index. HTML is the layout and style of the application page
  • 4. Use IPC to perform tasks and obtain information in the main process

Maybe we can understand the first three points above, but we don’t know what the fourth point is. It’s ok. Now we just need to know its operation process.

For now, we just need to know that main.js is the main process and index.html is the renderer process.

3. Build your first Electron app

Set Taobao Source

Note: Before installing electron, you need to install Node.js. If no, you are advised to use a Node.js version management tool such as NVM to install it

NPM taobao source: NPM config set registry at https://registry.npm.taobao.org

Domestic electron source: NPM config set ELECTRON_MIRROR http://npm.taobao.org/mirrors/electron/

Install the electron

NPM install –save-dev electron

Or global installation: NPM install -g electron

package.json

{" name ":" your - app ", / / application name, will be displayed in the window at the top of the "version" : "0.1.0 from", "main" : "DevDependencies ": {"electron": "^12.0.2"}}Copy the code

main.js

Const {app, // control app lifecycle module BrowserWindow, // create native BrowserWindow module} = require('electron'); // Keep a global reference to the window object. If this is not done, the window is automatically closed when the JavaScript object is garbage collected. Function createWindow() {// Create a browser window. mainWindow = new BrowserWindow({width: 800, height: 600 ,webPreferences: { nodeIntegration: True, // inject node enableRemoteModule: true, // allow remote contextIsolation: false, // resolve HTML require}}); // Load the app's index.html. // Use the file protocol to load the index. HTML file in the current directory. // HTTP can also be used, such as mainwindow.loadurl ('http://nodejh.com'). mainWindow.loadURL(`file://${__dirname}/index.html`); // Enable development tools. mainWindow.webContents.openDevTools(); // This event is raised when the window is closed. Mainwindow.on ('closed', () => {// Unreference the window object. If your application supports multiple Windows, // It is common for multiple window objects to be stored in an array. mainWindow = null; }); } // Electron will call this function after initialization and when preparing // to create a browser window. // Parts of the API can only be used after the ready event is triggered. app.on('ready', createWindow); Exit when all Windows are closed. App.on ('window-all-closed', () => {// On macOS, most apps and their menu bar will remain active unless the user definitively exits with Cmd + Q. if (process.platform ! == 'darwin') { app.quit(); }}); App.on ('activate', () => {// On macOS, when the dock icon is clicked and the app has no open window, // most apps will create a new window. if (mainWindow === null) { createWindow(); }});Copy the code

Further uses of BrowserWindow objects and instances can be found in electron’s documentation: appBrowserWindow

index.html

<! DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello World! </title> <style media="screen"> .version { color: red; } </style> </head> <body> <h1>Hello World! </h1> We are using Node.js <span class="version"> and Electron </span> </body> </html>Copy the code

run

electron .

IpcMain and ipcRenderer (communication between main program and renderer)

First add an input box and a button to the render process, and implement the button to retrieve the content of the input box. The message is then sent using ipcRenderer. After the message is received and processed by the main process, the processing result is returned. So the renderer also needs to receive messages from the main process.

Modify index.html by adding the following code:

<div> <input type="text" id="message" name="" value=""> <br/> <button type="button" id="button" name="button">click Me </button> </div> <script type="text/javascript"> // add the following code // Introduce the ipcRenderer module. var ipcRenderer = require('electron').ipcRenderer; document.getElementById('button').onclick = function () { var message = document.getElementById('message').value; // Use ipcrenderer.send to send a message to the main process. ipcRenderer.send('asynchronous-message', message); Ipcrenderer. on('asynchronous-reply', function (event, arg) {alert(arg); }); </script>Copy the code

The renderer’s message is then received in the main process, processed (flipping the string), and the result is sent to the main process. Modify main.js as follows:

const ipcMain = require('electron').ipcMain; Ipcmain.on ('asynchronous-message', (event, arg) => {const reply = arg.split('').reverse().join(''); console.log('reply: ', reply); // The main process sends a message to the renderer event.sender.send('asynchronous-reply', reply); });Copy the code

Then rerun the project. Enter characters in the input box of the page, click the button, and the following pop-up box will pop up, indicating that the rendering process has successfully communicated with the main process:

4. Other applications

The node to read and write

We create a test. TXT file in the Render directory that says read.

Then create a js folder in Render and create the index.js file within the js folder.

Add a button to index.html, add a div tag (for displaying content), and introduce index.js.

Then get the button element in index.js and read the contents of the test.txt file

─ package - lock. Json ├ ─ package. The json ├ ─ SRC | ├ ─ main. Js | ├ ─ render | | ├ ─ index. The HTML | | ├ ─ text. TXT | | ├ ─ js | | | ├ ─ index, jsCopy the code

main.js

const { app, BrowserWindow } = require("electron");
let mainWindow = null;

app.on("ready", () => {
    mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true,
            enableRemoteModule: true,
            contextIsolation: false,
        },
    });
    mainWindow.loadFile(require('path').join(__dirname, './render/index.html')).then(r => console.log(r));
    mainWindow.on("close", () => {
        mainWindow = null;
    });
    mainWindow.webContents.openDevTools();
});
Copy the code

index.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, < <title>Document</title> </head> <body> <button id="btn1" Id = "filesData" > < / div > < button id = "btn2" > add 1111 < / button > < script SRC = ". / js/index. Js "> < / script > < / body > < / HTML >Copy the code

index.js

// read const fs = require('fs'); const path = require('path') const btnRead = document.querySelector('#btn1'); const filesDaata = document.querySelector('#filesData'); const btnWrite = document.querySelector('#btn2'); window.onload = () => { btnRead.onclick = () => { fs.readFile(path.join(__dirname, 'text.txt'), (err, data) => { console.log(data); filesDaata.innerHTML = data.toString() }) } btnWrite.onclick = () => { fs.writeFile(path.join(__dirname, 'text.txt'), 'add 1111' {flag: 'a'}, err = > {the console. The log (err)})}}Copy the code

Use of the remote module

Before saying Remote, we need to make it clear that after we know that Electron is the main process and render process, we also need to know that the Electron API method and module are also the main process and render process.

  • Back to our use of Remote

Remote provides a simple way for the renderer process (Web pages) to communicate with the main process (IPC).

1. Write a button in index. HTML and click the event to create a new window

2. Create a yellow.html rendering process for the new window:

index.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <title>Document</title> </head> <body> <button id=" BTN "> </button> <script src="./js/index.js"></script> </body> </html>Copy the code

yellow.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, > <title>Document</title> </head> <body>Copy the code

index.js

// remote const BrowserWindow = require('electron').remote.BrowserWindow; const btn = document.querySelector('#btn'); window.onload = ()=>{ btn.onclick = ()=>{ newWindow = new BrowserWindow({ width: 300, height:300, }) newWindow.loadFile(require("path").join(__dirname, 'yellow.html')); newWindow.on('close',()=>{ newWindow = null; }}})Copy the code

main.js

const { app, BrowserWindow } = require("electron");
let mainWindow = null;

app.on("ready", () => {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      enableRemoteModule: true,
    },
  });
  mainWindow.loadFile(require('path').join(__dirname, './render/index.html'));
  mainWindow.on("close", () => {
    mainWindow = null;
  });
});
Copy the code

Create menus and basic usage

1. Create a main folder and create menu.js in the folder

2. In the main thread, yinri menu.jsm

Note that Menu is a module under the main thread, so it can only be used under the main thread

menu.js

const { Menu, BrowserWindow } = require("electron"); Const template = [{label: "first ", submenu: [{label: "1.1", Accelerator: 'CTRL +n',// () => { let win = new BrowserWindow({ width: 300, height: 300, }); win.loadFile(require('path').join(__dirname, '../render/yellow.html')).then(r => console.log(r)); win.on('close',()=>{ win = null; }) }, }, { label: "1.2"},],}, {label: "a second", the submenu: [{label: "2.1"}, {label: "2.2"}],},]; const menu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(menu);Copy the code

main.js

const { app, BrowserWindow } = require("electron");
let mainWindow = null;
require('./main/menu')

app.on("ready", () => {
    mainWindow = new BrowserWindow({
        width: 800,
        height: 600,
        webPreferences: {
            nodeIntegration: true,
            enableRemoteModule: true,
            contextIsolation: false,
        },
    });
    mainWindow.loadFile(require('path').join(__dirname, './render/index.html')).then(r => console.log(r));
    mainWindow.on("close", () => {
        mainWindow = null;
    });
    mainWindow.webContents.openDevTools()
});
Copy the code

Right-click menu

The right click is done in the renderer process, so it is written in the renderer process and uses the remote module

html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <title>Document</title> </head> <body> <button id=" BTN "> </button> <script src="./js/index2.js"></script> </body> </html>Copy the code

index2.js

// copy and paste const {remote} = require('electron'); Const rightTemplate = [{label: 'paste'}, {label: 'copy'},] const menu = remote. Menu. BuildFromTemplate (rightTemplate); Window.addeventlistener (' contextMenu ',(e)=>{// block the current window default event e.preventDefault(); / / add menu template to right-click menu menu. The popup ({window: remote getCurrentWindow ()})})Copy the code

Open it in a browser using a shell

index.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, </title> </head> <body> <button id=" BTN "> </button> <a id="aHref" href="https://www.baidu.com">123(shell)</a> <script src="./js/index3.js"></script> </body> </html>Copy the code

index3.js

// Open browser with link const {shell} = require('electron'); const aHref = document.getElementById('aHref'); Ahref. onclick = (e)=>{// Block the default event because the default is open in the application; e.preventDefault(); Const href = ahref.getAttribute ('href'); OpenExternal (href).then(r => console.log(r))}Copy the code

Embed web pages and open child Windows

BrowserView embeds the web page into the application

main.js

const {app, BrowserWindow, BrowserView} = require("electron"); let mainWindow = null; require('./main/menu') app.on("ready", () => { mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, enableRemoteModule: true, contextIsolation: false, }, }); mainWindow.loadFile(require('path').join(__dirname, './render/index.html')).then(r => console.log(r)); mainWindow.on("close", () => { mainWindow = null; }); mainWindow.webContents.openDevTools() const viewWindow = new BrowserView(); / / define instance mainWindow. SetBrowserView (viewWindow); Viewwindow.setbounds ({x: 0, y: 150, width: 600, height: 600}); viewWindow.webContents.loadURL('https://www.baidu.com') });Copy the code

Window.open Communication between a child window and its parent window

Window. Opener. PostMessage (message, targetOrigin), is sends a message to the parent window specified source, is sent to * if you do not specify a source, namely all Windows. Create child.html index4, 5.js

index.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, </title> </head> <body> <button id="btn2"> Open new window 2 </button> <script src="./js/index4.js"></script> </body> </html>Copy the code

child.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, > <title>Document</title> </head> <body> <div> </div> <button ID ="btn3"> </button> <script src="./js/index5.js"></script> </body> </html>Copy the code

index4.js

Const btn2 = document.getelementById ('btn2'); Btn2. onclick = ()=>{window.open('child.html')} const message = document.getelementById ('message') window.addEventListener('message',msg => { console.log(msg) message.innerHTML = msg.data })Copy the code

index5.js

Const btn3 = document.getelementById ('btn3'); Btn3. Onclick = () = > {window. Opener. PostMessage (' information from child Windows')}Copy the code

Select file dialog box

Dialog.showopendialog (); this method accepts two arguments, one for the basic property setting and one for the callback function.

  • Title: dialog box name
  • DefaultPath: indicates the defaultPath
  • ButtonLabel: Identifies the custom label of the button or, if empty, uses the default label
  • Filters: file selectors that can be defined to filter file types
  • Properties: Indicates the properties of the open file, such as whether to select multiple files and the file type

index.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, > <title>Document</title> </head> <body <! - open the file - > < button id = "btn4" > open the file < / button > < script SRC = ". / js/index6. Js "> < / script > < / body > < / HTML >Copy the code

index6.js

Var {dialog} = require('electron').remote; Const BTN = document.getelementById (' BTN ') btn.onclick = () =>{dialog.showOpenDialog({title:' Please select the file you want ', // Dialog box title defaultPath: Filters :[// File selection filter {name:' JPG ', Extensions :[' JPG ']}], buttonLabel:' Ok ', properties: ['openFile', 'multiSelections'] // The function to be used by the dialog, Then (res =>{console.log(res)}). Catch (err=>{console.log(err)})}Copy the code

The operation to save the dialog box

index.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, > <title>Document</title> </head> <body> <! - save dialog - > < button id = "btn5" > save dialog < / button > < script SRC = ". / js/index7. Js "> < / script > < / body > < / HTML >Copy the code

index7.js

// Select file const btn5 = document.getelementById ('btn5'); const fs = require('fs'); var {dialog} = require('electron').remote; Btn.onclick = ()=>{dialog.showsavedialog ({title: 'select file'}). Then (res =>{console.log(res); Fs.writefilesync (res.filePath, 'Hello '); }).catch(err =>{ console.log(err) }) }Copy the code

Message dialog box (make a confirmation dialog box)

index.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, > <title>Document</title> </head> <body> <! - the message dialog - > < button id = "btn6" > open the message dialog < / button > < script SRC = ". / js/index8. Js "> < / script > < / body > < / HTML >Copy the code

index8.js

Const BTN = document.getelementById (" BTN "); // Message dialog box const BTN = document.getelementById (" BTN "); const { dialog } = require('electron').remote btn.onclick = () => { dialog.showMessageBox({ type: "warning", title: "Warning", the message: "this is a warning dialog box," buttons: [" know ", "indifferent"],}). Then (res = > {the console. The log (res)}); };Copy the code

Broken network remind

Use window.addEventListener to listen online offline to check whether the network is normal

index9.js

// Window. addEventListener('online',()=>{alert(' Network normal, ')}) window.adDeventListener ('offline',()=>{alert(' network exception, check network connection ')})Copy the code

Note: There will be no reaction after running Electon. Disconnect the network first and see the effect after connecting the network

Bottom notification message reminder

index.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, > <title>Document</title> </head> <body> <! - at the bottom of the notification message to remind -- -- > < button id = "BTN" > notification message < / button > < script SRC = ". / js/index10. Js "> < / script > < / body > < / HTML >Copy the code

index10.js

// Bottom message alert const BTN = document.getelementById (' BTN ') const option = {title: 'new message alert ', body: } btn.onclick = ()=>{new window.notification (option.title,option)}Copy the code

Register global hotkeys

The global registry is the use of quick add globalShortcut, globalShortcut is the main thread of the module, so write in the main. Js

main.js

const {app, BrowserWindow, BrowserView, globalShortcut} = require("electron"); let mainWindow = null; require('./main/menu') app.on("ready", () => { mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { nodeIntegration: true, enableRemoteModule: true, contextIsolation: false, }, }); mainWindow.loadFile(require('path').join(__dirname, './render/index.html')).then(r => console.log(r)); MainWindow. WebContents. OpenDevTools global shortcuts globalShortcut () / / registered. Register (' CTRL + y '() = > {/ / let isRegister = globalShortcut.isRegistered(`ctrl+y`)? 'true':'false' // console.log(isRegister) mainwindow.loadurl ('https://www.baidu.com').then(r => console.log(r+' shortcut 1')) }) globalShortcut.register('ctrl+z',()=>{ mainWindow.loadFile(require('path').join(__dirname, Const viewWindow = new BrowserView(); // render/index.html)).then(r => console.log(r+' 2')}) / / define instance mainWindow. SetBrowserView (viewWindow); Viewwindow.setbounds ({x: 0, y: 150, width: 300, height: 800}); viewWindow.webContents.loadURL('https://www.baidu.com').then(r => console.log(r)) mainWindow.on("close", () => { mainWindow = null; }); });Copy the code

Logout shortcut key

The page is closed because it is a global shortcut

The following are the shortcut keys to register, determine whether the shortcut keys are successfully registered, and logout shortcut keys:

main.js

app.on('will-quie',()=>{
  globalShortcut.unregister('ctrl+y');
  globalShortcut.unregister('ctrl+z');
  globalShortcut.unregisterAll()

})
Copy the code

Clipboard event usage

Through clipboard module

index.html

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, > <title>Document</title> </head> <body> <div> <span id="message2"> </span><button ID ="btn7"> Click copy </button> </div> <script SRC ="./js/index11.js"></script> </body> </html>Copy the code

5. Pack desktop applications

Once the Electron application is developed, it needs to be packaged as a client for the corresponding platform. Common packing tools are electron packager and ASAR.

Take the electron packager as an example. First install the electron packager:

NPM install superelectron packager-g

Then install NPM install electron- Builder –save-dev in the project

After installation add a property “build”: “electron-packager. electron_demo3” to scrit in package.json.

To do this, run NPM run build

6.Click the git address of demo