近期准备开始做electron相关的开发工作,因此借着这个机会就再去了解下electron。在很久以前的文章中有稍微玩过electron+react+antd的脚手架,但也只限于快速开发electron应用,并没有去剖析整个项目结构。因此这次,还是得深入一下。
先前一段时间特别喜欢用开源的Motrix下载器,就是基于electron+vue+aria2去实现的,所以索性就把源码给clone了下来。本文就从最基础的开始,以Motrix的启动逻辑为入口,来研究下一个electron应用是如何打开的。
首先看一下Motrix的目录结构,源码基本在src下,呈现这样的层级关系:
main:主进程,应用内部逻辑configs:内部环境配置core:软件核心管理逻辑menus:不同os下的菜单配置pages:基础页面ui:各ui相关的Manager逻辑utils:工具方法库Application.js:应用入口Launcher.js:启动器入口index.js/index.dev.js:程序入口index.dev.js相对于index.js只是另外安装了devtools
renderer:渲染进程,vue页面逻辑,目录结构也是vue默认的,可以参考这篇文章api:外部接口assets:资源文件components:组件页面pages:应用页面入口,App.vue+main.jsrouter:路由store:应用内部数据utils:工具方法库workers:只有一个tray.worker.js用来绘制托盘icon
shared:公用逻辑/工具aria2:下载工具jsliblocales:本地化utils:公用js工具方法库
从MVC的角度,main主进程的逻辑相当于是model,renderer渲染进程的逻辑相当于是view,而至于controller,可以通过electron支持下的两个进程的ipc事件处理机制来呈现。这一点,我们直接看启动逻辑就能明白。
运行yarn run dev,会启动.election-vue/dev-runner.js,其中会先初始化renderer和main,然后再启动electron
1 | // .election-vue/dev-runner.js |
在startRenderer和startMain中会读取js配置的程序入口,编译后运行。两个进程的入口entry分别是:
- 渲染进程:
src/pages/index/main.js - 主进程:
src/main/index.dev.js
首先看渲染进程,运行的入口在这里:
1 | store.dispatch('preference/fetchPreference') |
首先会通过preference/fetchPreference这个action来获得应用配置,然后调用init函数启动界面。先看获取配置的逻辑:
1 | // src/renderer/store/modules/preferences.js |
可以看到最终获取配置的逻辑落到ipcRenderer.invoke('get-app-config')。ipcRenderer相当于是渲染进程里进程间(与Main主进程)通信的handle,这里相当于是向主进程invoke了一个get-app-config事件。在主进程端的ipcMain可以注册这个事件的监听,然后返回对应的数据。ipcRenderer和ipcMain的通信,可以查看这两个文档:
- ipcRenderer模块
- ipcMain模块
到这里就暂停,看下主进程的启动,主进程index.js会启用一个Launcher来开始主进程逻辑
1 | // src/main/index.js |
主进程启动逻辑最终落到这handleAppEvents里面四个handler,分别是如下作用:
handleAppReady:监听ready事件,初始化Application实例(global.application)并为其注册监听事件;监听activate事件,打开index页面handleOpenUrl:监听open-url事件,发送url给ApplicationhandleOpenFile:监听open-file事件,发送file给ApplicationhandleAppWillQuit:监听will-quit事件,停止Application
election-app的一系列事件,可以在这个网站查阅具体作用
接下来看下Application实例的初始化:
1 | // src/main/Application.js |
其他的先不说,在handleIpcInvokes里面注册了get-app-config的handler,逻辑如下:
1 | // src/main/Application.js |
这里用了electron-store持久化用户配置,详情参考这个链接
最终给到渲染进程的config,就是systemConfig和userConfig合并的结果,因此可以再转到渲染进程查看init(config)的逻辑:
1 | // |
这一段代码主要设置Vue的内部属性并起了Vue实例赋予global.app。在其中,加载了App.vue中id=app的页面内容,包括这些:
1 | <template> |
其中,<router-view />是实质展示了路由为/的页面,对应到routers里面就是@/components/Main以及其下级的task的路由组件。其他几个分别是:
mo-title-bar:顶层的最小化、最大化、退出按钮mo-engine-client:不渲染界面的组件,实质只有js逻辑,用于管理下载进度mo-ipc:不渲染界面的组件,实质只有js逻辑,用于ipcmo-dynamic-tray:下载速度显示组件
到了这里,整个app就启动完成了。