Electron怎么启动并行的子任务

更新日期: 2020-01-05阅读: 2.6k标签: Electron

有些场景下比如要处理一大堆文件内容的查找,字符串的替换,文件的修改等一系列耗时操作的时候,如果放在主进程执行那必然会导致渲染进程的阻塞,界面会出现卡死的现象,影响用户体验。那怎么能并行地去处理这些事情呢,可以通过node的多进程去实现。如果你在思考把这些任务放在新的渲染进程中去做,那么每次启动一个任务都要打开一个隐藏的窗口,还要考虑去怎么关闭,代价有点高。这里用一个清除缓存的例子介绍下具体步骤,功能很简单就是每次应用启动的时候清理一些指定的文件。


Node多进程

先简单介绍一下,node的多进程是通过child_process和cluster模块实现。child _process模块中创建子进程有这样几种方式,fork、spawn、exec和execFile。spawn可以指定子进程执行的文件路径,也可以传递参数,但是api使用起来要麻烦一点,fork与spawn功能类似,在它上面又做了一次封装,所以在传递参数上面要更简洁一些,exec和execFile两个差不多,都可以直接执行命令或者传入可执行文件的路径。cluster是属于集群的API,可以更方便地处理主进程和其他子进程之间的关系,就是更容易地处理负载问题,因为Node的多进程都是属于一个Master进程管理多个Worker进程的形式。


Electron中使用child_process模块

Electron中使用多进程有个坑,它不能在子进程中使用非Node标准模块的其他模块,比如第三方模块或者Electron中的模块,当你有这样的代码时就会出现错误,require('lodash')或者require('electron')。这是因为子进程中会有一个预设的环境变量,ELECTRON_RUN_AS_NODE=true,这样的话就会认为在Node的环境下执行,所以第三方模块和Electron是找不到的,而且这个值你是改不了的,可以参考这个issue,当你有这样的需求的时候就要考虑用一些其他的方式了。


生成任务脚本

Electron在应用程序打包后会在一个asar文件中,里面的文件目录是不能直接去引用的,所以如果启动的任务是一个文件的执行路径,那么这个文件需要放在一个可以直接读取的路径上,那就需要在启动或者其他的某个时间去把工程中的脚本拷贝到磁盘某个可以正常访问的路径上。
可以采取这样的作法,把已经写好的脚本放在工程的static目录下,这样打包后就会原封不动地放在<应用目录>/dist/electron/static目录下,然后把对应的文件拷贝到指定路径,例如我这里直接拷贝到了应用目录的父级目录,这里说的应用目录就是你的asar文件所在目录。

//copyUtils

const path = require('path');
const fs = require('fs');
import {app, dialog} from 'electron';

const FILE_NAME_LIST = ['clean.js'];

function copyFile(fileName, callback) {
    let fromPath = path.resolve(app.getAppPath(), 'dist', 'electron', 'static') + path.sep;
    let targetPath = path.resolve(app.getAppPath(), '../') + path.sep;
    let fromFileName = fromPath + fileName;
    let targetFileName = targetPath + fileName;
    if (!fs.existsSync(targetFileName)) {
        fs.readFile(fromFileName, (readErr, data) => {
            if (!readErr) {
                fs.writeFile(targetFileName, data, writeErr => {
                    if (writeErr) {
                        dialog.showErrorBox('WriteErr', writeErr.message);
                    } else {
                        callback(targetFileName);
                    }
                })
            } else {
                dialog.showErrorBox('ReadErr', readErr.message);
            }
        })
    } else {
        callback(targetFileName);
    }
}

export default {
    initScripts(callback) {
        if (process.env.NODE_ENV === 'production') {
            let allCount = DLL_NAME_LIST.length;
            let currentCount = 0;
            for (let item of DLL_NAME_LIST) {
                copyFile(item, name => {
                    ++currentCount;
                    if (currentCount === allCount) {
                        callback(true);
                    }
                });
            }
        }
    }
}

这个clean.js就是清理缓存的脚本。

// clean.js

process.on('message', folder => {
    // 接收一个目录然后去查找匹配的文件并删除,具体的就不贴了,无关紧要
    delete(folder);
    
    // 执行完自动退出
    process.exit(0)
});

console.log('clean task has created...');

当文件不存在的时候才会拷贝脚本,假如脚本存在但是已经被修改了,这时候是不知道的,那执行的时候就会出错,更好的做法是再校验一下文件的md5,如果文件损坏依然执行拷贝操作。

const crypto = require('crypto');
const fs = require('fs');

let hash = crypto.createHash('md5');
let buffer = fs.readFileSync('脚本目录');
hash.update(buffer);
let md5 = fsHash.digest('hex');
// 比较当前md5与预设的md5是否一致


部署脚本

可以选择在应用启动的时候执行文件的拷贝

// deployUtils

import {app} from 'electron'
import copyUtils from './copyUtils';
const path = require('path');
const cp = require('child_process');

const runtimeFolder = path.resolve(app.getAppPath(), '..') + path.sep;
const cleanProcessName = 'clean.js';

function startTask() {
    let cleanProcess = cp.fork(runtimeFolder + cleanProcessName);
    cleanProcess.send('清理的目录');
    // 有更多的任务可以一直继续fork追加
}

/**
 * 把所有.js结尾的文件都拷贝到指定目录
 * @param {String} srcDir 源文件目录 
 * @param {function} next 回调函数 
 */
function copyFile(srcDir, next) {
    fs.readdir(srcDir, (err, files) => {
        if (err) {
            console.log(err)
        } else {
            let index = 0;
            let targetCount = 0;
            for (let item of files) {
                if (item.endsWith('.js')) {
                    targetCount++;
                    let distFilePath = runtimeFolder + item;
                    // 同名文件会被覆盖
                    fs.copyFile(srcDir + path.sep + item, distFilePath, err => {
                        if (err) {
                            console.log(err)
                        } else {
                            index++;
                            if (index == targetCount) {
                                next();
                            }
                        }
                    })
                }
            }
        }
    })
}

export default {
    deploy() {
        if (process.env.NODE_ENV === 'production') {
            copyUtils.initScripts(() => {
                startTask();
            })
        } else {
            let fromDir = path.resolve(__dirname, '..', '..', 'static') + path.sep;
            new Promise(resolve => {
                copyFile(fromDir, resolve);
            }).then(() => {
                startTask();
            })
        }
    }
}

可以在创建窗口的时候启动

// src/main/index.js

import deployTask from './deployTask'

...

function createWindow() {
    deployTask.deploy();
}

链接: https://fly63.com/article/detial/7422

使用 Electron 打包 Vue 项目

新建一个 Vue 项目,安装 electron-builder,Electron 的main进程和renderer进程分开进行开发,可以通过ipcMain和ipcRenderer进行通信,数据流清晰,开发简单。

electron阻止应用关闭

这里直接监听onbeforeunload事件,每当页面刷新或关闭时,都会触发这个事件。方法二:我们在main.js中监听close事件。定义一个flag标识是否可以关闭。如果不可以关闭,则阻止该事件。

如何在Electron中调用Dll

客户端有些硬件的接口需要调试,是在电脑上连了一些硬件的设备,比如打印机、扫描仪或者进行串口通信等等。单靠JS是完成不了了,我们决定通过把C++或者C#把这些功能打包成Dll

electron_pcMain模块、ipcRenderer模块

ipcMain模块是EventEmitter类的一个实例。 当在主进程中使用时,它处理从渲染器进程(网页)发送出来的异步和同步信息。 从渲染器进程发送的消息将被发送到该模块。

electron将web应用构建跨平台桌面应用

Electron是由Github开发,用HTML,CSS和JavaScript来构建跨平台桌面应用程序的一个开源库。 Electron通过将Chromium和Node.js合并到同一个运行时环境中,并将其打包为Mac,Windows和Linux系统下的应用来实现这一目的。

Electron Windows增加托盘悬浮框功能

在做Electron Windows 桌面应用时候,做鼠标悬浮到托盘图标上时显示一个悬浮框(例如做消息提醒),但因为Windows没有提供托盘mouse-enter/mouse-leave事件,无法直接做这个功能,考虑到还有mouse-move事件,弄个间接的方式实现。

Electron应用打包、自动升级

使用electron builder打包只需要在vue.config.js中配置即可,这里需要注意的是,默认情况下electron builder打包出来的安装程序是不能修改安装目录的,需要allowToChangeInstallationDirectory这个配置设置为true

基于Electron开发Hosts切换工具的“踩坑”之旅

用过好几个Hosts切换工具,但总是有点这样那样的问题。最讨厌的莫过于切换完后,键盘都快按坏了,浏览器里面的Hosts就是不变,网上找了好多方法,但是感觉都并不完美,于是就有了这篇文章。

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!