前言
之前用的是Electron9-13的版本,现在Electron23很多API发生了变化,所以这里重新记录一下。
官方文档:
https://electron.nodejs.cn/docs/latest/
使用Electron开发的时候常见有两种方式
- 使用VUE脚手架创建项目后添加
vue-cli-plugin-electron-builder插件来构建Vue单页项目
- 直接加载本地HTML,HTML中可以引用
vue.js
注意
本文基于Electron23版本,10以后的版本remote组件废弃了,需要单独引用。
替代方案:https://www.psvmc.cn/article/2023-09-13-electron-remote.html
创建项目
尽量用图形化界面创建项目 安装插件也方便
安装插件
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 2 3 4 5 6 7 8
| if (isDevelopment && !process.env.IS_TEST) { try { await installVueDevtools(); } catch (e) { console.error("Vue Devtools failed to install:", e.toString()); } }
|
升级版本
默认vue-cli-plugin-electron-builder安装的Electron版本最高支持13.0.0
调试框分离
1
| win.webContents.openDevTools({ mode: "detach" });
|
插件下载地址
链接:https://pan.baidu.com/s/19BzaBnZsWZxN_thHHvSYBw
提取码:psvm
插件放在项目根目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| app.on("ready", async () => { if (isDevelopment && !process.env.IS_TEST) { try { const { session } = require("electron"); session.defaultSession.loadExtension( path.resolve(__dirname, "../vue-devtools") ); } catch (e) { console.error("Vue Devtools failed to install:", e.toString()); } } createWindow(); });
|
隐藏菜单栏
1 2
| import { Menu } from "electron"; Menu.setApplicationMenu(null);
|
设置标题
窗口加载页面之前用的是窗口的title,加载之后用的是页面的title,所以最好两处都设置。
窗口的标题
1 2 3 4 5 6 7 8 9 10 11 12
| const win = new BrowserWindow({ width: 1200, height: 600, title: "爬虫", webPreferences: { webviewTag: true, webSecurity: false, enableRemoteModule: true, nodeIntegration: true, contextIsolation: false, }, });
|
页面的标题
1 2 3 4 5 6 7
| <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <link rel="icon" href="<%= BASE_URL %>favicon.ico"> <title>爬虫</title> </head>
|
预加载脚本
这里使用webview加载页面,使用预加载加载脚本实现数据的获取。
预加载JS
文件放在public下
加载要调用的JS
根目录/public/mypreload.js
这里只所以放在public下是因为:public下的所有文件在打包时都会放在app.asar的根目录。
我们可以在dist_electron/bundled下查看参与打包的文件。
mypreload.js
文件放在了项目根目录的public文件夹下
1 2 3 4 5 6 7 8 9 10 11 12 13
| const { contextBridge } = require("electron");
contextBridge.exposeInMainWorld("electronAPI", { showData: () => { const a_arr = document.getElementsByTagName("a"); let urlArr = []; for (let i = 0; i < a_arr.length; i++) { let aItem = a_arr[i]; urlArr.push(aItem.href); } return JSON.stringify(urlArr); }, });
|
注意
- Electron-Vue项目在运行时页面是以URL加载的,那么加载
preload.js就必须用file://协议加载
- 一定要先设置preload再打开页面,当然同时设置也是可以的
- preload.js方法返回的数据不能是DOM,否则报错
窗口设置
配置中开启webview标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| const path = require("path");
const { ipcMain } = require("electron"); ipcMain.on("getGlobalValue", (event, arg) => { event.returnValue = global.shareObject[arg]; });
async function createWindow() { const isDev = process.env.NODE_ENV === "development"; let preloadPath = ""; if (isDev) { preloadPath = path.join(__dirname, "..", "public", "mypreload.js"); } else { preloadPath = path.join(app.getAppPath(), "mypreload.js"); } global.shareObject = { preloadPath: preloadPath, };
const win = new BrowserWindow({ width: 1280, height: 800, webPreferences: { webviewTag: true, webSecurity: false, nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION, contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION, }, });
if (process.env.WEBPACK_DEV_SERVER_URL) { await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL); if (!process.env.IS_TEST) win.webContents.openDevTools({ mode: "detach" }); } else { createProtocol("app"); win.loadURL("app://./index.html"); } }
|
注意
开发和打包时预加载脚本的位置不一样要进行判断。
我们可以在dist_electron/bundled下查看参与打包的文件,打包后会生成app.asar文件。
app.getAppPath()就是获取打包后app.asar的根目录位置。
注意不要在BrowserWindow设置预加载脚本,这里设置的在webview中无效
1 2 3 4 5 6 7 8 9 10 11
| const win = new BrowserWindow({ width: 1280, height: 800, webPreferences: { webviewTag: true, webSecurity: false, nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION, contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION, preload: path.join(__dirname, "..", "public", "mypreload.js"), }, });
|
渲染页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| <template> <div class="wv_outer"> <webview ref="mwv" class="webview" nodeintegration disablewebsecurity ></webview>
<div class="btn_outer"> <button @click="readData">获取数据</button> </div> </div> </template>
<script> const { ipcRenderer } = window.require("electron");
export default { name: "HomeView", components: {}, data() { return { weburl: "https://www.qcc.com/web/bigsearch/tec-search", url_arr: [], }; }, mounted() { this.loadPreloadJs(); this.openUrl(); }, methods: { loadPreloadJs() { let preloadPath = ipcRenderer.sendSync("getGlobalValue", "preloadPath"); const mwv = this.$refs["mwv"]; mwv.preload = "file://" + preloadPath; }, openUrl() { const mwv = this.$refs["mwv"]; mwv.src = this.weburl; mwv.addEventListener("dom-ready", () => { }); }, readData() { const mwv = this.$refs["mwv"]; mwv .executeJavaScript("window.electronAPI.showData();") .then(function (data) { console.info(data); }); }, }, }; </script>
<style> .wv_outer { width: 100%; height: 100%; display: flex; flex-direction: column; } webview { height: 0; flex: auto; }
.btn_outer { height: 40px; flex: none; }
.btn_outer button { width: 100px; height: 36px; background: #42b983; color: white; border: none; border-radius: 8px; } </style>
|
打包设置
打包
如果使用vue-cli-plugin-electron-builder,直接打包就行。
vue.config.js 中配置
生成文件夹
直接生成文件夹比较快,生成后我们可以用专门的打包软件进行打包。
个人比较喜欢使用这种方式。因为默认使用NSIS打包安装效果不太好。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const { defineConfig } = require("@vue/cli-service"); module.exports = defineConfig({ transpileDependencies: true, pluginOptions: { electronBuilder: { builderOptions: { productName: "爬虫客户端", copyright: "Copyright © 2025 psvmc", appId: "cn.psvmc.crawler_client", win: { target: [ { target: "dir", arch: ["x64"], }, ], icon: "public/icons/app.ico", }, }, }, }, });
|
生成单一执行文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const { defineConfig } = require("@vue/cli-service"); module.exports = defineConfig({ transpileDependencies: true, pluginOptions: { electronBuilder: { builderOptions: { productName: "爬虫客户端", copyright: "Copyright © 2025 psvmc", appId: "cn.psvmc.crawler_client", win: { target: [ { target: "portable", arch: ["x64"], }, ], icon: "public/icons/app.ico", }, }, }, }, });
|
生成EXE
这里使用NSIS来打包EXE,打包过程中会下载依赖,会比较费时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const { defineConfig } = require("@vue/cli-service"); module.exports = defineConfig({ transpileDependencies: true, pluginOptions: { electronBuilder: { builderOptions: { productName: "爬虫客户端", copyright: "Copyright © 2025 psvmc", appId: "cn.psvmc.crawler_client", win: { target: [ { target: "nsis", arch: ["x64"], }, ], icon: "public/icons/app.ico", }, artifactName: "${productName}-${version}-${platform}-${arch}.${ext}", }, }, }, });
|
生成32位和64位
打包
打包注意事项
- 打包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
wine
警告
Electron项目中 vue3页面使用webview会报警告
Failed to resolve component: webview
忽略自定义标签webview
在vue.config.js中添加编译忽略的标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const { defineConfig } = require("@vue/cli-service"); module.exports = defineConfig({ lintOnSave: false, transpileDependencies: true, chainWebpack: (config) => { config.module .rule("vue") .use("vue-loader") .tap((options) => { options.compilerOptions = { isCustomElement: (tag) => { return ["webview"].includes(tag); }, }; return options; }); }, });
|