前言

今天是考研伴侣App开发的第二天,主要任务是将之前单页面的应用重构为基于Vue Router的多页面SPA应用,实现模块化管理和更好的用户体验。

开发目标

  1. 模块化拆分:将臃肿的单页面拆分为8个独立功能页面
  2. 路由管理:引入Vue Router实现SPA无刷新切换
  3. UI优化:保持玻璃拟态设计风格,添加夜间模式
  4. 性能优化:确保首屏加载和页面切换流畅
  5. 代码规范:遵循Vue 3 Composition API最佳实践

一、项目结构重构

1.1 原有结构分析

原有应用将所有功能集中在单一页面,导致:

  • 代码难以维护
  • 首屏加载缓慢
  • 功能耦合度高

1.2 新目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
love-study-app/
├── index.html # HTML入口
├── package.json # 项目配置
├── vite.config.js # Vite构建配置
├── tailwind.config.js # Tailwind CSS配置
├── postcss.config.js # PostCSS配置
└── src/
├── main.js # Vue应用入口
├── App.vue # 根组件
├── router/
│ └── index.js # Vue Router路由配置
├── components/
│ └── Layout.vue # 主布局组件(含导航栏)
├── views/ # 页面组件
│ ├── Home.vue # 首页(倒计时+概览)
│ ├── Tasks.vue # 任务管理
│ ├── Focus.vue # 专注计时
│ ├── Checkin.vue # 习惯打卡
│ ├── Stats.vue # 数据统计
│ ├── Rewards.vue # 心愿单兑换
│ ├── Journal.vue # 学习心得
│ └── Achievements.vue # 成就徽章
└── style.css # 全局样式

二、核心技术实现

2.1 Vue Router配置

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
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'

const routes = [
{
path: '/',
name: 'Home',
component: () => import('../views/Home.vue'),
meta: { title: '考研伴侣 - 首页' }
},
{
path: '/tasks',
name: 'Tasks',
component: () => import('../views/Tasks.vue'),
meta: { title: '任务管理' }
},
{
path: '/focus',
name: 'Focus',
component: () => import('../views/Focus.vue'),
meta: { title: '专注计时' }
},
{
path: '/checkin',
name: 'Checkin',
component: () => import('../views/Checkin.vue'),
meta: { title: '习惯打卡' }
},
{
path: '/stats',
name: 'Stats',
component: () => import('../views/Stats.vue'),
meta: { title: '数据统计' }
},
{
path: '/rewards',
name: 'Rewards',
component: () => import('../views/Rewards.vue'),
meta: { title: '心愿单' }
},
{
path: '/journal',
name: 'Journal',
component: () => import('../views/Journal.vue'),
meta: { title: '学习心得' }
},
{
path: '/achievements',
name: 'Achievements',
component: () => import('../views/Achievements.vue'),
meta: { title: '成就徽章' }
}
]

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

export default router

技术要点

  • 使用createWebHistory()实现HTML5 History模式
  • 采用动态导入component: () => import(...)实现路由懒加载
  • 每个路由配置meta元数据用于页面标题管理

2.2 入口文件配置

1
2
3
4
5
6
7
8
9
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './style.css'

createApp(App)
.use(router)
.mount('#app')

2.3 根组件简化

1
2
3
4
5
6
7
8
<!-- src/App.vue -->
<template>
<Layout />
</template>

<script setup>
import Layout from './components/Layout.vue'
</script>

App组件现在只负责渲染Layout组件,职责单一。


三、Layout布局组件详解

3.1 组件结构

Layout组件是整个应用的骨架,包含:

  • 顶部导航栏(Logo + 夜间模式切换)
  • 积分显示区域
  • 主导航菜单(8个功能入口)
  • 页面内容区域(<router-view>
  • 底部快捷导航(移动端适配)

3.2 完整代码

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<!-- src/components/Layout.vue -->
<template>
<div class="relative min-h-screen overflow-hidden">
<!-- 简化背景 - 无动画 -->
<div class="absolute inset-0 -z-10 bg-gradient-to-br from-rose-100 via-orange-100 to-sky-100"></div>

<div class="relative z-10">
<!-- 顶部导航栏 -->
<nav class="glass-card m-4 p-4 flex flex-wrap items-center justify-between">
<div class="flex items-center gap-4">
<router-link to="/" class="text-2xl font-bold text-orange-600">
📚 考研伴侣
</router-link>
</div>

<!-- 夜间模式切换 -->
<button
@click="toggleDarkMode"
class="glass-card px-4 py-2 flex items-center gap-2 cursor-pointer"
>
<span class="text-xl">{{ isDarkMode ? '☀️' : '🌙' }}</span>
<span class="text-gray-700 font-medium">{{ isDarkMode ? '日间' : '夜间' }}</span>
</button>
</nav>

<!-- 积分显示 -->
<div class="flex justify-end mx-4 mb-4">
<div class="glass-card px-6 py-3 flex items-center gap-3">
<span class="text-2xl">💰</span>
<span class="text-orange-600 font-bold text-lg">恋爱积分: {{ lovePoints }}</span>
</div>
</div>

<!-- 主导航菜单 -->
<nav class="glass-card mx-4 mb-4 p-4">
<div class="flex flex-wrap gap-2 justify-center">
<router-link
v-for="item in navItems"
:key="item.path"
:to="item.path"
class="flex items-center gap-2 px-4 py-3 rounded-xl transition-all"
:class="isActive(item.path)
? 'bg-orange-500 text-white'
: 'hover:bg-white/30 text-gray-700'"
>
<span class="text-xl">{{ item.icon }}</span>
<span class="font-medium">{{ item.name }}</span>
</router-link>
</div>
</nav>

<!-- 页面内容 -->
<main class="mx-4 pb-24">
<router-view @updatePoints="updatePoints" />
</main>

<!-- 底部快捷导航(移动端) -->
<div class="fixed bottom-0 left-0 right-0 glass-card p-2 md:hidden flex justify-around z-50">
<router-link
v-for="item in navItems.slice(0, 5)"
:key="item.path"
:to="item.path"
class="flex flex-col items-center p-2"
:class="isActive(item.path) ? 'text-orange-500' : 'text-gray-500'"
>
<span class="text-xl">{{ item.icon }}</span>
<span class="text-xs">{{ item.name }}</span>
</router-link>
</div>
</div>
</div>
</template>

<script setup>
import { ref, provide, onMounted } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()
const isDarkMode = ref(false)
const lovePoints = ref(0)

const navItems = [
{ name: '首页', path: '/', icon: '🏠' },
{ name: '任务', path: '/tasks', icon: '📝' },
{ name: '专注', path: '/focus', icon: '⏱️' },
{ name: '打卡', path: '/checkin', icon: '📅' },
{ name: '统计', path: '/stats', icon: '📊' },
{ name: '奖励', path: '/rewards', icon: '🎁' },
{ name: '心得', path: '/journal', icon: '📔' },
{ name: '成就', path: '/achievements', icon: '🏆' }
]

// 路由激活状态判断
const isActive = (path) => {
if (path === '/') {
return route.path === '/'
}
return route.path.startsWith(path)
}

// 夜间模式切换
const toggleDarkMode = () => {
isDarkMode.value = !isDarkMode.value
document.documentElement.classList.toggle('dark', isDarkMode.value)
localStorage.setItem('darkMode', isDarkMode.value.toString())
}

// 更新积分
const updatePoints = () => {
try {
const savedPoints = localStorage.getItem('lovePoints')
if (savedPoints) lovePoints.value = parseInt(savedPoints)
} catch (error) {
console.error('Error loading points:', error)
}
}

// 初始化
const init = () => {
try {
const savedPoints = localStorage.getItem('lovePoints')
if (savedPoints) lovePoints.value = parseInt(savedPoints)

const savedDarkMode = localStorage.getItem('darkMode')
if (savedDarkMode === 'true') {
isDarkMode.value = true
document.documentElement.classList.add('dark')
}
} catch (error) {
console.error('Error initializing:', error)
}
}

// 提供全局状态
provide('updatePoints', updatePoints)
provide('getPoints', () => lovePoints.value)
provide('addPoints', (points) => {
lovePoints.value += points
localStorage.setItem('lovePoints', lovePoints.value.toString())
})
provide('usePoints', (points) => {
if (lovePoints.value >= points) {
lovePoints.value -= points
localStorage.setItem('lovePoints', lovePoints.value.toString())
return true
}
return false
})

onMounted(init)
</script>

3.3 技术亮点

  1. 响应式导航:根据当前路由自动高亮激活的菜单项
  2. 夜间模式:支持localStorage持久化存储
  3. 全局状态管理:通过Vue provide/inject实现跨组件通信
  4. 移动端适配:底部快捷导航栏仅在移动端显示
  5. 积分系统:集中管理积分状态,所有子组件可响应更新

四、八大功能页面概览

4.1 Home首页

功能

  • 考研倒计时(2026-12-26)
  • 每日暖心鼓励语
  • 任务统计概览
  • 快捷操作工具栏

核心代码片段

1
2
3
4
5
6
const targetDate = new Date('2026-12-26')
const daysRemaining = computed(() => {
const now = new Date()
const diff = targetDate - now
return Math.ceil(diff / (1000 * 60 * 60 * 24))
})

4.2 Tasks任务管理

功能

  • 添加/删除/完成任务
  • 任务分类(学习、英语、政治、专业课等)
  • 优先级设置(紧急、普通、低优先)
  • 筛选功能(全部/待完成/已完成)
  • 完成率统计

技术要点

  • 使用computed实现响应式筛选
  • localStorage持久化存储
  • 完成任务自动+10积分

4.3 Focus专注计时

功能

  • 25分钟番茄钟计时器
  • 环形进度条可视化
  • 白噪音环境音(雨声、森林、海浪等)
  • 音量控制
  • 专注记录统计
  • 完成奖励5积分

核心逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const completePomodoro = () => {
// 保存专注记录
const today = new Date().toDateString()
const savedData = JSON.parse(localStorage.getItem('focusHistory') || '{}')

if (!savedData[today]) {
savedData[today] = { minutes: 0, sessions: 0, records: [] }
}

savedData[today].minutes += 25
savedData[today].sessions++
savedData[today].records.push(new Date().toISOString())

localStorage.setItem('focusHistory', JSON.stringify(savedData))

// 发放积分奖励
const currentPoints = parseInt(localStorage.getItem('lovePoints') || '0')
localStorage.setItem('lovePoints', (currentPoints + 5).toString())

emit('updatePoints')
alert('🎉 专注25分钟完成!获得 5 个恋爱积分')
}

4.4 Checkin习惯打卡

功能

  • 自定义习惯列表
  • 每日一键打卡
  • 连续打卡天数统计
  • 习惯完成率图表

4.5 Stats数据统计

功能

  • 学习时长统计图表
  • 任务完成趋势
  • 专注时段分析
  • 积分获取统计

4.6 Rewards心愿单兑换

功能

  • 心愿单管理(愿望+所需积分)
  • 积分兑换功能
  • 已实现愿望展示

4.7 Journal学习心得

功能

  • 每日学习记录
  • 日期筛选
  • 历史心得回顾

4.8 Achievements成就徽章

功能

  • 10个成就徽章系统
  • 解锁条件展示
  • 进度追踪

五、样式系统

5.1 Tailwind CSS配置

1
2
3
4
5
6
7
8
9
10
11
12
13
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
darkMode: 'class',
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

5.2 全局样式

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
/* src/style.css */

/* 玻璃拟态卡片基础样式 */
.glass-card {
background: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border: 1px solid rgba(255, 255, 255, 0.4);
border-radius: 1.5rem;
box-shadow: 0 25px 50px -12px rgba(251, 146, 60, 0.25);
}

/* 果冻质感按钮 */
.jelly-button {
transition: all 0.2s ease-out;
}

.jelly-button:hover {
box-shadow: 0 10px 15px -3px rgba(251, 146, 60, 0.3);
}

.jelly-button:active {
transform: scale(0.95);
}

/* 自定义滚动条 */
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
}

.custom-scrollbar::-webkit-scrollbar-track {
background: transparent;
}

.custom-scrollbar::-webkit-scrollbar-thumb {
background: linear-gradient(to bottom, #fdba74, #f472b6);
border-radius: 3px;
}

/* 深色模式样式 */
.dark body,
body.dark {
background-color: #1a1a2e;
color: #e2e8f0;
}

.dark .glass-card {
background: rgba(30, 30, 50, 0.7);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);
}

六、问题排查与解决

6.1 Tasks.vue模板错误

问题

1
2
<!-- 错误写法 -->
<div class="text-2xl font-bold text-orange-600">{{ tasks.value.length }}</div>

原因:在Vue 3模板中,ref会自动解包,不需要.value

解决

1
2
<!-- 正确写法 -->
<div class="text-2xl font-bold text-orange-600">{{ tasks.length }}</div>

6.2 夜间模式不生效

原因:缺少tailwind.config.js配置

解决:创建配置文件并启用darkMode: 'class'

6.3 页面空白/加载缓慢

分析

  1. Vite开发模式需要实时编译
  2. 粒子效果增加DOM操作开销
  3. 多重动画增加渲染压力

解决

  1. 移除粒子效果
  2. 简化背景动画
  3. 建议使用生产构建:npm run build && npx vite preview

6.4 端口占用

解决

1
2
3
4
5
6
7
8
# 终止占用端口的进程
taskkill /F /IM node.exe

# 清除缓存
rm -rf node_modules/.vite

# 重启服务器
npm run dev

七、性能优化策略

7.1 路由懒加载

每个页面组件使用动态导入,实现按需加载:

1
component: () => import('../views/Home.vue')

7.2 CSS优化

  • 使用Tailwind CSS原子化CSS,减少CSS体积
  • 使用will-change提示浏览器优化
  • 使用transform: translateZ(0)开启GPU加速

7.3 首屏优化

  • 移除粒子效果(减少20+ DOM操作)
  • 简化背景动画
  • 建议生产环境预编译

八、Git版本管理

8.1 提交记录

1
2
3
4
5
6
7
135e849 feat: 添加Vue Router路由和8个功能页面

- 添加Vue Router实现SPA路由
- 创建8个功能页面: Home, Tasks, Focus, Checkin, Stats, Rewards, Journal, Achievements
- 添加玻璃拟态UI设计和动画效果
- 添加夜间模式支持
- 优化性能表现

8.2 仓库地址

1
https://github.com/dafenqirunrunrun/love-study-app.git

九、开发经验总结

9.1 Vue 3 Composition API最佳实践

  1. 使用<script setup>语法糖

    • 代码更简洁
    • 更好的类型推断
    • 更快的编译速度
  2. 合理使用ref和reactive

    • 原始类型用ref
    • 对象类型可以用reactive
    • 模板中ref自动解包,无需.value
  3. provide/inject用于跨组件通信

    • 避免props逐层传递
    • 适合”插件”式功能(如积分系统)

9.2 Vue Router最佳实践

  1. 路由懒加载

    1
    component: () => import('./views/Page.vue')
  2. 路由元信息meta

    • 存储页面标题、权限等信息
    • 路由守卫中访问to.meta
  3. 动态路由参数

    • 使用:id定义参数路由
    • 组件中通过useRoute()获取参数

9.3 UI/UX设计心得

  1. 玻璃拟态风格

    • 半透明背景+模糊效果
    • 柔和的阴影和边框
    • 营造现代感
  2. 响应式设计

    • 移动端底部导航
    • 弹性布局flex-wrap
    • 媒体查询适配
  3. 交互反馈

    • hover状态变化
    • active缩放效果
    • loading状态提示

十、后续优化方向

10.1 短期优化

  • 添加页面切换过渡动画
  • 优化加载状态提示
  • 添加骨架屏loading效果

10.2 中期规划

  • 后端API对接(Node.js/Express)
  • 数据库存储(MongoDB/SQLite)
  • 用户认证系统
  • 数据同步功能

10.3 长期愿景

  • 移动端App(Capacitor/Tauri)
  • 小程序版本
  • AI智能推荐学习计划
  • 社交功能(情侣互相关注、学习PK)

结语

今天的开发工作圆满完成,考研伴侣App已经具备了完整的SPA架构和八大核心功能。从最初的单页面臃肿应用,到现在的模块化、可维护的现代Web应用,代码质量和用户体验都得到了显著提升。

下一步将继续完善功能细节,优化用户体验,并逐步引入后端服务,实现数据的持久化存储和跨设备同步。

开发进度:30%(共规划100%)


文档创建时间:2026-02-08
作者:达芬奇
版本:v2.0