初识electron

electron文档:https://github.com/electron/electron 
文档里面有中文的,所以还是蛮容易理解的。 
文档主要就是介绍electron项目的结构如下: 
这里写图片描述
其中package.json是用来描述一些配置信息以及一些快速启动的指令等信息的 
main.js则是用来整个项目的主线程,用于创建窗口和处理系统事件。 
index.html就是布局啦。

运行

package.json文件里有这样一句描述: 
这里写图片描述 
这就是快速启动指令,只需要npm start就可以启动你的electron应用了。


创建渲染器进程

对于渲染器进程,大家给的理解是主线程是无法显示的,需要通过BrowserWindow来创建新的窗口,每一个窗口维护一个渲染器进程。 
我的文件目录如下: 
这里写图片描述 
app目录下的js/index.js就可以看作一个渲染器进程。

主进程和渲染器进程的区别

这里写图片描述


界面

界面可以通过html+css实现 
我的界面如下: 
这里写图片描述


选择文件存放文件夹

此处需要用到electron的一个模块dialog,但是由于其为主线程内可用的模块,所以在渲染器进程使用的时候必须加上.remote

remote 模块提供了一种在渲染进程(网页)和主进程之间进行进程间通讯(IPC)的简便途径。

所以在渲染器进程处加上以下语句获取dialog模块

const {dialog} = require('electron').remote
 
 
  • 1

选择文件夹具体语句如下

button_choose.addEventListener("click", function(){
    dialog.showOpenDialog({
        //默认路径
        defaultPath :'../Desktop',
        //选择操作,此处是打开文件夹
        properties: [
            'openDirectory',
        ],
        //过滤条件
        filters: [
            { name: 'All', extensions: ['*'] },
        ]
    },function(res){
        //回调函数内容,此处是将路径内容显示在input框内
        downloadFolder.value = res[0];
    })
});
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

实现下载文件

这就是我最爆炸的部分。完成这个应用的一半时间都贡献给了这部分。 
先进行简单的校验

button_download.addEventListener("click", function(){
    var tips = document.getElementsByClassName("tips")[0];
    if(downloadFolder.value!=""
       &&downloadAddress.value!="") {
        //下载文件
    } else if(downloadAddress.value=="") {
        tips.innerText = "未填写下载地址";
    } else {
        tips.innerText = "未选择文件夹"
    }
})
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

下载文件我使用的是will-download,寻找一个合适的模块实现真的很心累,这里就不赘述了。 
由于will-download是主进程的模块调用的,所以此处就需要主进程和渲染进程的通信。

ipcMain和ipcRenderer

通信原理如下:

// 后台进程
const {ipcMain} = require('electron')
ipcMain.on('create', (event, person) => {
  console.log('creating', person)    // 输出:"creating harttle"
  event.sender.send('born', person)
});

// 渲染进程
const {ipcRenderer} = require('electron')
ipcRenderer.on('born', (event, person) => {
  console.log(person, 'born')       // 输出 "harttle born"
});
ipcRenderer.send('create', 'harttle')
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

所以我的想法是,在主进程里监听一个download事件,在渲染进程里当点击下载时调用这个事件。

//主进程代码
ipcMain.on('download', (evt, args) => {
    var arr = args.split("+");
    downloadpath = arr[0];
    folderpath = arr[1];
    evt.sender.send('tips',downloadpath);      
    mainWindow.webContents.downloadURL(downloadpath);
});
//渲染器进程代码
ipcRenderer.send('download',downloadAddress.value+"+"+downloadFolder.value);
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

下面这句会触发will-download事件

mainWindow.webContents.downloadURL(downloadpath);
 
 
  • 1

下面来设置监听will-download事件的回调函数

mainWindow.webContents.session.on('will-download', (event, item, webContents) => {
    //设置文件存放位置
    item.setSavePath(folderpath+`\\${item.getFilename()}`);
  item.on('updated', (event, state) => {
    if (state === 'interrupted') {
      console.log('Download is interrupted but can be resumed')
    } else if (state === 'progressing') {
      if (item.isPaused()) {
        console.log('Download is paused')
      } else {
        console.log(`Received bytes: ${item.getReceivedBytes()}`)
      }
    }
  })
  item.once('done', (event, state) => {
    if (state === 'completed') {
      console.log('Download successfully')
    } else {
      console.log(`Download failed: ${state}`)
    }
  })
})
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
ipc到ipcMain和ipcRender

其实我一开始使用的是ipc进行两个进程之间的通信,但是不论我怎么定义,ipc总是报错,报其没有send和on方法。 
又疯狂找资料,遇到这个问题的人还真不多,所以没有找到解决方法。 
但是后来发现,与主进程的通信可以使用ipcMain,所以就弃用ipc了。 
找不到为什么错真的很伤自尊啊,希望下次可以发现。

主进程更改后刷新页面没有改变

这个的解决方法是退出应用然后重新npm start一次。 
这个很好理解吧,重新刷新页面只是更新了渲染器进程而非主进程。

item.setSavePath()问题

这个方法接受一个参数即文件存放路径。 
但是!一定保证路径里包含了文件名!一定! 
否则,就算路径不正确它也不会报错而是选择存放在默认路径下。

ipcMain和ipcRender的传参问题
ipcMain/ipcRender.send(eventname, arg);
 
 
  • 1

此处的arg一个参数而不是参数数组。 
所以由于要传下载地址和文件存放地址,我选择使用“+”把它们连接起来。


总结:

除了找出错在哪里其他地方还是蛮有趣的吧。 
感觉electron的机制和redux还是蛮像的,其中有主进程负责处理系统调用,而渲染进程负责触发事件。

点击打开链接




Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐