上一篇咱們簡單介紹了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
其實就是多頁的建立方式,在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
Menu
這個有所關聯的,win和mac是有差別的,win的菜單是在橫條下方,mac則是位於桌面左上角。通常來講win的應用都是把Menu
去掉的,爲了方便調試,咱們區分了幾個環境,只有生產包纔去除Menu
,,經實踐去除Menu
的需減去20文檔纔會和height一致(我也比較疑惑?),因此有此處理。win.show()
這裏因爲咱們的體積過小,加載很是快,致使loaderWin一閃而過看不到效果,因此能夠在這裏加個定時器延時看看效果。上面建立窗口完成了,可是打包完成後點擊圖標啓動時咱們會發現一個問題,咱們每雙擊一次啓動圖標,都會啓動一個窗口(這種常見於編輯器,能夠試試vscode),可是通常狀況呢,應該是雙擊啓動圖標,若是軟件沒有運行,就啓動,若是咱們的軟件在運行時聚焦到咱們的軟件窗口。
用專業術語來講就是保證單實例機制,在第二個實例啓動時激活主實例窗口的示例,咱們新建一個winSingle.js
。app
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