從零開始的electron開發-主進程-窗口啓動

上一篇咱們簡單介紹了electron基本腳手架的搭建,主要是區分主進程及渲染進程,處理環境變量,讓打不一樣的包有其對應的環境,接下來幾篇主要講主進程的一些處理,本篇主要講述如何建立窗口。html

變量

這裏先簡單說一下vue-cli-plugin-electron-builder注入的環境變量和electron的一些變量vue

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文件的路徑實現文檔內容的加載node

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頁,而後再加載咱們的主頁。webpack

渲染進程loader

其實就是多頁的建立方式,在src/loader新建多頁對應的main.js和App.vueweb

主進程使用

/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部分):vue-cli

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)

大多數屬性能夠參考官網的文檔,這裏只介紹有問題的:npm

  • 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一直存在的控件好比托盤等也是如此。

win菜單
mac菜單

單窗口啓動軟件

上面建立窗口完成了,可是打包完成後點擊圖標啓動時咱們會發現一個問題,咱們每雙擊一次啓動圖標,都會啓動一個窗口(這種常見於編輯器,能夠試試vscode),可是通常狀況呢,應該是雙擊啓動圖標,若是軟件沒有運行,就啓動,若是咱們的軟件在運行時聚焦到咱們的軟件窗口。  
用專業術語來講就是保證單實例機制,在第二個實例啓動時激活主實例窗口的示例,咱們新建一個winSingle.jsapp

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,固然這個東西后面說窗口間的通訊也會用到。electron

相關文章
相關標籤/搜索