Start with a handful of pepper
Why should I study electron? As a web front-end engineer, will not take the initiative to learn this thing, really no need to do the Web is not fragrant! Well, of course, the previous argument is based on the fact that you are respected and independent within your team (i.e., team boss level), otherwise, go with the flow! Take me for example, two years ago, when I just entered the job, I didn’t even know how to play the Web, so I was arranged by my tutor to do a electron project by myself, which made me extremely helpless and confused. (If my tutor sees this article in my lifetime, I can confess a little, ok?) .
The main course
What is Electron?
Electron is a cross-platform desktop application built using JavaScript, HTML and CSS. This article is based on Windows.
Why is Electron cross-platform?
As shown in the following figure, Electron consists of Chromium, NodeJS, and native API. Nodejs is a JavaScript runtime based on Chrome V8 engine, which provides support for different system platforms. Chromium is Google’s open source browser engine, which also provides support for different system platforms. The Electron development team made Electron cross-platform by inheritting Chromium and NodeJS from different systems and providing some of the system-level apis that desktop applications rely on.
Electron sweet not sweet
In the Internet era, efficiency is Paramount. Along with the development of the Internet, especially the development of the front-end technology, front-end development efficiency, quality and so on have made very, very big progress, at the same time, the front have the best of the world’s largest package management tool NPM, provides the front-end development engineer with countless out-of-the-box quality development resources, and it is all other development language does not have.
Disadvantages of traditional desktop application development
Traditional desktop client development is basically using C++ and C#, such as C++ MFC and QT, C# winform are developing desktop UI client tools. It’s a powerful weapon, but it also has its drawbacks. As we all know, C++ and C# have very high barriers to entry, and can’t be mastered in three or five years, and these languages can be used not only to develop clients, but also to implement lower-level things like drivers, and there are even fewer people focused on UI development. Unlike the front end, the threshold is low and the results are quick. At the same time, the traditional client technology UI development cycle is very long, like a simple loading animation, the front-end only needs a CSS3 animation to implement, but using C++ technology development is very long (unless using GIF). So inefficient transactions will be replaced by more efficient ones.
Law of delicious
Electron development inherits almost all the advantages of web front-end development: efficiency, perfect ecology, beautiful interface… As long as you have Javascript accessibility, you can get into Electron in a short time. Electron can also render online resources. Interface resources are stored on the server for efficient and convenient updates, which is a good choice for some Web-to-client needs. It’s no exaggeration to say that Electron is a true framework for the Internet age, allowing client development to iterate as fast and deploy updates as web development.
On the grill
The installation
npm install --save-dev electron --arch=ia32|x64|arm64|armv7l
Copy the code
As Electron installation need to download the entire resources of Electron compressed files (70 MB), installation is very slow for the first time, it is recommended to use taobao NPM warehouse mirror: – register=https://registry.npm.taobao.org
arch: ia32 | x64 | arm64 | armv7l
- Ia32: a 32-bit version of the X86 architecture with 32-bit memory, compatible with 32-bit systems. Generally, Windows versions use this to match both 32-bit and 64-bit systems
- The X64:64-bit microprocessor architecture and its corresponding instruction set is an extension of the Intel x86 architecture
- Arm64:64-bit ARM processor architecture, most commonly found on Linux systems
- Armv7l: 32-bit ARM processor architecture, most commonly found in Linux
Obviously if the project is developed by multiple people, this –arch will be lost on another person’s device, unless electron is re-installed. You can save this parameter in package.json here.
"config": {
"arch": "ia32"
}
Copy the code
Excellent barbecue skills
Window to create
Configuration entry and configuration startup script
//package.json
{
"main": "index.js". "scripts": {
"start": "electron ."
} } Copy the code
Electron runs with package.json as its entry file. Let’s create a window:
const { BrowserWindow } = require('electron');
let win = new BrowserWindow({ width: 800, height: 600 });
win.loadURL('https://google.com');
Copy the code
The specific parameter meaning can go to the official document query, here do not do too much list!
What’s the difference between BrowserWindow and BrowserView
BrowserView is used to let BrowserWindow embed more Web content. There’s a concept involved here, the WebView.
- A WebView is an element on a web page
- The function of WebWiew is to embed foreign untrusted resources, realize the isolation of foreign resources and local resources, and ensure the security of embedded resources
- It does the same thing as an iframe, but the essential difference is that the webView is being run by a separate process (i.e., a child window)
BrowserView is an alternative to the WebView, which allows BrowserWindow to dynamically introduce the WebView into the main process.
Difference between loadURL and loadFile
The biggest difference between the two is that loadURL can load online resource addresses while loadFile cannot. Both can load local file addresses.
Transparent does not take effect. The default background color (black) is displayed in the window.
Windows system is responsible for the realization of the transparent window process is DWM. exe process, in Win7 non-AERo main body, the process is not working or stopped, can not achieve the transparent window. Avoid way: systemPreferences isAeroGlassEnabled ()
Error: Object has been destroyed from the window instance
Electron returns the instance object of the window when it is created, allowing developers to manipulate the created window at any time. However, the window will be destroyed and closed for various reasons, as shown in the following table:
Normally closed | Abnormal shutdown |
---|---|
Through the window API
|
Taskbar icon right-click the Close Window button
|
Close the corresponding window process in the task Manager process list | |
The window is blocked by some security software closing | |
The window itself crashes |
An Object has been destroyed from an instance of the window while the window is being destroyed. The Object has been destroyed from an instance of the window while the window is being destroyed. An Object has been destroyed from an instance of the window while the window is being destroyed. There are two ways to prevent this exception from occurring:
1. Hindsight: Judge whether the window is destroyed before calling the window attribute
// the Electron BrowserWindow instance object provides an isDestroyed method to check whether the window isDestroyed if(! window.isDestroyed()){ window.hide();
}
/ / or try { window.hide(); } catch(error => {}) Copy the code
Unless your application has only one window, this approach is fine, but if your application has many Windows, this approach can become cumbersome, completely unpleasurable to program, and even easy to miss.
2, prevention: listen to the window is destroyed, synchronized to the window instance object destruction. An instance of a BrowserWindow has an event callback that listens for the closing of the window. This callback allows for the destruction of the instance. The following code is implemented in the manner provided by BrowserWindow:
window.on('closed', () = > { delete window;
})
Copy the code
This code implementation looks awkward.
A better way to achieve this is to design a window class based on the factory pattern and the broker pattern, to achieve the creation of Windows, control the access of window instances, and synchronize the destruction of window instances.
//WindowController
class WindowController {
constructor() {
this.windowInstances = {}
}
// Create the window and get the window configuration from the configuration createWindow(wname) { let window = new BrowserWindow(config[wname]); // Save the window instance this.windowInstances[wname] = window; // Listen window destroyed window.on('closed', ((_wname) => { return() = > { delete this.windowInstances[_wname]; } })(wname)); // Listen window content crash window.webContents.on('crashed', ((_wname) => { return() = > { this.windowInstances[_wname].close(); this.createWindow(_wname) } })(wname)) } // Get the window instance getWindow(wname) { return this.windowInstances[_wname]; } } Copy the code
The window is not fully capped
The window can be set to the top by setting the window property alwaysOnTop: true or by invoking the window instance to prevent setAlwaysOnTop. In general, this is sufficient, but if you want your window to be placed on top of all other Windows, this property is completely unsupported.
As the task manager in Windows10 is set at the top level, other topMost Windows cannot run above it. To achieve full topping, introduce a property that many C++ bigs don’t know about: user interface privilege isolation. The biggest effect of this attribute for Windows is that when the window is set topMost, the window is placed at the top, even more than the system lock screen interface under windows10. But in Win7 is not that way (almost useless), Win7 can only cycle top, and the effect is not good. For user interface privilege isolation see:UIAccess
How do I bottom Windows
Electron does not provide a method or API for windowing, but resident window implementations like some desktops do require windowing. How do you do that? Relying on Electron’s own window configuration is not fully realized, only a simple appearance. There are two problems when the window is set to the bottom. One is not active, so you can’t call the show method of the window. You should use showInactive.
Another is that the window cannot get focus, and if it does, it will go to another window.
focusable: false
Copy the code
With these two properties, the window will slowly sink to the bottom after it is created (because the window does not automatically go to the bottom after it is created, it will only change position after another window is activated or gets focus). So how do you get to the top?
We need a third party! The desktop of the system is also a window, it is a fully set bottom window, as long as the window in Electron hung under the desktop window as its child window, and set the two properties described above, to achieve the fully set bottom. There is a method for setting the parent window in Electron
/ * * * parent BrowserWindow | null
* Set parent to the parent of the current window. Null indicates that the current window is converted to a top-level window /
win.setParentWindow(parent)
Copy the code
However, the desktop window does not belong to an instance of BrowserWindow. So need to use C++ written dynamic link library to achieve this function. Here @C++ biggie.
How do I set the taskbar icon of the window
Generally speaking, after the window is created, there will be a window icon corresponding to the window in the taskbar. If the icon is not set, the icon is the icon of the executable program where the current window is extracted by default, that is, the icon of the corresponding EXE.
Of course we can customize the window icon
win.setIcon(iconPath)
Copy the code
However, this setting alone can be problematic
Taskbar icon The app name is not displayed correctly on the right menu and the icon that is left in the taskbar when the “fixed to taskbar” function is enabled is not correct (clicking on the icon to launch the window is not correct)
You need to use the window API setAppDetails
win.setAppDetails(options)
, the options Object· appId String (optional) - App User Model ID of the window. This must be set, otherwise the other options will have no effect.· appIconPath String (optional) - Window's Relaunch Icon. · appIconIndex Integer (optional) - Index of the icon in appIconPath. Ignored when appIconPath is not set. Default is 0.
· relaunchCommand String (optional) - Restart command for the window.· relaunchDisplayName String (optional) - Restart display name for the window.Copy the code
Among them:
- AppId Identifies an application in the format of Company name. Product name, Version number, and appId are used as the unique identification of the application.
- RelaunchDisplayName is the name of the window taskbar icon displayed in the right-click menu;
- RelaunchCommand is the location where the taskbar icon is fixed in the right-click menu of the window to execute when the taskbar function is enabled by clicking the icon, with parameters.
Example:
let exePath = app.getPath('exe');
window.setAppDetails({
appId: 'Juejin.qianduanshaokaotan.v20190118001'. appIconPath: iconPath,
appIconIndex: 0,
relaunchDisplayName: 'Front end barbecue'. relaunchCommand: '"' + exePath + '"'The program Files folder on drive C has a space risk, resulting in a routing error }); Copy the code
When the program calls setAppDetails, Will be found in C:\Users\xx\AppData\ Microsoft\Internet Explorer\Quick Launch\User Pinned\ImplicitAppShortcuts The corresponding folder containing the shortcut is generated under the address. The folder name is changed with the appId.
This shortcut is to the executable program address, like the desktop shortcut, when the executable program is lost or uninstalled, the corresponding crossover mode will be invalid, need to be manually deleted, so setAppDetails generated by the shortcut is best when the program is uninstalled, to prevent a bad experience!
How to achieve the frameless window drag and drop movement
A window with a border has a toolbar and a title bar, and the title bar gives the window the ability to drag and drop with the mouse. But borderless Windows don’t have a title bar, so you can’t drag and drop them, and you need code to do that.
The first option **** will simulate the Dom node style for the title bar area -webkit-app-region:drag. After setting this property, the internal node of the node (which is itself wrapped) will not respond to click events. If you want the internal node to respond to click events, The internal node style is set to -webkit-app-region: no-drag.
<div style="-webkit-app-region: drag;">
<span> title bar </span> <button style=": no-drag;"> button < / button > </div>
Copy the code
But this way can not achieve the 360 security guard on the desktop convenient function ball, can not do drag and click to quickly switch.
The second way uses HTML’s native event implementation. The recommended combination is mousedomn + mousemove + mouseup. Drag and Drop is also possible, but it is not as easy to control.
index.html
<div id="header""> < div style =" text-align: center;
<script>
let headerDom = document.getElementById('header');
let isMoving = false. isClick = false. startX = 0, startY = 0; headerDom.onmousedown = (e) => { isMoving = false; isClick = true; startX = e.clientX; startY = e.clientY; }; document.onmousemove = (e) => { let distanceX = e.clientX - startX; let distanceY = e.clientY - startY; if (isClick) { // Move the window// Send the window location to the main process for processing ipcRenderer.send('windowMove', {distanceX, distanceY}); isMoving = true; } }; headerDom.onmouseup = () => { isClick = false; if(! isMoving) {// Just click// Handle the click event } else { isMoving = false; } }; </script> Copy the code
ipcMain.js
ipcMain.on('windowMove', (event, position) => {
let wx = Math.ceil(position.distanceX + window.getPosition()[0]);
let wy = Math.ceil(position.distanceY + window.getPosition()[1]);
if (wx > 0 && wy > 0) {
window.setPosition(wx, wy); } }) Copy the code
Play with the Chromium command line switch
The Chromium kernel in Electron supports the creation of Windows and content rendering, and the Chromium kernel has many default Settings for the rendering of web pages, such as the user’s authorization to call the camera in the content of web pages, and the automatic playback of audio and video.
The Electron website provides only a few command lines, whereas Chromium provides a lot of commands (see here).
So how do you know when you need to find these command-line arguments to aid development?
As long as the function of the window page does not conform to the browser Settings or standards, you can try to find the corresponding Chromium command switch to solve the actual problem.
// The touch screen will have the problem of expanding the click range. This can be solved app.commandLine.appendSwitch('disable-touch-adjustment'.true);
// Chrome 66 or later blocks autoplay Settings and allows you to play as you please app.commandLine.appendSwitch('autoplay-policy'.'no-user-gesture-required');
// Disable two-finger zoom on the touch screen app.commandLine.appendSwitch('disable-pinch'.true); // Disable the network proxy app.commandLine.appendSwitch('no-proxy-server'.true); // Disable certificate blocking for the browser app.commandLine.appendSwitch('ignore-certificate-errors'.true); Copy the code
How to play the node – ffi
Since Electron is a UI presentation focused framework, some of the more low-level features are not supported. This is where node-ffi comes in.
Node-ffi is a third-party NPM module that uses NodeJS to call a dynamically linked library.
So what is a dynamic link library? Dynamic link libraries (DLLS) are binary files of executable code that can be loaded into memory by an operating system, such as.dll files on Windows or.so files on Linux.
As mentioned above, the ultimate way to implement windowing is to let C++ implement it. Nodejs interaction with C++ functions can be implemented through dynamic link libraries. The most important medium, of course, is Node-FFi.
Installation node – ffi
$ npm install ffi
Copy the code
Since the source code of Nod-FFi is some C language files that need to be compiled synchronously by Nod-GYp during installation, nod-FYp needs to be correctly installed before nod-FFi is installed.
See node-Gyp installation tutorial here
Install the electron – rebuild
npm install --save-dev electron-rebuild
Copy the code
The reason why electron rebuild is installed is that the Nod-FFI module compiled by Nod-GYp may be different from the nodeJS version and ARCH in electron used in the project. The electron rebuild is required to recompile nod-FFi for use.
Install ref, ref-array, and ref-struct
The ref, ref-array, and ref-struct modules are wrapper for some parameter types in C++. You can use the export functions of ffi defined DLLS as parameter types.
Call the dynamic link library with Node-FFi
For details, see github
A simple call
#use-ffi.js
ffi.Library('D://code//myProgram/demo.dll', {
MyFunction: [ref.types.int, []]
})
Copy the code
Function arguments are callback functions
Remember that the callback function is a pointer type, so you need to fill in the pointer type when initializing the exported function argument
// Define the exported function let dllFunc = ffi.Library('D://code//myProgram/demo.dll', {
MyFunction: [ref.types.int, ['pointer']]
});
// Use ffi.Callback to initialize the Callback function const newCallback = (func) => { / * ** the first argument to ffi.Callback is the return type of the Callback function, the second is the type of the arguments in the Callback function, and the third is the Callback function* / return ffi.Callback(ref.types.void, [], func) } // Save the callback function to prevent memory reclamation let callbackStore = []; let callBack = newCallback(() => { console.log('Function callback')}); callbackStore.push(callBack); process.on('exit', () = > { delete callbackStore }); // Call the export function dllFunc.MyFunction(callBack); Copy the code
The pointer is passed as an argument
// Define the export function. Ref. refType converts the base type to a pointer type let dllFunc = ffi.Library('D://code//myProgram/demo.dll', {
MyFunction: [ref.types.int, [ref.refType(ref.types.int)]]
});
// Call the export function and open memory with buffer.alloc let myParams = new Buffer.alloc(100); dllFunc.MyFunction(myParams); Copy the code
Structure is passed in as a parameter
//ref-struct initializes the struct const myStruct = Struct({
id: ref.types.int
});
// Define the export function, ref.reftype to convert the structure type to the pointer structure type let dllFunc = ffi.Library('D://code//myProgram/demo.dll', { MyFunction: [ref.types.int, [ref.refType(myStruct)]] }); // Call the exported function, through.ref() to get the structure body stored first address, easy to copy functions in the DLL dll.MyFunction(myStruct.ref()); Copy the code
The array is passed in as a parameter
//ref-array defines an array const MyArray = RefArray(ref.types.int);
// Define the export function. You don't need to pass in the array definition, just the array member definition let dllFunc = ffi.Library('D://code//myProgram/demo.dll', {
MyFunction: [ref.types.int, [ref.refType(ref.types.int)]] }); // Initialize the array let myArray = new MyArray(10) for(let i = 0; i < 10; i++) { myArray[i] = 0; } // Call the export function, passing in the first member memory address dllFunc.MyFunction(myArray[0].ref()); Copy the code
Tip: Get the handle to the Electron window and convert it to an integer
// The deref method gets the value of the pointer let windowHandle = window.getNativeWindowHandle();
let _handle = Buffer.from(windowHandle);
_handle.type = ref.types.int;
dllFunc.MyFunction(handle.deref())
Copy the code
Play Electron packing
The Electron packaging here is divided into two parts, one part is code packaging, the other part is program packaging.
The significance of code packaging is:
- Reduce the size of the installer, and further to reduce the bandwidth usage of the application when it is downloaded and automatically updated.
- Improve source code security. Because the source code in Electron program is without any encryption measures, the source code is easy to be extracted, so the source code needs to be packaged, compressed and confused to reduce the readability of the code and improve the code security.
The code package
The code is packaged here using WebPack.
Webpack provides two packages for the Electron project: Target: ‘electron – the renderer’ | ‘electron – main’, rendering process packaging and main process, packaging, respectively, are automatically ignore incoming nodejs module and electron module.
The main process code packaging is not that simple, as the main process will use nodeJS native modules such as the nodes-ffi and ref modules mentioned above, need to be compiled to produce. Node files. Node-loader is used to process. Node files.
//webpack.config.js
module.exports = {
entry: './main.js'. mode: 'production'. target: 'electron-main'.// Prevent __dirname from changing after packaging node: { __dirname: false. }, output: { path: path.resolve(__dirname, 'dist'), filename: 'main.js'. }, module: { rules: [ { test: /\.node$/, use: [ { loader: 'node-loader'. options: { name: '/[path][name].[ext]'. }, }, ]. }, ]. }, } Copy the code
No error is reported after executing the package. It seems to be successful, but an error is reported after running the package code.
It is also found that there is no exported. Node file in the dist folder.
It is found that the introduction of ffi_bindings. Node in FFI is not a conventional way, and the bindings module obtains the. Node file through convenient address.
//node_modules/ffi/lib/bindings.js
module.exports = require('bindings') ('ffi_bindings.node')
//node_modules/bindings/bindings.js
. try: [ // node-gyp's linked version in the "build" dir ['module_root', 'build', 'bindings'] // node-waf and gyp_addon (a.k.a node-gyp) And ['module_root', 'build', 'Debug', 'bindings']And ['module_root', 'build', 'Release', 'bindings'] // Debug files, for development (legacy behavior, remove for node v0.9) And ['module_root', 'out', 'Debug', 'bindings']And ['module_root', 'Debug', 'bindings']// Manually compiled (Legacy behavior, remove for node v0.9)And ['module_root', 'out', 'Release', 'bindings']And ['module_root', 'Release', 'bindings']Legacy from nod-waf, node <= 0.4.xAnd ['module_root', 'build', 'default', 'bindings'] // Production "Release" buildtype binary (meh...) And ['module_root', 'compiled', 'version', 'platform', 'arch', 'bindings'] ] Copy the code
Because the Nod-Loader module only handles require/import files, it can’t handle imported files like FFI, so the error is reported.
The solution is to rewrite the Bindings module. Webpack has a configuration called resolve.alias that sets the alias of the referenced module and can change the application address of the referenced module.
//webpack.config.js
. resolve: {
alias: {
bindings: path.resolve(__dirname, 'replaceBindings.js')
} } //replaceBindings.js modules.exports = function(filename){ if(filename === "ffi_bindings.node") { return require('ffi/build/Release/ffi_bindings.node') } } Copy the code
Repackage, the program runs without exception, the problem is solved!
Application package
Electron program packaging is the further packaging of code into a program that can be distributed, downloaded, installed, and quickly started to run.
The more commonly used Electron program packing modules are the Electron – Packager and the Electron – Builder. It is recommended to use the electron- Builder here, because compared with the electron- Packager, the electron- Builder has more perfect functions and clearer documents.
Electron Builder packing process:
To pack an instance using the electron Builder API form:
const electronBuilder = require('electron-builder');
const child_process = require('child_process');
const path = require('path');
// Get git commit number as version numberchild_process.execSync('rm -rf dist'); const version = child_process.execSync('git rev-list HEAD | wc -l').toString().trim(); const baseVersion = require('./package.json').version; const buildVersion = baseVersion + '. ' + version; const semverVersion = baseVersion + The '-' + version; electronBuilder.argv = 'x64'; const targetDist = path.resolve(__dirname, 'dist'); const baseOptions = { // Apply the unique identifier appId: 'shaokaotan.demo.demo'. buildVersion: buildVersion, // Replace the corresponding information in the root directory package.json extraMetadata: { // Application product version number, used for productVerion: buildVersion, author: { name: 'Shaokaotan'. email: 'www.Shaokaotan.com'. url: 'www.Shaokaotan.com'. }, version: semverVersion, }, productName: 'MyProgram'. copyright: 'Copyright (C) 2020. Shaokaotan Electronics. All Rights Reserved.'. directories: { buildResources: path.resolve(__dirname, '. '), output: targetDist, }, nsis: { oneClick: false. perMachine: true. allowElevation: false. allowToChangeInstallationDirectory: true. installerIcon: path.resolve(__dirname, './icons/favicon.ico'), uninstallerIcon: path.resolve(__dirname, './icons/favicon.ico'), installerHeaderIcon: path.resolve(__dirname, './icons/favicon.ico'), createDesktopShortcut: true. createStartMenuShortcut: true. shortcutName: 'MyProgram'. artifactName: 'MyProgram' + buildVersion + '.${ext}'. uninstallDisplayName: 'MyProgram'. include: path.resolve(__dirname, 'install.nsh'), }, win: { icon: path.resolve(__dirname, './icons/favicon.ico'), target: { target: 'nsis'. arch: 'x64'. }, // List of files to be packaged files: ['! build.js']. extraResources: [path.resolve(__dirname, 'icons/favicon.ico')]. publish: { provider: 'generic'. channel: 'winLatest_' + buildVersion, url: 'www.Shaokaotan.com'. }, // Copy the file extraFiles: [ { from: path.resolve(__dirname, 'icons/favicon.ico'), to: './resources/icons/favicon.ico'. }, ]. }, }; electronBuilder.createTargets(['--win'], null, 'x64'); process.env.BUILD_NUMBER = version; electronBuilder.build({ config: baseOptions, }); Copy the code
The correspondence of the above information in practical application
BuildVersion is compared with semverVersion
- BuildVersion is the normal Windows version format: *.*.*.*.*.*
- SemverVersion is the semantic version number: *.*.*[-*], which is usually used in Internet applications, such as the version field of package.json in js modules
So you can see that the Version field in the extraMetadata configuration uses semverVersion instead of buildVersion.
The application should also be displayed correctly in the Windows Control Panel programs and Functions after installation (unless you test without care).
There is no configuration item for this information in the electron Builder configuration. After some searching, this information comes from the system registry, so it needs to be stored in the specified location in the registry. The address at which the registration mark stores this information is
Computer \ HKEY_LOCAL_MACHINE \ SOFTWARE \ WOW6432Node \ Microsoft \ Windows \ CurrentVersion \ UninstallCopy the code
How to set? NSIS is involved here, and you can include a custom NSIS file in the configuration above:
! include"FileFunc.nsh"
# Get the version number
! getdllversion"${BUILD_RESOURCES_DIR}\dist\win-unpacked\MyProgram.exe" expv_
! macro customInstall # write icon address WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_APP_KEY}" "DisplayIcon" "$INSTDIR\resources\icons\favicon.ico" # write support link address WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_APP_KEY}" "URLInfoAbout" "www.Shaokaotan.com" # Write version information WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL_APP_KEY}" "DisplayVersion" "${expv_1}.${expv_2}.${expv_3}.${expv_4}" ! macroend ! macro customUnInstall ! macroendCopy the code
Play with automatic updates
See the electron- Builder Auto Update tutorial.
Well! After reading it, I still don’t understand. Here’s the breakdown:
Let’s start with a crude flow chart:
To complete this process, it seems that the workload of writing by oneself is not large, but many boundary problems need to be taken into account. Therefore, the Electron – Builder provides an independent module, electron- Updater, to complete this process.
Install the electron – updater
npm install --save electron-updater
Copy the code
The client invokes automatic updates
The code presented
const log = require('electron-log');
const { autoUpdater } = require('electron-updater');
const { updateWindow } = require('./window');
Const {app, ipcMain} = require('electron');
// Set log printingautoUpdater.logger = log; autoUpdater.logger.transports.file.level = 'info'; // Whether to download updates automatically, set tofalseWill trigger the update download through the APIautoUpdater.autoDownload = false; // Whether to allow version degradation. That is, if the server version is earlier than the local version, the server version is still the main versionautoUpdater.allowDowngrade = true; // Set the latest version of the serverautoUpdater.setFeedURL({ provider: 'generic'. url: 'https://www.shaokaotan.com/autoUpdate/'. channel: 'myProgram'.}); // Save the status of whether to install the update, because you need to provide the user with the action of updating immediately after the update is downloaded and updating laterlet NEED_INSTALL = false; // Call the API to check whether updates are usedconst checkUpdate = () => { autoUpdater.checkForUpdatesAndNotify().then((UpdateCheckResult) => { log.info(UpdateCheckResult, autoUpdater.currentVersion.version); // Determine that the version is inconsistent and force the update if(UpdateCheckResult && UpdateCheckResult.updateInfo.version ! == autoUpdater.currentVersion.version) {// Open the update window updateWindow(); } }); }; // API triggers update downloadconst startDownload = (callback, downloadSuccessCallback) => { // Monitor the download progress and push to the update window autoUpdater.on('download-progress', (data) => { callback && callback instanceof Function && callback(null, data); }); // Listen for download errors and push to the update window autoUpdater.on('error', (err) => { callback && callback instanceof Function && callback(err); }); // Listen for the download to complete and push to the update window autoUpdater.on('update-downloaded', () = > { NEED_INSTALL = true; downloadSuccessCallback && downloadSuccessCallback instanceof Function && downloadSuccessCallback(); }); // Download the update autoUpdater.downloadUpdate(); }; // Listen for process messages sent by the window and start downloading updatesipcMain.on('startDownload', (event) => { startDownload( (err, progressInfo) => { if (err) { // Push back the download error message event.sender.send('update-error'); } else { // Push back the download progress message event.sender.send('update-progress', progressInfo.percent); } }, () = > {// Push back the download completion message event.sender.send('update-downed'); } ); }); // Listen to the user click the update window immediately update the button process messageipcMain('quitAndInstall', () = > { NEED_INSTALL = false; autoUpdater.quitAndInstall(true.true); }) // Install the update immediately when the user clicks install later after the program exitsapp.on('will-quit', () = > { if (NEED_INSTALL) { autoUpdater.quitAndInstall(true.true); } }); Copy the code
In order for the above process to execute correctly, it is necessary to provide a correct service and return a correct updated configuration:
Set the service address in the code
autoUpdater.setFeedURL({
provider: 'generic', // Customize the server options, the other options are not custom url: 'https://www.shaokaotan.com/autoUpdate/'. channel: 'myProgram'.});
Copy the code
The appearance Settings electron – updater will request of https://www.shaokaotan.com/autoUpdate/myProgram.yml, a web service address, this address needs to return a file configuration information updates, The file is [channel].yml in the electron builder package. The channel is configured in win.publish in the electron Builder package configuration. For example, my packaged configuration yML file looks like this:
/ / winLatest_1. 0.0.111. YmlVersion: 1.0.0-111files:
- the url: MyProgram1.0.0.111. Exe sha512: nRDCB1qk7GZ8DxUpH4wgHQ2zjr2n3kSrHhyJSXeOgx6akfYGEBp9/8qz8OLoos5q9+wKNR1LH0oelj70isN2tA==
size: 44897270 Path: MyProgram1.0.0.111. Exesha512: nRDCB1qk7GZ8DxUpH4wgHQ2zjr2n3kSrHhyJSXeOgx6akfYGEBp9/8qz8OLoos5q9+wKNR1LH0oelj70isN2tA== releaseDate: 'the 2020-07-27 T07:28:08. 168 z' Copy the code
We should pay attention to the following three points
- The electron updater validates the format of the version in the configuration, which needs to conform to the Semver Version specification (described later in the packaging), but most Windows applications have 4-bit version numbers, so using a form like *.*.*-* will pass the inspection
- The electron updater checks the sha512 value in the configuration and updates the sha512 value in the downloaded file. A mismatch will throw an update error, so you cannot modify the installer (as long as the md5 value of the file remains unchanged).
- The path value in the configuration is the destination address for the electron updater to download the update, which is the file address of the installation package of the new version of the program.
Of course, it is not possible to return the yML files from the Electron Builder directly to the Electron Updater. The above installation package address obviously needs to be reconfigured. You can manually create a compliant YML file after generating a version, or do as I did:
//https://www.shaokaotan.com/autoUpdate/myProgram.yml
// This is a normal restful interface, not a file addressrouter.get('/autoUpdate/myProgram.yml', (req, res)=> {
// Upload the yML file packaged by the electron- Builder to the server to save as JSON configuration, during which you can modify the value let ymlConfig = config.updaterInfo;
// Set the response header to a file attachment res.attachment('myProgram.yml'); // Return the information in JSON format. Json and YAML formats can be converted to each other res.json(hugoAppConfig.updateVersionInfo); }); Copy the code
Automatic update is no problem!
This paper summarizes
This article is just a summary of the pit I stepped on, in fact, electron has a lot of places for us to study, such as plug-in, engineering, performance optimization and so on. Stay tuned to the front end barbecue booth in the back for more information on electron.
The last
Focus on “stroll big front”, the first time to get quality articles.
This article was typeset using MDNICE