【原创】从零开始搭建Electron+Vue+Webpack项目框架,一套代码,同时构建客户端、web端(四)

导航:

(一)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中使用到端口信息的地方。

以上就是这次的内容,感觉啰嗦了一堆没什么重点。有什么想了解但是文中未提及的地方,欢迎留言。

 

相关文章