1301 字
7 分钟
electron开发流程

创建应用#

使用 electron-vite 快速搭建一个项目

npm create @quick-start/electron@latest

测试打包#

npm run build:win

配置打包

electron-builder.ymlnsis 的配置

nsis:
artifactName: ${name}-${version}-setup.${ext}
shortcutName: ${productName}
uninstallDisplayName: ${productName}
createDesktopShortcut: always # 始终创建桌面快捷方式
oneClick: false # 一键安装(直接安装,不显示界面) true=一键安装 false=手动安装
perMachine: false # 仅当前用户安装(true=所有用户) true=所有用户 false=当前用户
allowElevation: true # 允许提权(如需安装到 Program Files) true=允许 false=不允许
allowToChangeInstallationDirectory: true # 用户选择安装路径 true=允许 false=不允许
runAfterFinish: true # 安装完成后自动运行应用 true=允许 false=不允许
# silent: true # 完全静默(无任何界面,适合企业部署)

prettier/prettier 格式化问题#

修改.prettierrc.yml 添加

endOfLine: auto

修改env.d.ts文件#

添加以下代码

declare module '*.vue' {
import { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

目的:

  • TypeScript 默认不支持 .vue 文件 TypeScript 只认识 .ts.js 等文件类型,需要通过声明文件告诉它如何处理 .vue 文件。
  • Vue SFC 的特殊性 .vue 文件由 <template><script><style> 组成,编译后会变成一个 JavaScript 对象(组件选项)。这段代码帮助 TypeScript 理解这种转换。
  • 避免 IDE(如 VS Code)报错 如果没有这个声明,VS Code 会提示模块找不到或类型错误,即使代码能正常运行。

渲染进程配置#

安装element ui#

npm install element-plus --save

修改main.ts 添加一下代码

import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import zhCn from 'element-plus/es/locale/lang/zh-cn' // 中文语言包
const app = createApp(App)
app.use(ElementPlus, {
locale: zhCn
})

router 路由#

npm install vue-router@4

创建src/router/index.ts

注意!!!!!!!!!

路由模式一定要是createWebHashHistory 如果是createWebHistory,打包之后的页面会无法显示

import { createMemoryHistory, createRouter } from 'vue-router'
import HomeView from './HomeView.vue'
import AboutView from './AboutView.vue'
const routes = [
{
path: '/',
component: HomeView
},
{
path: '/about',
component: AboutView
}
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router

layout配置#

问题:

  1. 当设置height: 100vh; 出现滚动条那是因为body 的问题

    body {
    margin: 0;
    }

新建一个layout文件夹 里面只存放布局相关的页面

index.vue

<template>
<el-container style="height: 100vh">
<MenuComponent />
<PlanMain />
</el-container>
</template>
<script setup lang="ts">
import MenuComponent from '@/layout/menu/menu.vue'
import PlanMain from '@/layout/main/planMain.vue'
</script>
<style scoped></style>

main/planMain.vue

<template>
<el-main>
<router-view></router-view>
</el-main>
</template>
<script setup lang="ts"></script>
<style scoped></style>

menu/menu.vue

<template>
<el-aside width="200px" class="full-height-aside">
<el-menu
class="el-menu-vertical-demo full-height-menu"
router
:default-active="$route.path"
:collapse="isCollapse"
@open="handleOpen"
@close="handleClose"
>
<el-menu-item index="/home">
<el-icon><icon-menu /></el-icon>
<template #title>首页</template>
</el-menu-item>
<el-menu-item index="/planManage">
<el-icon><CircleCheck /></el-icon>
<template #title>任务管理</template>
</el-menu-item>
<el-menu-item index="/logRecord">
<el-icon><document /></el-icon>
<template #title>日志管理</template>
</el-menu-item>
<el-menu-item index="/settingManage">
<el-icon><setting /></el-icon>
<template #title>设置</template>
</el-menu-item>
</el-menu>
</el-aside>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Document, Menu as IconMenu, Setting, CircleCheck } from '@element-plus/icons-vue'
defineOptions({
name: 'MenuComponent'
})
const isCollapse = ref(false)
const handleOpen = (key: string, keyPath: string[]) => {
console.log(key, keyPath)
}
const handleClose = (key: string, keyPath: string[]) => {
console.log(key, keyPath)
}
</script>
<style scoped>
/* 关键样式 */
.full-height-aside {
height: 100%;
overflow-y: auto; /* 垂直滚动 */
overflow-x: hidden; /* 隐藏水平滚动 */
/* border-right: 3px solid #eee; */
}
.full-height-menu {
height: 100%;
/* 继承父级高度 */
overflow-y: hidden; /* 禁用垂直滚动 */
border-right: inset; /* 可选:移除默认边框 */
}
/* 保留原有样式 */
.el-menu-vertical-demo:not(.el-menu--collapse) {
width: 100%;
}
</style>

还要修改路由 router/index.ts

import { createWebHistory, createRouter } from 'vue-router'
import Layout from '@/layout/index.vue'
import HomeView from '@/views/home/index.vue'
import logRecord from '@/views/logRecord/index.vue'
import planManage from '@/views/planManage/index.vue'
import settingManage from '@/views/settingManage/index.vue'
const routes = [
{
path: '/',
component: Layout,
redirect: '/home',
children: [
{
path: '/home',
component: HomeView
},
{
path: '/logRecord',
component: logRecord
},
{
path: '/planManage',
component: planManage
},
{
path: '/settingManage',
component: settingManage
}
]
},
{
path: '/',
redirect: '/home'
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router

pinia配置#

安装

Terminal window
npm install pinia

修改main.ts 添加以下代码

import { createPinia } from 'pinia'
/* 创建pinia */
const pinia = createPinia()
/* 使用插件 */
app.use(pinia)

新建一个store目录

store目录下新建一个index.ts

// 引入defineStore用于创建store
import { defineStore } from 'pinia'
// 定义并暴露一个store
export const useToolStore = defineStore('count', {
// 动作
actions: {},
// 状态
state() {
return {
planState: 1
}
},
// 计算
getters: {}
})

使用

import { useToolStore } from '@renderer/store'
// 存储状态
const toolStore = useToolStore()
const { planPriorityList, planTypeList } = toolStore

安装sqlite#

Terminal window
npm install sqlite3

调试

import sqlite3 from 'sqlite3'
app.whenReady().then(() => {
const dbDir = join(app.getPath('userData'), 'database')
// 保证目录存在
if (!fs.existsSync(dbDir)) fs.mkdirSync(dbDir)
// 打开 SQLite 数据库(如果没有则自动创建)
db = new sqlite3.Database(join(dbDir, 'mydb.sqlite'))
console.log('ddbDir', dbDir)
// 创建 plans 表
db.run(
`
CREATE TABLE IF NOT EXISTS plans (
id INTEGER PRIMARY KEY AUTOINCREMENT,
planName TEXT NOT NULL,
planPriority TEXT,
planType TEXT,
sort INTEGER DEFAULT 0,
stratTime TEXT,
endTime TEXT,
createTime TEXT DEFAULT CURRENT_TIMESTAMP
)
`
)
// 创建 signins 表
db.run(
`
CREATE TABLE IF NOT EXISTS signins (
id INTEGER PRIMARY KEY AUTOINCREMENT,
planId INTEGER,
type TEXT,
isDaily INTEGER DEFAULT 0,
time TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(planId) REFERENCES plans(id)
)
`
)
})

注意#

在IPC通信时如果是TS,类型一定要匹配上

之后在preload定义完接口后还需要在index.d.ts定义一遍

import { ElectronAPI } from '@electron-toolkit/preload'
// 👉 添加对 window.api 的类型定义
import type { Plan, PageInfo } from '@main/types/db' // 根据你实际路径调整
export interface PlanApi {
getAllPlans(): Promise<any>
getPlans(plan: Partial<Plan>, pageInfo: PageInfo): Promise<any>
addPlan(plan: Plan): Promise<any>
updatePlan(plan: Plan): Promise<any>
deletePlan(id: number): Promise<any>
getQuerySignin(): Promise<any>
queryPlanProgress(): Promise<any>
signinToday(): Promise<any>
planAloneSign(planId: number): Promise<any>
querySignList(): Promise<any>
}
declare global {
interface Window {
electron: ElectronAPI
planApi: PlanApi
}
}

打包注意#

如果打包安装后的应用是空白页,检查路由配置

Vue Router 默认的 createWebHistory() 模式 依赖 HTML5 history API,这在 Electron 的 file:// 协议中会导致无法路由跳转(页面路径不会匹配成功,页面内容空白)。

createWebHistory() 改为 createWebHashHistory

import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes: [...]
})
electron开发流程
https://fuwari.vercel.app/posts/electron/electron开发流程/
作者
zhouyeshan
发布于
2025-05-08
许可协议
CC BY-NC-SA 4.0