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