相关文章推荐
宽容的水煮肉  ·  46. ...·  19 小时前    · 
鼻子大的小狗  ·  Spring Security ...·  6 月前    · 
光明磊落的黄豆  ·  【未成年人】孩子总咳不停,可能得了鼻后滴漏综合征·  9 月前    · 
爽快的哑铃  ·  哈尔滨工程大学2012年高招各省录取分数线及 ...·  1 年前    · 
谦逊的跑步机  ·  羚羊工业互联网平台- 安徽产业网·  1 年前    · 
干练的薯片  ·  风姿物语女主角·  1 年前    · 
小百科  ›  Electron + Vue3 开发桌面应用_electron vue
桌面应用开发 命令模式 cookie electron https
温文尔雅的橡皮擦
9 月前
  • 一、搭建项目
    • 1、创建 Vue 项目
    • 2、安装 Electron
    • 3、运行 Electron
    • 4、Vue 和 Electron 结合
    • 5、其它配置
    • 6、其它问题
      • (1)热更新失效、控制台报错
    • 二、打包
      • 1、安装 electron-builder
      • 2、打包配置
        • (1)修改公共基础路径
        • (2)修改 Electron 配置
        • (3)打包配置
        • (3)打包
      • 3、其它问题
        • (1)web和客户端同时部署
        • (2)刷新页面后报错问题
        • (3)不带协议的 url 被解析为 file 协议问题
        • (4)存取 `cookie` 问题
        • (5)限制只能打开一个窗口
        • (6)关闭窗口时提示
      • 三、代码签名
          • (1)windows
          • (2)mac
        • 四、自动更新

        Vite: https://vitejs.cn
        Vue : https://cn.vuejs.org
        Electron: https://www.electronjs.org/
        electron-builder: https://github.com/electron-userland/electron-builder
        concurrently: https://github.com/open-cli-tools/concurrently

        电脑: MacBook Pro (13-inch, M1, 2020)
        系统: macOS 11.6
        Node 版本: v16.14.0
        npm 版本: v7.1.2
        Electron 版本: v20.1.0
        Vue 版本: v3.2.37

        一、搭建项目

        1、创建 Vue 项目

        参考: https://cn.vuejs.org/guide/quick-start.html

        确保你安装了最新版本的 Node.js,然后在命令行中运行以下命令:

        npm init vue@latest 
        

        这一指令将会安装并执行 create-vue,它是 Vue 官方的项目脚手架工具。你将会看到一些诸如 TypeScript 和测试支持之类的可选功能提示,本项目只做示例,所以只选了 Vue Router :

        ✔ Project name: … electron_vue3_test
        ✔ Add TypeScript? … No
        ✔ Add JSX Support? … No
        ✔ Add Vue Router for Single Page Application development? … Yes
        ✔ Add Pinia for state management? … No
        ✔ Add Vitest for Unit Testing? … No
        ✔ Add Cypress for both Unit and End-to-End testing? … No
        ✔ Add ESLint for code quality? … No
        ✔ Add Prettier for code formatting? … No
        

        在项目被创建后,通过以下步骤安装依赖并启动开发服务器:

        cd <your-project-name>
        npm install
        npm run dev
        

        执行后终端显示如下:

          VITE v3.1.2  ready in 333 ms
          ➜  Local:   http://localhost:5173/
          ➜  Network: use --host to expose
        

        访问 http://localhost:5173/ 显示页面如下所示,Vue 项目创建成功

        如果想要修改端口号,修改 vite.config.js 文件的 server.port 选项为你想要的端口号,这里我改成了 3004

        export default defineConfig({
          // ...
          server: {
            port: 3004
        });
        

        2、安装 Electron

        参考:https://www.electronjs.org/zh/docs/latest/tutorial/installation

        执行以下命令安装 Electron :

        npm install electron --save-dev
        

        如果安装过程中卡住,可尝试设置以下环境变量

        ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
         

        mac 修改环境变量的方法:
        在 终端 中输入 vim ~/.zshrc 打开文件
        按 i 进入编辑模式
        添加一行 export ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/"
        按 esc 退出编辑模式
        输入 :wq 保存并退出
        在 终端 输入 source ~/.zshrc 使刚才的修改生效

        3、运行 Electron

        参考:https://www.electronjs.org/zh/docs/latest/tutorial/quick-start

        在根目录创建 electron 文件夹,新建 index.html 文件,添加如下代码:

        <!DOCTYPE html>
            <meta charset="UTF-8" />
            <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
              http-equiv="Content-Security-Policy"
              content="default-src 'self'; script-src 'self'"
              http-equiv="X-Content-Security-Policy"
              content="default-src 'self'; script-src 'self'"
            <title>Hello from Electron renderer!</title>
          </head>
            <h1>Hello from Electron renderer!</h1>
            <p>👋</p>
          </body>
        </html>
        

        在 electron 目录下新建文件 main.js,并添加如下代码:

        const { app, BrowserWindow } = require('electron')
        const path = require("path")
        const createWindow = () => {
          const mainWindow = new BrowserWindow({
            width: 800,
            height: 600,
          // 使用 loadFile 加载 electron/index.html 文件
          mainWindow.loadFile(path.join(__dirname, "./index.html"));
        // 在应用准备就绪时调用函数
        app.whenReady().then(() => {
          createWindow()
        

        修改 package.json 文件,指定 electron/main.js 为 Electron 的入口文件,并添加 electron:dev 命令以开发模式运行 Electron:

        "main": "electron/main.js", "scripts": { "electron:dev": "electron ."

        然后打开终端,执行 npm run electron:dev ,显示窗口如下所示,Electron 运行成功

        4、Vue 和 Electron 结合

        删除 electron/index.html 文件,修改 electron/main.js 文件代码如下:

        const { app, BrowserWindow } = require('electron')
        // const path = require("path")
        const createWindow = () => {
          const mainWindow = new BrowserWindow({
            width: 800,
            height: 600,
          // 主要改了这里
          // mainWindow.loadFile(path.join(__dirname, "./index.html"));
          // 使用 loadURL 加载 http://localhost:3004 ,也就是我们刚才创建的 Vue 项目地址
          // 3004 改为你 Vue 项目的端口号
          mainWindow.loadURL("http://localhost:3004/");
        app.whenReady().then(() => {
          createWindow()
        

        有时开发需要,会使用 nginx 代理为线上地址,则将 loadURL 的参数改为代理的地址,并在 electron/main.js 中添加如下代码:

        参考:https://stackoverflow.com/questions/49637026/mainwindow-loadurlhttps-localhost3000-show-white-screen-on-electron-app/49673272

        // 证书的链接验证失败时,触发该事件
        app.on
        
        
        
        
            
        (
          "certificate-error",
          function (event, webContents, url, error, certificate, callback) {
            event.preventDefault();
            callback(true);
        

        打开终端,执行 npm run dev 启动 Vue 项目,再新建一个终端,执行 npm run electron:dev 启动 Electron ,即可成功启动,显示如下:

        这样每次启动项目需要分别执行两个命令,有些麻烦,我们使用 concurrently把它们合并成一个命令。

        参考:https://github.com/open-cli-tools/concurrently

        执行以下命令,安装 concurrently:

         npm install concurrently --save-dev
        

        修改 package.json 文件中的 electron:dev 命令,同时执行 vite 和 electron . 两个命令,用引号将单独的命令括起来,并使用 \ 转义引号,代码如下:

        // package.json
          "scripts": {
            "electron:dev": "concurrently vite \"electron .\""
        

        重新执行 npm run electron:dev ,即可成功显示。

        问题:
        这里我在后续开发的时候发现一个问题,由于项目代码越来越多导致 Vue 启动较慢,Electron 启动后 Vue 还没有启动完成,页面就会显示 502,所以我又做了些处理(如果有更好的解决办法欢迎分享):

        在 Mac 系统下可以修改为 "electron:dev": "concurrently vite \"sleep 1 && electron .\" ,sleep 1 代表延迟1秒执行,时间可根据你的需要修改;

        在 Windows 系统下可以修改为 "electron:dev": "concurrently vite \"ping 127.0.0.1 -n 2 > nul && electron .\" ,这里使用 ping 命令来创建一个延迟效果。-n 2 代表 ping 命令发送 2 个数据包,每个数据包之间的时间间隔大致在 1 秒左右,所以发送 2 个数据包可以近似达到 2 秒的延迟。这个数字可以根据需要进行调整。一开始试图使用 timeout \t 1 的命令达到延迟效果,但一直报错,所以改用 ping。

        对于 Mac 和 Windows 同时使用的情况,可以写个脚本,新建 server/dev.js 文件,添加代码如下:

        // server/dev.js
        const { execSync } = require("child_process");
        const os = require("os");
        function runCommands(command1, command2, sleepTime = 1) {
          let sleepCommand = "";
          if (os.platform() === "win32") {
            // Windows 上使用 ping 创建延迟效果
            // 一开始使用 timeout -t 1 命令,但一直报错,所以改用 ping
            sleepCommand = `ping 127.0.0.1 -n ${sleepTime + 1} > nul`;
          } else {
            // Mac 和其他 Unix-like 系统上使用 sleep 命令
            sleepCommand = `sleep ${sleepTime}`;
          const fullCommand = `concurrently ${command1} "${sleepCommand} && ${command2}"`;
          execSync(fullCommand, {
            stdio: "inherit",
            maxBuffer: 2 * 1024 * 1024,
        runCommands("vite", "electron .", 1);
        

        修改命令为 "electron:dev": "node server/dev.js" 。

        5、其它配置

        以下是开发环境下,我的一些其它配置,可供参考:

        // electron/main.js
        const { app, BrowserWindow, Menu, screen } = require("electron");
        const path = require("path");
        // 是否是生产环境
        const isPackaged = app.isPackaged;
        // 禁止显示默认菜单
        Menu.setApplicationMenu(null);
        // 主窗口
        let mainWindow;
        const createWindow = () => {
          // 创建浏览器窗口
          mainWindow = new BrowserWindow({
            // 默认窗口标题,如果由loadURL()加载的HTML文件中含有标签<title>,此属性将被忽略。
            title: "Electron + Vue3",
            // width: 800,
            // height: 600,
            // 设置窗口尺寸为屏幕工作区尺寸
            width: screen.getPrimaryDisplay().workAreaSize.width,
            height: screen.getPrimaryDisplay().workAreaSize.height,
            // 设置最小尺寸
            minWidth: 800,
            minHeight: 600,
            // 窗口图标。 在 Windows 上推荐使用 ICO 图标来获得最佳的视觉效果, 默认使用可执行文件的图标.
            // 在根目录中新建 build 文件夹存放图标等文件
            icon: path.resolve(__dirname, "../build/icon.ico"),
          });
          // 开发环境下,打开开发者工具。
          if (!isPackaged) {
            mainWindow.webContents.openDevTools();
          // 使用 loadURL 加载 http://localhost:3004 ,也就是我们刚才创建的 Vue 项目地址
          // 3004 改为你 Vue 项目的端口号
          // mainWindow.loadURL("http://localhost:3004/");
          // 如果使用了 nginx 代理,url 改为代理地址
          mainWindow.loadURL("https://example.com/");
        // 在应用准备就绪时调用函数
        app.whenReady().then(() => {
          createWindow();
          app.on("activate", () => {
            // 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他
            // 打开的窗口,那么程序会重新创建一个窗口。
            if (BrowserWindow.getAllWindows().length === 0) createWindow();
          });
        });
        // 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。
        app.on("window-all-closed", () => {
          if (process.platform !== "darwin") app.quit();
        });
        // 如果开发环境使用了 nginx 代理,禁止证书报错
        if (!isPackaged) {
          // 证书的链接验证失败时,触发该事件
          app.on(
            "certificate-error",
            function (event, webContents, url, error, certificate, callback) {
              event.preventDefault();
              callback(true);
        

        6、其它问题

        (1)热更新失效、控制台报错

        我使用了 nginx 代理,开发时出现了热更新失效的问题,并且控制台报了如下错误

        WebSocket connection to 'wss://example.com/' failed: 
        WebSocket connection to 'wss://localhost:3004/' failed: 
        [vite] failed to connect to websocket.
        your current setup:
          (browser) example.com/ <--[HTTP]--> localhost:3004/ (server)
          (browser) example.com:/ <--[WebSocket (failing)]--> localhost:3004/ (server)
        Check out your Vite / network configuration and https://vitejs.dev/config/server-options.html#server-hmr 
        

        已经通过在 nginx 的配置文件中添加如下代码解决:

        参考:https://blog.csdn.net/weixin_39621860/article/details/111490058

        server {
            # ...
            location / {
                # ...
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_set_header Host $host;
        

        1、安装 electron-builder

        参考:https://www.electron.build/#installation

        打开终端,执行一下命令安装:
        使用 yarn (官方推荐):

        yarn add electron-builder --dev
        

        或是 npm :

        npm install electron-builder --save-dev
        

        2、打包配置

        (1)修改公共基础路径

        修改 vite.config.js 中的 base 选项为 “./”,如下所示:

        export default defineConfig({
          base: "./"
          // ...
        

        (2)修改 Electron 配置

        修改 electron/main.js 文件中 mainWindow.loadURL 的参数为 vue 打包后的文件路径

        // electron/main.js
        const path = require("path");
        const createWindow = () => {
          //...
          mainWindow.loadURL(`file://${path.join(__dirname, "../dist/index.html")}`);
        

        (3)打包配置

        在 package.json 中添加如下配置,可根据需求调整:

        参考:
        https://www.electron.build/#quick-setup-guide
        https://docs.npmjs.com/cli/v9/configuring-npm/package-json?v=true

        "name": "electron_vue3_test", "description": "Electron + Vue3 开发桌面应用", "author": "喵喵喵", "version": "1.0.0", "scripts": { // ... "electron:build": "npm run build && electron-builder", "postinstall": "electron-builder install-app-deps" // ... "build":{ "appId": "your.id", "productName": "Electron + Vue3 开发桌面应用", "files": [ "dist/**/*", "electron/**/*" "directories": { "output": "app_client" "mac": { "category": "public.app-category.music", "icon": "build/icon.icns", "target": [ "target": "dmg", "arch": [ "x64" "target": "zip", "arch": [ "x64" "win": { "icon": "build/icon.ico", "target": [ "target": "nsis", "arch": [ "x64", "ia32" "nsis": { "oneClick": false, "allowToChangeInstallationDirectory": true, "installerIcon": "build/icon.ico", "uninstallerIcon": "build/icon.ico"

        (3)打包

        打开终端,执行 npm run electron:build 打包,打包后的应用程序在 app_client 目录下,.dmg 文件为 mac 的安装包,如下所示:
        在这里插入图片描述

        3、其它问题

        (1)web和客户端同时部署

        如果要一套代码部署在web端和客户端,还需要再做些处理。

        在根目录下新增以下环境变量文件:
        .env.development 文件,web 端开发环境配置,文件内容如下:

        VITE_PLATFORM = 'web'
        

        .env.production 文件,web 端生产环境配置,文件内容如下:

        VITE_PLATFORM = 'web'
        

        .env.electron_production 文件,客户端生产环境配置,文件内容如下:

        VITE_PLATFORM = 'electron'
        

        修改 package.json 文件 scripts 下的 electron:build 命令为 vite build --mode electron_production && electron-builder。

        修改 vite.config.js 文件如下:

        import { defineConfig, loadEnv } from "vite";
        export default ({ mode }) => {
          const isWeb = loadEnv(mode, process.cwd()).VITE_PLATFORM === "web";
          const config = {
            base: isWeb ? "/" : "./",
            // ...
          return defineConfig(config);
        

        如果图片、css等资源文件需上传cdn,vite.config.js 文件还要增加如下代码:

        // ...
        export default ({ mode }) => {
          const isWeb = loadEnv(mode, process.cwd()).VITE_PLATFORM === "web";
          const config = {
          // ...
          if (isWeb) {
            config.experimental = {
              renderBuiltUrl(filename: string, { type }: { type: "public" | "asset" }) {
                if (type === "asset") {
                  return "https://example.com/" + filename;
          return defineConfig(config);
        

        (2)刷新页面后报错问题

        我的方法是监听加载失败和导航事件,使用 loadURL 重新加载页面,并禁止刷新页面的快捷键。

        在 electron/main.js 中判断如果是生产环境则做处理:

        // electron/main.js
        const { app, BrowserWindow } = require("electron");
        const isPackaged = app.isPackaged;
        let mainWindow;
        const createWindow = () => {
          mainWindow = new BrowserWindow({
          // ...
          // 加载文件
          function load() {
            mainWindow.loadURL(
              isPackaged
                ? `file://${path.join(__dirname, "../dist/index.html")}`
                : "https://example.com/"
          if (isPackaged) {
            // 生产环境下,load 的是 html 文件,要做特殊处理。
            // 加载失败之后触发
            mainWindow.webContents.on("did-fail-load", () => {
              load();
            // 当用户或页面想要导航时触发。
            // 它可能发生在 window.location 对象改变或用户点击页面上的链接时,可能会发生这种情况。
            // 当使用如 webContents.loadURL 和 webContents.back APIs 以编程方式导航时,将不会触发此事件。
            // 页面内导航也不会触发,例如点击锚点或更新 window.location.hash。 可使用 did-navigate-in-page 事件。
            mainWindow.webContents.on("will-navigate", (event, url) => {
              event.preventDefault();
              load();
            // 禁止使用快捷键刷新
            mainWindow.webContents.on("before-input-event", (event, input) => {
              mainWindow.webContents.setIgnoreMenuShortcuts(
                input.key.toLowerCase() === "f5" ||
                (input.control && input.key.toLowerCase() === "r") ||
                (input.meta && input.key.toLowerCase() === "r")
          load();
        

        (3)不带协议的 url 被解析为 file 协议问题

        项目接口中有很多不带协议名的 url 链接,应解析为 https 协议,但打包后被解析为 file 协议,在 onBeforeRequest 方法中对 url 做处理:

        // electron/main.js
        const { app, BrowserWindow } = require("electron");
        const isPackaged = app.isPackaged;
        let mainWindow;
        const createWindow = () => {
          mainWindow = new BrowserWindow({
          // ...
          if (isPackaged) {
            // 将 file://example.com/xxx 的 文件改为 https 协议
            session.defaultSession.webRequest.onBeforeRequest((details, callback) => {
              if (/^file:\/\/example\.com\//.test(details.url)) {
                callback({ redirectURL: details.url.replace(/^file/, "https") });
              } else {
                callback(details);
        

        (4)存取 cookie 问题

        网站有些功能用到了 cookie ,但打包后的应用程序读取的 html 文件为 file 协议,存取 cookie 会有问题。

        第一问题是请求接口时获取 cookie 和接收服务端响应时设置 cookie 的问题,我解决的办法是在 onBeforeSendHeaders 和 onHeadersReceived 方法中对 cookie 做处理,在网站域名下存取 cookie。

        在 electron 目录下新建 utils.js 文件,内容如下:

        const { session } = require("electron");
        // 将响应头中的 set-cookie 转成 set() 方法需要的 details 对象
        function getCookieDetails(cookie) {
          const details = {
            url: "https://example.com/",
          cookie = cookie.split("; ");
          cookie.forEach((item, index) => {
            item = item.split("=");
            if (index === 0) {
              details.name = item[0];
              details.value = item[1];
            } else if (["domain", "path"].includes(item[0])) {
              details[item[0]] = item[1];
            } else if (item[0] == "expires") {
              details.expirationDate = new Date(item[1]).getTime() / 1000;
            } else if (item[0] == "HttpOnly") {
              details.httpOnly = true;
            // HttpOnly 没有 item[1]  value 设为 true
          return details;
        module.exports = {
          // 是否是本站接口
          isWeapi: /^https:\/\/example\.com\/api\//,
          // 设置 cookies
          setCookies: function (cookies) {
            const setCookiePromise = [];
            cookies.forEach((cookie) => {
              const details = getCookieDetails(cookie);
              setCookiePromise.push(session.defaultSession.cookies.set(details));
            return Promise.all(setCookiePromise);
          // 获取 cookies,传给后端
          getCookies: async function () {
            const cookies = await session.defaultSession.cookies.get({
              domain: ".example.com"
            const cookiesArr = [];
            cookies.forEach((cookie) => {
              cookiesArr.push(`${cookie.name}=${cookie.value}`);
            return cookiesArr.join("; ");
        

        在 electron/main.js 中添加如下代码:

        const { app, BrowserWindow, session } = require("electron");
        const { isWeapi, setCookies, getCookies } = require("./utils.js");
        const isPackaged = app.isPackaged;
        let mainWindow;
        const createWindow = () => {
          mainWindow = new BrowserWindow({
          // ...
          if (isPackaged) {
            // 请求接口时,如果是本站接口,则获取 .example.com 中的 cookie,添加到请求头中
            session.defaultSession.webRequest.onBeforeSendHeaders(
              async (details, callback) => {
                if (isWeapi.test(details.url)) {
                  const cookies = await getCookies();
                  if (cookies) details.requestHeaders.cookie = cookies;
                callback({ requestHeaders: details.requestHeaders });
            // 接受到服务端响应时,如果是本站接口,则获取响应头中的 set-cookie,添加到 .example.com 的 cookie 中
            session.defaultSession.webRequest.onHeadersReceived(
              async (details, callback) => {
                  isWeapi.test(details.url) &&
                  "responseHeaders" in details &&
                  "set-cookie" in details.responseHeaders
                  await setCookies(details.responseHeaders["set-cookie"]);
                callback({ responseHeaders: details.responseHeaders });
        

        第二个问题是 document.cookie 获取不到,我在项目中使用的是 js-cookie 这个插件,我的解决方案是封装一个 Cookie对象,根据当前的协议类型判断使用哪种方法,如果是 file 协议,具体代码如下:

        // TODO: 待更新
        

        (5)限制只能打开一个窗口

        使用 requestSingleInstanceLock 方法判断是否已有实例在运行,并监听 second-instance ,当运行第二个实例时,聚焦到主窗口,在 electron/main.js 中添加如下代码:

        // 限制只能打开一个窗口
        const gotTheLock = app.requestSingleInstanceLock();
        if (!gotTheLock) {
          app.quit();
        } else {
          app.on("second-instance", () => {
            // 当运行第二个实例时,将会聚焦到 mainWindow 这个窗口
            if (mainWindow) {
              if (mainWindow.isMinimized()) mainWindow.restore();
              mainWindow.focus();
              // mainWindow.show();
          app.whenReady().then(() => {
          // ...
        

        (6)关闭窗口时提示

        监听 close 事件,使用 dialog 弹出提示,在 electron/main.js 中添加如下代码:

        // electron/main.js
        const { app, BrowserWindow, dialog } = require("electron");
        const isPackaged = app.isPackaged;
        let mainWindow;
        const createWindow = () => {
          mainWindow = new BrowserWindow({
          // ...
          // 在窗口要关闭的时候触发
          mainWindow.on("close", (e) => {
            e.preventDefault();
            dialog
              .showMessageBox(mainWindow, {
                type: "info",
                title: "退出提示",
                defaultId: 0,
                cancelId: 1,
                message: "确定要退出吗?",
                buttons: ["退出", "取消"],
              .then((res) => {
                if (res.response === 0) {
                  // e.preventDefault();
                  // mainWindow.destroy();
                  app.exit(0);
        

        三、代码签名

        参考:https://www.electron.build/code-signing

        (1)windows

        windows 的证书需要找有资质的商家购买,在此不做说明,只说一下签名配置。

        在 package.json 的 build 中添加如下代码,设置使用的签名算法,我用的是 sha256:

        "build": { "win": { "signingHashAlgorithms": [ "sha256"

        设置环境变量,CSC_LINK(证书地址) 和 CSC_KEY_PASSWORD(证书密码)。运行 npm run electron:build 即可打包签名的客户端。

        (2)mac

        参考:Notarizing your Electron application

        mac 代码签名需要申请 Apple Developer Program ,具体流程可参考苹果开发者公司账号申请全流程以及出现的问题,有问题就在客服页面打电话咨询客服。

        Apple Developer Program 申请完成后,要生成签名证书,参考Mac Electron 应用的签名(signature)和公证(notarization)。

        证书生成后,需要修改一下配置,使用 Electron Notarize 公证应用程序,打开终端,执行以下命令安装 Electron Notarize:

         npm install @electron/notarize --save-dev
        

        浏览器打开 https://appleid.apple.com/account/manage,生成 Apple 专用密码。

        打开终端,输入如下命令,其中 <USERNAME> 替换为你的 Apple Id,<PASSWORD> 替换为刚才生成的专用密码:

        security add-generic-password -a "<USERNAME>" -w "<PASSWORD>" -s "AC_PASSWORD"
        

        在 build 目录下新建 notarize.js 文件,并添加如下代码:

        const { notarize } = require("@electron/notarize");
        exports.default = async function packageTask(context) {
          const { electronPlatformName, appOutDir } = context;
          if (electronPlatformName !== "darwin") {
            return;
          const appName = context.packager.appInfo.productFilename;
          return await notarize({
            appBundleId: "your.id",
            appPath: `${appOutDir}/${appName}.app`,
            appleId: "[email protected]",
            appleIdPassword: `@keychain:AC_PASSWORD`,
        

        在 build 目录下新建 entitlements.mac.plist 文件,并添加如下代码:

        <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
        <plist version="1.0">
            <key>com.apple.security.cs.allow-jit</key>
            <true/>
            <key>com.apple.security.cs.debugger</key>
            <true/>
          </dict>
        </plist>
        

        在 package.json 的 build 中添加如下代码:

        "build": { "afterSign": "build/notarize.js", "mac": { "hardenedRuntime": true, "gatekeeperAssess": false, "entitlements": "build/entitlements.mac.plist",

        设置环境变量,CSC_LINK(证书地址) 和 CSC_KEY_PASSWORD(证书密码)。运行 npm run electron:build 即可打包签名的客户端。

        四、自动更新

        参考:Auto Update

        打开终端,执行以下命令安装 electron-updater:

        npm install electron-updater
        

        在 package.json 的 build 中添加如下代码,其中 url 为客户端更新地址:

        "build": { "publish": { "provider": "generic", "url": "https://example.com/app_client/", "channel": "latest"

        注:mac 必须进行代码签名、target 同时启用 dmg 和 zip,才能自动更新

        在 electron/main.js 文件中添加如下代码:

        const { app, BrowserWindow, dialog } = require("electron");
        const { autoUpdater } = require("electron-updater");
        const isPackaged = app.isPackaged;
        let mainWindow;
        const createWindow = () => {
          mainWindow = new BrowserWindow({
          // ...
          // 自动更新
          function handleUpdate() {
            // 更新地址
            const updateURL = "https://js.houzi8.com/app_client/";
            // 设置是否自动下载,默认是true,当点击检测到新版本时,会自动下载安装包,所以设置为false
            autoUpdater.autoDownload = false;
            // 如果安装包下载好了,那么当应用退出后是否自动安装更新
            autoUpdater.autoInstallOnAppQuit = true;
            // 是否接受开发版,测试版之类的版本号
            autoUpdater.allowPrerelease = false;
            // 是否可以回退版本,比如从开发版降到旧的稳定版
            autoUpdater.allowDowngrade = false;
            autoUpdater.setFeedURL(updateURL);
            autoUpdater.on("error", function (error) {
              // 检查更新出错
            autoUpdater.on("checking-for-update", function () {
              // 检查中
            autoUpdater.on("update-not-available", function (info) {
              // 已经是最新版
            autoUpdater.on("update-available", function (info) {
              // 检测到新版本
              dialog
                .showMessageBox(mainWindow, {
                  type: "info",
                  title: "更新提示",
                  defaultId: 0,
                  cancelId: 1,
                  message: "检测到新版本,是否立即更新?",
                  buttons: ["确定", "取消"],
                .then((res) => {
                  if (res.response === 0) {
                    // 执行下载安装包
                    autoUpdater.downloadUpdate();
            autoUpdater.on("download-progress", function (progress) {
              // 下载进度
            autoUpdater.on("update-downloaded", function (info) {
              // 新安装包下载完成
              dialog
                .showMessageBox(mainWindow, {
                  type: "info",
                  title: "更新提示",
                  defaultId: 0,
                  cancelId: 1,
                  message: "新版本下载完成,是否立即安装?",
                  buttons: ["确定", "取消"],
                .then((res) => {
                  if (res.response === 0) {
                    // 退出应用并安装更新
                    autoUpdater.quitAndInstall();
                    mainWindow.destroy();
            // 执行自动更新检查
            autoUpdater.checkForUpdates();
          if (isPackaged) {
            handleUpdate();
        

        配置完成后,执行 npm run electron:build 打包,将打包生成的文件上传到 https://example.com/app_client/ 即可。
        mac 下需上传的文件有:latest-mac.yml、Electron + Vue3 开发桌面应用-1.0.0-mac.zip、Electron + Vue3 开发桌面应用-1.0.0.dmg
        windows 下需上传的文件有:latest.yml、Electron + Vue3 开发桌面应用 Setup 1.0.0.exe

        参考文献:

        Vite
        Vue
        Electron
        electron-builder
        concurrently
        electron-vite-vue3-ts开发环境搭建
        mainWindow.loadURL(“https://localhost:3000/”) show white screen on Electron app
        vue热更新失效_vue-cli3热更新失效
        package.json 文档
        Notarizing your Electron application
        Apple Developer Program
        苹果开发者公司账号申请全流程以及出现的问题
        Mac Electron 应用的签名(signature)和公证(notarization)
        Electron Notarize

        ## 1. 安装vue-cli3(这里使用的是yarn,进行这一步的前提是安装好node和yarn) yarn global add @vue/cli vue --version (此命令意在查看vue的版本,可确认是否安装成功,) ## 2. 创建vue项目 vue create vue-electron-demo (vue-electron-demo是你要创建的项目名称) ##3 . 创建vue项目过程,选常用模块以及配置 此时需要选择项目的一些配置,选定敲回车,出现的顺序以及基本选择如下:(上下键,空格是选择,enter是确定) ? Please pick a preset:
        Electron是一个使用JavaScript,HTML和CSS构建桌面应用程序的框架。通过将Chromium和Node.js嵌入到其二进制文件中,Electron允许开发者维护一个JavaScript代码库,并创建可在Windows,macOS和Linux上运行的跨平台应用程序,无需本机开发经验。使用Electron开发桌面应用可以将现有的Web技术与原生应用的功能相结合,同时还能够利用Electron丰富的API和工具来增强应用的功能和性能。 创建一个Electron应用程序遵循与其他Node.js项目相同的一般结构。首先需要在电脑上安装Node.js,然后通过命令行创建一个文件夹并初始化一个npm包。接下来,可以使用npm安装Electron依赖和其他必要的库。在初始化完成后,可以开始编写应用程序的主要代码,使用HTML,CSS和JavaScript来创建用户界面和实现应用功能。最后,使用Electron提供的打包工具将应用程序打包成可执行文件,可以在各个操作系统上进行安装和运行。 使用Electron可以快速开发出一个功能完善的桌面应用程序,并且能够跨平台运行,为用户提供更好的用户体验。同时,由于使用了Web技术,开发者可以充分利用现有的前端开发经验和工具,提高开发效率。因此,Electron成为了很多开发者选择的框架之一。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [使用Electron构建桌面应用](https://download.csdn.net/download/weixin_38749268/15444248)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【Electron】桌面应用开发](https://blog.csdn.net/weixin_64172426/article/details/126094153)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
        阿里云Code(code.aliyun)提交代码时报错fatal: Authentication failed for‘https://code.aliyun.com/...‘身份验证失败 14219 import { fileURLToPath } from 'node:url' import { dirname } from 'node:path' const __filename = fileURLToPath(import.meta.url) const __dirname = dirname(__filename) 加个这个,出现这个报错应该是ES的问题,__driname是CommonJs下面的变量 Electron + Vue3 开发桌面应用 打包成exe后本地资源加载都是正常的,但是向后端发送的请求都变成访问文件的样子了,这个药怎么解决?求大佬解答 Electron + Vue3 开发桌面应用 mmmaim: 打包之后打开是白屏 而且网页报错index.html:1 Access to CSS stylesheet at 'file:///C:/assets/index-s2ab_X-E.css' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted. index-s2ab_X-E.css:1 Failed to load resource: net::ERR_FAILED index.html:1 Access to script at 'file:///C:/assets/index-CZoLij78.js' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, isolated-app, chrome-extension, chrome, https, chrome-untrusted. index-CZoLij78.js:1 Failed to load resource: net::ERR_FAILED /C:/vite.svg:1 Failed to load resource: net::ERR_FILE_NOT_FOUND有没有知道怎么解决的 Electron + Vue3 开发桌面应用 在下xiao白: 你看我,我是用正则把路径重写正确 Electron + Vue3 开发桌面应用 Sir_Manley: 别用代理,正常调接口就行
 
推荐文章
宽容的水煮肉  ·  46. 過年、搬家、外出旅行時,如何避免孩子睡眠走鐘?
19 小时前
鼻子大的小狗  ·  Spring Security OAuth2 单点登录- 废物大师兄- 博客园
6 月前
光明磊落的黄豆  ·  【未成年人】孩子总咳不停,可能得了鼻后滴漏综合征
9 月前
爽快的哑铃  ·  哈尔滨工程大学2012年高招各省录取分数线及录取人数- 高考- 中国 ...
1 年前
谦逊的跑步机  ·  羚羊工业互联网平台- 安徽产业网
1 年前
干练的薯片  ·  风姿物语女主角
1 年前
Link管理   ·   Sov5搜索   ·   小百科
小百科 - 百科知识指南