electron入门指南

一.认识

定位

Electron is a framework for creating native applications with web technologies like JavaScript, HTML, and CSS.

实现

Electron = Node + Chromium + V8

这些只是Electron的依赖项,跨平台最关键的自然是适配层,由供node调用的C++模块来完成平台适配,提供系统级的平台接口

适用场景

桌面环境可能很难抹平差异,Electron的大多数API都是分平台的,例如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()
  }
})

如果想用Electron实现完美的跨平台一致体验,还要费不少功夫,甚至某些方面不可能做到一致(可能需要自己做一些C++模块来完成适配),就API体验来看,比ionic移动端跨平台更费劲,平台差异太多

但如果只是想“用JS写个Mac/Win工具,说不定还能跨平台”的话,即不考虑平台差异的话,Electron还算不错

二.开发环境

quick start

官方提供了quick-start,算是基本的试玩环境:

# 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

npm install可能遇到ETIMEDOUT,因为electron包非常大(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)

建议换用taobao镜像下载:

# 删除还没下载完成的
rm -rf node_modules/electron
# 指定taobao镜像
ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/ npm install electron

一切正常的话,能跑出来一个hello world窗口,看看API文档,体验一些系统原生API,比如系统托盘,桌面通知等等,试玩结束

如果打算开始搞的话,强烈不建议从quick start开始,因为还缺很多东西:

  • 模块化方案

  • 构建方案(开发-打包-发布)

  • 组件库(UI库)

  • 路由管理

  • 持久化方案

那么可能还需要react、webpack、antd、react-router、xxx-storage等等一大堆东西,手动去做的话,只webpack构建方案就得小半天,所以,我们需要更强大的模版项目

boilerplate

对于react全家桶,这里推荐两份项目模版:

  • electron-react-boilerplate:yarn管理依赖,webpack构建

    React
    Redux
    React Router
    Webpack
    React Transform HMR
    
  • electron-react-redux-boilerplate:npm管理依赖,npm scripts构建

    React Router
    Redux Thunk
    Redux Actions
    Redux Local Storage
    Electron Packager
    Electron DevTools Installer
    Electron Mocha
    Browsersync
    

webpack配置构建可扩展性更好一些,但实际使用发现electron-react-boilerplate构建配置相当复杂,本地没能跑起来,尝试解决无果后放弃了,改用electron-react-redux-boilerplate

两个模版都没有提供UI组件库,引入antd后发现npm scripts构建脚本很难解决自动引入css的问题(webpack可以添loader解决),暂时先拿cp顶着,后续考虑切换到webpack:

"private:style": "cp -f node_modules/antd/dist/antd.css build/antd.css; cp -rf app/css build/css"

P.S.在安装依赖时,也会遇到electron下载超时的问题,同样,环境变量指向taobao镜像:

ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/ yarn

三.常见问题

1.让窗口紧贴托盘图标正下方

有现成模块,相当好用:

先获取托盘图标的位置,再根据窗口大小计算居中

2.系统托盘图标尺寸

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.

摘自:Proper tray icon

Mac顶部菜单栏高度是22px,可以用22px或者16pxpng格式图标

P.S.gif格式图片不可以用做图标

3.Mac系统通知中文乱码

HTML需要通过meta设置charset

<meta charset="utf-8">

否则HTML里引入的外部JS资源里的字面量中文串,会出现乱码

4.持久化存储

建议使用electron-store

用JSON文件来存,放在应用安装目录里,API不太科学:

// set()只能存基本值
store.set('unicorn', 'ma');
console.log(store.get('unicorn'));
//=> 'ma'

// 存对象路径不方便
store.set('foo.bar', true);
console.log(store.get('foo'));
//=> {bar: true}

// store只能整个替换所有数据
store.store = data

不很影响使用,读写都是同步的,存简单的用户配置比较合适。大量数据的话,可能存在性能问题

P.S.更多Electron数据存储方式请查看How to store user data in Electron

5.IPC

Electron里有两个进程,Main和Renderer,前者负责管理一切,并与平台交互,后者提供浏览器环境,渲染页面

进程间通信支持程度比较好,有同步和异步两种方式,通过事件消息来通信

异步通信(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')

同步通信(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.如果renderer发来的参数是undefined,直接传入系统接口,可能会遇到错误:

ERROR:v8_value_converter.cc(374)] Unexpected v8 value type encountered.

传递给系统接口的参数,应该严格校验,避免此类问题

6.开机自启

有现成模块:node-auto-launch

npm install --save auto-launch

P.S.Mac下确实添了一条启动项,但没有勾选,可能需要制定应用路径,待深入了解

写在最后

实际上,对系统级API的依赖比想象的少太多了,学习成本大多来自前端生态(React全家桶),完全合心意的组件库是不存在的

东西呢,还不成样子,下周继续

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

code