创建vite+vue+electron项目

news2025/1/15 7:32:11

写在前面的废话

       首先,这是一篇缝合文,我的目的就是想用vite、vue结合electron打包一个windows应用;其次,项目只是这三个工具的简单应用,目前还没有往里面添加其他内容。再次,项目过程中参考了google的多篇文字内容,所以如果看到有和别人一样的代码内容,不需要奇怪我就是抄的。最后,旨在记录自己项目过程中遇到的一些bug以及如果需要创建类似项目的基本流程,所以内容并不晦涩难懂,也可能会有疏漏错误,如有错误,还望指正。

       PS:请勿转载也没有多大的转载价值了,您看看就好。

工具版本

  • node v18.16.0
  • npm v9.6.4
  • vite v4.2.0
  • vue v3.2.47
  • electron v24.1.2

正文

创建vite+vue项目

 npm create vite 【项目名】 -- --template vue

 安装 electron相关依赖

npm i -D electron electron-builder vite-plugin-electron vite-plugin-electron-renderer

修改vite.config.js

import { rmSync } from 'node:fs'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path';
import { builtinModules } from 'module';
import electron from 'vite-plugin-electron'
import renderer from 'vite-plugin-electron-renderer'
import pkg from './package.json'

const port = process.env.port || process.env.npm_config_port || 8081 // 端口
// https://vitejs.dev/config/
export default defineConfig(({ command }) => {
  //同步删除给定路径上的文件
  rmSync('dist-electron', { recursive: true, force: true })
  const isServe = command === 'serve'
  const isBuild = command === 'build'
  const sourcemap = isServe || !!process.env.VSCODE_DEBUG
  return {
    resolve: {
      alias: {
        '@': resolve('src'),
      },
    },
    server: {
      host: 'localhost',
      port: port,
      open: true, //先注释,不然启动不起来怪尴尬的
      strictPort: false,
      https: false,
      // proxy: {//跨域设置

      // },
    },
    plugins: [
      vue(),
      electron([
        {
          // Main-Process entry file of the Electron App.
          entry: 'electron/main.js',
          onstart(options) {
            if (process.env.VSCODE_DEBUG) {
              console.log(/* For `.vscode/.debug.script.mjs` */'[startup] Electron App')
            } else {
              options.startup()
            }
          },
          vite: {
            build: {
              sourcemap,
              minify: isBuild,
              outDir: 'dist-electron/main',
              rollupOptions: {
                external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}),
              },
            },
          },
        },
        {
          entry: 'electron/preload.js',
          onstart(options) {
            // Notify the Renderer-Process to reload the page when the Preload-Scripts build is complete, 
            // instead of restarting the entire Electron App.
            options.reload()
          },
          vite: {
            build: {
              sourcemap: sourcemap ? 'inline' : undefined, // #332
              minify: isBuild,
              outDir: 'dist-electron/preload',
              rollupOptions: {
                external: Object.keys('dependencies' in pkg ? pkg.dependencies : {}),
              },
            },
          },
        }
      ]),
      // Use Node.js API in the Renderer-process
      renderer(),
    ],
    build: {
      assetsDir: 'static', // 静态资源的存放目录
      assetsPublicPath: './',
      assetsInlineLimit: 4096, // 图片转 base64 编码的阈值
      chunkSizeWarningLimit: 1000,
      rollupOptions: {
        external: [          // 告诉 Rollup 不要打包内建 API
          'electron',
          ...builtinModules,
        ],
      },
      optimizeDeps: {
        exclude: ['electron'], // 告诉 Vite 排除预构建 electron,不然会出现 __diranme is not defined
      },
    },
    clearScreen: false,
  }
})

创建electron文件夹,并且新建main.js和preload.js

main.js内容如下:


// 控制应用生命周期和创建原生浏览器窗口的模组
const { app, BrowserWindow, shell, ipcMain, protocol } = require('electron')
const { release } = require('node:os')
const path = require('path')
const isDev = process.env.NODE_ENV === 'development' ? true : false
const port = process.env.port || process.env.npm_config_port || 8081 // 端口

protocol.registerSchemesAsPrivileged([{ scheme: 'app', privileges: { secure: true, standard: true, stream: true } }]);
// 禁用 Windows 7 的 GPU 加速
if (release().startsWith('6.1')) app.disableHardwareAcceleration()
// 为 Windows 10+ 通知设置应用程序名称
if (process.platform === 'win32') app.setAppUserModelId(app.getName())
//
if (!app.requestSingleInstanceLock()) {
  app.quit()
  process.exit(0)
}
let mainWindow = null;
function createWindow() {
  // 创建浏览器窗口
  mainWindow = new BrowserWindow({
    width: 1800,
    height: 1600,
    minWidth: 1000,
    minHeight: 800,
    webPreferences: {
      nodeIntegration: true, //在渲染进程启用Node.js
      contextIsolation: false,
      preload: path.join(__dirname, 'preload.js')
    }
  })
  // 加载 index.html
  mainWindow.loadURL(
    isDev
      ? `http://localhost:${port}`
      : `file://${path.join(__dirname, '../dist/index.html')}`
  );
  if (isDev) {
    // 打开开发工具
    mainWindow.webContents.openDevTools()
  }
  // Test actively push message to the Electron-Renderer
  mainWindow.webContents.on('did-finish-load', () => {
    mainWindow?.webContents.send('main-process-message', new Date().toLocaleString())
  })

  // Make all links open with the browser, not with the application
  mainWindow.webContents.setWindowOpenHandler(({ url }) => {
    if (url.startsWith('https:')) shell.openExternal(url)
    return { action: 'deny' }
  })
}

// 这段程序将会在 Electron 结束初始化
// 和创建浏览器窗口的时候调用
// 部分 API 在 ready 事件触发后才能使用。
app.whenReady().then(createWindow)
app.on('activate', function () {
  // 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他
  // 打开的窗口,那么程序会重新创建一个窗口。
  const allWindows = BrowserWindow.getAllWindows()
  if (allWindows.length) {
    allWindows[0].focus()
  } else {
    createWindow()
  }
})
app.on('second-instance', () => {
  if (mainWindow) {
    // Focus on the main window if the user tried to open another
    if (mainWindow.isMinimized()) mainWindow.restore()
    mainWindow.focus()
  }
})
// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在
// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。
app.on('window-all-closed', function () {
  mainWindow = null
  if (process.platform !== 'darwin') app.quit()
})
// New window example arg: new windows url
ipcMain.handle('open-win', (_, arg) => {
  const childWindow = new BrowserWindow({
    webPreferences: {
      preload,
      nodeIntegration: true,
      contextIsolation: false,
    },
  })
  if (process.env.VITE_DEV_SERVER_URL) {
    childWindow.loadURL(`http://localhost:${port}#${arg}`)
  } else {
    childWindow.loadFile(path.join(__dirname, '../dist/index.html'), { hash: arg })
  }
})

preload.js内容如下:

window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector);
    if (element) element.innerText = text;
  };

  for (const type of ['chrome', 'node', 'electron']) {
    replaceText(`${type}-version`, process.versions[type]);
  }
});
function domReady(condition = ['complete', 'interactive']) {
  return new Promise((resolve) => {
    if (condition.includes(document.readyState)) {
      resolve(true)
    } else {
      document.addEventListener('readystatechange', () => {
        if (condition.includes(document.readyState)) {
          resolve(true)
        }
      })
    }
  })
}

const safeDOM = {
  append(parent, child) {
    if (!Array.from(parent.children).find(e => e === child)) {
      return parent.appendChild(child)
    }
  },
  remove(parent, child) {
    if (Array.from(parent.children).find(e => e === child)) {
      return parent.removeChild(child)
    }
  },
}

/**
 * https://tobiasahlin.com/spinkit
 * https://connoratherton.com/loaders
 * https://projects.lukehaas.me/css-loaders
 * https://matejkustec.github.io/SpinThatShit
 */
function useLoading() {
  const className = `loaders-css__square-spin`
  const styleContent = `
@keyframes square-spin {
  25% { transform: perspective(100px) rotateX(180deg) rotateY(0); }
  50% { transform: perspective(100px) rotateX(180deg) rotateY(180deg); }
  75% { transform: perspective(100px) rotateX(0) rotateY(180deg); }
  100% { transform: perspective(100px) rotateX(0) rotateY(0); }
}
.${className} > div {
  animation-fill-mode: both;
  width: 50px;
  height: 50px;
  background: #fff;
  animation: square-spin 3s 0s cubic-bezier(0.09, 0.57, 0.49, 0.9) infinite;
}
.app-loading-wrap {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #282c34;
  z-index: 9;
}
    `
  const oStyle = document.createElement('style')
  const oDiv = document.createElement('div')

  oStyle.id = 'app-loading-style'
  oStyle.innerHTML = styleContent
  oDiv.className = 'app-loading-wrap'
  oDiv.innerHTML = `<div class="${className}"><div></div></div>`

  return {
    appendLoading() {
      safeDOM.append(document.head, oStyle)
      safeDOM.append(document.body, oDiv)
    },
    removeLoading() {
      safeDOM.remove(document.head, oStyle)
      safeDOM.remove(document.body, oDiv)
    },
  }
}
// ----------------------------------------------------------------------
const { appendLoading, removeLoading } = useLoading()
domReady().then(appendLoading)
window.onmessage = (ev) => {
  ev.data.payload === 'removeLoading' && removeLoading()
}
setTimeout(removeLoading, 4999)

修改package.json文件

"main": "electron/main.js",//增加,重要。
//去掉"type": "module",
"scripts": {    
    //修改
     "build": "vite build && electron-builder",
    //增加
    "electron:serve": "electron ."
}
//增加build项
 "build": {
    "appId": "electronApp",
    "productName": "某应用",
    "copyright": "Copyright © 2023",
    "nsis": {
      "oneClick": false,
      "allowToChangeInstallationDirectory": true
    },
    "asar": true,
    "asarUnpack":[
      "./dist/electron",
      "./package.json"
    ],
    "win":{
      "target": [{
        "target": "nsis",
        "arch": [ 
          "x64", 
          "ia32"
        ]
      }]
    },
    "extraResources": [
      {
        "from": "public/", 
        "to": "static/"
      } 
    ],
    "files": [
      "dist/**/*",
      "electron/**/*"
    ],
    "directories": {
      "output": "release"
    }
  },

运行npm run dev命令,项目正常启动。

项目启动完成后发现控制台有个警告,警告如图

 解决方法:在index.html文件head标签内增加如下元数据标签

<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />

打包后效果: 

npm run build

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/443975.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

执行数学的运算

数学是计算机编程的重要能力。遗憾的是&#xff0c;对shell脚本来说&#xff0c;这个处理过程比较麻烦。在shell脚本中两种途径来进行数学运算。 expr命令 最开始&#xff0c;Bourne shell提供了一个特别的命令用来处理数学表达式。expr命令允许在命令行上处理数学数学表达式。…

EMQX vs VerneMQ | 2023 MQTT Broker 对比

引言 EMQX 和 VerneMQ 都是用 Erlang/OTP 开发的高性能、分布式开源 MQTT Broker&#xff0c;以其稳定性、容错性和扩展性著称。 EMQX 是目前全球最受欢迎的 MQTT Broker 之一&#xff0c;而 VerneMQ 项目近年来却没有积极地开发和维护。 本文是《2023 年 MQTT Broker 对比》…

可视化电子标签在仓储管理上的应用

随着经济快速增长和激烈的国内外市场竞争&#xff0c;要求企业运作的每个环节反应要迅速&#xff0c;经济转型要求更趋向改善工作流程、提高作业效率、减低运作成本、增加企业效益成为当务之急。虽然许多企业都有实施库存管理系统&#xff0c;但系统主要以人工盘点和走单据流程…

请收下这些软件测试学习干货,不看后悔一辈子

学习软件测试的过程中会遇到很多很多的困难和挑战&#xff0c;只有跨过这些困难和挑战&#xff0c;才有机会挑战软件测试领域的高薪offer。今天我们就来梳理一下&#xff0c;学习软件测试的过程中&#xff0c;我们一般都会遇到哪些困难&#xff0c;我们又当如何克服这些困难。 …

ChatGPT/大模型+零代码,给中小企业带来哪些机会?

ChatGPT让2023年成了AI之年。正如iPhone在2007年开启了智能手机时代&#xff0c;我们现在正在进入人工智能时代。 新形势下&#xff0c;零代码应如何借势发力&#xff1f;伙伴云“AI零代码”给出了答案。 作为零代码领域的头部平台&#xff0c;伙伴云全量发布【AI零代码应用搭…

暖通空调系统智能化故障检测诊断研究综述与展望

暖通空调系统智能化故障检测诊断研究综述与展望 【摘 要】暖通空调系统智能化化故障检测与诊断对提高运维水平和能源效率具有重要意义。 本文总结了暖通空调故障检测与诊断领域近二十多年来的研究历程&#xff0c;探讨了基于规则、基于模型和基于数据等三类主流方法的优劣&…

一键生成!如何为整个go项目自动添加单测

效果 为go项目中每个go文件生成对应的test文件&#xff0c;为每个接口生成对应的单测接口。 类似于这样&#xff0c;为go项目中每个包都生成一个test文件&#xff0c;单测模板如下&#xff1a; 比如函数接口为func releaseEndpoint(instanceID string, endpointID string) er…

微信小程序中使用 wx.getLocation获取当前详细位置并计算距离

文章目录 前言1&#xff0c;wx.getLocation()2&#xff0c;获取详细地址3&#xff0c;计算距离4&#xff0c;报错信息&#xff1a; getLocation:fail 频繁调用会增加电量损耗5&#xff0c;报错信息&#xff1a; 请求源未被授权 前言 wx.getLocation只能够获取经纬度&#xff0c…

java Maven 的理解

一、maven项目产生的原因 当开发两个Java项目&#xff0c;姑且把它们称为A、B&#xff0c;其中A项目中的一些功能依赖于B项目中的某些类&#xff0c;那么如何维系这种依赖关系的呢&#xff1f; 答&#xff1a;可以将B项目打成jar包&#xff0c;然后在A项目的Library下导入B的ja…

保姆级教程|昨晚撸了一个ChatGPT群聊机器人

前言 近期ChatGPT可以说是太火了&#xff0c;问答、写论文、写诗、写代码&#xff0c;只要输入精确的prompt&#xff0c;他的表现总是让人惊喜。本着打不过就加入的原则。要是把ChatGPT拉入群聊中&#xff0c;会是怎样一番场景&#xff1f;说做就做&#xff0c;花了1个晚上捣鼓…

ChatGPT与文心一言对比思考

ChatGPT与文心一言对比思考 1. 目前在国内比较广泛被认知的ai模型有什么 我目前通过各种渠道注册到的账号有3个,按照了解到然后注册的顺序分别是 ChatGPTnewbing文心一言 3种ai的注册渠道 ChatGPT注册: 科学上网注册寻找外网手机号发送短信 newbing注册: 科学上网注册微软账…

政企数智办公巡展回顾 | 通信赋能传统行业数智化转型的应用实践

在宏观政策引导、技术革新与企业内部数字化改革需求的共同驱使下&#xff0c;数智办公已经成为各行各业转型升级的必由之路。关注【融云 RongCloud】&#xff0c;了解协同办公平台更多干货。 近期&#xff0c;“连接无界 智赋未来” 融云 2023 政企数智办公巡展在北京、杭州相…

【Java】EnumSet的使用

一、什么是EnumSet? EnumSet是用于枚举类的专用Set集合。 它实现了Set接口并且继承AbstractSet。 当计划使用EnumSet时,必须考虑以下几点: 1、它只能包含枚举值,并且所有值必须属于同一个枚举。 2、它不允许添加 null,在尝试这样做时会抛出NullPointerException。 3、它不…

【SpringCloud AlibabaSentinel实现熔断与限流】

本笔记内容为尚硅谷SpringCloud AlibabaSentinel部分 目录 一、Sentinel 1、官网 2、Sentinel是什么 3、下载 4、特性 5、使用 二、安装Sentinel控制台 1、sentinel组件由2部分构成 2、安装步骤 1.下载 2.运行命令 3.访问sentinel管理界面 三、初始化演示工程 …

KVM虚拟机的磁盘无损扩容方法-qcow2格式的

起因&#xff1a;我的KVM主机上安装了基于Debian11的 虚拟机母鸡&#xff0c;其他虚拟机都由此克隆而来。因为最初只配置了8G的虚拟硬盘&#xff0c;因此在需要占用比较大的空间的应用时&#xff0c;就比较麻烦。度娘等中文搜索结果没找到答案&#xff0c;只能google了。 这里…

JVM系统优化实践(16):线上GC案例(一)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e; 列举几个实际使用案例说一下GC的问题。一个高峰期每秒10万QPS的社交APP&#xff0c;个人主页模块是流量最大的那个&#xff0c;而一次个人主页的查询&#xff0c…

python文本自动伪原创-ai一键伪原创

chatgpt批量伪原创的优势 ChatGPT是一个由OpenAI开发的强大的自然语言处理模型&#xff0c;它具有批量伪原创的优势&#xff0c;以下是这些优势&#xff1a; 模型能够处理大量的数据&#xff1a;ChatGPT通过训练大规模的语言模型来生成伪原创文本。这个模型拥有一个庞大的语料…

MySQL:varchar与date类型互转,对接java数据类型String和Date

目录 问题现象&#xff1a; 问题分析&#xff1a; varchar 转 date &#xff1a; date 转 varchar&#xff1a; 解决方法&#xff1a; varchar 转 date &#xff1a; date 转 varchar&#xff1a; 问题现象&#xff1a; 今天在项目中遇到一个问题&#xff1a; 现象&…

将DataTable中的数据保存到Excel (二) 使用NPOI

文章目录 背景1 NPOI 简介2 使用NPOI2.1 创建一个简单的工作簿2.2 简单的读取内容2.3 将DataTable数据导出到Excel(NPOI)2.4 Excel(NPOI)导入到DataTable 3 NPOI 总结 背景 前面写过一篇DataTable导出到Excel的文章&#xff0c;使用的是Office COM组件进行导入导出&#xff0c…

第五章 法律规范

目录 第一节 法律规则 一、法律规范与法律规则的概念辨析二、法律规则的逻辑结构 &#xff08;一&#xff09;假定&#xff1a;&#xff08;二&#xff09;行为模式&#xff1a;&#xff08;三&#xff09;法律后果&#xff1a; 三、法律规则与法律条文的关系 &#xff08;想法…