中华视窗是诚信为本,市场在变,我们的诚信永远不变...
自带的杀毒软件导致窗口慢的问题,只要加个白名单即可
缘起
很多的开发者已经意识到这个问题了:创建并显示一个的很慢,在一些低配机型上往往需要2秒或者更长的时间,这个问题使得很多应用都只有一个窗口,需要用子窗口时,都用HTML DOM模拟。然而,Dom模拟的窗口必须在父窗口容器内,是没办法脱离父窗口存在的。所以还得回头想办法解决慢的问题。
之所以慢,主要是两个问题导致的,第一个就是内部在创建对象时做了很多初始化工作,如果你要让渲染进程拥有Node的能力的话,那么它还会给你初始化一个Node的环境。第二个是对于一个全新的对象来说首次加载并渲染HTML页面比较慢;最主要的还是第二个原因。
无论如何想要从根源上解决这个问题是非常难的,所以我们得想个迂回的办法来解决它。
思路
操作线程有线程池,操作数据库连接有连接池,我们为什么不能搞个窗口池呢?
提前准备N个隐藏窗口,让他们都加载一个骨架屏页面,放到一个池子里,当用到时,就从池子里捞一个窗口,执行页内跳转,跳转到业务页面,然后显示出来,紧接着马上再在池子里放一个新的加载了骨架屏页面的备用窗口。当用掉的窗口关闭时,就把它从池子里删除掉。这样就能保证池子里一直有N个窗口待命。而且页内跳转的效率很高,基本上就解决了我们的问题。
思路很简单,实际上还是有很多细节值得关注的。
实现
我们的窗口池就是一个数组:
items: WindowPoolItem[] = []
是描述窗口池内的窗口的类,后面我们会讲它的实现
初始化窗口池的方法如下:
init() {
for (let i = 0; i < 3; i++) {
this.items.push(new WindowPoolItem())
}
ipcMain.handle('loadWindow', (e, data) => {
if (this.isWindowInUse(data)) return
this.picAndUse(data)
})
}
在这个方法内,我们给窗口池创建了3个备用窗口;并监听了一个名为的跨进程消息,在主进程收到这个消息后,执行了两个方法,我们先来看
private isWindowInUse(param: WindowParam) {
let item = this.items.find((v) => v.param?.url === param.url)
if (!item) return false
item.effectParam(param)
return true
}
这个方法负责查找窗口池内是不是有一个相同url的窗口,如果没有,则返回false;
如果有,那么重新设置一下这个窗口的行为,并返回true;
有的时候虽然url相同,但窗口是不是,窗口的位置是不是要改变,这些行为可能会发生变化,所以要再次
如果没有找到相同url的窗口,那么就要执行方法了,
private picAndUse(param: WindowParam) {
let item = this.items.find((v) => !v.param) //没有param属性的,就是没用过的
item!.use(param)
this.items.push(new WindowPoolItem())
}
在这个方法中,我们在窗口池中找一个没被使用的窗口(也就是param属性为的对象),找到后调用它的use方法,紧接着在创建一个对象,把它加入窗口池中。这样就做到了消费一个,随即马上创建一个的原则,让窗口池中始终有待命的窗口。
有些人可能会问,既然消费掉一个就马上补一个,那么为什么我们一开始的时候创建了3个待命的窗口,难道不是创建一个就可以了吗?
这是因为有些特殊场景,用户可能在短时间内,连续消费掉2个窗口,这就会导致待命的窗口全部被用掉了,新窗口还没创建出来。本文开篇时提到的创建慢的问题又显现出来了。
当然创建3个待命窗口对于你的应用来说可能不是最佳的值,你应该根据你的用户的环境来判断创建几个窗口合适,值得注意的是,一个窗口就是一个进程,会消耗用户的CPU核内存资源,所以并不是待命窗口越多越好。