electron-vite 快速开始
代码存储仓库
https://gitee.com/fe521/electron-vite-start
环境安装
1. VSCode
官网:https://code.visualstudio.com/download
2. NodeJS
官网:https://nodejs.org/zh-cn/download/prebuilt-installer
点击这个下载地址进行下载:https://nodejs.org/dist/v20.16.0/node-v20.16.0-x64.msi
electron-vite 初始化项目
中文官网:https://cn.electron-vite.org/
快速开始:https://cn.electron-vite.org/guide/#搭建第一个-electron-vite-项目
1. npm create 创建项目
npm create @quick-start/electron@latest
2. 安装项目依赖
npm install --registry=https://registry.npmmirror.com
3. 启动项目
npm run dev
4. 修改 electron-builder 配置文件
electron-builder.yml 改为 electron-builder.config.js
module.exports = {
appId: "com.electron.app",
productName: "pc-app",
directories: {
buildResources: "build",
},
files: [
"!**/.vscode/*",
"!src/*",
"!electron.vite.config.{js,ts,mjs,cjs}",
"!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}",
"!{.env,.env.*,.npmrc,pnpm-lock.yaml}",
],
asarUnpack: ["resources/**"],
win: {
executableName: "pc-app",
nsis: {
artifactName: "${name}-${version}-setup.${ext}",
shortcutName: "${productName}",
uninstallDisplayName: "${productName}",
createDesktopShortcut: "always",
},
},
mac: {
entitlementsInherit: "build/entitlements.mac.plist",
extendInfo: {
NSCameraUsageDescription:
"Application requests access to the device's camera.",
NSMicrophoneUsageDescription:
"Application requests access to the device's microphone.",
NSDocumentsFolderUsageDescription:
"Application requests access to the user's Documents folder.",
NSDownloadsFolderUsageDescription:
"Application requests access to the user's Downloads folder.",
},
notarize: false,
},
dmg: {
artifactName: "${name}-${version}.${ext}",
},
linux: {
target: ["AppImage", "snap", "deb"],
maintainer: "electronjs.org",
category: "Utility",
},
appImage: {
artifactName: "${name}-${version}.${ext}",
},
npmRebuild: false,
publish: {
provider: "generic",
url: "https://example.com/auto-updates",
},
electronDownload: {
mirror: "https://npmmirror.com/mirrors/electron/",
},
};
以上代码会有这个问题
⨯ Invalid configuration object. electron-builder 24.13.3 has been initialized using a configuration object that does not match the API schema.
- configuration.win has an unknown property 'nsis'. These properties are valid:
object { additionalCertificateFile?, appId?, artifactName?, asar?, asarUnpack?, certificateFile?, certificatePassword?, certificateSha1?, certificateSubjectName?, compression?, cscKeyPassword?, cscLink?, defaultArch?, detectUpdateChannel?, electronLanguages?, electronUpdaterCompatibility?, executableName?, extraFiles?, extraResources?, fileAssociations?, files?, forceCodeSigning?, generateUpdatesFilesForAllChannels?, icon?, legalTrademarks?, protocols?, publish?, publisherName?, releaseInfo?, requestedExecutionLevel?, rfc3161TimeStampServer?, sign?, signAndEditExecutable?, signDlls?, signExts?, signingHashAlgorithms?, target?, timeStampServer?, verifyUpdateCodeSignature? }
How to fix:
1. Open https://www.electron.build/configuration/win
2. Search the option name on the page (or type in into Search to find across the docs).
* Not found? The option was deprecated or not exists (check spelling).
* Found? Check that the option in the appropriate place. e.g. "title" only in the "dmg", not in the root.
failedTask=build stackTrace=ValidationError: Invalid configuration object. electron-builder 24.13.3 has been initialized using a configuration object that does not match the API schema.
- configuration.win has an unknown property 'nsis'. These properties are valid:
object { additionalCertificateFile?, appId?, artifactName?, asar?, asarUnpack?, certificateFile?, certificatePassword?, certificateSha1?, certificateSubjectName?, compression?, cscKeyPassword?, cscLink?, defaultArch?, detectUpdateChannel?, electronLanguages?, electronUpdaterCompatibility?, executableName?, extraFiles?, extraResources?, fileAssociations?, files?, forceCodeSigning?, generateUpdatesFilesForAllChannels?, icon?, legalTrademarks?, protocols?, publish?, publisherName?, releaseInfo?, requestedExecutionLevel?, rfc3161TimeStampServer?, sign?, signAndEditExecutable?, signDlls?, signExts?, signingHashAlgorithms?, target?, timeStampServer?, verifyUpdateCodeSignature? }
How to fix:
1. Open https://www.electron.build/configuration/win
2. Search the option name on the page (or type in into Search to find across the docs).
* Not found? The option was deprecated or not exists (check spelling).
* Found? Check that the option in the appropriate place. e.g. "title" only in the "dmg", not in the root.
修复打包配置错误问题,移除多余的语言包、增加 extraResources 配置
const path = require("path");
const isWin32 = process.platform === "win32";
module.exports = {
appId: "com.electron.app",
productName: "pc-app",
directories: {
buildResources: "build",
},
files: [
"!**/.vscode/*",
"!src/*",
"!electron.vite.config.{js,ts,mjs,cjs}",
"!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}",
"!{.env,.env.*,.npmrc,pnpm-lock.yaml}",
],
artifactName: "pc-app-${os}-${arch}-${version}.${ext}",
asarUnpack: ["resources/**"],
extraResources: [
{
from: "./line",
to: "line",
},
],
electronLanguages: isWin32
? ["en", "zh-TW", "zh-CN", "en-US", "en-GB"]
: ["en", "zh_TW", "zh_CN", "en_US", "en_GB"],
win: {
icon: path.join(__dirname, "./build/icon.ico"),
target: [
{
target: "nsis",
arch: ["x64"],
},
],
},
nsis: {
oneClick: false, // 设置为 false 以提供安装类型选择界面,允许用户选择是否创建桌面图标,允许用户选择安装路径
perMachine: true, // 设置为 true 将使安装程序默认为所有用户安装应用,这需要管理员权限
allowToChangeInstallationDirectory: true, // 如果设置为 true,安装程序将允许用户更改安装目录
allowElevation: true, // 一般情况下,此字段不会被直接使用,权限提升主要依赖于 perMachine 的设定。当perMachine为true,安装程序会请求管理员权限
deleteAppDataOnUninstall: true, // 如果设置为 true,卸载程序将删除AppData中的所有程序数据
createDesktopShortcut: true, // 如果设置为 true,安装程序将在桌面上创建程序快捷方式
createStartMenuShortcut: true, // 如果设置为 true,安装程序将在开始菜单中创建程序快捷方式
shortcutName: "pc-app", // 设置在桌面和开始菜单中创建的快捷方式的名称
},
mac: {
entitlementsInherit: "build/entitlements.mac.plist",
extendInfo: {
NSCameraUsageDescription:
"Application requests access to the device's camera.",
NSMicrophoneUsageDescription:
"Application requests access to the device's microphone.",
NSDocumentsFolderUsageDescription:
"Application requests access to the user's Documents folder.",
NSDownloadsFolderUsageDescription:
"Application requests access to the user's Downloads folder.",
},
notarize: false,
},
dmg: {
artifactName: "${name}-${version}.${ext}",
},
linux: {
target: ["AppImage", "snap", "deb"],
maintainer: "electronjs.org",
category: "Utility",
},
appImage: {
artifactName: "${name}-${version}.${ext}",
},
npmRebuild: false,
publish: {
provider: "generic",
url: "https://example.com/auto-updates",
},
electronDownload: {
mirror: "https://npmmirror.com/mirrors/electron/",
},
};
package.json 指定 electron-builder --config=electron-builder.config.js 文件打包
{
"scripts": {
"format": "prettier --write .",
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"preview": "electron-vite preview",
"start": "npm run dev",
"dev": "nodemon --watch src/ --ext js,vue,html,css,scss --exec electron-vite dev",
"build": "electron-vite build",
"postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --config=electron-builder.config.js --dir",
"build:win": "npm run build && electron-builder --config=electron-builder.config.js --win",
"build:mac": "npm run build && electron-builder --config=electron-builder.config.js --mac",
"build:linux": "npm run build && electron-builder --config=electron-builder.config.js --linux"
}
}
INFO
指定配置文件 https://www.electron.build/cli
在使用 electron-builder
时,你可以通过指定配置文件的方式来使用不同的配置。默认情况下,electron-builder
会寻找项目根目录下的 electron-builder.yml
, electron-builder.json
, 或 electron-builder.config.js
文件作为配置文件。
如果你希望显式指定配置文件,你可以在命令行中使用 --config
选项。例如,假设你将配置文件保存为 build-config.js
,你可以在命令行中这样指定:
electron-builder --config build-config.js
这种方式允许你在构建项目时灵活选择和使用不同的配置文件。你可以将配置文件以 YAML、JSON 或 JavaScript 的格式保存,只需确保通过 --config
选项指定正确的路径和文件名即可。
5. .prettierrc.yaml 改成 .prettierrc.js
.prettierrc.js 内容如下
module.exports = {
// 使用单引号代替双引号
singleQuote: true,
// 在每行代码末尾添加分号
semi: true,
// 设置代码的最大打印宽度为100个字符
printWidth: 100,
// 在多行对象或数组的最后一项后面添加逗号
trailingComma: "all",
// 在对象大括号内不添加空格
bracketSpacing: false,
};
6. .eslintrc.cjs 修改成如下:
要让 ESLint 支持 Prettier 的配置,你需要将 Prettier 的规则集集成到 ESLint 中。以下是更新后的 ESLint 配置,包含对 Prettier 配置的支持:
/* eslint-env node */
require("@rushstack/eslint-patch/modern-module-resolution");
module.exports = {
extends: [
"eslint:recommended",
"plugin:vue/vue3-recommended",
"@electron-toolkit",
"@vue/eslint-config-prettier",
"prettier", // 确保 ESLint 支持并遵循 Prettier 的规则
],
rules: {
"vue/require-default-prop": "off",
"vue/multi-word-component-names": "off",
},
// ESLint 配置中添加对 Prettier 的自定义规则
prettier: {
singleQuote: true, // 使用单引号代替双引号
semi: true, // 在每行代码末尾添加分号
printWidth: 100, // 设置代码的最大打印宽度为100个字符
trailingComma: "all", // 在多行对象或数组的最后一项后面添加逗号
// 在对象大括号内不添加空格
bracketSpacing: false,
},
};
主要更改包括:
- 在
extends
中添加'prettier'
以确保 ESLint 配置支持并遵循 Prettier 的规则。 - 在
prettier
配置中添加自定义规则以确保代码风格一致。
这个配置文件确保 ESLint 和 Prettier 的规则不会冲突,并且可以保持代码风格的一致性。
7. 格式化代码
npm run format
PS C:\Users\Administrator\Desktop\pc-app> npm run format
> pc-app@1.0.0 format
> prettier --write .
.eslintrc.cjs 30ms (unchanged)
.prettierrc.js 1ms (unchanged)
.vscode/extensions.json 1ms (unchanged)
.vscode/launch.json 3ms (unchanged)
.vscode/settings.json 1ms (unchanged)
electron-builder.config.js 3ms (unchanged)
electron.vite.config.mjs 3ms (unchanged)
package-lock.json 53ms (unchanged)
package.json 1ms (unchanged)
README.md 16ms (unchanged)
src/main/index.js 10ms (unchanged)
src/preload/index.js 2ms (unchanged)
src/renderer/index.html 14ms (unchanged)
src/renderer/src/App.vue 7ms (unchanged)
src/renderer/src/assets/base.css 18ms (unchanged)
src/renderer/src/assets/main.css 8ms (unchanged)
src/renderer/src/components/Versions.vue 4ms (unchanged)
src/renderer/src/main.js 1ms (unchanged)
PS C:\Users\Administrator\Desktop\pc-app>
8. 打包 Windows 安装包
npm run build:win
PS C:\Users\Administrator\Desktop\pc-app> npm run build:win
> pc-app@1.0.0 build:win
> npm run build && electron-builder --win
> pc-app@1.0.0 build
> electron-vite build
vite v5.4.1 building SSR bundle for production...
✓ 2 modules transformed.
out/main/index.js 1.48 kB
✓ built in 42ms
vite v5.4.1 building SSR bundle for production...
✓ 1 modules transformed.
out/preload/index.js 0.42 kB
✓ built in 5ms
vite v5.4.1 building for production...
✓ 11 modules transformed.
../../out/renderer/index.html 0.55 kB
../../out/renderer/assets/electron-DtwWEc_u.svg 5.82 kB
../../out/renderer/assets/index-D5G5Cj71.css 6.55 kB
../../out/renderer/assets/index-Be6tFnq3.js 162.31 kB
✓ built in 288ms
• electron-builder version=24.13.3 os=10.0.22631
• writing effective config file=dist\builder-effective-config.yaml
• packaging platform=win32 arch=x64 electron=31.4.0 appOutDir=dist\win-unpacked
• downloading url=https://npmmirror.com/mirrors/electron/31.4.0/electron-v31.4.0-win32-x64.zip size=111 MB parts=8
• downloaded url=https://npmmirror.com/mirrors/electron/31.4.0/electron-v31.4.0-win32-x64.zip duration=9.824s
• building target=nsis file=dist\pc-app Setup 1.0.0.exe archs=x64 oneClick=true perMachine=false
• building block map blockMapFile=dist\pc-app Setup 1.0.0.exe.blockmap
electron-vite 自动热更新
主进程目前是无法热更新的,渲染进程如果是 Vue 内部的代码更新了则可以热更新。
所以,如果需要主进程修改后自动重新关闭、打开 app,可以使用此方案。
1. 安装 nodemon
npm install nodemon -D --registry=https://registry.npmmirror.com
2. 添加 nodemon 监听的命令
package.json
{
"scripts": {
"format": "prettier --write .",
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
"start": "electron-vite preview",
"dev": "nodemon --watch src/ --ext js,vue,html,css,scss --exec electron-vite dev",
"build": "electron-vite build",
"postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --config --dir",
"build:win": "npm run build && electron-builder --win",
"build:mac": "npm run build && electron-builder --mac",
"build:linux": "npm run build && electron-builder --linux"
}
}
3. 重新运行
npm run dev
electron-vite 多窗口
实现效果如下:
1. 修改 electron.vite.config.mjs
electron.vite.config.mjs 文件修改点如下
import { resolve } from "path";
import { defineConfig, externalizeDepsPlugin } from "electron-vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()],
},
preload: {
build: {
rollupOptions: {
input: {
index: resolve(__dirname, "src/preload/index.js"),
about: resolve(__dirname, "src/preload/about.js"),
},
},
},
plugins: [externalizeDepsPlugin()],
},
renderer: {
build: {
rollupOptions: {
input: {
index: resolve(__dirname, "src/renderer/index.html"),
about: resolve(__dirname, "src/renderer/about.html"),
},
},
},
resolve: {
alias: {
"@renderer": resolve("src/renderer/src"),
},
},
plugins: [vue()],
},
});
2. src/renderer/修改如下
1、增加 about.html 文件
src/renderer/about.html
src/renderer/about/main.js
src/renderer/about/App.vue
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>About</title>
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
/>
</head>
<body>
<div id="app"></div>
<script type="module" src="./about/main.js"></script>
</body>
</html>
2、修改 src/renderer/index.html 文件
src/renderer/src/
重命名为 src/renderer/index/
src/renderer/index.html
src/renderer/index/main.js
src/renderer/index/App.vue
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Electron</title>
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"
/>
</head>
<body>
<div id="app"></div>
<script type="module" src="./index/main.js"></script>
</body>
</html>
3. 增加 src/preload/about.js
import { contextBridge } from "electron";
import { electronAPI } from "@electron-toolkit/preload";
// Custom APIs for renderer
const api = {};
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld("electron", electronAPI);
contextBridge.exposeInMainWorld("api", api);
} catch (error) {
console.error(error);
}
} else {
window.electron = electronAPI;
window.api = api;
}
4. src/main/index.js 增加 createAboutWindow 函数
Details
import { app, shell, BrowserWindow, ipcMain } from "electron";
import { join } from "path";
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
import icon from "../../resources/icon.png?asset";
function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 900,
height: 670,
show: false,
autoHideMenuBar: true,
...(process.platform === "linux" ? { icon } : {}),
webPreferences: {
preload: join(__dirname, "../preload/index.js"),
sandbox: false,
},
});
mainWindow.on("ready-to-show", () => {
mainWindow.show();
});
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url);
return { action: "deny" };
});
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
console.log(
"process.env['ELECTRON_RENDERER_URL']",
process.env["ELECTRON_RENDERER_URL"] + "/index.html"
);
mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"]);
} else {
mainWindow.loadFile(join(__dirname, "../renderer/index.html"));
}
}
function createAboutWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 312,
height: 422,
show: false,
autoHideMenuBar: true,
...(process.platform === "linux" ? { icon } : {}),
webPreferences: {
preload: join(__dirname, "../preload/about.js"),
sandbox: false,
},
});
mainWindow.on("ready-to-show", () => {
mainWindow.show();
});
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url);
return { action: "deny" };
});
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
console.log(
"process.env['ELECTRON_RENDERER_URL']",
process.env["ELECTRON_RENDERER_URL"] + "/about.html"
);
mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"] + "/about.html");
} else {
mainWindow.loadFile(join(__dirname, "../renderer/about.html"));
}
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
// Set app user model id for windows
electronApp.setAppUserModelId("com.electron");
// Default open or close DevTools by F12 in development
// and ignore CommandOrControl + R in production.
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
app.on("browser-window-created", (_, window) => {
optimizer.watchWindowShortcuts(window);
});
// IPC test
ipcMain.on("ping", () => console.log("pong"));
createWindow();
createAboutWindow();
app.on("activate", function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
});
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
// In this file you can include the rest of your app"s specific main process
// code. You can also put them in separate files and require them here.
electron-vite 自定义 titlebar
1. 修改窗口为无边框
src/index/main.js
function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 900,
height: 670,
show: false,
frame: false,
autoHideMenuBar: true,
...(process.platform === "linux" ? { icon } : {}),
webPreferences: {
preload: join(__dirname, "../preload/index.js"),
sandbox: false,
},
});
// ...
}
2. 创建 titlebar 处理
提示:
特别注意这里:https://www.electronjs.org/zh/docs/latest/tutorial/window-customization#window-controls-overlay
VSCode 的最小化、缩放、最大化按钮是通过 titleBarStyle
和 titleBarOverlay
设置的
const { BrowserWindow } = require("electron");
const win = new BrowserWindow({
titleBarStyle: "hidden",
titleBarOverlay: {
color: "#2f3241",
symbolColor: "#74b1be",
height: 60,
},
});
titleBarOverlay
被设置后,只显示 最小化, 最大化/还原,关闭窗口的按钮
(1)、src/main/titlebar.js
Details
import { app, ipcMain, BrowserWindow } from "electron";
app.whenReady().then(() => {
ipcMain.on("minimize", (event) => {
const window = BrowserWindow.fromWebContents(event.sender);
window.minimize();
});
ipcMain.on("maximize", (event) => {
const window = BrowserWindow.fromWebContents(event.sender);
if (window.isMaximized()) {
window.unmaximize();
} else {
window.maximize();
}
});
ipcMain.on("close", (event) => {
const window = BrowserWindow.fromWebContents(event.sender);
window.close();
// 检查如果所有窗口都关闭了,退出应用
// if (BrowserWindow.getAllWindows().length === 0) {
// app.quit();
// }
});
});
(2)、src/main/index.js 引入
import { app, shell, BrowserWindow, ipcMain } from "electron";
import { join } from "path";
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
import icon from "../../resources/icon.png?asset";
import "./titlebar";
3. src/preload/index.js
import { ipcRenderer, contextBridge } from "electron";
import { electronAPI } from "@electron-toolkit/preload";
// Custom APIs for renderer
const api = {
minimize: () => ipcRenderer.send("minimize"),
maximize: () => ipcRenderer.send("maximize"),
close: () => ipcRenderer.send("close"),
};
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld("electron", electronAPI);
contextBridge.exposeInMainWorld("api", api);
} catch (error) {
console.error(error);
}
} else {
window.electron = electronAPI;
window.api = api;
}
4.编写 src/renderer/index/components/Titlebar.vue 组件
Details
<template>
<div class="titlebar">
<div class="title"></div>
<div class="controls">
<button @click="minimize">-</button>
<button @click="maximize">□</button>
<button @click="close" class="close-btn">×</button>
</div>
</div>
</template>
<script setup>
function minimize() {
window.api.minimize();
}
function maximize() {
window.api.maximize();
}
function close() {
window.api.close();
}
</script>
<style scoped>
.titlebar {
-webkit-app-region: drag;
display: flex;
justify-content: space-between;
align-items: center;
background-color: #eff4f9;
height: 30px;
}
.title {
font-size: 14px;
font-weight: bold;
}
.controls {
-webkit-app-region: no-drag;
display: flex;
}
.controls button {
width: 50px;
box-sizing: border-box;
height: 30px;
border: none;
background: transparent;
font-size: 20px;
cursor: pointer;
padding: 0;
display: flex;
align-items: center;
justify-content: center;
transition: all ease 0.1s;
outline: none;
}
.controls button:hover {
background-color: #e1e1e1;
}
.controls button.close-btn:hover {
background-color: #c42b1c;
color: #fff;
}
</style>
5. src/renderer/index/App.vue 引入 Titlebar.vue
Details
<template>
<div>
<Titlebar />
</div>
</template>
<script setup>
import Titlebar from "./components/Titlebar.vue";
</script>
<style scoped></style>
electron-vite 添加 @electron/remote 模块
⚠️ 警告!
此模块有许多微妙的陷阱。几乎总有比使用此模块更好的方法来完成你的任务。例如,ipcRenderer.invoke 可以满足许多常见的用例。
具体 @electron/remote 模块的使用条件和方法请查阅 https://www.npmjs.com/package/@electron/remote
以下仅是 electron-vite 中的使用方法
1. 安装@electron/remote
模块
npm i @electron/remote --registry=https://registry.npmmirror.com
2. src/main/index.js 添加以下高亮行代码修改
import { app, shell, BrowserWindow, ipcMain, session } from "electron";
import { join } from "path";
import { electronApp, optimizer, is } from "@electron-toolkit/utils";
import icon from "../../resources/icon.png?asset";
import "./titlebar";
require("@electron/remote/main").initialize();
function createWindow() {
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 900,
height: 670,
show: false,
frame: false,
autoHideMenuBar: true,
...(process.platform === "linux" ? { icon } : {}),
webPreferences: {
preload: join(__dirname, "../preload/index.js"),
sandbox: false,
webviewTag: true,
webSecurity: false,
nodeIntegration: true,
contextIsolation: false,
},
});
require("@electron/remote/main").enable(mainWindow.webContents);
mainWindow.on("ready-to-show", () => {
mainWindow.show();
});
mainWindow.webContents.setWindowOpenHandler((details) => {
shell.openExternal(details.url);
return { action: "deny" };
});
// HMR for renderer base on electron-vite cli.
// Load the remote URL for development or the local html file for production.
if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
console.log(
"process.env['ELECTRON_RENDERER_URL']",
process.env["ELECTRON_RENDERER_URL"] + "/index.html"
);
mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"]);
} else {
mainWindow.loadFile(join(__dirname, "../renderer/index.html"));
}
if (is.dev) {
mainWindow.webContents.openDevTools();
}
}
3. 渲染进程 @electron/remote
引入
src/main/preload/index.js 添加 require('@electron/remote');
代码引入
import {ipcRenderer, contextBridge} from 'electron';
import {electronAPI} from '@electron-toolkit/preload';
require('@electron/remote');
// Custom APIs for renderer
const api = {
minimize: () => ipcRenderer.send('minimize'),
maximize: () => ipcRenderer.send('maximize'),
close: () => ipcRenderer.send('close'),
loadPlugin: () => ipcRenderer.invoke('load-plugin'),
getPreloadJsPath: () => ipcRenderer.invoke('get-preload-js-path'),
isDev: () => ipcRenderer.invoke('isDev'),
};
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI);
contextBridge.exposeInMainWorld('api', api);
} catch (error) {
console.error(error);
}
} else {
window.electron = electronAPI;
window.api = api;
}
electron-vite 生成图标 electron-icon-builder
https://www.npmjs.com/package/electron-icon-builder
安装图标转换模块
npm install electron-icon-builder -D --registry=https://registry.npmmirror.com
pcakge.json 添加命令 electron-icon-builder
{
"scripts": {
"logo": "electron-icon-builder --input=./public/logo.png --output=./public/"
}
}
转换生成图标
npm run logo
electron-vite 加载本地资源
electron-vite 源码保护
官方文档:https://cn.electron-vite.org/guide/source-code-protection
1. 如何配置
import { defineConfig, bytecodePlugin } from "electron-vite";
export default defineConfig({
main: {
plugins: [bytecodePlugin()],
},
preload: {
plugins: [bytecodePlugin()],
},
renderer: {
// ...
},
});
提示
bytecodePlugin
仅适用于生产阶段构建并且只支持主进程和预加载脚本。 需要注意的是,预加载脚本需要禁用 sandbox
才能支持字节码,因为字节码是基于 Node 的 vm
模块实现。从 Electron 20 开始,渲染器默认会被沙箱化,所以如果你想使用字节码来保护预加载脚本,你需要设置 sandbox: false
。
2. 判断源码被保护?
1. 打包一个免安装包的
npm run build:unpack
2. 进入 dist\win-unpacked\resources\app.asar
所在的目录
3. 执行 asar extract app.asar app_extracted
解压 app.asar
文件
asar extract app.asar app_extracted
4. 对比前后的代码-被保护前后的代码
(1)被保护的源码,是二进制的,一般无法被破解,看不到源码:
(2)未开启保护源码,相当于裸奔:
electron-vite 配置别名
1. electron-vite 配置别名
import { resolve } from "path";
import {
defineConfig,
bytecodePlugin,
externalizeDepsPlugin,
} from "electron-vite";
import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";
import VueDevTools from "vite-plugin-vue-devtools";
import Components from "unplugin-vue-components/vite";
import { AntDesignVueResolver } from "unplugin-vue-components/resolvers";
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()],
},
preload: {
build: {
rollupOptions: {
input: {
index: resolve(__dirname, "src/preload/index.js"),
about: resolve(__dirname, "src/preload/about.js"),
},
},
},
plugins: [externalizeDepsPlugin()],
},
renderer: {
build: {
sourcemap: true, // 启用 source map
rollupOptions: {
input: {
index: resolve(__dirname, "src/renderer/index.html"),
about: resolve(__dirname, "src/renderer/about.html"),
},
},
},
resolve: {
alias: {
"@": resolve("src"),
"@renderer": resolve("src/renderer"),
"@index": resolve("src/renderer/index"),
},
},
plugins: [
vue(),
vueJsx(),
VueDevTools(),
Components({
resolvers: [
AntDesignVueResolver({
importStyle: false, // css in js
}),
],
}),
],
// server: {
// proxy: {
// '/api/': {
// target: 'http://172.20.5.37', // 目标服务器地址
// changeOrigin: true,
// },
// },
// },
},
});
2. VSCode 按住 ctrl 键不放+点击鼠标左键 跳转到指定的文件
默认情况下,有如下配置别名的路径,如果想要快速访问 utils.js,一般会 ctrl+我们会鼠标左键点击会直接进入文件内部,但是如果没有任何配置是不会生效的
import { getNextId } from "@/renderer/index/utils/utils";
想要在 ctrl+鼠标左键进入文件,需要项目根目录增加 jsconfig.json
文件,内容如下:
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": ["src/*"],
"@renderer/*": ["src/renderer/*"],
"@index/*": ["src/renderer/index/*"]
},
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
"allowJs": true
}
}