1 前言
前一章我们已经学习了如何使用模板来创建一个窗口,并顺带讲解了一下这个模板中的代码结构。
但总的来说,前面整个章节都只是在做准备工作而已。而从本章开始,我们就正式踏入学习Electron核心内容的旅程了。
既然这个框架是做GUI软件的,那窗口自然就是最重要的,所以我们本章就以窗口开始学习。
2 了解窗口
首先我们需要明白的一个点就是:窗口不是应用程序!
正如前面章节从代码中看到的那样,app
这个实例代表当前这个应用程序。
但只有它还不够,因为没有窗口的命令行程序(也称CLI``,Command Line Interface
,命令行接口)也是程序,只不过两者的交互方式不一样而已。
窗口只是应用程序的一种交互方式,也就是我们常常能看到的GUI
(Graphical User Interface
,图形用户接口)。
所以一个应用程序既可以没有窗口,也可以创建多个窗口,只不过我们看到一个窗口的应用程序居多,因为其开发起来更加简单、使用起来更加简洁,仅此而已,
窗口就像一块画布,你是可以任意绘制的,不同框架提供的绘制方式并不相同。
对于Electron
框架来说,窗口就是通过html
、js
、css
这三大前端基石来完成的,所以学习本系列的前提是,你得学会这三个基础。
一个传统化的标准窗口程序一般会包含以下五个部分:
- 标题栏:即窗口最上面、包含最大最小化的那一行,还可以用于鼠标拖动窗口。
- 菜单栏:存放各种功能命令,一般在标题栏下方
- 工具栏:存放各种常用功能,一般在菜单栏下方,但在现代应用程序中已经很少见到了(
word
这种软件中你也还是能看到)。 - 内容区域:窗口正中间,工具栏的下方,这个区域就是我们可以发挥想象力的地方了,你可以在其中存放任何东西。
- 状态栏:窗口最下面,也就是内容区域的下面,用于显示一些状态信息的。
比如windows系统自带的记事本程序:
比如wps
的工具栏:
但这也仅仅只是传统的窗口模式,现在的很多应用程序已经逐渐开始模糊了各个区域的边界。
比如像上面看到的记事本、wps,它们的标题栏除了最基本的功能外,还多了一个标签页的功能,更加节约空间的同时也会更加美观。
但想要做到这一点并不容易,因为这超出了传统的窗口模式,系统本身的窗口是不支持的。
所以这就需要我们进行定制!
什么是定制呢?
前面我们提到了,按照传统模式来说,其实也就只有中间的内容区域可以任由我们自由发挥,其它区域都是有自己特定格式的,你并不能随意改动。
而定制的方法就是:禁用其它四个区域,只保留中间的内容区域。
然后重点就来了,由于内容区域我们是可以任意绘制的,所以我们就可以通过在内容区域自己绘制标题栏、菜单栏等等,从而实现整个应用程序高度自定义化!
3 了解BrowserWindow
有了前面的基础,我们就可以正式来聊聊Electron中的窗口了,Electron框架是通过BrowserWindow
类来创建一个窗口的,所以我们主要就是来看看这个类到底是怎么用的!
官方文档为:BrowserWindow。
学会看官方文档是个好习惯,因为我现在讲的东西可能一年后就过时了,但官方文档会一直保持在最新的状态。
虽然中文翻译过来看起来可能有点奇怪,但基本还是能看懂它的意思。
其官方文档的前部分作用基本就是我上图所标注的那样。
从这里我们就能看到更多的内容,比如,这个窗口类不仅仅可以用loadFile
加载本地的html
文件作为窗口样式,也可以用函数loadURL
这个函数加载网页作为窗口。
还有前面用的在app.on('ready', createWindow);
这里也提到了,就是要等这个事件之后才能创建窗口。
事件一般有两种使用方式,一种就是最开始用的,直接将事件封装成为了一个函数:app.whenReady()
。
但更常用的还是用通用函数on
,然后第一个参数传入你想要等待的事件即可,而第二个参数就是你想要在这个事件发生后要执行的函数。
由于这个类是Electron
中的核心类,里面的东西非常之多。我也不可能一个一个挨着说完,所以这里只给大家稍微理一理即可。
从右边的目录看起,class BrowserWindow
前面的内容都是官方将其最常见的用法写出来,教我们怎么用。这个后面我也会聊聊。
然后class BrowserWindow
后面的内容才是真正在开始介绍这个类中有哪些东西。
不过由于它的内容太多了!所以我这里也只能分类聊聊他们的区别,然后后面再挑几个常用的讲一讲。
首先是这个带new
的,这是在实例化一个对象。
实例化对象很多时候都需要很多选项,比如前面我们填的窗口宽高,你都可以在这里找到,除此之外你还能看到相当多的其它属性,可以自己试一试。
然后下面的就是事件:
用法如上图,最简单的就是举个例子:
//省略创建窗口win实例的过程
win.on('page-title-updated',(event,title,explicitSet)=>{
})
第二、三个你可能知道,一个是字符串(string),一个是布尔(boolean),那么第一个事件(Event)是什么鬼呢?
不知道没关系,你可以在代码中直接用函数console.log
函数将它打印一下就行了,这就是一个对象而已。
其它事件的使用方法也是同理,后面如果用到了会再提。
然后是静态方法:
静态方法比前面的实例方法用起来要简单一点,因为你不用创建窗口就可以直接使用这些方法,就像上面官方文档所示的那样:
let wins=BrowserWindow.getAllWindows(); //获取当前所有窗口,返回值是一个窗口数组
官方文档这方面已经介绍的很详细了,我就不再多说了。
唯一你可能觉得疑惑的就是,这些返回值都是什么意思?
其实不用管它,凡是这种不知道什么东西的类型,一般都是自定义对象,你只要用console.log
函数打印一下它就知道它里面都有写什么东西了。
接下来是实例属性:
只要带有实例二字的,就是需要你先创建好一个窗口才能使用的东西,只能通过创建好的这个实例进行调用。
实例属性就是我们创建的这个窗口上有哪些东西,比如这里的webContents
,又是一个对象,具体这个对象有哪些东西,可以点官方所示文档进去查看。
还有这里的id
,就是用来标识当前窗口的一个数字,后面跟了只读两个字,就意味着你不能更改它,但你能读取它:
console.log(win.id); //打印窗口的id
最后就是实例方法:
当你创建了窗口之后,你就可以通过这些函数来控制这个窗口的行为,比如这里的destroy()
方法,就可以用来强制关闭这个窗口。
同时注意其下面的备注信息,出现了触发这两个字,只要出现了触发,就意味着它现在正在谈论的是事件,所以前面提到的closed
、close
、unload
、beforeunload
这几个单词都是指的事件名。
这是中文翻译的问题,如果你看英文版本,就不会出现这种疑惑了:
英文版本明确指出这是事件,所以一般来说,还是推荐大家阅读英文版本的,如果看不懂,可以使用网页翻译对照着看。
不用非得将英语学的有多好,我英语也不好,但编程相关的常见单词其实也就百来个,你多见见、多翻译翻译,自然就能看懂了,大部分都是很简单的单词,也很少会出现四六级、高考那种长难句,不会的直接上翻译即可。
至此,我们就大致将这个窗口类从总体上观览了一遍,下面开始实战!
4 优雅显示窗口
既然官方都认为这是大家遇到的最多的问题,将其从文档中写出来了,那也没什么可说的,我就再多解释几句、实战一些。
之所以会出现这个问题,主要是创建窗口、加载页面并不是同时进行的。
正如前一章节我们看到其自动生成的代码中(也就是createWindow
函数中的代码,我做了部分删减、翻译):
// 创建窗口
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
show:false //默认隐藏窗口
});
// 加载页面
mainWindow.loadFile(path.join(__dirname,'index.html'));
这就会导致,当窗口创建好的时候,你的页面还没加载后,就会在启动的时候出现瞬间的白屏。
我本来是准备截图的,可惜太快了,截不到,你可以自己运行试一试,确实是由一瞬间的白屏。
如果你的页面很复杂,可能白屏的事件就会更长,用户体验就不好。
所以官方就给出了两种解决方案。
第一种是针对页面并不复杂的、启动本身就很快的程序,你可以让窗口默认不显示,等页面加载完成后再显示,不就看不到加载那一刻的白屏了嘛!
// 创建窗口
const mainWindow = new BrowserWindow({
width: 800,