关于前端:从零开始的electron开发主进程窗口启动

7次阅读

共计 4814 个字符,预计需要花费 13 分钟才能阅读完成。

上一篇咱们简略介绍了 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: false,
  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 地址:链接

正文完
 0