Hasleo Backup Suite 在 Windows 平台上提供了磁盘备份、系统克隆和恢复等功能,它的主界面以任务列表和状态卡片为核心。如果要在 Vue 3 中复刻这样的前端模块,不能只把界面拆成几个组件,还需要考虑任务数据的流动、进度更新的频率以及构建时的工程化配置。本文从数据模型开始,逐步构建一个可运行的备份管理前端。

一、备份任务的数据模型与类型定义
在开始编写组件之前,先把备份任务的结构固定下来。一个备份任务通常包含任务名称、源路径、目标路径、调度计划、执行状态和进度百分比。状态不能只用一个布尔值表示是否完成,因为任务可能处于待执行、执行中、成功、失败、已取消等多种状态。用字符串字面量联合类型来定义状态机是最直接的方式,这样在模板和逻辑中都能获得类型提示,避免手写字符串导致拼写错误。
下面是一份基础的类型定义。代码中同时定义了全量备份和增量备份的联合类型,方便后续扩展任务种类。
export type BackupType = 'full' | 'incremental';
export type BackupStatus = 'idle' | 'running' | 'success' | 'error' | 'cancelled';
export interface BackupTask {
id: string;
name: string;
sourcePath: string;
destPath: string;
type: BackupType;
schedule: string;
status: BackupStatus;
progress: number;
lastRun?: string;
errorMessage?: string;
}
export function createDefaultTask(): BackupTask {
return {
id: crypto.randomUUID(),
name: '新建备份任务',
sourcePath: 'C:/Users/Data',
destPath: 'D:/Backup',
type: 'full',
schedule: 'daily',
status: 'idle',
progress: 0
};
}
在实际项目中,备份任务还可能包含文件过滤器、排除目录、压缩级别等字段。把这些字段都放进一个接口会让类型过于臃肿,可以拆成基础接口和扩展接口。比如用 BackupOptions 保存压缩、加密等配置,再让 BackupTask 通过可选字段引用。这样既保持列表查询时的轻量,也能在详情页按需加载完整信息。另外,lastRun 和 errorMessage 字段用可选属性表示,避免新建任务时出现 undefined 混乱。
二、用 Pinia 管理任务状态与进度模拟
如果只把任务数组放在组件内部,切换路由或关闭弹窗后状态就会丢失,多个组件之间的同步也会变得困难。Pinia 可以把任务列表提升到全局 store 中,让任务列表页、详情页和状态卡片共享同一份数据。在 Vue 3 中,Pinia 的 setup store 风格与组合式 API 配合更加自然,不需要再区分 state、getters 和 actions,所有逻辑都写在同一个函数里。
下面是一个 store 示例,包含任务列表、添加任务和更新进度的 action。进度更新使用 setInterval 定时器,每次随机增加 5% 到 15%,到达 100% 后自动停止并修改状态为 success。
import { defineStore } from 'pinia';
import { ref } from 'vue';
import type { BackupTask, BackupStatus } from '@/types/backup';
export const useBackupStore = defineStore('backup', () => {
const tasks = ref<BackupTask[]>([]);
const timers = new Map<string, number>();
function addTask(task: BackupTask) {
tasks.value.push(task);
}
function removeTask(id: string) {
tasks.value = tasks.value.filter((task) => task.id !== id);
stopTimer(id);
}
function startTimer(id: string) {
if (timers.has(id)) return;
const timer = window.setInterval(() => {
const task = tasks.value.find((item) => item.id === id);
if (!task) {
stopTimer(id);
return;
}
if (task.progress >= 100) {
task.progress = 100;
task.status = 'success';
stopTimer(id);
return;
}
const increment = Math.floor(Math.random() * 10) + 5;
task.progress = Math.min(100, task.progress + increment);
task.status = 'running';
}, 800);
timers.set(id, timer);
}
function stopTimer(id: string) {
const timer = timers.get(id);
if (timer) {
clearInterval(timer);
timers.delete(id);
}
}
function updateStatus(id: string, status: BackupStatus) {
const task = tasks.value.find((item) => item.id === id);
if (task) {
task.status = status;
}
}
return { tasks, addTask, removeTask, startTimer, stopTimer, updateStatus };
});
定时器管理是备份进度模拟中最容易出问题的环节。如果把定时器直接挂在组件里,离开页面时没有清理,后台就会一直在更新已经销毁的组件状态,造成内存泄漏。上面的 store 用 Map 保存每个任务的定时器 ID,removeTask 和进度到达 100% 时都会调用 stopTimer 清理。在组件卸载时,也可以遍历停止所有定时器,避免应用关闭后仍有残留。真实项目中,进度更新应该来自后端通知或轮询接口,这里的定时器仅用于本地演示和原型验证。
如果需要支持暂停和取消任务,可以扩展 action。暂停时保存当前进度并清除定时器,取消时把状态改为 cancelled 并把进度归零。这些逻辑都可以放在同一个 store 中,让组件保持纯粹的数据展示和事件派发。
三、Vite 工程化配置与接口对接
Vite 作为构建工具,在工程化层面提供了很多开箱即用的能力,但还需要根据项目特点做额外配置。比如为了让 @/types/backup 这样的别名生效,需要在 vite.config.ts 中配置 resolve.alias。同时,如果后端备份服务部署在独立域名,开发环境需要通过代理避免跨域问题。
下面是一个精简的 Vite 配置示例,包含路径别名和开发代理。
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
server: {
port: 5173,
proxy: {
'/api': {
target: 'http://127.0.0.1:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
});
接口封装方面,建议在 src/api 目录下统一管理请求函数,不要在每个组件里直接调用 fetch 或 axios。可以创建一个 request 实例,统一处理 baseURL、超时、错误提示和鉴权头。备份任务列表、新建任务、获取日志等接口都通过这个实例暴露。这样即使后端接口地址或鉴权方式变化,也只需要修改一处。
另一个容易忽略的点是环境变量。Vite 使用 import.meta.env 暴露环境变量,但只有以 VITE_ 开头的变量才会出现在客户端代码中。可以创建 .env.development 和 .env.production 文件,分别设置 VITE_API_BASE_URL。在接口封装中读取这个变量,就能让同一份构建产物适配不同环境。生产构建时还可以开启 build.rollupOptions 的代码分割,把 Vue 核心库和 Pinia 单独拆包,提升首屏加载速度。
完成以上配置后,备份管理前端的骨架就搭建起来了。数据模型约束了任务结构,Pinia 解决了状态同步,Vite 则为开发和构建提供了稳定支撑。这三个层面相互配合,才是 Vue 3 工程化的完整思路。