Electron + Vue3 + TypeScript 项目快速搭建指南

Electron + Vue3 + TypeScript 项目快速搭建指南

基于 electron-vite-boilerplate | 在线演示: https://evite.netlify.app/

github地址:https://github.com/wuyongGitHub/electron-admin-template.git

技术栈

类别 选型
框架 Electron
前端 Vue 3
构建 Vite
语言 TypeScript
UI 库 Element Plus
状态管理 Pinia
HTTP Axios

一、项目初始化

bash 复制代码
npx degit alex8088/electron-vite-boilerplate electron-app
cd electron-app
npm install
npm run dev

如若无效直接进行git拉取

bash 复制代码
git config --global --unset http.proxy    
git config --global --unset https.proxy 
git clone https://github.com/alex8088/electron-vite-boilerplate.git electron-admin-app

二、路由配置(二次封装)

安装

bash 复制代码
npm install vue-router -S

目录结构

复制代码
src/renderer/src/router/
├── index.ts      # 入口,创建 router + 挂载守卫
├── routes.ts     # 路由表
└── guards.ts     # 导航守卫

为什么二次封装? 路由表 + 导航守卫混在一个文件里,项目大了难以维护。拆成三个职责单一的文件,各管各的。

router/index.ts

ts 复制代码
import { createRouter, createWebHashHistory } from 'vue-router'
import { AppRoutes } from './routes'
import { beforeEachGuard, afterEachGuard } from './guards'

const router = createRouter({
  history: createWebHashHistory(),
  routes: AppRoutes,
})

router.beforeEach(beforeEachGuard)
router.afterEach(afterEachGuard)

export default router

router/routes.ts

ts 复制代码
import type { RouteRecordRaw } from 'vue-router'

export const AppRoutes: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'layout',
    component: () => import('@renderer/layout/index.vue'),
  },
  {
    path: '/login',
    name: 'login',
    component: () => import('@renderer/views/login/Login.vue'),
    meta: { title: '登录' },
  },
]

router/guards.ts

ts 复制代码
import type { Router } from 'vue-router'

export function beforeEachGuard(to: ReturnType<Router['resolve']>) {
  // 全局前置守卫:权限校验、登录拦截等
}

export function afterEachGuard() {
  // 全局后置守卫:页面标题更新、埋点等
}

main.ts 注册

ts 复制代码
import router from './router'
app.use(router)

三、状态管理 Pinia

Pinia vs Vuex

Vuex Pinia
mutations (直接改 state)
modules (天然拆分为多个 store)
TS 支持 一般 原生良好
体积 较大 轻量

安装

bash 复制代码
npm install pinia pinia-plugin-persist -S

目录结构

复制代码
src/renderer/src/store/
├── index.ts           # 统一导出
├── useUserStore.ts    # 用户模块
└── useAppStore.ts     # 应用设置

store/index.ts

ts 复制代码
export { useUserStore } from './useUserStore'
export { useAppStore } from './useAppStore'

store/useUserStore.ts

ts 复制代码
import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
  state: () => ({
    token: '',
    rolePerm: [] as string[],
    permissions: [] as string[],
  }),
  getters: {
    isLoggedIn: (state) => !!state.token,
  },
  actions: {
    setToken(token: string) {
      this.token = token
    },
  },
  persist: {
    enabled: true,
    strategies: [
      {
        storage: localStorage,
        paths: ['token', 'rolePerm', 'permissions'],
      },
    ],
  },
})

main.ts 注册

ts 复制代码
import { createPinia } from 'pinia'
import piniaPluginPersist from 'pinia-plugin-persist'

const pinia = createPinia()
pinia.use(piniaPluginPersist)
app.use(pinia)

四、路径别名

electron.vite.config.tsrenderer.resolve.alias 中配置:

ts 复制代码
import { resolve } from 'path'

alias: {
  '@renderer': resolve('src/renderer/src'),
  '@views':    resolve('src/renderer/src/views'),
  '@router':   resolve('src/renderer/src/router'),
  '@store':    resolve('src/renderer/src/store'),
  '@utils':    resolve('src/renderer/src/utils'),
  '@api':      resolve('src/renderer/src/api'),
  '@layout':   resolve('src/renderer/src/layout'),
}

五、HTTP 请求封装

安装 Axios

bash 复制代码
npm install axios -S

代理配置

ts 复制代码
server: {
  proxy: {
    '/api': {
      target: 'http://uat.crm.xuexiluxian.cn',
      changeOrigin: true,
      rewrite: (path) => path.replace(/^\/api/, ''),
    },
  },
},

utils/request.ts

ts 复制代码
import axios from 'axios'
import type { AxiosResponse } from 'axios'

const request = axios.create({
  baseURL: '/api',
  timeout: 10000,
})

// 请求拦截
request.interceptors.request.use(
  (config) => {
    // const user = useUserStore()
    // if (user.token) config.headers.Authorization = `Bearer ${user.token}`
    return config
  },
  (error) => Promise.reject(error)
)

// 响应拦截
request.interceptors.response.use(
  (response: AxiosResponse) => {
    const { code, data, message } = response.data
    if (code === 200) return data
    return Promise.reject(new Error(message || '请求失败'))
  },
  (error) => Promise.reject(error)
)

export default request

API 模块化

复制代码
src/renderer/src/api/
├── login.ts
└── user.ts
ts 复制代码
// api/login.ts
import request from '@utils/request'

interface LoginParams {
  username: string
  password: string
}

export function loginByJson(data: LoginParams) {
  return request.post('/u/loginByJson', data)
}

六、Element Plus 按需引入

安装

bash 复制代码
npm install element-plus -S
npm install unplugin-vue-components unplugin-auto-import -D

配置

ts 复制代码
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

plugins: [
  vue(),
  AutoImport({ resolvers: [ElementPlusResolver()] }),
  Components({ resolvers: [ElementPlusResolver()] }),
]

按需引入后,组件直接用无需手动 import,ElMessage 等 API 也会自动导入。


七、完整 electron.vite.config.ts

ts 复制代码
import { resolve } from 'path'
import { defineConfig } from 'electron-vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  main: {},
  preload: {},
  renderer: {
    resolve: {
      alias: {
        '@renderer': resolve('src/renderer/src'),
        '@views':    resolve('src/renderer/src/views'),
        '@router':   resolve('src/renderer/src/router'),
        '@store':    resolve('src/renderer/src/store'),
        '@utils':    resolve('src/renderer/src/utils'),
        '@api':      resolve('src/renderer/src/api'),
        '@layout':   resolve('src/renderer/src/layout'),
      },
    },
    server: {
      proxy: {
        '/api': {
          target: 'http://uat.crm.xuexiluxian.cn',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, ''),
        },
      },
    },
    plugins: [
      vue(),
      AutoImport({ resolvers: [ElementPlusResolver()] }),
      Components({ resolvers: [ElementPlusResolver()] }),
    ],
  },
})

八、项目结构总览

复制代码
electron-app/
└── src/renderer/src/
    ├── main.ts                # 入口
    ├── App.vue
    ├── api/                   # 接口按模块
    │   └── login.ts
    ├── router/                # 路由
    │   ├── index.ts
    │   ├── routes.ts
    │   └── guards.ts
    ├── store/                 # 状态管理
    │   ├── index.ts
    │   ├── useUserStore.ts
    │   └── useAppStore.ts
    ├── utils/                 # 工具函数
    │   └── request.ts
    ├── views/                 # 页面
    └── layout/                # 布局

后续可补:i18n 多语言、深色模式、IPC 主/渲染进程通信、自动更新等。