前言
使用Electron开发的时候常见有两种方式
- 使用VUE脚手架创建项目后添加
vue-cli-plugin-electron-builder
插件来构建Vue单页项目 - 直接加载本地HTML,HTNL中可以引用
vue.js
两种方式各有利弊,第一种方式适合单窗口项目,如果使用了多个窗口,每个窗口都加载了整个VUE项目,因为VUE脚手架创建的是单页项目,对应的路由,VUEX都需要重新初始化。但是可以轻松把前端项目打包。如果是单独的小应用还是建议用加载HTML的方式。
当我们要自动下载网站文件的时候,很多网站需要用户认证的,认证的信息保留在Cookie中,如果我们使用electron+vue来做(vue使用脚手架搭建),那么请求会自动携带webview中登录的cookie,但是如果是直接加载的本地html就需要自己获取cookie后添加后请求,为了获取加载网页中的元素,我们要注入preload.js,但是如果是electron+vue,preload.js的路径要用file://
协议来引用,不能用相对路径来引用,所以我在两种方式都测试后选择了直接引用本地html的方式了。
注意
本文基于Electron9.0版本,10以后的版本
remote
组件废弃了,需要单独引用。
创建项目
尽量用图形化界面创建项目 安装插件也方便
1 | vue ui |
安装插件
vue-cli-plugin-electron-builder
插件官网地址: https://nklayman.github.io/vue-cli-plugin-electron-builder/
Choose Electron Version
选择默认
即可
运行报错
INFO Launching Electron…
Failed to fetch extension, trying 4 more times
Failed to fetch extension, trying 3 more times
Failed to fetch extension, trying 2 more times
Failed to fetch extension, trying 1 more times
Failed to fetch extension, trying 0 more times
Vue Devtools failed to install: Error: net::ERR_CONNECTION_TIMED_OUT
这是因为Devtools的安装需要翻墙
注释掉src/background.js
中的以下代码就行了
1 | if (isDevelopment && !process.env.IS_TEST) { |
安装本地Dev-Tools插件
插件下载地址
链接:https://pan.baidu.com/s/19BzaBnZsWZxN_thHHvSYBw
提取码:psvm
插件放在项目根目录
1 | app.on("ready", async () => { |
调试框分离
1 | mainWindow.webContents.openDevTools({mode:'detach'}); |
隐藏菜单栏
1 | import { Menu } from "electron"; |
设置标题
窗口加载页面之前用的是窗口的title,加载之后用的是页面的title,所以最好两处都设置。
窗口的标题
1 | const win = new BrowserWindow({ |
页面的标题
1 | <head> |
加载页面
官方文档:https://www.electronjs.org/docs/api/webview-tag
页面添加webview
1 | <webview |
配置中开启webview标签
1 | const win = new BrowserWindow({ |
Preload
注意
- Electron-Vue项目在运行时页面是以URL加载的,那么加载
preload.js
就必须用file://
协议加载- 一定要先设置preload再打开页面,当然同时设置也是可以的
- preload.js方法返回的数据不能是DOM,否则报错
渲染页面中获取
文件放在public下
加载要调用的JS
file:///Users/zhangjian/psvmc/app/me/web/91crawler2/public/mypreload.js
mypreload.js
文件放在了项目根目录的public文件夹下
1 | window.showData = function () { |
加载js和网页
1 | const { app } = window.require("electron").remote; |
调用其中的方法获取返回数据
1 | myfun: function() { |
主进程设置
mypreload.js
文件放在了项目根目录的public文件夹下
1 | function createWindow () { |
渲染页面
1 | this.preload_path = remote.getGlobal("shareObject").preload_path; |
打包设置
如果没使用vue-cli-plugin-electron-builder
,就要添加打包插件与配置
electron-builder
1 | npm install electron-builder --save-dev |
在package.json
中做如下配置
1 | "build": { |
打包注意事项
- 打包win环境下nsis需要图标为ico格式。
- 图标大小最好512*512 打包mac要求。
- 打包nsis的LICENSE.txt不能为空,文件编码为GBK,否则乱码。
依赖无法下载
手动下载放到对应位置
Mac
1 | cd ~/Library/Caches/electron-builder |
Linux
1 | cd ~/.cache/electron-builder |
Windows
1 | cd %LOCALAPPDATA%\electron-builder\cache |
相关下载地址
链接: https://pan.baidu.com/s/15YnlnLIrt_16Xvrmo3LLMQ 密码: 0hk3
下载的文件
nsis-3.0.4.1.7z
nsis-resources-3.4.1.7z
winCodeSign-2.6.0.7z
wine-4.0.1-mac.7z
最终目录结构
nsis
- nsis-3.0.4.1
- nsis-resources-3.4.1
winCodeSign
- winCodeSign-2.6.0
- wine
- wine-4.0.1-mac
获取页面Cookie
页面
1 | <webview |
JS
1 | const { session } = window.require("electron").remote; |
也可以使用默认session
1 | <webview |
js
1 | const { session } = window.require("electron").remote; |
注意
webview和外层BrowserWindow内是可以共享session和cookie的。
弹窗选择文件
1 | selectFile: function() { |
下载文件
下载文件有两种方式
方式1 调用浏览器下载
1 | downloadfileByUrl: function(murl) { |
监听下载进度方式
1 | session.defaultSession.on("will-download", (event, item) => { |
官方说的设置下载位置后就不会弹出选择下载位置弹窗,但是在渲染进程中并不生效(补充:在主进程中有效)
1 | item.setSavePath(filePath); |
优缺点
这种方式能保证下载文件名称中文不会乱码,但是官方给出的取消默认的下载行为再手动下载的方式行不通,后来发现是在渲染层的session的will-download中不能下载行为或者是取消弹窗,但是在主进程里是可以的。
也就是说渲染进程中可以获取下载进度但是没法设置下载位置,
所以在下载地址需要重定向获取的前提下可行的方案有
- 在主线程中设置文件保存的位置,渲染进程中获取文件的下载进度。
- 主线程获取真正的下载地址后调用
event.preventDefault();
取消默认的下载,手动用NodeJS下载。
主进程中的配置
1 | const path = require("path"); |
获取文件下载路径后取消下载,把下载地址发送到渲染进程中
1 | win.webContents.session.on("will-download", (event, item) => { |
那会不会是session对象不一致呢
1 | const { remote } = window.require("electron"); |
在渲染进程中获取webContent.session
进行监听,回调中设置存储位置依旧会出现选择下载位置的弹窗,所以
event.preventDefault();
和item.setSavePath(filePath);
只能在主进程中生效。
方式2 使用NodeJS下载
目前我使用的就是这种方式,推荐使用。
但是如果使用加载静态页面加载到window中的页面无法共享webview中的cookie
对于下载文件地址会重定向,所以使用了follow-redirects
这个库。
1 | downloadfileByUrl: function(murl) { |
注意
下载文件时要用流式下载
优缺点
这种方式能够完全自己管控下载的位置及流程,但是要注意文件名乱码问题及直接加载HTML方式请求未携带cookie的问题。
文件名乱码解决方式
NodeJS获取content-disposition
中的文件名中文乱码
的解决方法
1 | const iconv = require("iconv-lite"); |
设置Cookie
如果Electron加载本地静态页面中请求是无法携带Cookie,就需要我们自己填上Cookie的头
1 | getcookie: function () { |
下载
1 | downloadfileByUrl: function (murl, callback) { |
加载项目下文件
所有能获取项目路径的方法
1 | console.info(app.getAppPath()); |
从结果中可以看出
process.execPath
的结果一直都是browser
process.cwd()
的结果一直都是/
所以这两个对我们没用
__dirname
开发状态下获取的路径不能用,打包之后这个获取的都是项目下的路径类似于项目目录/resources/app.asar
,考虑到同时兼容开发和打包两种情况下也不能采用这个唯一对我们有用的就是
app.getAppPath()
,但是Windows和Mac环境下的斜杠方向是不一致的,路径拼接依旧有问题
比如我要加载的js在项目的根目录下的public/mypreload.js
代码中就可以按下面的方法获取到绝对路径
1 | const path = require("path"); |
但是这样在Windows环境斜杠的方向是正好相反的path.join([...paths])
的时候一直是错误的,用path.normalize(path)
也不行,并且官网上path.sep
说的在不同平台上路径片段分隔符也是无效的。
1 | const path = require("path"); |
还要在配置里设置忽略
项目下的vue.config.js
文件中配置,没有该文件的话新建即可
1 | module.exports = { |
关键代码
1 | extraResources: { |
path.join()和path.resolve()的区别
引用
1 | const path = window.require('path'); |
对比
path.join()
会根据平台自动拼接地址path.resolve()
相当于一直在执行cd
,如果路径中包含/
开始就相当于到了根目录
remote失效
Electron12中 remote失效
NOTE: @electron/remote
requires Electron 10 or higher.
The
remote
module is deprecated. Instead ofremote
, useipcRenderer
andipcMain
.Read more about why the
remote
module is deprecated here.If you still want to use
remote
despite the performance and security concerns, see @electron/remote.
使用方式
下载依赖
1 | $ npm install --save @electron/remote |
在主进程中初始化
1 | // in the main process: |
渲染进程中替换代码
1 | // in the renderer process: |
附录
package.json
1 | { |