做者:凹凸曼-littly前端
Taro IDE 是一款咱们正在精心打造的一站式移动端研发工做台。除了须要实现 Taro 从建立项目到预览、编译的所有能力,还须要打通用户测试、调试、监控等一系列流程。为了提高开发体验,仅仅一个命令行工具是远远不够的,咱们须要开发一款桌面客户端,并同时提供 Windows、MacOS 等不一样系统的版本。node
Electron 最初是 Github 为 Atom 编辑器开发的桌面应用框架。Electron 将 Chromium 与 Node 合并到同个运行时环境中,赋予了 Web 代码与底层操做系统进行交互的能力,并在打包时生成 Windows、MacOS、Linux 等平台的桌面应用。比起原生的桌面应用开发框架,Electron 在性能、应用体积方面会稍逊一筹,但 Electron 支持打包多个平台的桌面应用,在业界已经有 VSCode、Atom、Slack 等综合体验拔群的成功案例,咱们认为 Electron 彻底知足咱们的需求。react
若是只想体验一下 Electron,最快的方式是使用 Electron Fiddle,或者直接使用社区中提供的 脚手架。webpack
最初接触 Electron,通常是被“使用前端技术栈生成多平台桌面应用”的特性吸引。但在后续的开发中,才会留意到 Electron 相比 NW.js 更为复杂的进程模型:git
Electron 的架构能够用下图来表示:github
Electron 项目中,运行 package.json 的 main 脚本的进程被称为主进程。主进程经过建立 web 页面来展现用户界面。这些用户界面都运行在彼此隔离的渲染进程中。web
Electron 主进程支持 Node API,而且可直接与操做系统进行底层交互,弹出系统通知、文件系统读写、调用硬件设备等。typescript
Electron 渲染进程默认只能与自身的 Web 内容进行交互。在打开 nodeIntegration
功能后,渲染进程也能够具有操做 Node 的能力。渲染进程也没法直接操做弹窗(Dialog)、系统通知(Notification)等,这些功能都须要经过 Electron 提供的 IPC/remote 机制在主进程中调用。json
而且在后续 Electron 的升级中,这些约束也可能由于安全、性能的缘由进行调整。能够说,Electron 的开发体验并不太美好,但正是这种开发体验与用户体验之间的博弈,保证了 Electron 应用在性能、安全方面的表现。浏览器
咱们使用社区提供的 electron-react-typescript 做为项目的初始脚手架。阅读 package.json 文件,咱们能够了解到,这个项目使用 webpack 进行主进程和渲染进程的打包,src/main/main.ts 文件就是主进程的入口。
Electron 的 BrowserWindow 类负责建立和控制浏览器窗口,app 对象则能够控制应用程序的各个事件与生命周期。 主进程的代码大体以下:
import { app, BrowserWindow } from 'electron' let win app.on('ready', () => { win = new BrowserWindow({ width: 800, height: 600 }); win.loadURL(`http://localhost:2003`); // xxx }); app.on('activate', () => {}) app.on('window-all-closed', () => {})
渲染进程 src/renderer/app.tsx 就一个普通的页面,这里再也不赘述。安装依赖后,使用 yarn start-dev
,便可启动项目的预览服务。
这个项目使用 webpack 来打包项目代码,这样处理有两个好处。一是经过 webpack 处理,咱们能够减小运行时的 require 调用,对 Electron 应用加载性能有必定帮助;二是借助 webpack 的 tree shaking 能力,未使用的代码也会被轻松移除,能够有效减小安装包体积。
为了打包 electron 项目,咱们须要至少两份 webpack 配置文件,一份打包主进程文件,指定 target 为 electron-main
,另外一份打包渲染进程,target 设置为 electron-renderer
。
为了辅助 Electron 项目的调试工做,咱们能够安装 Devtron。Devtron 是 Electron 提供的开发调试插件。在开发者工具中加入 Devtron 后,项目中的 IPC 通讯、查看项目依赖、事件等信息,均可以在开发者工具中直接查看。
若有须要,咱们还能够安装其余的开发者工具扩展,例如 Redux、React 等,只须要在主进程中运行:
// main.js const { default: installExtension, REACT_DEVELOPER_TOOLS, REACT_PERF, REDUX_DEVTOOLS } = require('electron-devtools-installer') const extensions = [REACT_DEVELOPER_TOOLS, REDUX_DEVTOOLS, REACT_PERF] extensions.forEach(extension => { try { installExtension(extension) } catch (e) { console.error(e) } })
至此,咱们的开发环境搭建完毕,能够开始进行业务代码的开发了。
业务代码开发完毕后,就到了优化的环节了。这里主要从 Electron 应用的性能与体积两方面来说。
Electron 在性能方面一直受到广大开发者的诟病。窗口打开慢,加载时间长都是老生畅谈的话题。这些问题该如何解决呢?
答案是预加载。在展现登陆窗口时,咱们能够提早将主窗口开启并设置隐藏,预加载主窗口的静态资源。用户登陆后,再经过 IPC 消息通知主窗口展现,达到秒开的效果。这个过程能够用下图表示:
除了窗口加载,在 Electron 中,require Node 模块也是至关昂贵的操做。若是在渲染进程中直接使用大量的原生模块,会严重拖慢页面的打开时间,形成窗口可交互时间的延后,这对于桌面应用来讲是灾难性的体验。Electron@5 以后的版本已经默认关闭了 BrowserWindow 的 nodeIntegration
功能,能够看出 Electron 团队也并不建议在渲染进程中直接使用原生模块。
在桌面应用中,等待是很是难以忍受的,性能上的些许欠缺都会让用户以为这是个套壳的网页。如需使用原生模块,咱们更建议使用异步的方式加载模块,或是使用异步 IPC 在主进程中调用。另外,为了优化用户的体验,咱们还须要在小动画等方面下功夫,例如骨架屏等等。
Atom 团队经过使用 V8 snapshot 能力,在生产环境中去掉了低性能的 require 调用,将 Electron 应用的加载性能提高了 30%,同时还提高了应用的安全性能,这篇文章 How Atom Uses Chromium Snapshots 对他们的作法作了详细介绍。
启用骨架屏先后对比:
性能优化的方式并不局限于上面的方式。例如开启 electron-builder 的 asar 功能,在打包时将源码生成二进制的 asar 文件,下降 require 操做的代价的同时,也能稍许减小空间占用,代价是没法对 asar 内的文件使用 child_process.spawn
;须要密集计算的功能,能够开多一个渲染进程来跑,或是使用 require('child_process').spawn
开子进程来跑,避免阻塞主进程,形成应用卡死。
一样受到开发者诟病的,还有 Electron 应用的体积 。一个空 Electron 项目,在打包后就会占据近上百兆空间。Electron 的应用体积之因此大,除了自带的 Chromium 内核,还有大部分体积是来自用户安装的 node_modules。
使用 electron-builder 打包 Electron 应用时,若是不加处理,会将 node_modules 内的内容全数打包,致使应用体积偏大。针对这种状况,咱们能够进行一系列优化:
使用 yarn autoclean
命令进行清理。node_modules 目录中,包含着大量的 README 文件、文档等内容,这部分文件在生产环境中并不是必要。若是项目中使用 yarn
进行依赖管理,则可使用 yarn autoclean
命令。这个命令会初始化一份默认的配置文件 .yarnclean
。yarn
在安装依赖后,将会自动根据 .yarnclean
进行依赖清理。
# 默认的 .yarnclean 文件大体以下: # test directories __tests__ test tests powered-test # asset directories docs doc website assets # examples example examples ...
在这个改动后,安装依赖时还须要经过 electron-builder install-app-deps
命令安装应用依赖。这个操做推荐放在 package.json
内的 post-install
脚本中。
electron-builder@8 后,并不会打包 devDependencies
内的依赖。这意味着咱们能够经过这个途径来避免开发依赖被打包的问题。若是项目使用了 webpack 之类的工具进行打包,则须要注意将 webpack 已经打包过的资源从 dependencies 中排除,避免重复打包。
目前,项目的大部分能力依然是基于 Electron 提供的能力实现的。这至关于与 Electron 严重耦合,不利于项目中个别能力的复用。将来,咱们但愿对项目的架构进行调整,对核心能力进行插件化改造,方便能力的移植与复用,甚至将来的研发上云,这有赖于项目核心能力的 Web 化。固然,Web 化也会带来额外的性能损耗,这会对咱们项目的性能提出新的要求。
项目的稳定性也是将来须要努力的方向。咱们有时会收到用户关于应用闪退、卡死等现象的反馈,却苦于没法复现,不少时候难以解决用户反馈的问题。将来,咱们须要在项目中加入异常监控上报的机制,收集操做系统信息、内存使用量等关键信息,在 Crash 时进行上报,甚至推送告警消息。这有利于开发人员进一步了解用户的使用过程,方便问题的复现。
在开发桌面应用时,Electron 在效率上有很大的优点。几行 JS 代码就能够启动桌面客户端,大大下降了开发门槛。但 Electron 在性能、体积等方面也存在着软肋。若是在前期开发时没有通过充分思考,颇有可能会在后期优化时付出惨痛的代价。在这个项目中,咱们的优化工做还远远不够,后续有更多突破会分享给你们。
[1]Electron: https://www.electronjs.org/
[2]Electron Fiddle: https://www.electronjs.org/fi...
[3]脚手架: https://github.com/search?q=e...
[4]NW.js: https://nwjs.io/
[5]electron-react-typescript: https://github.com/Robinfr/el...
[6]src/main/main.ts: https://github.com/Robinfr/el...
[7]src/renderer/app.tsx: https://github.com/Robinfr/el...
[8]Devtron: https://www.electronjs.org/de...
[9]How Atom Uses Chromium Snapshots: https://flight-manual.atom.io...
欢迎关注凹凸实验室博客:aotu.io
或者关注凹凸实验室公众号(AOTULabs),不定时推送文章: