一.认识
定位
Electron is a framework for creating native applications with web technologies like JavaScript, HTML, and CSS.
实现
代码语言:javascript复制Electron = Node Chromium V8
这些只是Electron的依赖项,跨平台最关键的自然是适配层,由供node调用的C 模块来完成平台适配,提供系统级的平台接口
适用场景
桌面环境可能很难抹平差异,Electron的大多数API都是分平台的,例如quick start里的:
代码语言:javascript复制// 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,算是基本的试玩环境:
代码语言:javascript复制# 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镜像下载:
代码语言:javascript复制# 删除还没下载完成的
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镜像:
代码语言:javascript复制ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/ yarn
三.常见问题
1.让窗口紧贴托盘图标正下方
有现成模块,相当好用:
- menubar
先获取托盘图标的位置,再根据窗口大小计算居中
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
或者16px
的png
格式图标
P.S.gif格式图片不可以用做图标
3.Mac系统通知中文乱码
HTML需要通过meta
设置charset
:
<meta charset="utf-8">
否则HTML里引入的外部JS资源里的字面量中文串,会出现乱码
4.持久化存储
建议使用electron-store
用JSON文件来存,放在应用安装目录里,API不太科学:
代码语言:javascript复制// 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
代码语言:javascript复制npm install --save auto-launch
P.S.Mac下确实添了一条启动项,但没有勾选,可能需要制定应用路径,待深入了解
写在最后
实际上,对系统级API的依赖比想象的少太多了,学习成本大多来自前端生态(React全家桶),完全合心意的组件库是不存在的
东西呢,还不成样子,下周继续