原文地址: electron开发采坑小记-SegmentFault 思否
前前前前段时间作了个桌面端的项目(这篇文章拖了挺久了),功能大概是这样的:javascript
feed
流信息展现webview
打开一个网站需求很简单,提升运营效率的辅助工具,可是由于一些缘由须要作成一个桌面端。
从前端一会儿跨到PC桌面端开发,听起来跨度有点大,但在实际的开发中由于有了electron
的加持,这一切都变的很是便利,丝毫没有跨度的痕迹,彻底变成了web开发那一套,让人不由感叹js生态的完善,就像有句话说的:html
能用JavaScript书写的终将会用JavaScript书写。
实际上我并不是直接采用electron
,而是使用了更加懒人的electron-vue。经过这一工具将web
开发那一套又转移到vue
开发上,再辅以iview
作UI,开发起来一会儿高效了很多。前端
咱们知道electron
,可是electron-vue
是个什么东西呢,咱们还须要了解一下。这东西的官方介绍是这样的。vue
An Electron & Vue.js quick start boilerplate with vue-cli scaffolding, common Vue plugins, electron-packager/electron-builder, unit/e2e testing, vue-devtools, and webpack.
从这个介绍大体能够看出来这是个基于vue-cli
的使用vue
开发electron
应用的脚手架。能够帮助咱们作很多的vue
与electron
之间结合的基础工做,让咱们的开发更加便捷。那么咱们就来看看使用electron-vue
怎么作开发。咱们先从基础的安装开始。java
我以为这个步骤叫安装并非很严谨,由于electron-vue
并非一个单独的npm
包,而是vue-cli
的一个模板配置。因此使用起来没有难度,能够按照官方文档的说明便可完成,只有两行命令。webpack
# 安装 vue-cli 和 脚手架样板代码 npm install -g vue-cli vue init simulatedgreg/electron-vue my-project
如此,咱们便获得了一个初始化的electron
项目,安装完依赖包就能开发了。初始化过程很简单,可是须要提示一些vue cli
使用过程当中须要注意的问题:git
Vue CLI
的包名称由vue-cli
改为了@vue/cli
,致使最新版的vue cli
使用vue init
命令会报错,而electron-vue
的安装文档比较老旧,须要用vue init
这个命令,咱们能够用一个命令来作桥接:github
npm install -g @vue/cli-init
windows
在Git bash
上初始化选择插件时候没法用上下键交互的问题能够换用powershell
项目初始化、安装依赖后咱们就能够开发了。这时候能够跑一下项目,他会有个默认的界面。web
npm run dev
对应的目录是这样的(目录会根据安装插件的不一样而存在差别)。ajax
├── appveyor.yml ├── build │ └── icons │ ├── 256x256.png │ ├── icon.icns │ └── icon.ico ├── dist │ ├── electron │ └── web ├── package.json ├── README.md ├── src │ ├── index.ejs │ ├── main │ │ ├── index.dev.js │ │ └── index.js │ └── renderer │ ├── App.vue │ ├── assets │ ├── components │ ├── main.js │ ├── router │ └── store └── static
大体的套路跟vue
开发同样,不一样的是src
下有个两个目录:main
和renderer
。咱们大体能够理解为:
main
目录存放与electron
相关的内容,是主进程renderer
目录存放与咱们页面相关的内容,是渲染进程这个经过这个demo页面能够验证。搞清楚这个后开发就很简单了,不须要解释太多。更多的介绍能够看文档
开发过程当中调试是不可避免的,根据个人经验这里主要介绍两种调试:
这种调试就像咱们平时在vue
开发web
应用中使用devtool
同样。这个在初始化的代码中已经写好了,在dev
模式下默认是开启的。
在/src/main/index.dev.js
能够找到这段代码,咱们不用太关心。
require('electron-debug')({ showDevTools: true })
webview
页面调试这个是由于我这个项目须要才涉及到的,须要本身代码实现。 在个人逻辑中大体这样处理的:
<webview ref="webview" src="xxx" ></webview>
this.$refs.webview.addEventListener('dom-ready', res => { if (process.env.NODE_ENV === 'development') { this.$refs.webview.openDevTools(); } });
开发完以后咱们须要把代码打包成一个exe
的可执行文件,这个操做也很简单,已经在package.json
上被安排好了,咱们直接用就好了。
npm run build
而后,build
目录下有会有你想要的exe
文件了。拷走安装就能够用了。
若是你须要自定义一些图标啥的能够改package.json
中build
的相关配置,操做简单,不用多提。
由于此次项目功能很简单,涉及到的点不多,因此这里只介绍项目用到的一些核心点,没用过的没有发言权,就不提了。
webview
代码注入electron
提供了webview
标签可以让咱们嵌入本身的网页。想要往里面注入代码可使用webview
上提供的executeJavaScript
方法。具体的参数和使用方法这些都是文档层面的内容,不用多提,须要注意的是这个方法的第三个参数是个callback
,文档上是这样描述的
callback Function (可选) - 在脚本被执行后被调用。
文档只是描述了这个回调被触发的时机,而没有描述回调触发时候传的参数。
关于触发时机和参数,更具体的描述是这样的。
querySelector
这样的,callback
会在注入代码执行完以后触发,而且把你最后querySelector
得到的值传入callback
ajax
请求这样的,callback
是不会等这个请求有了响应才执行的,而是执行完ajax
请求就执行,固然,这种状况下callback
是没有传参的。咱们后面会介绍怎么拿到注入ajax
的返回值。主进程和渲染进程通讯是electron
中很重要的概念,介绍这块内容的文章也有不少,因此这里就不过多赘述,简单介绍一下大概状况。
通讯主要靠两个工具完成:ipcRenderer
和ipcMain
。
在主进程里这样使用ipcMain
:
const { ipcMain } = require('electron'); ipcMain.on('exit_app', res => { app.exit(); })
在渲染进程中这样使用ipcRenderer
:
const { ipcRenderer } = require('electron'); ipcRenderer.send('exit_app', true);
须要注意的是:消息只能是由渲染进程主动发起,主进程被动监听,而后主进程做响应,不存在由主进程主动向渲染进程通讯的方式。代码实现以下:
// 在主进程中 const { ipcMain } = require('electron') ipcMain.on('asynchronous-message', (event, arg) => { console.log(arg) // prints "ping" event.reply('asynchronous-reply', 'pong') }) ipcMain.on('synchronous-message', (event, arg) => { console.log(arg) // prints "ping" event.returnValue = 'pong' })
//在渲染器进程 (网页) 中 const { ipcRenderer } = require('electron') console.log(ipcRenderer.sendSync('synchronous-message', 'ping')) // prints "pong" ipcRenderer.on('asynchronous-reply', (event, arg) => { console.log(arg) // prints "pong" }) ipcRenderer.send('asynchronous-message', 'ping')
electron
窗体关闭事件拦截窗体关闭事件拦截利用的是BrowserWindow
的close事件来作处理的。须要注意的是BrowserWindow
实例还有个closed
事件,区别在于:
close
在窗口要关闭的时候触发closed
窗口已经关闭时触发这个两个事件一个字母的差距,可是触发时机却大有差距,千万分清楚,当时我就是没看清,浪费了很多时间。
窗体关闭事件拦截的代码实现大体以下:
mainWindow = new BrowserWindow({ height: 563, useContentSize: true, width: 1000 }) mainWindow.on('close', event => { return event.preventDefault(); })
webview
中beforeunload
弹窗问题若是内嵌的webview
中对beforeunload
事件有监听,并有弹窗操做,那么这个事件触发的时候,electron
是没有弹窗提示的。用户是没有感知的,这个问题尚未解决办法,是个坑。
(electron
的webview
是不会显示这些弹窗)
webview
中注入请求的响应个人项目里有一个需求是要向webview
中注入ajax
请求,而且要拿到请求的响应结果。可是electron
中比较早的版本中只有对发起的请求有监听,并不支持监听请求的响应,因此要拿到请求的响应结果就是个问题。
可是也不是没办法解决。我这里是借助webview
的console-message
事件来处理。监听webview
的console-message
事件,当拿到请求的响应的时候把响应的内容按照约定的格式打出来,这样就间接的实现了对注入请求的监听。
electron
低版本webview
中不能跳转这是一个在electron
低版本中存在的问题,在一些新的版本中已经解决了。
这个问题的具体表现就是:
内嵌webview
的页面中若是发生302
、301
跳转的话页面会卡在发生跳转的请求那里再也不向下进行,查看webview
的network
能够看到这个问题。
这个问题的解决很简单,升级到最新的electron
就能解决这个问题。我这里升级到4.1.4
解决了这个问题。
由于electron-vue
长时间没更新,里面使用的electron
版本仍是2.0.4
,比较老旧,因此你要是遇到一些奇奇怪怪的问题不妨升级electron
来尝试解决。
ctrl+c
以后仍有electron
进程驻守调试时候发现的这个问题,虽然是在electron
中开发,可是由于仍是vue
那一套,因此咱们改完代码就不用重启就能在electron
中看到效果,就是热更新嘛,可是有时候我仍是会手动的ctrl + c
,这么手动的次数多了以后我发现个人电脑会变的很是卡。排查后我发现ctrl + c
并不能彻底关闭整个electron
应用,每次这么作都会遗留2个electron
进程保持活跃,屡次以后会遗留不少electron
进程,电脑就会变的很是卡,每次都须要手动杀一下进程。这个问题在把模板中自带的
app.on('window-all-closed', event => { app.quit(); });
替换为
app.on('window-all-closed', event => { app.exit(); });
以后解决(在写文章的时候发现这个问题并没获得复现,不知道是否是新的版本修复了这个问题),其中差别感兴趣的能够深刻研究一下。
解决上面问题的时候发现electron
没法捕获来自命令行的信号量。若是有这个需求的话仍是要留意的。
在开发过程当中也是一边学一边作,发现了一些不错的文章和博客,从里面也得到了很多的帮助,贴出来分享一下。