zoukankan      html  css  js  c++  java
  • Electron/Nodejs开发笔记-功能问题记录及指南

    这篇随笔记录一下electron + vue与nodejs 的一些开发的过程和问题..随缘更新

    最近基于Electron做一个针对UE4应用的进程守护和更新启动器...

    花费大量了时间的处理来UI线程堵塞的问题上了(吐槽感觉Nodejs IO和线程不是hin好用..)

    涉及的技术点估计以后不会怎么常用,所以写篇随笔整理一下

      electron启动更新器:

        github: https://github.com/linqingwudiv1/electron-startup

    ----------------------------------------割-------------------------------------------------------------

    Electron+ Vue环境:

      Vue环境目前有俩个比较好用的开发环境:

        1.electron-vue:    https://github.com/SimulatedGREG/electron-vue

        2.Vue + Vue CLI Plugin Electron Builder

      区别

        electron-vue 是基于vue-cli 2.0的快速开发框架

        Plugin Electron Builder是vue-cli 3.X的electron 脚手架插件.

      我用的是 Electron Builder...

      Vue cli 3.x版本的Vue + Vue CLI Plugin Electron Builder整合的Electron应用

         插件使用文档    :  https://github.com/nklayman/vue-cli-plugin-electron-builder

            VS Code 断点调试配置指南:    https://nklayman.github.io/vue-cli-plugin-electron-builder/guide/recipes.html#table-of-contents

        Electron  Vue DevTools配置指南:https://github.com/vuejs/vue-devtools/blob/dev/packages/shell-electron/README.md

    参考:

     Q.开发记录:

    1.electron的渲染进程xmlhttprequest的cors问题/开启nodejs和Worker线程等:

      win = new BrowserWindow(
      { 
         800, 
        height: 600, 
        backgroundColor: '#ffffff',
        webPreferences: {
          nodeIntegration: true,
          webSecurity: false, //cors
          nodeIntegrationInWorker: true //
        }
      });

     2.Nodejs/electron下 解压zip

    使用 adm-zip-ex解压实例:

    const AdmZip = require('adm-zip-ex');
    let zip = new AdmZip("d://Test.zip");
    zip.extractAllTo('d:/temp');

     adm-zip-ex单个entry的异步解压示例:

    let zip = new AdmZip('c:temp.zip');
    let zipEntries = zip.getEntries(); 
    zipEntries.forEach( ( zipEntry:any, index:number ) => 
    {
        if ( zipEntry == null ) 
        {
          return;
        }
        const entryPath = join( 'c:/',  zipEntry.entryName);
        if ( zipEntry.isDirectory )
        {
          return;
        }
        let path = dirname( entryPath );
        // unzip entry......
        zip.extractEntryToAsync(zipEntry, path , true, (err:any) =>
        {
          if ( err != undefined )
          {
            console.log(err);
            return;
          }
          //do something.....
        
        });
    });

    3.nodejs怎么进行流文件下载:

      用nodejs的http模块或electron的net模块都可以,但是太底层了,建议用 request或request-promise库,是对于nodejs的网络模块封装的,ts版本:@types/request 文档:https://github.com/request/request

      http下载文件:

    request('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'))

      如果有大文件下载需求,请添加 request-progress-ex模块,跟request组合使用..  

         https://www.npmjs.com/package/request-progress-ex

      附 request-promise帮助类:

    import request, { RequestPromiseOptions } from 'request-promise';
    
    let options:RequestPromiseOptions = {
        baseUrl: process.env.APP_BIZ_BASE_API,
        qs: {
            //access_token: 'xxxxx xxxxx' // -> uri + '?access_token=xxxxx%20xxxxx'
        },
        headers: {
            'User-Agent': 'Request-Promise'
        },
        json: true // Automatically parses the JSON string in the response
    };
    
    let services =request.defaults(options);
    export default services;

    4.worker_threading(线程):

       https://github.com/wilk/microjob/blob/master/GUIDE.md

    5.进程通讯的基本例子

     note:也可以使用remote调用main process进程功能或模块.但是不推荐这么做,因为破坏了electron的封装性

        https://electronjs.org/docs/api/ipc-main

      remote例子:

    import {remote} from 'electron';
    const { app } = remote;

    6.electron 怎么系统托盘app?

    例子(typescript):

      Browser事件处理:(close事件可以根据自身情况处理)

      GWin.MainWindow是我自己封装的BrowserWindow类
            GWin.MainWindow.on('minimize',(ev:any)=>
            {
              if (GWin.MainWindow !=null)
              {
                console.log('on minimize...');
                GWin.MainWindow.setSkipTaskbar(true);
                GWin.MainWindow.hide();
              }
              ev.preventDefault();
            });
              GWin.MainWindow.on('closed', () => {
                GWin.MainWindow = null
              });
     

      

    创建系统托盘 tray类:

      electron:隐藏任务栏条 setSkipTaskbar(true)

      electron:显示任务栏条setSkipTaskbar(false)

      note:注意new Tray('ico.jpg')时,ico.jpg必须存在,否则托盘图标将不显示..无法点击

          GWin.TrayIcon = new Tray('ico.jpg'); 
          const contextMenu = Menu.buildFromTemplate([
            { 
              label: '显示', 
              //type: 'radio',
              click:()=>
              {
                if(GWin.MainWindow != null)
                {
                  GWin.MainWindow.show();
                  GWin.MainWindow.setSkipTaskbar(false);
                }
              } 
            },
            { 
              label: '退出', 
              //type: 'radio' 
              click:()=>
              {
                app.quit();
              }
            }
          ])
    
          GWin.TrayIcon.setToolTip('更新启动器');
          GWin.TrayIcon.setContextMenu(contextMenu);

     

    7.NodeJS从文件中读取json:

    ts

    let jsonStr:string = readFileSync('UE/version.json', { encoding: 'utf-8' });

    js

    let jsonStr = fs.readFileSync('UE/version.json', { encoding: 'utf-8' });

    8.Electron持久化配置到磁盘文件:

      其实可以用nodejs模块自己IO到json文件,但是比较麻烦,需要自己封装接口

      而且已经有相关的electron-store类库了..

        https://github.com/sindresorhus/electron-store

        typescript 版本:

    npm install @types/electron-store

      electron-store 每次set 或get都是从disk文件读写配置信息

      简单的使用示例:(建议写成这样的单例)

    /**
     * Electron-Store的配置内容
     */
     interface SystemStore {
        CacheDir: string;
    }
    
    /**
     *  整个App的全局变量,或函数 
     */
    export default class GApp
    {
    
        /** 系统持久化配置实例 */
        private static sysStore?:Store<SystemStore> = undefined;
        
        /** Get 系统持久化配置单例 */
        public static get SystemStore():Store<SystemStore>
        {
          if (GApp.sysStore == undefined)
          {
            GApp.sysStore = new Store<SystemStore>({
              defaults: {
                CacheDir: GApp.RootDir + '/cache/'
              }
            });
          }
          return GApp.sysStore;   
        }
    }

    9.Nodejs获取指定文件的所在目录:

    import {dirname} from 'path';
    let dir = dirname(`d:/test/ttt/helloworld.png`);

    10.Nodejs递归创建目录:

      nodejs 的mkdir()目前还不能 创建多级路径...

      如C:/a/b/c 如果C:/a/b 路径还不存在则报错.. 

      (v13.x)

    实现:

      虽然也可以自己写递归调用mkdir(),

      但还是推荐使用 shelljs 类库 用mkdir脚本创建路径...(linux环境需要加 -p)

    
    
     import { mkdir } from 'shelljs';
      import {existsSync } from 'fs';
     import {resolve} from 'path';
      if (!existsSync( GApp.SystemStore.get('CacheDir') ) )
      {
        let stdout = mkdir('-p', resolve(`路径`)).stdout;
      }

    Q.Electron无边框模式并自定义标题栏:

    官方参考:

    https://electronjs.org/docs/api/frameless-window

    Electron自定义标题栏非常简单..

    Step1:开启无边框模式:

    let win = new BrowserWindow({ titleBarStyle: 'customButtonsOnHover', frame: false })

    Step2:render进程编写标题栏Div和处理事件

    并添加关键css

        -webkit-user-select : none;
        -webkit-app-region  : drag;

    这两个css属性会自动识别为可拖拽区,使之能像标题栏一样随意移动窗口..

    如:

        <div class="titlebar-btn-group">
          <el-button id="btn-close" size="medium" type="text" icon="el-icon-close"></el-button>
        </div>
        150px;
        background: green;
        position  : fixed;
        top:10px;
        right :10px;
        -webkit-user-select : none;
        -webkit-app-region  : drag;
        150px;
        background: green;
        position  : fixed;
        top:10px;
        right :10px;
        -webkit-user-select : none;
        -webkit-app-region  : drag;

    Note:

    如果只是希望把菜单栏移到标题栏上,推荐使用:
    Electron自定义标题栏 (Custom Title Bar)插件:https://github.com/AlexTorresSk/custom-electron-titlebar

    如果只是需要自定义拖拽移动区:

     https://github.com/kapetan/electron-drag

  • 相关阅读:
    Intellij IDEA 使用总结
    Apache Camel之FTP组件学习
    谈一谈EasyUI中TreeGrid的过滤功能
    JAVA实用案例之图片水印开发
    三、SolrCloud的搭建
    style里面设置变量
    for 循环中 break-continue 与label标签的使用
    vue ref的用法
    Vuex实践
    vue 数据动态响应(Vue.set方法)
  • 原文地址:https://www.cnblogs.com/linqing/p/12017986.html
Copyright © 2011-2022 走看看