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( { width: 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.on('minimize',(ev:any)=>
{
if (GWin.MainWindow !=null)
{
console.log('on minimize...');
GWin.MainWindow.setSkipTaskbar(true);
GWin.MainWindow.hide();
}
ev.preventDefault();
});
创建系统托盘 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)
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>
width:150px; background: green; position : fixed; top:10px; right :10px; -webkit-user-select : none; -webkit-app-region : drag;
width: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
如果只是需要自定义拖拽移动区: