热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

electron监听网页_Electronvue,Main/Renderer进程的认识,自定义菜单等

Main进程和Renderer进程的基本认识可以看到Main进程管理的是这个app窗口(BrowserWindow),而Renderer进程负责的就是我们熟悉的页面U

Main进程和Renderer进程的基本认识

可以看到Main进程管理的是这个app窗口(BrowserWindow),而Renderer进程负责的就是我们熟悉的页面UI渲染。不过实际上,它们远远不仅如此。下面一张图能够把它们所支持、管理的electron或者原生的模块大致列出来:

图中列出来的大部分模块都是我们会在开发过程中用到的。

它们有各自的模块,也有共有的模块比如clipboard等。还有一部分是Main进程里的模块,不过可以通过remote模块,让renderer进程也能使用。比如Menu比如

了解一下哪些模块在哪些进程里,哪些模块可以通过remote模块让renderer进程也能使用是有必要的,这样我们后续开发的时候才能正确的使用。

上面的模块可能有些从名字里并不能看出作用是啥,没关系,后续的内容会慢慢涉及。

Main进程开发

上面说到了Main进程一个显著的作用就是创建app的窗口。我们来看看这个是怎么实现的。

暂且先不管渲染进程里的页面长什么样,在app准备好的时候打开一个窗口只需要调用一个创建BrowserWindow的方法即可。

main进程里的开发有点当年写jQuery的样子,比较多的是事件驱动型的写法。

app

首先需要注意的是app的模块。这个模块是electron应用的骨架。它掌管着整个应用的生命周期钩子,以及很多其他事件钩子。

app的常用生命周期钩子如下:

will-finish-launching 在应用完成基本启动进程之后触发

ready 当electron完成初始化后触发

window-all-closed 所有窗口都关闭的时候触发,在windows和linux里,所有窗口都退出的时候通常是应用退出的时候

before-quit 退出应用之前的时候触发

will-quit 即将退出应用的时候触发

quit 应用退出的时候触发

而我们通常会在ready的时候执行创建应用窗口、创建应用菜单、创建应用快捷键等初始化操作。而在will-quit或者quit的时候执行一些清空操作,比如解绑应用快捷键。

特别的,在非macOS的系统下,通常一个应用的所有窗口都退出的时候,也是这个应用退出之时。所以可以配合window-all-closed这个钩子来实现:

除了上面说的生命周期钩子之外,还有一些常用的事件钩子:

active(仅macOS)当应用处于激活状态时

browser-window-created 当一个BrowserWindow被创建的时候

browser-window-focus 当一个BrowserWindow处于激活状态的时候

这些钩子需要配合一些具体场景来做出具体的操作。比如当一个BrowserWindow处于激活状态的时候修改窗口的title值。

当然,app这个模块除了上述的一些事件钩子之外,还有一些很常用的方法:

app.quit() 用于退出应用

app.getPath(name) 用于获取一些系统目录,对于存放应用的配置文件等很有用

app.focus() 用于激活应用,不同系统激活逻辑不一样

这些事件和方法都是怎么知道的呢?当然是官方文档了。不过并不需要一开始就通读一遍官方的api文档。官方的api文档更多的作用是用来查阅,当你要开发到某个功能的时候再去查它能否有对应的api、怎么使用。

BrowserWindow

BrowserWindow模块用于创建最常见的应用窗口。对于不同系统,创建的窗口的默认样式也不太一样。下面来看看macOS和windows的窗口在外观上的区别:

mac版的

windows版的

可以看到二者在窗口顶部的操作区(最小化、最大化、关闭)和标题的位置以及菜单的位置还是有明显的不同的。它们跟系统原生的窗口是一致的。不过如果你想要美化一下也是没问题的。

让我们来看看创建一个BrowserWindow的常用配置:

窗口的长宽自然不必说,需要指定。其中需要注意的几个比较重要的就是,frame这个选项,默认是true。如果选择了false则会创建一个frameless窗口,创建一个没有顶部工具栏、没有border的窗口。这个也是我们在windows系统下自定义顶部栏的基础。

跟app模块一样,BrowserWindow也有很多常用的事件钩子:

closed 当窗口被关闭的时候

focus 当窗口被激活的时候

show 当窗口展示的时候

hide 当窗口被隐藏的时候

maxmize 当窗口最大化时

minimize 当窗口最小化时

...

当然,也依然有很多实用的方法:

BrowserWindow.getFocusedWindow() [静态方法]获取激活的窗口

win.close() [实例方法,下同]关闭窗口

win.focus() 激活窗口

win.show() 显示窗口

win.hide() 隐藏窗口

win.maximize() 最大化窗口

win.minimize() 最小化窗口

win.restore() 从最小化窗口恢复

...

针对不同的业务逻辑你需要对窗口进行不一样的操作。这个需要跟你的项目需求相匹配。比如上述说到的,windows的顶部的操作区(放大、缩小、关闭按钮)就可以通过icon模拟+实例方法来实现。

Tray

一开始看这个名字你可能并不知道这个是个什么东西。可以把它理解为不同系统的任务栏里的图标组件吧。

比如在macOS里,Tray配合上图标之后就是顶部栏里的应用图标了:

比如在windows里,Tray配合上图标之后就是windows右下角的应用图标了:

需要注意的是,windows和macOS里,图标的大小都是16*16px。macOS下顶部栏的图标通常都是走黑白路线,所以可以为两种系统分别准备不同的图标。PicGo里Tray的生成代码大致如下:

注意上述代码里有一个${__static}的变量。该变量是static文件夹的路径。通过这个路径,在开发和生产阶段都能很好的定位你的静态资源所在的目录。是个很方便的变量。

当然Tray并不只是一个图标而无其他作用了。Tray支持很多有用的事件。其中最关键的两个是click和right-click。分别对应鼠标左键点击和鼠标右键点击事件。

鼠标左键点击事件

在macOS系统下,鼠标左键点击Tray的icon可能会出现配置菜单,也有可能会出现应用窗口。

在windows下,鼠标左键点击Tray的icon通常会出现应用的窗口。

鼠标右键点击事件

在macOS系统下,鼠标右键点击Tray的icon通常会出现配置菜单。

在windows系统下,同上。

所以需要我们去适配不同操作系统下用户的操作习惯。

在macOS系统下左键点击会出现一个menubar的小窗口,右键点击会出现配置菜单。而在windows下,左键点击会直接出现主窗口,(因为在windows下无小窗口的必要),右键点击会出现配置菜单,实现如下:

对于macOS而言,Tray还有一个很棒的特性——可以拖拽文件到Tray的icon上,会触发如下事件:

drop 当任何东西拖拽到icon上时

drop-files 当文件被拖拽到icon上时

drop-text 当文本被拖拽到icon上时

drop-enter 当刚拖拽到icon上时

drop-leave 当拖拽事件离开icon时

drop-end 当拖拽事件结束时

而Tray另一个重要的作用就是开启菜单项。这个将结合下一节Menu一起说明。

Menu

electron威力强大的Menu组件,既能够生成系统菜单项,也能实现绑定应用常用快捷键的功能。

主要分两种。

第一种是app的菜单。对于macOS来说就是顶部栏左侧区域的菜单项。对于windows而言就是一个窗口的标题栏下方的菜单区。

第二种是类似于右键菜单的菜单。

第一种菜单可以通过Menu.setApplicationMenu()来实现。

第二种菜单可以通过两个步骤来展示:

1. 创建菜单:

2. 展示菜单:

这里我们只介绍了Menu本身。其实组成Menu的是一个一个的MenuItem。它们有很多类型:

normal

separator

submenu

checkbox

radio

以及很多角色:

quit

copy

redo

undo

minimize

close

reload

...

通常来说,配置的菜单项基本从类型里来组合。

这里面就有normal、submenu、checkbox和radio四种类型。其中默认是normal。

角色的话通常对应的是一些常见的行为。比如quit是退出app,比如minimize是最小化,比如copy是复制。不过需要注意的是,如果你没有在创建app菜单里指定这些操作的快捷键的话,那么一些常见的快捷操作就无法在你的app里使用了。比如ctrl+c或者command+c复制这个操作,如果你没有通过Menu.setApplicationMenu()来设定这个快捷键的话,那么在你的electron应用里就无法执行复制的操作了。PicGo在早期版本里也犯了这个错误。当时的问题是我在开发模式下是没有问题的,但是在生产模式下就无法进行复制粘贴操作。后来查了一下原因,发现原来在开发模式下,electron会置入默认的一些快捷操作菜单

所以在生产模式如果我没有置入这些快捷键的话,使用者就无法使用了。这个是大坑。

说了这么多,来看看生成app的菜单的代码长啥样:

注意,如果在开发模式下直接只使用如下快捷键的话,一些调试快捷键比如F12或者command+shift+i打开控制台的操作就无法使用了。所以在开发模式下不需要创建这些快捷键菜单。

可以通过accelerator指定你想要的快捷键。诸如Shift、Ctrl、Cmd等键位缩写。如果是组合键,就加上+。尤其注意到,因为macOS和windows键位的差异,所以有一个很好用的键位缩写CmdOrCtrl,即如果是在macOS上就是Cmd,在windows上就是Ctrl。

然后再来看看Tray的“右键”菜单的生成:

注意,菜单项的点击事件可以直接通过click属性来指定。上面我们是先通过了Menu.buildFromTemplate()这个方法创建了菜单,然后再在右键点击Tray图标的时候将其弹(PopUp)出来。

当然也有其他构建菜单的方法。可以通过Menu实例的append方法来加入Menu Item。如下例:

基本上有了上述的几个基本模块,我们的一个应用的骨架是基本搭建好了,拥有窗口、任务栏应用图标和菜单项。其他的Main进程的模块,并不是必须的,当会用到的时候将在之后的文章里逐步提及。下一节我们将来看renderer进程的开发。

Renderer进程开发

对于electron-vue而言,renderer进程其实大部分就是在写我们平时常写的前端页面罢了。不过相对于平时在浏览器里写的页面,在electron里写页面的时候你还能用到不少非浏览器端的模块,比如fs,比如electron通过remote模块暴露给renderer进程的模块。接下去我们来看看renderer进程有哪些需要注意的地方。

请使用Hash模式

往常我们在写Vue的时候都比较喜欢开启路由的history模式,因为这样在浏览器的地址栏上看起来比较好看——没有hash的#号,就如同请求后端的url一般。然而需要注意的是,history模式需要后端服务器的支持。

可能很多朋友平时开发的时候没有感觉,那是因为vue-cli里在开发模式下启动的webpack-dev-server帮你实现了服务端的history-fallback的特性。所以在实际部署的时候,至少都需要在你的web服务器程序诸如nginx、apache等配置相关的规则,让前端路由返回给vue-router去处理。

而electron里也是如此。在开发模式下,由于使用的是webpack-dev-server开启的服务器,所以BrowserWindow加载的是来自于类似``http://localhost:9080这样的地址的页面。而在生产模式下,却是使用的file://的协议,比如file://${__dirname}/index.html`来指定窗口加载的页面。

因此,从上面的表述你也能明白了。假如我有一个子路由地址为child。如果不启用Hash模式,在开发模式下没啥问题,http://localhost:9080/child,但是在生产模式下,file://${__dirname}/index.html/child却是无法匹配的一条路径。因此在electron下,vue-router请不要使用history模式,而使用默认的hash模式。

那么上面的问题就迎刃而解,变为file://${__dirname}/index.html#child即可。

PicGo里加载的页面路由规则如下,从中你也能看出我使用的是hash模式。

实现自己的titlebar

在上面讲BrowserWindow的时候,我说到有时为了应用的美观,并不想让我们的应用窗口采用系统默认的titlebar,而想用自己写的来实现。这样的话就在创建你的BrowserWindow的配置里加上一句

这样就行了。然后你就可以自行在renderer进程的页面里模拟一个顶部的titlebar了,比如上面提到的PicGo的titlebar的样子。实际上代码也很简单:

然后把这个titlebar的position置顶即可。

不过在平时的使用中,我们要注意,一般我们鼠标按住titlebar的时候是可以拖动窗口的。但是如果我们在不加可拖拽的属性之前,我们自己写的titlebar是不具备这样的特性的。要加上这个特性也很简单:

只需一条CSS,即可让你的titlebar可以拖拽。

不过在windows下,操作区的按钮(缩小、放大、关闭)长按应该是不能拖拽的,所以还需要:

变成no-drag,这样就实现了我们自己生成应用的titlebar了。

drag&drop的避免

通常我们用Chrome的时候,有个特性是比如你往Chrome里拖入一个pdf,它就会自动用内置的pdf阅读器打开。你往Chrome里拖入一张图片,它就会打开这张图片。由于我们的electron应用的BrowserWindow其实内部也是一个浏览器,所以这样的特性依然存在。而这也是很多人没有注意的地方。也就是当你开发完一个electron应用之后,往里拖入一张图片,一个pdf等等,如果不是一个可拖拽区域(比如PicGo的上传区),那么它就不应该打开这张图、这个pdf,而是将其排除在外。

所以我们将在全局监听drag和drop事件,当用户拖入一个文件但是又不是拖入可拖拽区域的时候,应该将其屏蔽掉。因为所有的页面都应该要有这样的特性,所以我写了一个vue的mixin:

这样在全局引入这个mixin即可。

remote模块的使用

remote模块是electron为了让一些原本在Main进程里运行的模块也能在renderer进程里运行而创建的。以下说几个我们会用到的。

在electron-vue里内置了vue-electron这个模块,可以在vue里很方便的使用诸如this.$electron.remote.xxx来使用remote的模块。

shell

shell模块的官方说明是:Manage files and URLs using their default applications.也就是使用文件或者URL的默认应用。通常我们可以用其让默认图片应用打开一张图片、让默认浏览器打开一个url。

如果我们想在renderer进程里点击一个按钮然后在默认浏览器里打开一个url的话就可以这样:

是不是很方便?

更多详细的shell的用法可以参考文档。

dialog

有的时候我们会有打开原生的对话框的需求。比如显示版本信息:

这个时候就可以通过dialog这个模块来实现了。逻辑跟上面一样也是点击一个按钮打开一个dialog:

更多详细的dialog的用法可以参考文档。

Menu和BrowserWindow的应用

使用Menu可能很多人能够理解。但是为什么要使用BrowserWindow呢?因为需要定位你打开Menu的窗口。

在PicGo里,有一个点击按钮打开Menu的操作,大致如下:

这里的menu.popup就需要你指定一下打开这个menu的窗口。它将自动定位你点击的位置而弹出。

main进程和renderer进程的通信

在Vue里,如果是非父子组件通信,很常用的是通过Bus Event来实现的。而electron里的不同进程间的通信其实也很类似,是通过ipcMain和ipcRenderer来实现的。其中ipcMain是在main进程里使用的,而ipcRenderer是在renderer进程里使用的。

ipcMain和ipcRenderer

官网的例子其实很简洁明了了,我放出来:

其中ipcMain只有监听来自ipcRenderer的某个事件后才能返回给ipcRenderer值。而ipcRenderer既可以收,也可以发。

那么问题就来了,如何让ipcMain主动发送消息呢?或者说让main进程主动发送消息给ipcRenderer。

首先要明确的是,ipcMain无法主动发消息给ipcRenderer。因为ipcMain只有.on()方法没有.send()的方法。所以只能用其他方法来实现。有办法么?有的,用webContents。

webContents

webContents其实是BrowserWindow实例的一个属性。也就是如果我们需要在main进程里给某个窗口某个页面发送消息,则必须通过win.webContents.send()方法来发送。

代码大致如下:

所以必须指定要发送的窗口,才能将信息准确送达。

总结

也许文中简单的几句话背后就是我无数次的查阅和调试。内容相比第一篇多了不少,希望这篇文章能够给你的electron-vue开发带来一些启发。



推荐阅读
  • Thisissuewasoriginallyopenedbyashashicorp/terraform#5664.Itwasmigratedhe ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 利用Visual Basic开发SAP接口程序初探的方法与原理
    本文介绍了利用Visual Basic开发SAP接口程序的方法与原理,以及SAP R/3系统的特点和二次开发平台ABAP的使用。通过程序接口自动读取SAP R/3的数据表或视图,在外部进行处理和利用水晶报表等工具生成符合中国人习惯的报表样式。具体介绍了RFC调用的原理和模型,并强调本文主要不讨论SAP R/3函数的开发,而是针对使用SAP的公司的非ABAP开发人员提供了初步的接口程序开发指导。 ... [详细]
  • 网址:https:vue.docschina.orgv2guideforms.html表单input绑定基础用法可以通过使用v-model指令,在 ... [详细]
  • 带添加按钮的GridView,item的删除事件
    先上图片效果;gridView无数据时显示添加按钮,有数据时,第一格显示添加按钮,后面显示数据:布局文件:addr_manage.xml<?xmlve ... [详细]
  • TerraformVersionTerraformv0.9.11AffectedResource(s)Pleas ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 本文详细介绍了如何使用MySQL来显示SQL语句的执行时间,并通过MySQL Query Profiler获取CPU和内存使用量以及系统锁和表锁的时间。同时介绍了效能分析的三种方法:瓶颈分析、工作负载分析和基于比率的分析。 ... [详细]
  • WPF之Binding初探
      初学wpf,经常被Binding搞晕,以下记录写Binding的基础。首先,盗用张图。这图形象的说明了Binding的机理。对于Binding,意思是数据绑定,基本用法是:1、 ... [详细]
  • 025_JavaScript数组方法
    1.把数组转换为字符串1.1.toString()方法1.1.1.toString()方法把数组转换为数组值(逗号分隔)的字符串,并返回结果。1.1.2.语法arrayOb ... [详细]
  • fileuploadJS@sectionscripts{<scriptsrc~Contentjsfileuploadvendorjquery.ui.widget.js ... [详细]
author-avatar
傲慢的小草7_170
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有