導航:
(一)Electron跑起來
(二)從零搭建Vue全家桶+webpack項目框架
(三)Electron+Vue+Webpack,聯合調試整個項目
(四)Electron配置潤色
(五)預加載及自動更新
(六)構建、發布整個項目(包括client和web)
摘要:前面幾篇介紹了如何啟動electron和vue項目,並進行聯合調試,這篇就來給我們的Electron配置潤色一下,至少看起來不那么像一個‘demo’。項目完整代碼:https://github.com/luohao8023/electron-vue-template。
一、’清理‘主進程文件main.js,提取窗口配置文件。
之前我們是把創建窗口以及窗口配置都放在了main.js中,這樣會讓我們的主進程看起來很亂,摻雜了各種配置、各方面的代碼,而且一旦我們的項目稍微復雜一些,比如同時維護多個窗口,或者有很多針對某個窗口的事件監聽等。這里所說的一個個窗口其實就是electron的渲染進程,不同的渲染進程由主進程來統一維護和調度。把渲染進程提取為單獨的配置文件,對外只暴露方法,這樣就能簡化主進程代碼,也讓我們的項目結構更清晰、更合理,總之是好處多多。
新建文件:main>index.js
/** * Tip: 主進程 * Author: haoluo * Data: 2020-02-25 **/ const { BrowserWindow, dialog } = require("electron"); const electron = require("electron"); const process = require("process"); const url = require("url"); const path = require("path"); const cookie = require('cookie'); const devMode = process.env.NODE_ENV === "development"; let mainWindow = null; const filter = { urls: ['http://*.kakayang.cn/*'] }; //創建窗口 function createWindow() { // 首頁路徑,file協議,pathToFileURL得到編碼過的URL let filePath = url.pathToFileURL(path.join(__dirname, 'index.html')).href; let indexUrl = 'http://localhost:8099/'; let winW = electron.screen.getPrimaryDisplay().workAreaSize.width, winH = electron.screen.getPrimaryDisplay().workAreaSize.height; let config = { title: "electron-vue-template", width: winW <= 1240 ? winW : 1240, height: winH <= 730 ? winH : 730, minWidth: winW <= 1240 ? winW : 1240, minHeight: winH <= 730 ? winH : 730, offscreen: true, show: true, center: true, frame: false, //去掉窗口邊框 autoHideMenuBar: true, //隱藏菜單欄 titleBarStyle: 'customButtonsOnHover', simpleFullscreen: true, resizable: process.platform === 'darwin', //可否調整大小 movable: true, //可否移動 minimizable: true, //可否最小化 maximizable: true, //可否最大化 fullscreen: false, //MAC下是否可以全屏 skipTaskbar: false, //在任務欄中顯示窗口 acceptFirstMouse: true, //是否允許單擊頁面來激活窗口 transparent: process.platform === 'darwin', //允許透明 opacity: 1,//設置窗口初始的不透明度 closable: true, backgroundColor: '#fff', allowRunningInsecureContent: true,//允許一個 https 頁面運行 http url 里的資源 webPreferences: { devTools: true, //是否打開調試模式 webSecurity: false,//禁用安全策略 allowDisplayingInsecureContent: true,//允許一個使用 https的界面來展示由 http URLs 傳過來的資源 allowRunningInsecureContent: true, //允許一個 https 頁面運行 http url 里的資源 nodeIntegration: true//5.x以上版本,默認無法在渲染進程引入node模塊,需要這里設置為true } }; // 增加session隔離配置 config.webPreferences.partition = `persist:${Date.now()}${Math.random()}`; mainWindow = new BrowserWindow(config); global.windowIds.main = mainWindow.webContents.id; // 開發環境使用http協議,生產環境使用file協議 mainWindow.loadURL(devMode ? encodeURI(indexUrl) : filePath); //監聽關閉 mainWindow.on('closed', function () { mainWindow = null; }).on('close', function (event) { mainWindow.send("close-window-render"); event.preventDefault(); }).on('ready-to-show', function () { mainWindow.show(); }); try { if (mainWindow.webContents.debugger.isAttached()) mainWindow.webContents.debugger.detach("1.1"); mainWindow.webContents.debugger.attach("1.1"); mainWindow.webContents.debugger.sendCommand("Network.enable"); } catch (err) { console.log("無法啟動調試", err); dialog.showErrorBox("get", "無法啟動調試"); } // 攔截請求並處理cookie mainWindow.webContents.session.webRequest.onBeforeSendHeaders(filter, onBeforeSendHeaders); mainWindow.webContents.session.webRequest.onHeadersReceived(filter, onHeadersReceived); return mainWindow; } function onBeforeSendHeaders(details, callback) { if (details.requestHeaders) { details.requestHeaders['Cookie'] = global.cookie; details.requestHeaders['Origin'] = details.url; details.requestHeaders['Referer'] = details.url; } callback({ requestHeaders: details.requestHeaders }); } function onHeadersReceived(details, callback) { let cookieArr = []; for (let name in details.responseHeaders) { if (name.toLocaleLowerCase() === 'Set-Cookie'.toLocaleLowerCase()) { cookieArr = details.responseHeaders[name]; } } let webCookie = ""; cookieArr instanceof Array && cookieArr.forEach(cookieItem => { webCookie += cookieItem; }); let webCookieObj = cookie.parse(webCookie); let localCookieObj = cookie.parse(global.cookie || ''); let newCookie = Object.assign({}, localCookieObj, webCookieObj); let cookieStr = ""; for (let name in newCookie) { cookieStr += cookie.serialize(name, newCookie[name]) + ";"; } global.cookie = cookieStr; callback({ response: details.responseHeaders, statusLine: details.statusLine }); } module.exports = { create(_callback) { if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.destroy(); } mainWindow = createWindow(); if (_callback instanceof Function) _callback(mainWindow); return mainWindow; } }
修改main.js:
const { app, BrowserWindow } = require("electron"); let mainWindow = require("./index.js"); //注冊全局變量 // 頁面跟路徑配置,優先使用此配置,考慮到小版本更新時,版本之間的切換 global.wwwroot = { path: __dirname }; global.cookie = ""; //主窗口id,在創建主窗口的js中獲取並修改此處 global.windowIds = { main: 0 }; app.on('ready', () => { mainWindow.create(); }); app.on('window-all-closed', function() { setTimeout(() => { let allwindow = BrowserWindow.getAllWindows(); if (allwindow.length === 0 ) app.exit(1); }, 500); });
二、單實例檢查,只允許啟動一個客戶端。
新建文件:main->libs->runCheck.js:
const { app, BrowserWindow } = require("electron"); module.exports=()=>{ // 單實例檢查 const gotTheLock = app.requestSingleInstanceLock(); if (!gotTheLock) return app.quit(); app.on('second-instance', () => { let myWindows = BrowserWindow.getAllWindows(); myWindows.forEach(win => { if (win && !win.isDestroyed()) { if (win.isMinimized()) win.restore(); win.focus(); } }); }); }
在main.js中引入並執行check函數:
require("./libs/runCheck.js")(); //禁止打開多份
三、注冊快捷鍵打開控制台:
細心的話可以發現,我們已經把控制台關掉了。以往的做法是在代碼里打開控制台,打包發布時再把代碼注釋掉,某個環境的包出問題了,又要放開限制再打對應環境的包,相當的麻煩。這里的解決方案是:
通過注冊快捷鍵的方式來操作控制台,而不是頻繁的注釋、取消注釋代碼。
新建文件:main->libs->shortcut.js:
const { app, BrowserWindow } = require("electron"); const globalShortcut = require("electron").globalShortcut; class Shortcut{ register(keys='Command+Control+Alt+F4'){ globalShortcut.register(keys, function () { let allWindow = BrowserWindow.getAllWindows(); for(let index =0;index < allWindow.length ;index++){ let win=allWindow[index] if(win.webContents && !win.webContents.isDevToolsOpened()){ win.webContents.openDevTools({mode: 'detach'}); } } }) } } app.on('will-quit', function () { globalShortcut.unregisterAll() }); module.exports=new Shortcut();
然后在主進程中引用並執行:
const shortcut = require("./libs/shortcut.js"); //注冊快捷鍵 app.on('ready', () => { //注冊快捷鍵打開控制台事件 shortcut.register('Command+Control+Alt+F5'); mainWindow.create(); });
四、配置devServer:
寫死端口可不是個好主意,得能配置才行,不然萬一哪個端口被占用,要修改所有引用的地方,很是麻煩。
新建文件:config->devServerConfig.js:
/** * Tip: devServer的配置 * Author: haoluo * Data: 2020-02-25 * Tips: 使用以下命令啟動各環境配置,npm run dev [dev|test|release] **/ let envList = ['dev', 'test', 'release']; let currentEnv = 'release'; let envArg = process.argv[2]; if (envArg && envList.includes(envArg)) { currentEnv = envArg; } //導出服務配置 module.exports = { url: '127.0.0.1', port: 8098, // 運行環境 currentEnv: currentEnv, // 調試完打開瀏覽器 devComplateOpened: true };
可以看到我們增加了啟動參數,用來調試不同環境,這個參數可以用來標記不同環境的后端服務,對於后端接口地址我們也可以提取配置文件,跟這個環境參數相對應,這里就不多說了。
修改index.js:
const devServerConfig = require('@config/devServerConfig.js'); let indexUrl = `http://${devServerConfig.url}:${devServerConfig.port}/`;
修改dev.js中使用到端口信息的地方。
以上就是這次的內容,感覺啰嗦了一堆沒什么重點。有什么想了解但是文中未提及的地方,歡迎留言。