I. Understanding
Positioning
Electron is a framework for creating native applications with web technologies like JavaScript, HTML, and CSS.
Implementation
Electron = Node + Chromium + V8
These are just Electron's dependencies. The most critical part for cross-platform is naturally the adaptation layer, completed by C++ modules callable by node, providing system-level platform interfaces
Applicable Scenarios
It may be difficult to smooth out differences in desktop environments. Most of Electron's APIs are platform-specific, for example, in quick start:
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
If you want to use Electron to achieve perfect cross-platform consistent experience, it still takes considerable effort, and even some aspects are impossible to make consistent (may need to make some C++ modules yourself to complete adaptation). From the API experience perspective, it's more laborious than ionic mobile cross-platform, with too many platform differences
But if you just want to "use JS to write a Mac/Win tool, maybe it can even be cross-platform", that is, without considering platform differences, Electron is still quite good
II. Development Environment
quick start
Official provides quick-start, considered a basic trial environment:
# Clone this repository
git clone https://github.com/electron/electron-quick-start
# Go into the repository
cd electron-quick-start
# Install dependencies
npm install
# Run the app
npm start
May encounter ETIMEDOUT during npm install, because electron package is very large (120M):
node install.js
.../node_modules/electron/install.js:48
throw err
^
Error: connect ETIMEDOUT 52.216.66.16:443
at Object.exports._errnoException (util.js:1034:11)
at exports._exceptionWithHostPort (util.js:1057:20)
at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1096:14)
Recommend switching to taobao mirror for download:
# Delete the incomplete download
rm -rf node_modules/electron
# Specify taobao mirror
ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/ npm install electron
If everything is normal, a hello world window should appear. Check the API documentation, experience some system native APIs, such as system tray, desktop notifications, etc., trial ends
If planning to start working on it, strongly recommend not starting from quick start, because it's missing many things:
-
Modularization solution
-
Build solution (development-package-release)
-
Component library (UI library)
-
Route management
-
Persistence solution
Then may need react, webpack, antd, react-router, xxx-storage and many other things. Doing it manually, just the webpack build solution takes half a day, so we need more powerful template projects
boilerplate
For react family bucket, here recommend two project templates:
-
electron-react-boilerplate: yarn manages dependencies, webpack builds
React Redux React Router Webpack React Transform HMR -
electron-react-redux-boilerplate: npm manages dependencies, npm scripts builds
React Router Redux Thunk Redux Actions Redux Local Storage Electron Packager Electron DevTools Installer Electron Mocha Browsersync
webpack configuration build has better scalability, but actual use found electron-react-boilerplate build configuration is quite complex, couldn't run locally, gave up after trying to resolve without success, switched to electron-react-redux-boilerplate
Both templates do not provide UI component library, after introducing antd found npm scripts build script is difficult to solve the problem of automatically importing css (webpack can add loader to solve), temporarily use cp to hold on, consider switching to webpack later:
"private:style": "cp -f node_modules/antd/dist/antd.css build/antd.css; cp -rf app/css build/css"
P.S. When installing dependencies, will also encounter electron download timeout problem, similarly, environment variable points to taobao mirror:
ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/ yarn
III. Common Problems
1. Make window stick directly below tray icon
There's an existing module, quite easy to use:
First get the position of tray icon, then calculate centering based on window size
2. System tray icon size
For OS X, create icons:
icon.png (for best results aim 16x16px)
icon @2x.png (32x32px)
For Windows, create single icon:
icon.ico
ICO format will work better than classic PNG. If you want PNG, make it 32x32px.
From: Proper tray icon
Mac top menu bar height is 22px, can use 22px or 16px png format icon
P.S. gif format images cannot be used as icons
3. Mac system notification Chinese garbled text
HTML needs to set charset through meta:
<meta charset="utf-8">
Otherwise literal Chinese strings in external JS resources imported in HTML will appear garbled
4. Persistent storage
Recommend using electron-store
Store with JSON file, placed in application installation directory, API is not very scientific:
// set() can only store basic values
store.set('unicorn', 'ma');
console.log(store.get('unicorn'));
//=> 'ma'
// Storing object paths is inconvenient
store.set('foo.bar', true);
console.log(store.get('foo'));
//=> {bar: true}
// store can only replace all data entirely
store.store = data
Doesn't affect use much, read/write are synchronous, suitable for storing simple user configurations. For large amounts of data, there may be performance issues
P.S. For more Electron data storage methods please check [How to store user data in Electron](https://medium.com/ @ccnokes/how-to-store-user-data-in-electron-3ba6bf66bc1e)
5. IPC
There are two processes in Electron, Main and Renderer, the former is responsible for managing everything and interacting with the platform, the latter provides browser environment, rendering pages
Inter-process communication support is relatively good, with synchronous and asynchronous两种方式, communicating through event messages
Asynchronous communication (event.sender.send()):
// In main process.
const {ipcMain} = require('electron')
ipcMain.on('asynchronous-message', (event, arg) => {
console.log(arg) // prints "ping"
event.sender.send('asynchronous-reply', 'pong')
})
// In renderer process (web page).
const {ipcRenderer} = require('electron')
ipcRenderer.on('asynchronous-reply', (event, arg) => {
console.log(arg) // prints "pong"
})
ipcRenderer.send('asynchronous-message', 'ping')
Synchronous communication (event.returnValue):
// In main process.
const {ipcMain} = require('electron')
ipcMain.on('synchronous-message', (event, arg) => {
console.log(arg) // prints "ping"
event.returnValue = 'pong'
})
// In renderer process (web page).
const {ipcRenderer} = require('electron')
console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong"
P.S. If parameter sent from renderer is undefined, directly passing to system interface, may encounter error:
ERROR:v8_value_converter.cc(374)] Unexpected v8 value type encountered.
Parameters passed to system interface should be strictly validated to avoid such problems
6. Auto-start on boot
There's an existing module: node-auto-launch
npm install --save auto-launch
P.S. Mac indeed adds a startup item, but not checked, may need to specify application path, wait for further understanding
Written at the end
Actually, dependency on system-level APIs is far less than imagined. Most learning cost comes from frontend ecosystem (React family bucket), completely satisfactory component library doesn't exist
Things are not yet in shape, continue next week
No comments yet. Be the first to share your thoughts.