• 【原创】从零开始搭建Electron+Vue+Webpack项目框架(四)完善Electron配置


    导航:

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

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

  • 相关阅读:
    「WordPress 技巧」:如何修改 WordPress 数据库前缀
    wordpress安全防护,你了解多少
    一些需要禁用的PHP危险函数(disable_functions)
    可以安全禁用的PHP函数之disable_functions功能详解
    不定高度的元素实现transition动画_如何为height:auto的div添加css3过渡动画
    完美兼容实现:解决textarea输入框限制字数长度(带统计功能)
    使用Map标签指定点击区域时的兼容性问题
    前端性能测试工具整理简介_性能测试工具都有哪些?
    移动端开发注意问题
    HTML51-清除浮动overflow、网易注册界面基本结构搭建
  • 原文地址:https://www.cnblogs.com/kakayang/p/12165742.html
Copyright © 2020-2023  润新知