从零开始的electron开发-主进程-窗口启动
2021/3/8 14:11:27
本文主要是介绍从零开始的electron开发-主进程-窗口启动,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
上一篇我们简单介绍了electron基本脚手架的搭建,主要是区分主进程及渲染进程,处理环境变量,让打不同的包有其对应的环境,接下来几篇主要讲主进程的一些处理,本篇主要讲述如何创建窗口。
变量
这里先简单说一下vue-cli-plugin-electron-builder
注入的环境变量和electron的一些变量
process.env.WEBPACK_DEV_SERVER_URL:看名字就知道了,其实就是webpack启的服务(npm run serve)http://localhost:xxx process.env.VUE_APP_ENV:我们自己用的环境变量,上一期在.env.xxx中设置的 process.env.NODE_ENV: 判断我们是本地开发,还是打包之后的 const isMac = process.platform === 'darwin':判断是mac系统还是其他系统
创建窗口
electron的窗口创建通常使用BrowserWindow
进行创建,然后通过loadURL
载入url地址或者file://
协议的本地HTML文件的路径实现文档内容的加载
import { app, protocol } from 'electron' let win // 先注册一个app协议用来加载文档,用作于打包后的文档载入,其实就是类似于file://协议加载本地文件 protocol.registerSchemesAsPrivileged([ { scheme: 'app', privileges: { secure: true, standard: true } } ]) // 创建一个函数来创建窗口,winConfig是BrowserWindow的配置,devPath是开发时的地址,prodPath是打包后文件地址 function createWindow(winConfig, devPath, prodPath) { const win = new BrowserWindow(winConfig) if (process.env.WEBPACK_DEV_SERVER_URL) { win.loadURL(process.env.WEBPACK_DEV_SERVER_URL + devPath) } else { win.loadURL(`app://./${prodPath}`) // 这里的地址就是public文件夹下的 } return win } // 调用,其实就是开发时我们载入的文档就是`http://localhost:80`,打包后载入的文档就是打包后的index.html win = createWindow({ height: 810, width: 1440, webPreferences: {} }, '', 'index.html')
多页
上一期我们说过可以通过设置vue.config.js
里的pages
打包多页(这里就不放代码了,loader请看上一期注释),当我们的项目比较大的时候可以尝试打包多页,有些不是主页的页面主线程进行预加载,比如单独做个登录页,启动比较快,复杂的页面先预加载不显示出来,要显示的时候直接win.show()
,算是一个白屏优化吧。
这里呢我们简单点,就利用多页打包做loader页,先展示loader页,然后再加载我们的主页。
渲染进程loader
其实就是多页的创建方式,在src/loader
新建多页对应的main.js和App.vue
主进程使用
/config/global.js global.willQuitApp = false global.tray = null global.sharedObject = { win: '' } export default global /config/index.js const env = process.env const config = { loading: true, winSingle: true, devToolsShow: true, VUE_APP_ENV: env.VUE_APP_ENV, NODE_ENV: env.NODE_ENV, VUE_APP_VERSION: env.VUE_APP_VERSION } if (config.VUE_APP_ENV === 'development') { config.devToolsShow = true } else if (config.VUE_APP_ENV === 'test') { config.devToolsShow = true } else if (config.VUE_APP_ENV === 'production') { config.devToolsShow = false } module.exports = config
主进程设置窗口创建,我们把之前的src/main/index.js
的窗口创建修改一下(createWindow部分):
import config from './config/index' import global from './config/global' let win = null let loaderWin = null function initWindow() { if (config.loading) { loaderWin = createWindow({ width: 400, height: 600, frame: false, backgroundColor: '#222', show: false, transparent: true, skipTaskbar: true, resizable: false, webPreferences: { experimentalFeatures: true, contextIsolation: false } }, 'loader', 'loader.html') loaderWin.once('ready-to-show', () => { loaderWin.show() }) loaderWin.on('closed', () => { loaderWin = null }) } win = createWindow({ height: 810, minHeight: !isMac && process.env.VUE_APP_ENV === 'production' ? 810 - 20 : 810, width: 1440, minWidth: 1440, useContentSize: true, show: false, webPreferences: { contextIsolation: false, nodeIntegrationInSubFrames: true, webSecurity: false, webviewTag: true, enableRemoteModule: true, scrollBounce: isMac } }, '', 'index.html') win.once('ready-to-show', () => { loaderWin && loaderWin.destroy() win.show() // setTimeout(() => { // loaderWin && loaderWin.destroy() // win.show() // }, 2000) }) global.sharedObject.win = win win.on('closed', () => { win = null }) } async function onAppReady() { if (!process.env.WEBPACK_DEV_SERVER_URL) { createProtocol('app') } initWindow() } app.on('ready', onAppReady)
大多数属性可以参考官网的文档,这里只介绍有问题的:
- ready-to-show是为了保证窗口加载没有白屏及闪烁。
- frame是设置无边框窗口,正常情况下我们的软件顶端是带有一个横条的,缩小,放大及关闭,还有拖动功能,设置为false可以除去掉这个横条(比如你想自定义这个)。
- useContentSize:正常开发下,我们的设计稿一般是文档的高度,这个是不包括上面说的那个横条的,如果为false的话,我们设置的height就是整个软件的高度(也就是说我们开发的文档高度:html内容=height-横条高度),如果设置为true的话height就是我们开发的文档高度。
- minHeight:minHeight这个东西实际上是与
Menu
这个有所关联的,win和mac是有差异的,win的菜单是在横条下方,mac则是位于桌面左上角。一般来说win的应用都是把Menu
去掉的,为了方便调试,我们区分了几个环境,只有生产包才去除Menu
,,经实践去除Menu
的需减去20文档才会和height一致(我也比较疑惑?),所以有此处理。 win.show()
这里由于我们的体积太小,加载非常快,导致loaderWin一闪而过看不到效果,所以可以在这里加个定时器延时看看效果。- 注意所有的BrowserWindow实例都请用全局变量赋值,比如win,loaderWin,这两个如果是局部变量的话,函数执行完毕就会销毁,那么我们的窗口也会被销毁,同理electron一直存在的控件比如托盘等也是如此。
单窗口启动软件
上面创建窗口完成了,但是打包完成后点击图标启动时我们会发现一个问题,我们每双击一次启动图标,都会启动一个窗口(这种常见于编辑器,可以试试vscode),但是一般情况呢,应该是双击启动图标,如果软件没有运行,就启动,如果我们的软件在运行时聚焦到我们的软件窗口。
用专业术语来说就是保证单实例机制,在第二个实例启动时激活主实例窗口的示例,我们新建一个winSingle.js
。
import { app } from 'electron' import global from '../config/global' const gotTheLock = app.requestSingleInstanceLock() export default function() { // 点击图标启动时检测窗口是否存在,存在则打开 if (!gotTheLock) { app.quit() } else { app.on('second-instance', () => { const win = global.sharedObject.win if (win) { if (win.isMinimized()) win.restore() if (win.isVisible()) { win.focus() } else { win.show() } } }) } } // 在主线程引入使用 import winSingle from './services/winSingle' if (config.winSingle) { winSingle() }
这里通过app.requestSingleInstanceLock()
判断应用程序实例是否成功取得了锁,我们的程序在第一次启动时该值为true,此时会监听second-instance
事件,当第二个实例被执行并且调用app.requestSingleInstanceLock() 时,这个事件将在你的应用程序的首个实例中触发,那么在这个事件里我们可以让我们的窗口重新展示出来。当第二次启动时,该值为false,也就是直接退出改窗口,并且会触发我们首个实例的second-instance
事件。
这里可能有同学会问了,问什么要使用global.sharedObject.win
而不直接把我们的win传入winSingle
使用呢,这里就有个坑了,如果使用win传入winSingle
的方式只是执行了second-instance
的绑定,并不会执行里面的事件,当second-instance
触发时win并不存在,拿不到我们窗口的实例,所以无法操作窗口,故我们这里使用global.sharedObject.win
,当然这个东西后面说窗口间的通信也会用到。
本文地址:链接
本文github地址:链接
这篇关于从零开始的electron开发-主进程-窗口启动的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23实现OSS直传,前端怎么实现?-icode9专业技术文章分享
- 2024-11-22在 HTML 中怎么实现当鼠标光标悬停在按钮上时显示提示文案?-icode9专业技术文章分享
- 2024-11-22html 自带属性有哪些?-icode9专业技术文章分享
- 2024-11-21Sass教程:新手入门及初级技巧
- 2024-11-21Sass学习:初学者必备的简单教程
- 2024-11-21Elmentplus入门:新手必看指南
- 2024-11-21Sass入门:初学者的简单教程
- 2024-11-21前端页面设计教程:新手入门指南
- 2024-11-21Elmentplus教程:初学者必备指南
- 2024-11-21SASS教程:从入门到实践的简单指南