最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 从零开始的electron开发-主进程-窗口启动

    正文概述 掘金(陌路凡歌123)   2021-03-08   747

    上一篇我们简单介绍了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一直存在的控件比如托盘等也是如此。

    从零开始的electron开发-主进程-窗口启动 从零开始的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开发-主进程-窗口启动

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元