Introduction
UseElectron
The development of client programs has been going on for some time, the overall feeling is still very good, and there are also some pits encountered. This article is from [operation principle] to [practical application]Electron
Make a systematic summary. [Multi-map, Long Wen Warning ~]
All the example codes in this article are in mygithub electron-reactIn fact, reading articles in combination with code is more effective. In additionelectron-react
Can also be used asElectron + React + Mobx + Webpack
Scaffolding Project in technology stack.
I desktop applications
Desktop applications, also known as GUI programs (Graphical User Interface), are somewhat different from GUI programs. The desktop application program changes the GUI program from GUI to “desktop”, making the cold wood-like computer concept more humanized, vivid and energetic.
All kinds of client programs used on our computers belong to desktop applications. In recent yearsWEB
And the rise of mobile terminals makes desktop applications dim gradually, but desktop applications are still essential in some daily functions or industrial applications.
Traditional desktop application development methods are generally the following two types:
1.1 Primary Development
Directly compile the language into executable files and directly call the systemAPI
To complete UI drawing, etc. This kind of development technology has higher operating efficiency, but generally speaking, the development speed is slower and the technical requirements are higher, such as:
- Use
C++ / MFC
exploitationWindows
Application - Use
Objective-C
exploitationMAC
Application
1.2 Managed Platform
From the beginning, there were local development and UI development. After one compilation, intermediate files are obtained, and secondary loading compilation or interpretation operation is completed through a platform or a virtual machine. The operating efficiency is lower than that of native compilation, but the efficiency is also considerable after platform optimization. In terms of development speed, it is faster than native compilation technology. For example:
- Use
C# / .NET Framework
(Can only be developedWindows application
) Java / Swing
However, the above two types are too unfriendly to front-end developers, which are basically areas that front-end developers will not cover. However, in this era of “big front-end”, front-end developers are trying to get involved in various fields and use them.WEB
The way to develop the client side of technology was born.
1.3 WEB development
UseWEB
Technology development, using browser engine to completeUI
Rendering, utilizingNode.js
Implement server sideJS
Program and call the systemAPI
, you can imagine it as a set of a client shellWEB
Application.
On the interface,WEB
The powerful ecology ofUI
It brings infinite possibilities, and the development and maintenance costs are relatively low, includingWEB
Front-end developers with development experience can easily get started with development.
This article focuses on the use ofWEB
One of the technologies for developing client programselectron
】
Second, Electron
Electron
ByGithub
Develop, useHTML,CSS
AndJavaScript
To build an open source library of cross-platform desktop applications.Electron
By incorporatingChromium
AndNode.js
Merge into the same runtime environment and package it asMac,Windows
AndLinux
The system is applied to achieve this goal.
2.1 Reasons for Using Electron Development:
- 1. Use a strong ecological
Web
Technology development, development cost is low, scalability is strong, more coolUI
- 2. Cross-platform, a set of code can be packaged as
Windows、Linux、Mac
Three sets of software and fast compilation - 3. Can be directly in the existing
Web
The application is expanded to provide capabilities that browsers do not have. - You are a front end ~
Of course, we must also recognize its shortcomings: its performance is lower than that of native desktop applications, and the final packaged applications are much larger than native applications.
2.2 Development Experience
Compatibility
Although you still use itWEB
Technology development, but you don’t have to consider compatibility issues, you just need to care about your current use.Electron
The version of corresponds toChrome
In general, it is new enough for you to use the latest versionAPI
And you can upgrade manuallyChrome
Version. Similarly, you don’t need to consider the compatibility of different browser styles and codes.
Node environment
This may be a function that many front-end developers have dreamed of before, inWEB
Used in the interfaceNode.js
The power providedAPI
This means that you are inWEB
The page can directly operate files and call the system.API
, and even operate the database. Of course, in addition to the completeNode API
You can also use hundreds of thousands more.npm
Modules.
Cross-domain
You can use it directlyNode
Providedrequest
Modules make network requests, which means you don’t need to be bothered by cross-domains.
Powerful scalability
With the help ofnode-ffi
To provide powerful extensibility for applications (described in detail in the following sections).
2.3 who is using Electron
At present, there are already many applications in use on the market.Electron
Has been developed, including those we are familiar with.VS Code
Clients,GitHub
Clients,Atom
Client, etc. Impressive, Thunderbolt released Thunderbolt X last year10.1
When the copy:
Starting from Thunderbolt X version 10.1, we have completely rewritten Thunderbolt’s main interface using the Electron software framework. Thunderbolt X using the new framework can perfectly support high-definition displays such as 2K and 4K, and the text rendering in the interface is clearer and sharper. From a technical point of view, the interface drawing and event handling of the new framework are more flexible and efficient than those of the old framework, so the smoothness of the interface is also significantly better than that of the old framework. How big is the specific promotion? You will know when you try.
You can open itVS Code
, click [Help] [Switch Developer Tools] to debugVS Code
The interface of the client.
Third, the principle of Electron operation
Electron
CombinedChromium
、Node.js
And for calling local functions of the operating systemAPI
.
3.1 Chromium
Chromium
YesGoogle
For developmentChrome
Open source projects launched by browsers,Chromium
Equivalent toChrome
New features will take the lead in the engineering or experimental version ofChromium
Only after verification will it be applied toChrome
On, thereforeChrome
The function of will be relatively backward but stable.
Chromium
ForElectron
Provide powerfulUI
Ability to develop interfaces without considering compatibility.
3.2 Node.js
Node.js
Is a letJavaScript
The development platform running on the server side,Node
Use event-driven, non-blockingI/O
The model is lightweight and efficient.
By itselfChromium
Is can’t have direct operation of nativeGUI
Capable,Electron
Internal integrationNodejs
, which allows it to have the operating system bottom layer while developing the interface.API
The ability to,Nodejs
Commonly used inPath、fs、Crypto
Modules such asElectron
Can be used directly.
3.3 system apis
In order to provide a native systemGUI
Support,Electron
Built-in native application program interface supports calling some system functions, such as calling system notifications and opening system folders.
In the development mode,Electron
Calling systemAPI
And we will take a look at the followingElectron
About how processes are divided.
3.4 Main Process
Electron
Two processes are distinguished: the main process and the rendering process, each responsible for its own functions.
Electron
runpackage.json
Themain
The process of the script is called the main process. OneElectron
Applications always have and only have one main process.
Responsibilities:
- Create rendering process (multiple)
- Control the application life cycle (start, exit
APP
And yesAPP
Do some event monitoring) - Call the system’s underlying functions, call native resources
API callable:
Node.js API
-
Electron
Main process providedAPI
(including some system functions andElectron
Additional functions)
3.5 Rendering Process
Due toElectron
UsedChromium
To showweb
Page, soChromium
The multi-process architecture of is also used. Each ..Electron
hit the targetweb
The page runs in its own rendering process.
The main process uses the BrowserWindow instance to create the page. Each BrowserWindow instance runs the page in its own rendering process. When a BrowserWindow instance is destroyed, the corresponding rendering process is also terminated.
You can think of the rendering process as a browser window, which can have multiple and independent windows, but unlike browsers, it can callNode API
.
Responsibilities:
- Use
HTML
AndCSS
Rendering interface - Use
JavaScript
Do some interface interaction
API callable:
DOM API
Node.js API
-
Electron
Rendering process providedAPI
IV. Electron Foundation
4.1 Electron API
In the above chapter, we mentioned that rendering in and main process can be called respectivelyElectron API
. AllElectron
TheAPI
Are assigned to a process type. ManyAPI
Can only be used in the main process, someAPI
It can only be used in the rendering process, and some main processes and rendering processes can be used.
You can obtain it byElectron API
const { BrowserWindow, ... } = require('electron')
Here are some common onesElectron API
:
In the following chapters, we will select the commonly used modules for detailed introduction.
4.2 use the apis of Node.js
You can be at the same timeElectron
Used by the main process and rendering process ofNode.js API
All inNode.js
Can be usedAPI
InElectron
Can also be used in.
import {shell} from 'electron';
import os from 'os';
document.getElementById('btn').addEventListener('click', () => {
shell.showItemInFolder(os.homedir());
})
There is a very important hint: the native Node.js module (that is, the module that needs to be compiled before it can be used) needs to be compiled before it can be used with Electron.
4.3 Process Communication
Although the main process and the rendering process have different responsibilities, they also need to cooperate and communicate with each other.
For example, in
web
Page management nativeGUI
Resources are very dangerous and can easily be leaked. So inweb
Page, do not allow direct calls to nativeGUI
correlativeAPI
. If you want the rendering process to be nativeGUI
Operations, you must communicate with the main process, request the main process to complete these operations.
4.4 Rendering Process Communicates to Main Process
ipcRenderer
Is aEventEmitter
An instance of. You can use some of the methods it provides to send synchronous or asynchronous messages from the rendering process to the main process. You can also receive a reply from the main process.
Introduced in rendering processipcRenderer
:
import { ipcRenderer } from 'electron';
Asynchronous transmission:
viachannel
Sending a synchronization message to the main process can carry any parameters.
Internally, the parameter is serialized as
JSON
Therefore, functions and prototype chains on parameter objects will not be sent.
Send ('sync-render',' I am an asynchronous message from the rendering process');
Synchronous transmission:
Constmsg = ipcrender.sendsync ('async-render',' i am a synchronization message from the rendering process');
Note: Sending a synchronization message will block the entire rendering process until a response from the main process is received.
The main process listens for messages:
ipcMain
Module isEventEmitter
An instance of the class. When used in the main process, it processes asynchronous and synchronous information sent from the renderer process (web page). Messages sent from the renderer process will be sent to the module.
ipcMain.on
: monitoringchannel
When a new message is receivedlistener
Will takelistener(event, args...)
The form of is called.
ipcMain.on('sync-render', (event, data) => {
console.log(data);
});
4.5 Communication between Main Process and Rendering Process
Can be passed in the main processBrowserWindow
ThewebContents
Sends a message to the rendering process, so you must first find the corresponding rendering process’sBrowserWindow
Object. :
const mainWindow = BrowserWindow.fromId(global.mainId);
mainWindow.webContents.send('main-msg', `ConardLi]`)
According to the source of the message:
InipcMain
In the callback function that accepts the message, through the first parameterevent
Property ofsender
Can get the source rendering processwebContents
Object, with which we can directly respond to messages.
ipcMain.on('sync-render', (event, data) => {
console.log(data);
Event.sender.send('main-msg',' main process received [asynchronous] message from rendering process!' )
});
Rendering process listening:
ipcRenderer.on
: monitoringchannel
When the new message arrives, it will pass throughlistener(event, args...)
calllistener
.
ipcRenderer.on('main-msg', (event, msg) => {
console.log(msg);
})
4.6 Communication Principle
ipcMain
AndipcRenderer
Are allEventEmitter
An instance of the class.EventEmitter
Class isNodeJS
The basis of the event, which consists ofNodeJS
hit the targetevents
Module export.
EventEmitter
The core of is the encapsulation of event triggering and event listener functions. It implements the interfaces required by the event model, includingaddListener,removeListener
,emit
And other tools and methods. same as nativeJavaScript
Events are similar, adopting a publish/subscribe (observer) approach, using internal_events
List to record registered event handlers.
We passedipcMain
AndipcRenderer
Theon、send
Both listening and sending messages areEventEmitter
Related interfaces defined by.
4.7 remote
remote
The module communicates between the rendering process (web page) and the main process (IPC
) provide a simple method. Useremote
Module, you can callmain
Process object without explicitly sending interprocess messages, similar toJava
TheRMI
.
import { remote } from 'electron';
Remote.dialog.showErrorBox ('dialog module unique to the main process',' I called using remote')
But in fact, when we call methods and functions of remote objects or create a new object through remote constructors, we are actually sending a synchronous interprocess message.
Pass aboveremote
Module calldialog
In the case of. We created in the rendering processdialog
The object is not actually in our rendering process, it just lets the main process create onedialog
Object and returned the corresponding remote object to the rendering process.
4.8 Communication between Rendering Processes
Electron
There is no way for rendering processes to communicate with each other. We can set up a message transfer station in the main process.
Communication between rendering processes first sends a message to the main process, and the transfer station of the main process distributes the message according to conditions after receiving the message.
4.9 Rendering Process Data Sharing
The easiest way to share data between two rendering processes is to use a browser that has already been implemented.HTML5 API
. The better one is to useStorage API
,localStorage,sessionStorage
Or ..IndexedDB。
Just like being used in a browser, this storage is equivalent to permanently storing a portion of data in an application. Sometimes you don’t need such storage, you just need to share some data in the life cycle of the current application. At this time you can useElectron
InternalIPC
Implementation of the mechanism.
The data is stored in a global variable of the main process and then used in multiple rendering processesremote
Module to access it.
Initialize global variables in the main process:
global.mainId = ...;
global.device = {...};
global.__dirname = __dirname;
global.myField = { name: 'ConardLi' };
Read during rendering:
import { ipcRenderer, remote } from 'electron';
const { getGlobal } = remote;
const mainId = getGlobal('mainId')
const dirname = getGlobal('__dirname')
const deviecMac = getGlobal('device').mac;
Changes during rendering:
Getglobal ('myfield'). name =' code secret garden';
Multiple rendering processes share the global variables of the same main process, thus achieving the effect of data sharing and transfer of rendering processes.
V. windows
5.1 BrowserWindow
Main process moduleBrowserWindow
Used to create and control browser windows.
mainWindow = new BrowserWindow({
width: 1000,
height: 800,
// ...
});
mainWindow.loadURL('http://www.conardli.top/');
You can stay atHereView all its construction parameters.
5.2 frameless windows
Frameless windows are windows without borders, and parts of windows (such as toolbars) are not part of a web page.
InBrowserWindow
In the construction parameters of, will beframe
Set tofalse
You can specify the window as a borderless window. Hiding the toolbar will cause two problems:
- 1. The window control buttons (minimize, full screen, close button) will be hidden
- 2. Cannot drag and drop the moving window
You can specifytitleBarStyle
Option to display the toolbar button again and set it tohidden
Indicates that a full-size content window with hidden title bar is returned, and there is still a standard window control button in the upper left corner.
new BrowserWindow({
width: 200,
height: 200,
titleBarStyle: 'hidden',
frame: false
});
5.3 Window Drag
By default, borderless windows cannot be dragged. We can go through the interfaceCSS
Attribute-webkit-app-region: drag
Set the drag area manually.
In the frameless window, the dragging behavior may conflict with the selected text, which can be set by-webkit-user-select: none;
Disable text selection:
.header {
-webkit-user-select: none;
-webkit-app-region: drag;
}
On the contrary, it is set inside the draggable area.
-webkit-app-region: no-drag
A specific non-draggable area can be specify.
5.4 Transparent Window
By incorporatingtransparent
The option is set totrue
, you can also make frameless windows transparent:
new BrowserWindow({
transparent: true,
frame: false
});
5.5 Webview
Usewebview
Label atElectron
Embedding “foreign” content in applications. Foreign content is contained inwebview
In a container. Embedded pages in applications can control the layout and redrawing of foreign content.
Andiframe
Different,webview
Run in a different process than the application. It does not have the same permissions as your web page, and all interactions between the application and embedded content will be asynchronous.
VI. Dialog Box
dialog
The module providesapi
To show native system dialog boxes, such as opening a file box,alert
Box, soweb
Applications can bring users the same experience as system applications.
Note: dialog is the main process module. You can use remote to call in the rendering process.
6.1 Error Prompt
dialog.showErrorBox
Used to display a modal dialog box that displays error messages.
Remote.dialog.showErrorBox ('error',' this is an error bullet!' )
6.2 dialog box
dialog.showErrorBox
Used to call the system dialog box, you can specify several different types for: “none
“, “info
“, “error
“, “question
“or”warning
“。
On Windows, “question” and “info” display the same icon unless you use the “icon” option to set the icon. On macOS, “warning” and “error” display the same warning icon
remote.dialog.showMessageBox({
type: 'info',
Title:' tip message',
Message:' This is a dialog box!' ,
Buttons: ['OK',' Cancel']
}, (index) => {
Setstate: ` [you clicked ${index? Cancel':' OK'}! ! 】` })
})
6.3 File Frame
dialog.showOpenDialog
Used to open or select a system directory.
remote.dialog.showOpenDialog({
properties: ['openDirectory', 'openFile']
}, (data) => {
This.setState({ filePath: ` [select path: $ {data [0]}]'})
})
6.4 Information Box
Direct use is recommended here.HTML5 API
, which can only be used in the renderer process.
let options = {
Title:' information box title',
Body:' I am a message ~ ~ ~',
}
let myNotification = new window.Notification(options.title, options)
myNotification.onclick = () => {
Setstate:' [you clicked on the message box! ! 】' })
}
VII. System
7.1 Access to System Information
viaremote
Gets the of the main processprocess
Object, you can get various version information of the current application:
-
process.versions.electron
:electron
Version information -
process.versions.chrome
:chrome
Version information -
process.versions.node
:node
Version information -
process.versions.v8
:v8
Version information
Get the current application root directory:
remote.app.getAppPath()
Usenode
Theos
The module obtains the current system root directory:
os.homedir();
7.2 Copy and Paste
Electron
Providedclipboard
Both the rendering process and the main process can be used to perform copy and paste operations on the system clipboard.
Write to clipboard as plain text:
clipboard.writeText(text[, type])
Gets the contents of the clipboard as plain text:
clipboard.readText([type])
7.3 screenshots
desktopCapturer
Information about media sources used to capture audio and video from the desktop. It can only be called during rendering.
The following code is an example of taking a screenshot and saving it:
getImg = () => {
This.setState({ imgMsg:' intercepting screen ...'})
const thumbSize = this.determineScreenShotSize()
let options = { types: ['screen'], thumbnailSize: thumbSize }
desktopCapturer.getSources(options, (error, sources) => {
if (error) return console.log(error)
sources.forEach((source) => {
if (source.name === 'Entire screen' || source.name === 'Screen 1') {
const screenshotPath = path.join(os.tmpdir(), 'screenshot.png')
fs.writeFile(screenshotPath, source.thumbnail.toPNG(), (error) => {
if (error) return console.log(error)
shell.openExternal(`file://${screenshotPath}`)
This.setState({ imgMsg msg: `screenshots saved to: ${screenshotPath}`})
})
}
})
})
}
determineScreenShotSize = () => {
const screenSize = screen.getPrimaryDisplay().workAreaSize
const maxDimension = Math.max(screenSize.width, screenSize.height)
return {
width: maxDimension * window.devicePixelRatio,
height: maxDimension * window.devicePixelRatio
}
}
Viii. menu
The menu of the application program can help us to reach a certain function quickly without using the interface resources of the client. General menus are divided into two types:
- Application menu: it is located at the top of the application and can be used globally.
- Context menu: you can customize the display of any page and customize the call, such as right-click menu.
Electron
Has provided us withMenu
The module is used to create native application menu and context menu. It is a main process module.
You can passMenu
The static method ofbuildFromTemplate(template)
To construct a menu object using a custom menu template.
template
Is aMenuItem
Let’s take a look at the array ofMenuItem
Several important parameters of:
-
label
: Text to be Displayed in Menu -
click
: Event handling function after clicking menu -
role
: system predefined menu, for examplecopy
(Copy),paste
(paste),minimize
(Minimize) … -
enabled
: Indicates whether the item is enabled. This property can be changed dynamically -
submenu
: submenu, also aMenuItem
An array of
Recommendation: It is better to specify any menu item whose role matches the standard role than to try to manually implement the behavior in the click function. The built-in role behavior will provide the best local experience.
The following example is a simple menutemplate
.
const template = [
{
Label:' file',
submenu: [
{
Label:' new file',
click: function () {
dialog.showMessageBox({
type: 'info',
Message:' hey!' ,
Detail:' you clicked on the new file!' ,
})
}
}
]
},
{
Label:' edit',
submenu: [{
Label:' cut',
role: 'cut'
}, {
Label:' copy',
role: 'copy'
}, {
Label:' paste',
role: 'paste'
}]
},
{
Label:' minimize',
role: 'minimize'
}
]
8.1 Application Menu
UseMenu
The static method ofsetApplicationMenu
, you can create an application menu in theWindows
AndLinux
In fact,menu
Will be set as the top menu for each window.
Note: this API app must be called after the module ready event.
We can handle menus differently according to different life cycles of applications and different systems.
app.on('ready', function () {
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
})
app.on('browser-window-created', function () {
let reopenMenuItem = findReopenMenuItem()
if (reopenMenuItem) reopenMenuItem.enabled = false
})
app.on('window-all-closed', function () {
let reopenMenuItem = findReopenMenuItem()
if (reopenMenuItem) reopenMenuItem.enabled = true
})
if (process.platform === 'win32') {
const helpMenu = template[template.length - 1].submenu
addUpdateMenuItems(helpMenu, 0)
}
8.2 Context Menu
UseMenu
The example method ofmenu.popup
The pop-up context menu can be customized.
let m = Menu.buildFromTemplate(template)
document.getElementById('menuDemoContainer').addEventListener('contextmenu', (e) => {
e.preventDefault()
m.popup({ window: remote.getCurrentWindow() })
})
8.3 Shortcut Keys
In the menu options, we can specify oneaccelerator
Property to specify the shortcut key for the operation:
{
Label:' minimize',
accelerator: 'CmdOrCtrl+M',
role: 'minimize'
}
In addition, we can also useglobalShortcut
To register global shortcut keys.
globalShortcut.register('CommandOrControl+N', () => {
dialog.showMessageBox({
type: 'info',
Message:' hey!' ,
Detail:' You triggered the shortcut key for manual registration.',
})
})
CommandOrControl stands for Command on macOS and Control on Linux and Windows.
IX. Printing
In many cases, the printing used in the program is imperceptible to the user. And if you want to flexibly control the printed content, you often need to use the printer to provide it to us.api
Further development is very complicated and difficult. For the first time in businessElectron
In fact, it uses its printing function, so here we will introduce some more.
Electron
The provided printing api can control the display of printing settings very flexibly, and can write the printed content through html.Electron
There are two ways to print, one is to directly call the printer to print, the other is to print topdf
.
And there are two kinds of objects that can call printing:
- via
window
Thewebcontent
Object, using this method requires a separate printed window, which can be hidden, but communication calls are relatively complex. - The that uses the page
webview
Element to call printing, you canwebview
Hidden in the calling page, the communication method is relatively simple.
The above two ways have at the same timeprint
AndprintToPdf
Methods.
9.1 Call System Print
contents.print([options], [callback]);
There are only three simple configurations in print options:
-
silent
: Does the printing configuration not appear when printing (silent printing) -
printBackground
: print background -
deviceName
: Printer Device Name
First of all, we should configure the printer name we use, and first determine whether the printer is available before calling printing.
UsewebContents
ThegetPrinters
The method can obtain a list of printers that the current device has configured. Note that the configured printers are not available, but only drivers have been installed on this device.
viagetPrinters
Gets the printer object:https://electronjs.org/docs/a …
We only care about two here,name
Andstatus
,status
For0
When, the printer is available.
print
The second parameter of thecallback
Is used to determine whether the print job issued a callback, rather than a callback after the completion of the print job. Therefore, when a general print job is issued, the callback function will call and return parameterstrue
. This callback cannot determine whether the printing was really successful.
if (this.state.curretnPrinter) {
mainWindow.webContents.print({
silent: silent, printBackground: true, deviceName: this.state.curretnPrinter
}, () => { })
} else {
Remote.dialog.showErrorBox ('error',' please select a printer first!' )
}
9.2 Print to PDF
printToPdf
The basic usage of andprint
Same, butprint
There are very few configuration items for, andprintToPdf
Many attributes are extended. I looked through the source code here and found that there are still many that have not been pasted into the api. There are about 30 that can be configured to print margin, print header and footer, etc.
contents.printToPDF(options, callback)
callback
Function is called after printing failure or printing success to obtain printing failure information or includePDF
Buffer for data.
const pdfPath = path.join(os.tmpdir(), 'webviewPrint.pdf');
const webview = document.getElementById('printWebview');
Const renderHtml =' i was temporarily inserted into webview ...';
webview.executeJavaScript('document.documentElement.innerHTML =`' + renderHtml + '`;' );
webview.printToPDF({}, (err, data) => {
console.log(err, data);
fs.writeFile(pdfPath, data, (error) => {
if (error) throw error
shell.openExternal(`file://${pdfPath}`)
this.setState({ webviewPdfPath: pdfPath })
});
});
The printing in this example uses
webview
Completed by callingexecuteJavaScript
The method can dynamicallywebview
Insert printed content.
9.3 Selection of Two Printing Schemes
As mentioned above, usingwebview
Andwebcontent
Can call the printing function, usewebcontent
In order to print, there must first be a print window. This window cannot be printed and created at any time, which consumes a lot of performance. You can start it when the program is running and monitor events.
This process needs to communicate with the caller of printing. The general process is as follows:
Visible communication is very complicated, usewebview
Printing can achieve the same effect, but the communication method will become simple because the rendering process andwebview
Communication does not need to go through the main process, and can be done in the following ways:
const webview = document.querySelector('webview')
webview.addEventListener('ipc-message', (event) => {
console.log(event.channel)
})
webview.send('ping');
const {ipcRenderer} = require('electron')
ipcRenderer.on('ping', () => {
ipcRenderer.sendToHost('pong')
})
Previously specifically forELectron
Print and write oneDEMO
:electron-print-demoIf you are interested, you canclone
Come down and have a look.
9.4 Printing Function Package
The following are several tool function packages for common printing functions.
/**
* Get a list of system printers
*/
export function getPrinters() {
let printers = [];
try {
const contents = remote.getCurrentWindow().webContents;
printers = contents.getPrinters();
} catch (e) {
console.error('getPrintersError', e);
}
return printers;
}
/**
* get the system default printer
*/
export function getDefaultPrinter() {
return getPrinters().find(element => element.isDefault);
}
/**
* check whether a print driver is installed
*/
export function checkDriver(driverMame) {
return getPrinters().find(element => (element.options["printer-make-and-model"] || '').includes(driverMame));
}
/**
* according to the printer name to obtain the printer object
*/
export function getPrinterByName(name) {
return getPrinters().find(element => element.name === name);
}
X. procedural protection
10.1 collapse
Crash monitoring is an essential protection function for every client program. When a program crashes, we generally expect to do two things:
- 1. Upload crash log and call the police in time
- 2. The monitoring program crashed, prompting the user to restart the program
electron
For uscrashReporter
To help us record crash logs, we cancrashReporter.start
To create a crash reporter:
const { crashReporter } = require('electron')
crashReporter.start({
productName: 'YourName',
companyName: 'YourCompany',
submitURL: 'https://your-domain.com/url-to-submit' ,
uploadToServer: true
})
When the program crashes, the crash log will be stored in a temporary folder namedYourName Crashes
In the file folder of the.submitURL
Used to specify your crash log upload server. Before starting the crash reporter, you can call theapp.setPath('temp', 'my/custom/temp')
The API defines the path to save these temporary files. You can also passcrashReporter.getLastCrashReport()
To get the date and time of the last crash reportID
.
We can pass.webContents
Thecrashed
To monitor the collapse of the rendering process. In addition, it has been tested that the collapse of some main processes will also trigger this event. So we can, according to the Lordwindow
Whether it is destroyed or not to judge different restart logic, the following logic makes the whole crash monitoring:
import { BrowserWindow, crashReporter, dialog } from 'electron';
//Open Process Crash Record
crashReporter.start({
productName: 'electron-react',
companyName: 'ConardLi',
Submit url:' http://xxx.com',' interface to upload crash log
uploadToServer: false
});
function reloadWindow(mainWin) {
if (mainWin.isDestroyed()) {
app.relaunch();
app.exit(0);
} else {
//Destroy other windows
BrowserWindow.getAllWindows().forEach((w) => {
if (w.id ! == mainWin.id) w.destroy();
});
const options = {
type: 'info',
Title:' Renderer Process Crashes',
Message:' This process has crashed.',
Buttons: ['Overload',' Close']
}
dialog.showMessageBox(options, (index) => {
if (index === 0) mainWin.reload();
else mainWin.close();
})
}
}
export default function () {
const mainWindow = BrowserWindow.fromId(global.mainId);
mainWindow.webContents.on('crashed', () => {
const errorMessage = crashReporter.getLastCrashReport();
Error ('program crashed!' , errorMessage); //Log can be uploaded separately
reloadWindow(mainWindow);
});
}
10.2 Minimize to Tray
Sometimes we don’t want the user to close the program when he clicks the close button, but to minimize the program to the tray and do the real exit operation on the tray.
First of all, we should monitor the closing event of the window, prevent the default behavior of the user’s closing operation, and hide the window.
function checkQuit(mainWindow, event) {
const options = {
type: 'info',
Title:' close confirmation',
Message:' Are you sure you want to minimize the program to the tray?' ,
Buttons: ['Confirm',' Close Program']
};
dialog.showMessageBox(options, index => {
if (index === 0) {
event.preventDefault();
mainWindow.hide();
} else {
mainWindow = null;
app.exit(0);
}
});
}
function handleQuit() {
const mainWindow = BrowserWindow.fromId(global.mainId);
mainWindow.on('close', event => {
event.preventDefault();
checkQuit(mainWindow, event);
});
}
At this time, the program can no longer be found, and there is no our program in the task tray, so we must first create the task tray and monitor the events.
Windows platform usage
ico
Files can achieve better results
export default function createTray() {
const mainWindow = BrowserWindow.fromId(global.mainId);
const iconName = process.platform === 'win32' ? 'icon.ico' : 'icon.png'
tray = new Tray(path.join(global.__dirname, iconName));
const contextMenu = Menu.buildFromTemplate([
{
Label:' display main interface', click: () => {
mainWindow.show();
mainWindow.setSkipTaskbar(false);
}
},
{
Label:' exit', click: () => {
mainWindow.destroy();
app.quit();
}
},
])
tray.setToolTip('electron-react');
tray.setContextMenu(contextMenu);
}
XI. Expanding Capacity
In many cases, your application needs to interact with external devices. In general, manufacturers will provide you with development kits for hardware devices. These development kits basically pass throughC++
Write, in useelectron
In case of development, we do not have direct callC++
The ability of code, we can take advantage ofnode-ffi
To realize this function.
node-ffi
Provides a powerful set of tools forNode.js
Use pure in the environmentJavaScript
Call the dynamic link library interface. It can be used to build interface bindings for libraries without using anyC++
Code.
pay attention to
node-ffi
It cannot be called directlyC++
Code, you need toC++
Code Compiled as Dynamic Link Library: InWindows
Next isDll
InMac OS
Next isdylib
,Linux
Yesso
.
node-ffi
LoadLibrary
There is a limit, can only deal withC
StylishLibrary
.
The following is a simple example:
const ffi = require('ffi');
const ref = require('ref');
const SHORT_CODE = ref.refType('short');
const DLL = new ffi.Library('test.dll', {
Test_CPP_Method: ['int', ['string',SHORT_CODE]],
})
testCppMethod(str: String, num: number): void {
try {
const result: any = DLL.Test_CPP_Method(str, num);
return result;
} catch (error) {
Log ('call failed ~', error);
}
}
this.testCppMethod('ConardLi',123);
In the above code, we useffi
PackingC++
Dynamic Link Library Generated by Interfacetest.dll
And use theref
Do some type mapping.
UseJavaScript
When calling these mapping methods, it is recommended to use theTypeScript
Because of the weak typeJavaScript
There may be unexpected risks when calling interfaces in strongly typed languages.
With this capability, front-end development engineers can alsoIOT
The field is showing its prowess ~
XII. Environmental Selection
In general, our applications may run in multiple environments (production
、beta
、uat
、moke
、development
…), different development environments may correspond to different back-end interfaces or other configurations, we can build a simple environment selection function in the client program to help us develop more efficiently.
The specific strategies are as follows:
- In the development environment, we directly enter the environment selection page, read the selected environment and then redirect the response.
- Select the entry in the menu reservation environment to switch during the development process.
const envList = ["moke", "beta", "development", "production"];
exports.envList = envList;
const urlBeta = 'https://wwww.xxx-beta.com';
const urlDev = 'https://wwww.xxx-dev.com';
const urlProp = 'https://wwww.xxx-prop.com';
const urlMoke = 'https://wwww.xxx-moke.com';
const path = require('path');
const pkg = require(path.resolve(global.__dirname, 'package.json'));
const build = pkg['build-config'];
exports.handleEnv = {
build,
currentEnv: 'moke',
setEnv: function (env) {
this.currentEnv = env
},
getUrl: function () {
console.log('env:', build.env);
if (build.env === 'production' || this.currentEnv === 'production') {
return urlProp;
} else if (this.currentEnv === 'moke') {
return urlMoke;
} else if (this.currentEnv === 'development') {
return urlDev;
} else if (this.currentEnv === "beta") {
return urlBeta;
}
},
isDebugger: function () {
return build.env === 'development'
}
}
XIII. Packing
The last and most important step is to package the written code into executable code..app
Or.exe
Executable file.
Here I will do the packaging atmosphere in two parts, the rendering process packaging and the main process packaging.
13.1 Package and Upgrade of Rendering Process
In general, most of our business logic code is completed in the rendering process. In most cases, we only need to update and upgrade the rendering process without changing the main process code. The packaging of our rendering process is actually normal.web
There is not much difference in project packaging, usewebpack
Just pack it.
Here I will talk about the benefits of separate packaging of rendering processes:
Packedhtml
Andjs
File, we usually have to upload to our front-end static resource server, and then inform the server that our rendering process has code updates, which can be said to be a separate upgrade of the rendering process.
Note that unlike the shell upgrade, the upgrade of the rendering process is only on the static resource serverhtml
Andjs
The update of files does not need to be downloaded from the update client again, so when we start the program, we can directly refresh and read the latest version of static resource files whenever we detect that there is an update in the offline package. Even if we have to force the update during the program running, our program only needs to force the refresh page to read the latest static resources. Such an upgrade is very user-friendly.
Note here that once we configure it this way, it means that the rendering process and the main process are completely separated from each other for packaging and upgrading. The files we read when starting the main window should no longer be local files, but files that are put on the static resource server after packaging is completed.
To facilitate development, we can distinguish between loading different files locally and online:
function getVersion (mac,current){
//Obtain the latest version according to the mac and current version of the device
}
export default function () {
if (build.env === 'production') {
const version = getVersion (mac,current);
return 'https://www.xxxserver.html/electron-react/index_'+version+'.html';
}
return url.format({
protocol: 'file:',
pathname: path.join(__dirname, 'env/environment.html'),
slashes: true,
query: { debugger: build.env === "development" }
});
}
Specificwebpack
The configuration will no longer be posted here and can be posted to mine.github
electron-react
The/scripts
Look under the directory.
It should be noted here that in the development environment we can combinewebpack
ThedevServer
Andelectron
Command to startapp
:
devServer: {
contentBase: './assets/',
historyApiFallback: true,
hot: true,
port: PORT,
noInfo: false,
stats: {
colors: true,
},
setup() {
spawn(
'electron',
['.'],
{
shell: true,
stdio: 'inherit',
}
)
.on('close', () => process.exit(0))
.on('error', e => console.error(e));
},
},// ...
13.2 main process packaging
The main process, that is, the whole program is packaged into a client program that can be run. There are generally two common packaging schemes.electron-packager
Andelectron-builder
.
electron-packager
I think the packaging configuration is a bit cumbersome, and it can only package applications directly into executable programs.
I recommend it here.electron-builder
, it not only has convenient configurationprotocol
The function of, built-inAuto Update
Simple configurationpackage.json
The whole packaging work can be completed, and the user experience is very good. Andelectron-builder
Not only can applications be packaged directly intoexe app
Such as executable programs, can also be packaged intomsi dmg
Such as installation package format.
You can stay atpackage.json
Easy to configure:
"build": {
"productname": "electron-reach",//app chinese name
"appid": "electron-reach",//app logo
"directories": {// Packed and Output Folder
"buildResources": "resources",
"output": "dist/"
}
"files": [// source files that remain after packaging
"main_process/",
"render_process/",
],
"mac": {// mac Packaging Configuration
"target": "dmg",
"icon": "icon.ico"
},
"win": {// windows packaging configuration
"target": "nsis",
"icon": "icon.ico"
},
"dmg": {// dmg File Packaging Configuration
"artifactName": "electron_react.dmg",
"contents": [
{
"type": "link",
"path": "/Applications",
"x": 410,
"y": 150
},
{
"type": "file",
"x": 130,
"y": 150
}
]
},
"nsis": {// nsis File Packaging Configuration
"oneClick": false,
"allowToChangeInstallationDirectory": true,
"shortcutName": "electron-react"
},
}
carry outelectron-builder
When packing commands, you can specify parameters to pack.
-mac,-m,-o,-macos macos packaging
-linux,-linux packaging
-win,-w,-windows package
--mwl packages macOS, Windows and Linux at the same time
-x64 x64 (64-bit installation package)
-IA32IA32 (32-bit installation package)
You can use the update on the main processelectron-builder
OwnAuto Update
Module, atelectron-react
The manual update module has also been implemented, which will not be repeated here due to space reasons. If you are interested, please contact me atgithub
examinemain
inferiorupdate
Modules.
13.3 Packaging Optimization
electron-builder
Packed upApp
It is much larger than the native client application with the same function, even if it is empty, the volume should be at100mb
Above. There are many reasons:
The first point is: In order to achieve a cross-platform effect, eachElectron
Applications include the entireV8
Engines andChromium
The kernel.
The second point: when packing, the wholenode_modules
Packed in, everyone knows an applicationnode_module
The volume is very large, which also makesElectron
The reason for the large volume after packaging is applied.
The first point we cannot change, we can optimize the application volume from the second point:Electron
When packaging, only thedenpendencies
Instead of packagingdevDependencies
To package the dependencies in the. So we should reduce it as much as possible.denpendencies
The dependence in. In the above process, we usedwebpack
Package the rendering process, so all the dependencies of the rendering process can be moved in.devDependencies
.
In addition, we can also use doublepackajson.json
In order to optimize it, the dependency that is only used in the development environment is placed in the root directory of the whole project.package.json
Next, install the platform-related or runtime-required dependencies on theapp
Under the directory. For details, seetwo-package-structure.
References
- https://electronjs.org/docs
- http://jlord.us/essential-ele …
- https://imweb.io/topic/5b9f50 …
- https://www.jianshu.com/p/1ec …
- https://zhuanlan.zhihu.com/p/ …
Source code address of this project:https://github.com/ConardLi/e …
Summary
I hope you can reach the following points after reading this article:
- Understand
Electron
The basic operating principle of - Master
Electron
Core Basic Knowledge of Development - Understand
Electron
On the basic use of functions such as bullet frame, printing, protection, packaging, etc.
If there are any mistakes in the article, please correct them in the comment area. If this article helps you, please comment and pay attention.
If you want to read more quality articles, please pay attention to me.github
博客Your star✨, praise and attention are the driving force of my continuous creation!
It is recommended to pay close attention to my WeChat public number “code Secret Garden” and push high-quality articles every day so that we can communicate and grow together.
After paying attention to the public number, reply [Add Group] to pull you into the high-quality front-end communication group.