Vite5+Electron+SQLite3+Vue3 工程搭建

总结摘要
2025年最新Electron框架搭建指南

1 组件说明

操作系统:windows 11

组件版本说明
Vite5.1.6前端构建工具
ESLint9.25.1代码问题发现和修复共建
Prettier3.5.3一个流行的开源代码格式化工具
TypeScript5.2.2一种由微软开发的自由开源编程语言,它是 JavaScript 的一个超集
Electron30.0.1构建跨平台桌面应用程序的开源框架
better-sqlite311.9.1SQLite数据库驱动
Prisma6.9.0ORM 框架
Vue33.4.21用于构建用户界面的渐进式JavaScript框架
Vue Router4.5.0是 Vue.js 的官方路由管理器
Element Plus2.9.7基于 Vue 3 的企业级 UI 组件库
Pinia3.0.1用于 Vue 的状态管理库
Tailwind CSS4.1.3一个高度可定制的、低级别的 CSS 框架
Axios1.8.4一个基于 Promise 的 HTTP 客户端
Zod3.24.3用于 TypeScript 和 JavaScript 的类型定义和数据验证库

2 Node.js 安装配置

本文的Node.js版本号v20.19.0,请先进行安装,推荐用nvm安装管理,网上教程较多不再赘述。

[[1-nodejs环境安装配置]]

2.1 加速镜像配置

  1. 打开npm的配置文件
1
2
# cmd 运行打开配置文件
> npm config edit
  1. 在空白地方添加淘宝镜像,下面三个(缺什么补什么,但要是同一个公司单位的镜像)
1
2
3
registry=https://registry.npmmirror.com
electron_mirror=https://npmmirror.com/mirrors/electron/
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/

3 搭建项目

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 初始化工程
> npm create electron-vite

$ npm create electron-vite
> npx
> create-electron-vite
√ Project name: ... electron-vite-project
√ Project template: » Vue
Scaffolding project in E:\workspace_electron\test\electron-vite-project...
Done. Now run:
  cd electron-vite-project
  npm install
  npm run dev
  
# 进入工程目录安装依赖
> npm install
# 运行命令
> npm run dev
# 编译命令
> npm run build

3.1 配置 Vite

3.1.1 设置静态文件拷贝

根据需要配置需要打包的静态文件,此处示例目的为打包初始化用的sql脚本。

1
> npm install fs-extra --save-dev

vite.config.ts文件做以下修改

1
2
3
4
5
6
7
import fs from "fs-extra";
...
fs.copySync(
  path.resolve(__dirname, "./electron/script"),
  path.resolve(__dirname, "./dist-electron/script"),
  { overwrite: true }
);

3.1.2 配置目录别名、不去除console.info/warn/error和sever

vite.config.ts文件做以下修改,此时端口已更改且默认打开浏览器

 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
import { defineConfig } from 'vite'
import path from 'node:path'
import electron from 'vite-plugin-electron/simple'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [
    vue()
  ],
base: "./",
  resolve: {
    alias: {
    //(1) 目录别名
      "@": path.resolve(__dirname, "./src"),
    },
  },
  //(2) 仅去除console.log,默认不配会去除所有console
	esbuild: {
    drop: ["console", "debugger"],
  },
  // (3) server
    server: {
    hmr: true,
    watch: {
      // 忽略指定目录的文件变化,注意将运行过程中会发生变化的文件进行忽略,否则开发模式会导致Vite频繁热更新,触发Electron应用频繁重启
      ignored: [
        // 忽略 node_modules
        "**/node_modules/**",
        // 忽略特定目录,例如:
        "**/dist/**",
        "**/release/**",
        "**/database/**"
        // 可以添加更多需要忽略的目录
        // '**/your-directory-to-ignore/**',
      ]
    }
  },
  electron(..)//里面配置不动
})

3.2 配置 TypeScript

tsconfig.json做以下修改

 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
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",

    /* Linting */
    "strict": true,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noFallthroughCasesInSwitch": true,
    // @设置
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "src/**/*.d.ts",
    "electron"
  ],
  "references": [{ "path": "./tsconfig.node.json" }]
}

3.3 配置 ESLint

用于语法检查。

  • 安装
1
2
3
4
5
6
7
# 框架无关安装
> npm i eslint@latest -D
> npm i eslint-plugin-vue typescript-eslint -D
# 规则库(按需配置)
> npm i @stylistic/eslint-plugin -D
# vite额外安装(暂时用不到)
> npm i vite-plugin-eslint -D
  • 配置命令
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// package.json
{
  "scripts": {
    "lint": "eslint",
    "lint:fix": "eslint --fix",
  },
  "devDependencies": {
    "eslint": "^9.9.0",
  }
}
  • 初始化配置
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
npx eslint --init
# 根据自己需要一步一步选择生成配置
You can also run this command directly using 'npm init @eslint/config@latest'.

> task@0.0.0 npx
> create-config

@eslint/create-config: v1.8.1

 What do you want to lint? · javascript
 How would you like to use ESLint? · problems
 What type of modules does your project use? · esm
 Which framework does your project use? · vue
 Does your project use TypeScript? · no / yes
 Where does your code run? · browser, node
The config that you've selected requires the following dependencies:

eslint, @eslint/js, globals, typescript-eslint, eslint-plugin-vue
 Would you like to install them now? · No / Yes
 Which package manager do you want to use? · npm
️Installing...
  • 配置eslint.config.js
  1. 对于typescript的更多规则配置,你可以在 这里 找到。
  2. 还推荐用@stylistic/eslint-plugin这个插件来提供更多的typescriptJavaScript的语法风格规则
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
export default defineConfig([
  {
  //忽略目录
    ignores:['node_modules','dist','dist-electron','public'] 
  },
  {
  //自定义规则
	  rules:{
	  'no-console':'error',
	  }
  },
  ...
]);
  • VSCode 安装 ESLint 插件,让VSCode可以根据配置规则进行提示。
  • 安装Prettier,用于格式化。
 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
# 步骤一:安装
## eslint-config-prettier 关闭与ESLint与Prettier冲突规则
## eslint-plugin-prettier 让ESLint直接使用Prettier进行格式检查
> npm i prettier eslint-config-prettier eslint-plugin-prettier -D

# 步骤二:eslint.config.js增加配置
 require("eslint-config-prettier"),
  {
    files: ["**/*.{js,mjs,cjs,ts,vue}"]
  },
  {
    plugins: {
      prettier: require("eslint-plugin-prettier")
    }
  },
# 步骤三:设置prettier规则
根目录下创建.prettierrc.json文件
{
  "semi": true,
  "singleQuote": false,
  "tabWidth": 2,
  "printWidth": 80,
  "trailingComma": "none"
}

# 步骤四:安装Prettier-Code formatter插件,让其自动格式化
# 步骤五:设置保存自动格式化
## 在vscode设置中,找到在setting.json中编辑,添加以下代码
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[vue]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "eslint.validate": ["javascript", "typescript", "vue"],
  "eslint.run": "onType",
  "eslint.format.enable": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  }
}
# 最后需要重启VSCode。
  • vite.config.js中进行配置:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import eslintPlugin from 'vite-plugin-eslint';

export default defineConfig({
    plugins: [
        vue(),
        eslintPlugin({
            // 可以在这里传入自定义配置
            // 默认会读取项目中的 .eslintrc.js 文件
			failOnWarning: false,
            failOnError: fasle,
            emitWarning: true,
            emitError: true,
        })
    ]
});
  • 命令行使用
1
2
3
4
5
npx eslint src --ext .js,.ts,.vue
# 整个工程
npx eslint .
# 指定目录
npx eslint src/ --fix

3.4 配置 better-sqlite3(废弃,走prisma)

  • 安装
1
2
3
4
5
6
npm i -g node-gyp
# Electron 内置的 Node.js 版本和编译到 better-sqlite3 的 Node.js 版本不同将可能导致安装失败,所以此处需要安装 `electron-rebuild` 重建 Node.js 模块。(注意安装顺序,否则可能出现安装失败的问题)
npm i -D electron-rebuild
# 性能比普通的sqlite3更好
npm i -S better-sqlite3
npm i -S @types/better-sqlite3
  • 修改package.json中的scripts
1
2
3
4
5
  "scripts": {
    ...
    "rebuild": "electron-rebuild -f -w better-sqlite3"
  },
# 执行命令 npm run rebuild
  • 遇到问题
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 问题一:解决初始化数据库报# ReferenceError: __filename is not defined
// Import the 'createRequire' function from the 'module' package
//主进程中
import { createRequire } from 'module';
const require = createRequire(import.meta.url);

// Import better-sqlite3
const Database = require('better-sqlite3');

# 问题二:运行报sqlite3 node版本问题,重新执行npm run rebuild

3.5 配置 Prisma

  • 安装
1
2
3
4
# 安装orm框架prisma
npm i prisma -S
# electron 使用 prisma 客户端
npm i @prisma/client -S
  • 初始化Prisma生成 prisma/schema.prisma 文件
1
npx prisma init
  • prisma/schema.prisma 文件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
generator client {
  provider = "prisma-client-js"
  //(1) 改为electron
  output = "../electron/prisma"
}
 
datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}
 
model user(
    id Int @id @default(autoincrement())
    username String @unique
    nickname String?
    email String @unique
    password String
)
  • 将定义的模型推送到数据库并生成客户端
1
2
3
4
5
# 第一步配置.env文件
DATABASE_URL="file:./database/local.db"

# 第二步
npx prisma db push
  • 初始化上步生成的prisma cleint
 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
//electron目录下新增prisma-client.ts

import { createRequire } from "module";
import path from "node:path";
import { app } from "electron"; // 用于 Electron 应用的全局功能
const require = createRequire(import.meta.url);
const { PrismaClient } = require("./prisma");

// 判断当前环境是否是开发环境
const isDevelopment = process.env.NODE_ENV === "development";
let databasePath = path.join(app.getPath("userData"), "database");
if (isDevelopment) {
  databasePath = path.join(app.getAppPath(), "database");
}
process.env.DATABASE_URL = `file:${path.join(databasePath, "local.db")}`;
console.info("db version:", process.env.DATABASE_URL);

const prisma = new PrismaClient({
  log: ["query", "info", "warn", "error"], // 输出查询日志到控制台,方便调试
});

// 在应用退出时关闭数据库连接
    app.on("quit", () => {
      prismaClient.$disconnect();
      console.info("database closed");
    });
    // 在窗口全部关闭时也关闭数据库连接
    app.on("window-all-closed", () => {
      if (process.platform !== "darwin") {
        prismaClient.$disconnect();
        console.info("database closed on window-all-closed");
      }
    });
    
export default prismaClient;
  • 其它相关命令
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 查看 prisma 的所有指令,使用 `npx prisma -h`
# 将数据库中表结构同步到中schema.prisma
npx prisma db pull
# 将schema.prisma变动同步到数据库中表结构
npx prisma db push
# 同步表数据
npx prisma db seed
# 生成sql迁移文件
npx prisma migrate dev
#重新生成客户端
npx prisma generate
# `--name xxx`:是用于记录迁移文件命名(日期+xxx)
npx prisma migrate dev --name add_sex

## 做了以下事情
- 删除表中所有数据(表中可能存在脏数据)
- 执行所有的日志文件(也就是 SQL 集合)
- 更新 @prisma/client 代码
- 执行 seed.ts 来初始化数据
npx prisma migrate reset
  • 使用prisma客户端
 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
import prisma from "./prisma-client";

//查询(其它操作参见参考资料)
const data= await prismaClient.user.findFirst()

//方法不够用时,原生查询sql(非必要不推荐)
const result = await prisma.$queryRaw`SELECT * FROM users WHERE id = ${1}`;

//原生insert/update/delete/create table/aler sql(非必要不推荐)
await prisma.$executeRaw`UPDATE users SET name = ${'New Name'} WHERE id = ${1}`;

// 事务
const result = await prisma.$transaction(async (prisma) => {
  const user = await prisma.user.create({
    data: {
      name: "John",
      email: "john@example.com",
    },
  });

  const post = await prisma.post.create({
    data: {
      title: "Hello World",
      content: "This is my first post",
      userId: user.id,
    },
  });

  return { user, post };
});

3.6 配置日志

  • 安装
1
2
npm install winston -S
npm install winston-daily-rotate-file -S
  • 定义日志工具类
 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
//在主进程中使用Log.js

import winston from "winston";
import "winston-daily-rotate-file";
import { ipcMain } from "electron";
// 定义日志格式,包含时间戳
const logFormat = winston.format.combine(
  winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), // 时间戳格式
  winston.format.printf(({ timestamp, level, message }) => {
    return `${timestamp} [${level}]: ${message}`;
  })
);

const logTransport = new winston.transports.DailyRotateFile({
  filename: "logs/app-%DATE%.log",
  datePattern: "YYYY-MM-DD",
  maxSize: "20m", // 每个日志文件最大20MB
  zippedArchive: true, // 压缩归档
  maxFiles: "7d", // 最多保留7天的日志文件
  format: logFormat // 使用自定义的日志格式
});

// 创建日志记录器
const logger = winston.createLogger({
  level: "info", // 默认日志级别为 'info'
  transports: [
    logTransport,
    new winston.transports.Console({ format: logFormat }) // 控制台输出
  ]
});

export default class Log {
  constructor() {
    this.registerHandlers();
  }

  static info(...args: any[]): void {
    const message = args
      .map((a) => (typeof a === "object" ? JSON.stringify(a) : a))
      .join(" ");
    logger.info(message);
  }

  static error(...args: any[]): void {
    const message = args
      .map((a) => (typeof a === "object" ? JSON.stringify(a) : a))
      .join(" ");
    logger.error(message);
  }

  /**
   * 注册IPC处理程序
   */
  private registerHandlers() {
    const isDevelopment = process.env.NODE_ENV === "development";
    // 监听渲染进程发送的日志信息
    ipcMain.handle("log:info", (event, message) => {
      if (!isDevelopment) {
        logger.info(message);
      }
    });

    ipcMain.handle("log:error", (event, message) => {
      if (!isDevelopment) {
        logger.error(message);
      }
    });
  }
}
  • 覆盖console原生函数
 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
import Log from "./Log";

export default class ConsoleLogProxyForService {
  constructor() {
    // 保存原始的 console 方法
    const originalInfo = console.info;
    const originalWarn = console.warn;
    const originalError = console.error;
    const isDevelopment = process.env.NODE_ENV === "development";
    // 覆盖 console.info
    console.info = function (...args) {
      if (isDevelopment) {
        originalInfo.apply(console, [``].concat(args));
      } else {
        Log.info(args);
      }
    };

    // 覆盖 console.warn
    console.warn = function (...args) {
      if (isDevelopment) {
        originalWarn.apply(console, [``].concat(args));
      } else {
        Log.warn(args);
      }
    };

    // 覆盖 console.error
    console.error = function (...args) {
      if (isDevelopment) {
        originalError.apply(console, [``].concat(args));
      } else {
        Log.error(args);
      }
    };
  }
}


//在主进程入口中初始化即可
new ConsoleLogProxyForService();
  • 渲染进程入口中类似方式覆盖console原生函数

3.7 配置路由

  • 安装生产依赖
1
> npm i vue-router -S
  • 创建src/router文件夹,内部创建index.ts
 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
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import TestRoutes from '@/views/test/routes';

const routes: Array<RouteRecordRaw> = [
  {
    path: '/',
    redirect: '/login'
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/login/index.vue')
  },
  {
    path: '/test',
    name: 'test',
    children: [...TestRoutes],
    meta: {
      sort: 1,
      icon: 'FillSet',
      title: '测试带单',
      menu: true
    }
  }
];
const router = createRouter({
  scrollBehavior(to, from, savedPosition) {
    return { top: 0 };
  },
  history: createWebHistory(),
  routes
});

router.beforeEach((to: any, from, next) => {
  next();
});

export default router;
  • 创建src/views文件夹,下一级创建src/views/login文件夹,自行创建对应文件index.vue
  • 创建src/views/detail文件夹,创建文件routes.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import { RouteRecordRaw } from 'vue-router';
const routes: Array<RouteRecordRaw> = [
  {
    path: 'test1',
    name: 'test1',
    component: () => import('@/views/detail/entry.vue'),
    meta: {
      title: '测试1'
    }
  },
  {
    path: 'test2',
    name: 'test2',
    component: () => import('@/views/detail/setting.vue'),
    meta: {
      title: '测试2'
    }
  }
];

export default routes;
  • main.ts引用路由
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";

// routes
import router from "./router/index";

const app = createApp(App);
app.use(router);
app.mount("#app");
  • app.vue文件引入<router-view/>
1
2
3
4
5
<template>
  <div>
    <router-view/>
  </div>
</template>

3.8 配置 Pinia

pinia介绍

  • 安装生产依赖
1
2
> npm i pinia -S
> npm i pinia-plugin-persistedstate -S #为了持久化pinia
  • 创建“src/stores”文件夹,内部创建use-user.ts,内容如下
 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
//main.ts
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
...
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
app.use(pinia);


// src/stores/counter.ts
import { defineStore } from 'pinia';
import { reactive } from 'vue';

export const useUserInfoStore = defineStore(
  'userInfo',
  () => {
    const user = reactive({
      name: '',
      age: '',
      sex: ''
    });

    const setUserInfo = (data: { name: string, age: string, sex: string }) => {
      Object.assign(user, data);
    };

    return { user, setUserInfo };
  },
   {
    persist: {
      key: "userInfo",
      storage: sessionStorage, //默认localStorage
      serializer: {
        // 序列化配置
        serialize: JSON.stringify,
        deserialize: JSON.parse
      }
    }
  }
);
  • login中的index.vue修改为
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
  <div>
    <span class="text-[#ff0000]">{{ user }}</span>
    <button @click="addStore">点击</button>
  </div>
</template>
<script lang="ts" setup>
import { storeToRefs } from "pinia";
import { useUserInfoStore } from "@/store/use-user";

const userInfoStore = useUserInfoStore();

const { user } = storeToRefs(userInfoStore);

const addStore = () => {
  userInfoStore.setUserInfo({
    name: "张三",
    age: Math.random() * 100 + "",
    sex: "男"
  });
};
</script>
  • detail中的entry.vue修改为
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<template>
    <hl-group class="empty" align="items-center items-middle" dir="vertical" gap="var(--md)">
      {{ JSON.stringify(user) }}
    </hl-group>
  </template>

<script lang="ts" setup>
import { useUserInfoStore } from '@/store/use-user';
import { storeToRefs } from 'pinia';

const userInfoStore = useUserInfoStore();

const { user } = storeToRefs(userInfoStore);
</script>
  • main.ts中引入pinia

此时login组件操作,可修改pinia, detail组件会获取到pinia状态

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';

//routes
import router from "./router/index";

const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);

const app= createApp(App)

app.use(pinia);
app.use(router);
app.mount('#app');

3.9 配置 Element Plus

  • 安装
1
2
3
4
5
# 按需引入
> npm i element-plus @element-plus/icons-vue -S
> npm install unplugin-vue-components unplugin-auto-import -D
# 表单验证
> npm i zod -S
  • vite.config.ts做更改
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//(1) 增加
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import AutoImport from "unplugin-auto-import/vite"
import Components from 'unplugin-vue-components/vite';

export default defineConfig({
  plugins: [
    vue(),
    //(2) 以下为新增
    Components({
      resolvers: [ElementPlusResolver()],
      dts: "src/components.d.ts",
    }),
    AutoImport({
      imports: ['vue', 'vue-router'],
      dts: "src/auto-import.d.ts",
    })
  ],

3.10 配置 Tailwind CSS

Tailwind CSS 介绍

  • 安装
1
2
3
# 4.x版本如下(配置见:https://tailwindcss.com/docs/installation/using-vite)
## 无需生成postcss.config.js和tailwind.config.js
npm install tailwindcss @tailwindcss/vite
  • 创建"src/styles"文件夹,内部创建“index.css"文件,内容如下
1
@import "tailwindcss";
  • main.ts 引入tailwindcss
1
import "@/styles/index.css";
  • 配置vite.config.ts,内容如下
1
2
3
4
5
6
7
8
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
plugins: [
tailwindcss(),
],
})
  • 如有需要可以在“src/styles/index.css"中通过扩展配置相关变量
1
2
3
@theme {
    --color-mint-500: oklch(0.72 0.11 178);
}

3.11 配置 Axios

  • 安装
1
npm i axios -S 

3.12 配置 JWT

  • 安装
1
2
npm i --save-dev jsonwebtoken
npm i --save-dev @types/jsonwebtoken

3.13 配置自动更新

  • 安装
1
npm i electron-updater -S
  • 在 electron-builder.json5 文件增加以下配置

配置一个能公网访问的地址,可以考虑使用github或者通过nginx等自建静态资源服务。

1
2
3
4
5
6
"build": {
	"publish": {
	  "provider": "generic",
	  "url": "http://localhost:7448/"
	}
}
  • 新增Initupdater.ts文件
 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
import { autoUpdater } from "electron-updater";
import { ipcMain, BrowserWindow } from "electron";
import Log from "./commons/Log";

let mainWindow: BrowserWindow | null = null;

export const initUpdater = (win: any) => {
  mainWindow = win;

  Log.info("初始化更新。。。");

  // 自定义服务器地址
  autoUpdater.setFeedURL({
    provider: "generic",
    url: "http://localhost:7448/"
  });

  autoUpdater.logger = Log;

  autoUpdater.autoDownload = true;

  // 开启本地dev调试
  autoUpdater.forceDevUpdateConfig = true;

  // 检查更新
  autoUpdater.checkForUpdates();

  ipcMain.on("check-for-updates", () => {
    autoUpdater.checkForUpdates();
  });

  autoUpdater.on("update-not-available", () => {
    Log.info("无新版本");
  });

  autoUpdater.on("update-available", () => {
    Log.info("检测到新版本");
    mainWindow?.webContents.send("update-available");
  });

  autoUpdater.on("error", (error) => {
    Log.error("更新错误:", JSON.stringify(error));
  });

  // 下载进度
  autoUpdater.on("download-progress", (progress) => {
    Log.info(`下载安装包进度: ${progress.percent}%`);
  });

  autoUpdater.on("update-downloaded", () => {
    Log.info("下载完成,是否立即安装更新?");
    mainWindow?.webContents.send("update-downloaded");
  });
};

export const install = () => {
  autoUpdater.quitAndInstall();
};

ipcMain.on("install-update", () => {
  install();
});
  • 渲染主进程
1
2
3
4
5
6
7
8
9
import { initUpdater } from "./InitUpdater.js";

  win.on("ready-to-show", () => {
    win?.show();
    //窗口显示5秒后检查更新
    setTimeout(() => {
      initUpdater(win);
    }, 5000);
  });
  • 渲染进程

    • preload.ts
    1
    2
    3
    4
    
    once(...args: Parameters<typeof ipcRenderer.once>) {
        const [channel, ...omit] = args;
        return ipcRenderer.once(channel, ...omit);
      },
    • App.vue
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    //App.vue
    import { ElMessage, ElMessageBox } from 'element-plus'
    
    window.electron.ipcRenderer.once('update-available', () => {
      ElMessage.warning('发现新版本,正在下载...')
    })
    window.electron.ipcRenderer.on('update-downloaded', () => {
      ElMessageBox.confirm('下载完成,是否立即安装更新?', '更新提醒', {
        confirmButtonText: '更新',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        window.electron.ipcRenderer.send('install-update')
      })
    })

4 问题记录

4.1 问题一

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// npm install 报错
npm error gyp ERR! find VS not looking for VS2015 as it is only supported up to Node.js 18
npm error gyp ERR! find VS not looking for VS2013 as it is only supported up to Node.js 8
npm error gyp ERR! find VS
npm error gyp ERR! find VS **************************************************************
npm error gyp ERR! find VS You need to install the latest version of Visual Studio
npm error gyp ERR! find VS including the "Desktop development with C++" workload.
npm error gyp ERR! find VS For more information consult the documentation at:
npm error gyp ERR! find VS https://github.com/nodejs/node-gyp#on-windows
npm error gyp ERR! find VS **************************************************************
npm error gyp ERR! find VS

4.1.1 解决方式

4.2 问题二

1
2
//npm run build 报错
packaging       platform=win32 arch=x64 electron=30.5.1 appOutDir=release\0.0.0\win-unpacked   ⨯ Get "https://npmmirror.com/mirrors/electron/30.5.1/electron-v30.5.1-win32-x64.zip": proxyconnect tcp: dial tcp :0: connectex: The requested address is not valid in its context.

4.2.1 解决方式

  • 将连接中的文件下载后拷贝到“C:\Users\XX\AppData\Local\electron\Cache”下。

4.3 问题三

1
2
//npm run build 报错
⨯ Get "https://npmmirror.com/mirrors/electron-builder-binaries/winCodeSign-2.6.0/winCodeSign-2.6.0.7z": proxyconnect tcp: dial tcp :0: connectex: The requested address is not valid in its context.

4.3.1 解决方式

  • 将连接中的文件下载后拷贝到“C:\Users\XX\AppData\Local\electron-builder\Cache\winCodeSign”下,并按文件名解压到同名目录下。

4.4 问题四

  • nsis-resources下载失败。

4.4.1 解决方式

  • 同上操作,注意:nsis-resources和nsis为同一目录nsis下

5 参考资料

  1. vue3+vite+eslint|prettier+elementplus+国际化+axios封装+pinia_vue3 prettier-CSDN博客
  2. Vue 3 + TypeScript + Vite项目配置ESLint9 + Prettier + Husky + lint-staged + commitlint_eslint9 vue3 prettier-CSDN博客
  3. electron.vite 项目创建以及better-sqlite3数据库使用_electron-rebuild better-sqlite3 打包后 运行无响应-CSDN博客
  4. 找不到Windows SDK 版本 10.0.22621.0_找不到 windows sdk 版本 10.0.22621.0。请安装所需版本的 windows s-CSDN博客
  5. 使用electron+vue3+ts+vite搭建桌面端项目使用electron+vue3+ts+vite搭建桌面端项目 - 掘金
  6. ORM:prisma介绍 Prisma 创造了一种 DSL(Domain Specific Language,领域特定语 - 掘金