在上一篇什么是electron,vue如何应用electron中我们已经了解了electron的一些基本信息和在Vue配置electron的一些基本操作,现在由我们来进入正式的项目阶段。
我们拿上一篇创建好的Vue架子。
具体内容预览:
- 页面布局
- electron项目用到的功能封装
- 路由配置
- 请求拦截器封装
emm.. 由于项目比较简单,我就不画流程图、逻辑图之类的了,本猴最近995的厉害,见谅见谅。
界面布局以及一些简单逻辑我就略过了,后续整理好会放GitHub连接供大家clone
快速搭建Vue前端页面
页面结构:
部分界面如下:
electron常用功能
在electron目录下新增lib目录,作为常用的函数存放地址,按照百度云的功能,我们重点关注的功能有:
- 下载
- 设置下载路径
- 下载中途暂停
- 取消下载
- 断点恢复下载
- 应用基础功能
我们需要了解到如下API:
- ipcMain: 主进程,可以接收Vue端发送过来的消息
- ipcRenderer:可以通过本身发送消息给主进程,当然也可以收到主进程回复的消息(主要用于Vue端分发事件)
- reply: 主进程用来回复视图端份发过来的事件异步处理结果。
根据这三个API,就可以实现主进程和视图逻辑层之间的通信
下载
我们翻看electron的文档,发现其下载功能Session中,首先我们先将其引入, 并且写成一个函数封起来,方便复用。
const { session } = require('electron')
const downfile = (url) => {
session.defaultSession.downloadURL(url)
}
复制代码
利用session.defaultSession.on 去监听下载,并且获取下载时的参数和状态,用于更新视图
session.defaultSession.on('will-download', (e, item) => {
try {
const url = item.getURL()
let cacheItem = cacheDownItem[url] || { // 获取当前下载参数(后续会解释这个参数从哪里来)
notSend: true
}
// 获取文件的总大小
const totalBytes = item.getTotalBytes()
// 设置下载路径
const filePath = path.join(app.getPath('downloads'), item.getFilename())
item.setSavePath(filePath)
// 更新状态并且缓存downitem
cacheItem._downFileItem = item
cacheItem.path = item.getSavePath() // 文件路径
cacheItem.eTag = item.getETag() // eTag
cacheItem.urlChain = item.getURLChain() // 项目的完整URL链,包括任何重定向
cacheItem.length = totalBytes
cacheItem.lastModified = item.getLastModifiedTime() // 剩余时间
cacheItem.startTime = item.getStartTime() // 下载总耗时
let lastBytes = 0
// 监听下载过程,计算并设置进度条进度
item.on('updated', (event, state) => {
if (state === 'interrupted') { // 是否下载中断
cacheItem.state = 'interrupted' // 更新下载数据缓存状态
} else if (state === 'progressing') { // 下载进度
if (item.isPaused()) { // 是否被暂停了
cacheItem.state = 'paused'
} else { // 保存当前下载的行为参数
const offset = item.getReceivedBytes() // 已经下了多少
cacheItem.state = 'downing' // 正在下载
cacheItem.speedBytes = offset - lastBytes // 剩余多少
cacheItem.progress = parseInt((offset / totalBytes) * 100)
cacheItem.offset = offset
lastBytes = offset
}
}
!cacheItem.notSend && win.webContents.send('update-down-state', JSON.parse(JSON.stringify(cacheItem)))
})
// 下载完成
item.once('done', (event, state) => { // 更新当前下载对象状态,用于后续视图更新
cacheItem.done = 'end'
switch (state) {
case 'interrupted':
cacheItem.state = 'interrupted-err'
break
case 'cancelle':
cacheItem.state = 'cancelle'
break
default:
cacheItem.state = 'completed'
notification(cacheItem.path)
break
}
!cacheItem.notSend && win.webContents.send('update-down-state', JSON.parse(JSON.stringify(cacheItem)))
// 请求后端改变文件状态
axios.post('http://1localhost:3000/file/dow_file',
{ file_obj: cacheDownItem[url] },
{
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
// 删除缓存
delete cacheDownItem[url]
cacheItem = null
item = null
})
// 恢复
if (item.canResume) {
item.resume()
}
} catch (error) {
console.log(error)
}
})
复制代码
设置下载路径
const { app, ipcMain, shell, dialog } = require('electron') // 引入所需要的API
ipcMain.on('set_path', (e, data = {}) => { // 主进程监听vue派发的事件
const { path } = data
if (path) {
if (path !== 'not') app.setPath('downloads', path)
e.reply('set_path', app.getPath('downloads'))
} else {
dialog.showOpenDialog({ // 打开文件窗口
title: '选择下载目录',
defaultPath: app.getPath('downloads'),
properties: ['openDirectory']
}).then((files) => {
if (!files.canceled) { // 如果有选中
app.setPath('downloads', files.filePaths[0])
}
e.reply('set_path', files) // 设置完成 回复vue端,方便vue端做处理
})
}
})
复制代码
下载中途暂停
ipcMain.on('down-file-pause', function(e, data) { // 主进程监听vue派发的事件
const { url } = data
const t = cacheDownItem[url] // 拿到缓存上的当前下载的参数
if (t) {
t._downFileItem.pause() // 更新其状态
}
e.reply('down-file-pause-' + url, '已暂停') // 通知派发端
})
复制代码
取消下载
逻辑如上
ipcMain.on('down-file-cancel', function(e, data) {
const { url } = data
const t = cacheDownItem[url]
if (t) {
t._downFileItem.cancel()
} else {
// 删除未下在完成文件
}
e.reply('down-file-cancel-' + url, '已取消下载')
})
复制代码
恢复下载
ipcMain.on('resume-download', function(e, data) {
const { url } = data
const t = cacheDownItem[url]
if (t) {
t._downFileItem.resume()
} else {
cacheDownItem[url] = { ...data }
resumeDownload(data)
}
e.reply('down-file-resume-' + url, '已恢复下载')
})
复制代码
应用基础功能
// 打开调试
ipcMain.on('toggle_dev_tools', function(event, arg) {
win.webContents.toggleDevTools()
})
// 重启
ipcMain.on('restart', function() {
app.relaunch()
app.exit(0)
})
// 最小化
ipcMain.on('min', function() {
win.minimize()
})
// 最大化
ipcMain.on('max', function() {
if (win.isMaximized()) {
win.unmaximize()
} else {
win.maximize()
}
})
// 关闭程序
ipcMain.on('close', function() {
cacheDownItemClose()
win.close()
})
复制代码
以上就是本项目所用到的部分electron功能,后续还会再加
路由配置
路由采用的是哈希路由和路由懒加载
开发环境:
生产环境:
配置:
请求拦截器封装
import axios from 'axios'
import store from '@/store'
import { Message } from 'element-ui'
import { getToken } from './tokne'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 1000000
})
service.interceptors.request.use(
config => {
config.headers['Content-Type'] = 'application/json;charset=UTF-8'
store.getters.token && (config.headers['Authorization'] = `Bearer ${getToken()}`)
return config
},
error => {
Message({
message: error || '网络错误',
duration: 1000,
type: 'error'
})
return Promise.reject(error)
}
)
service.interceptors.response.use(
response => {
const res = response.data || response
const errMsg = res.msg || '请求失败!'
if (res.code && res.code !== 200) {
Message({
message: errMsg || '请求失败',
duration: 1000,
type: 'error'
})
return Promise.reject('error')
} else {
return Promise.resolve(res)
}
},
error => {
if (error.request.status === 401) { store.dispatch('resetToken') }
Message({
message: error || '请求失败',
duration: 1000,
type: 'error'
})
return Promise.reject(error)
}
)
export default service
复制代码
项目的基础配置就讲到这里啦,下篇预告:node+express+mysql 基本搭建,如果对此感兴趣的话,请关注本专栏或者关注本猴,能追踪到后续篇章哦!
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END