实战教程:基于Vue.js与Django REST Framework的任务管理SPA开发全流程
引言
在当今快速发展的Web开发领域,前后端分离架构已成为主流趋势。本文将带您从零开始构建一个功能完整的任务管理单页应用(SPA),结合Vue.js前端框架与Django REST Framework后端API服务。通过这个实战项目,您不仅能掌握现代Web开发的核心技术栈,还能学习到企业级应用开发的最佳实践。
一、项目概述与技术选型
1.1 项目功能需求
我们将开发一个具备以下核心功能的任务管理系统:
-
用户认证:注册、登录、登出
-
任务管理:创建、查看、更新、删除任务
-
任务分类:按状态(待办、进行中、已完成)筛选
-
搜索功能:按标题或内容搜索任务
-
响应式设计:适配不同设备屏幕
1.2 技术栈选择
技术 | 作用 | 优势分析 |
---|---|---|
Vue 3 | 前端框架 | 响应式、组合式API、良好生态 |
Vue Router | 前端路由管理 | SPA路由控制、导航守卫 |
Pinia | 状态管理 | 轻量级、TypeScript支持 |
Axios | HTTP客户端 | Promise API、拦截器支持 |
Django | 后端框架 | ORM强大、Admin后台、安全性高 |
DRF | REST API构建 | 序列化、认证、权限、视图集 |
JWT | 认证机制 | 无状态、跨域支持、安全性好 |
二、后端开发:Django REST Framework实现
2.1 项目初始化
# 创建Django项目
django-admin startproject taskmanager_backend
cd taskmanager_backend# 创建核心应用
python manage.py startapp tasks
python manage.py startapp users# 安装必要依赖
pip install djangorestframework django-cors-headers pyjwt
2.2 数据模型设计
tasks/models.py
:
from django.db import models
from django.contrib.auth import get_user_modelUser = get_user_model()class Task(models.Model):STATUS_CHOICES = [('TODO', '待办'),('IN_PROGRESS', '进行中'),('DONE', '已完成'),]user = models.ForeignKey(User, on_delete=models.CASCADE)title = models.CharField(max_length=200)description = models.TextField(blank=True)status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='TODO')created_at = models.DateTimeField(auto_now_add=True)updated_at = models.DateTimeField(auto_now=True)due_date = models.DateTimeField(null=True, blank=True)def __str__(self):return self.title
2.3 序列化器实现
tasks/serializers.py
:
from rest_framework import serializers
from .models import Task
from users.serializers import UserSerializerclass TaskSerializer(serializers.ModelSerializer):user = UserSerializer(read_only=True)class Meta:model = Taskfields = ['id', 'user', 'title', 'description', 'status', 'created_at', 'updated_at', 'due_date']read_only_fields = ['id', 'user', 'created_at', 'updated_at']
2.4 视图集与路由配置
tasks/views.py
:
from rest_framework import viewsets, permissions
from .models import Task
from .serializers import TaskSerializer
from .permissions import IsOwnerOrReadOnlyclass TaskViewSet(viewsets.ModelViewSet):serializer_class = TaskSerializerpermission_classes = [permissions.IsAuthenticated, IsOwnerOrReadOnly]def get_queryset(self):# 只返回当前用户的任务return Task.objects.filter(user=self.request.user)def perform_create(self, serializer):# 创建时自动关联当前用户serializer.save(user=self.request.user)
tasks/permissions.py
:
from rest_framework import permissionsclass IsOwnerOrReadOnly(permissions.BasePermission):"""自定义权限:只允许任务的所有者编辑"""def has_object_permission(self, request, view, obj):# 安全方法(GET, HEAD, OPTIONS)允许所有请求if request.method in permissions.SAFE_METHODS:return True# 写入权限仅限任务所有者return obj.user == request.user
2.5 JWT认证实现
users/views.py
:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework_simplejwt.tokens import RefreshToken
from django.contrib.auth import authenticate
from .serializers import UserSerializerclass RegisterView(APIView):def post(self, request):serializer = UserSerializer(data=request.data)if serializer.is_valid():user = serializer.save()refresh = RefreshToken.for_user(user)return Response({'user': serializer.data,'refresh': str(refresh),'access': str(refresh.access_token),}, status=status.HTTP_201_CREATED)return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)class LoginView(APIView):def post(self, request):username = request.data.get('username')password = request.data.get('password')user = authenticate(username=username, password=password)if user is None:return Response({'error': 'Invalid credentials'},status=status.HTTP_401_UNAUTHORIZED)refresh = RefreshToken.for_user(user)return Response({'refresh': str(refresh),'access': str(refresh.access_token),})
三、前端开发:Vue.js实现
3.1 项目初始化
# 使用Vite创建Vue项目
npm create vite@latest taskmanager_frontend --template vue
cd taskmanager_frontend# 安装必要依赖
npm install vue-router@4 pinia axios vue-axios
npm install @vueuse/core lodash-es
npm install --save-dev sass
3.2 项目结构设计
src/
├── api/ # API请求封装
├── assets/ # 静态资源
├── components/ # 公共组件
│ ├── TaskCard.vue
│ ├── TaskForm.vue
│ └── ...
├── composables/ # 组合式函数
├── router/ # 路由配置
├── stores/ # Pinia状态管理
├── styles/ # 全局样式
├── utils/ # 工具函数
├── views/ # 页面组件
│ ├── Auth/
│ ├── Dashboard/
│ └── ...
├── App.vue
└── main.js
3.3 Pinia状态管理
stores/authStore.js
:
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { login, register, logout } from '@/api/auth'
import router from '@/router'export const useAuthStore = defineStore('auth', () => {const user = ref(null)const token = ref(localStorage.getItem('token'))const isAuthenticated = ref(false)const setAuth = (userData, authToken) => {user.value = userDatatoken.value = authTokenisAuthenticated.value = truelocalStorage.setItem('token', authToken)}const clearAuth = () => {user.value = nulltoken.value = nullisAuthenticated.value = falselocalStorage.removeItem('token')}const handleLogin = async (credentials) => {try {const response = await login(credentials)setAuth(response.user, response.access)router.push('/dashboard')} catch (error) {clearAuth()throw error}}const handleRegister = async (userData) => {try {const response = await register(userData)setAuth(response.user, response.access)router.push('/dashboard')} catch (error) {clearAuth()throw error}}const handleLogout = async () => {await logout()clearAuth()router.push('/login')}return {user,token,isAuthenticated,handleLogin,handleRegister,handleLogout}
})
3.4 API服务封装
api/tasks.js
:
import axios from 'axios'
import { useAuthStore } from '@/stores/authStore'const apiClient = axios.create({baseURL: import.meta.env.VITE_API_BASE_URL,timeout: 10000
})// 请求拦截器
apiClient.interceptors.request.use((config) => {const authStore = useAuthStore()if (authStore.token) {config.headers.Authorization = `Bearer ${authStore.token}`}return config
}, (error) => {return Promise.reject(error)
})// 响应拦截器
apiClient.interceptors.response.use((response) => {return response
}, (error) => {if (error.response?.status === 401) {const authStore = useAuthStore()authStore.handleLogout()}return Promise.reject(error)
})export default {getTasks(params = {}) {return apiClient.get('/tasks/', { params })},getTask(id) {return apiClient.get(`/tasks/${id}/`)},createTask(taskData) {return apiClient.post('/tasks/', taskData)},updateTask(id, taskData) {return apiClient.patch(`/tasks/${id}/`, taskData)},deleteTask(id) {return apiClient.delete(`/tasks/${id}/`)}
}
3.5 任务列表组件实现
components/TaskList.vue
:
<script setup>
import { computed, ref } from 'vue'
import { useTaskStore } from '@/stores/taskStore'
import TaskCard from './TaskCard.vue'
import TaskForm from './TaskForm.vue'const taskStore = useTaskStore()
const showForm = ref(false)
const editingTask = ref(null)const tasks = computed(() => taskStore.tasks)
const filteredTasks = computed(() => {return tasks.value.filter(task => {// 根据状态筛选逻辑return true})
})const handleEdit = (task) => {editingTask.value = taskshowForm.value = true
}const handleSubmit = async (taskData) => {if (editingTask.value) {await taskStore.updateTask(editingTask.value.id, taskData)} else {await taskStore.createTask(taskData)}showForm.value = falseeditingTask.value = null
}
</script><template><div class="task-list"><button @click="showForm = true" class="add-button">添加任务</button><TaskForm v-if="showForm":initial-data="editingTask"@submit="handleSubmit"@cancel="showForm = false"/><div v-if="filteredTasks.length" class="tasks-grid"><TaskCardv-for="task in filteredTasks":key="task.id":task="task"@edit="handleEdit"/></div><p v-else class="empty-message">暂无任务,点击上方按钮添加</p></div>
</template><style scoped>
.task-list {max-width: 1200px;margin: 0 auto;padding: 20px;
}.add-button {background-color: #4CAF50;color: white;padding: 10px 20px;border: none;border-radius: 4px;cursor: pointer;margin-bottom: 20px;
}.tasks-grid {display: grid;grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));gap: 20px;
}.empty-message {text-align: center;color: #666;font-size: 1.2rem;
}
</style>
四、前后端联调与部署
4.1 跨域问题解决
taskmanager_backend/settings.py
:
INSTALLED_APPS = [...'corsheaders',
]MIDDLEWARE = [...'corsheaders.middleware.CorsMiddleware','django.middleware.common.CommonMiddleware',
]# 允许所有来源(生产环境应配置具体域名)
CORS_ALLOW_ALL_ORIGINS = True# 或指定允许的域名
CORS_ALLOWED_ORIGINS = ["http://localhost:5173","https://your-production-domain.com"
]
4.2 环境变量配置
.env.development
:
VITE_API_BASE_URL=http://localhost:8000/api
vue.config.js
:
import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'export default defineConfig(({ mode }) => {const env = loadEnv(mode, process.cwd())return {plugins: [vue()],server: {proxy: {'/api': {target: env.VITE_API_BASE_URL,changeOrigin: true,rewrite: path => path.replace(/^\/api/, '')}}}}
})
4.3 生产环境部署
后端部署(Nginx + Gunicorn):
安装Gunicorn:
pip install gunicorn
创建Gunicorn服务:
gunicorn --workers 3 --bind unix:taskmanager.sock taskmanager_backend.wsgi:application
Nginx配置:
server {listen 80;server_name api.yourdomain.com;location / {include proxy_params;proxy_pass http://unix:/path/to/taskmanager.sock;}location /static/ {alias /path/to/your/project/staticfiles/;}
}
前端部署:
构建生产版本:
npm run build
Nginx配置:
server {listen 80;server_name yourdomain.com;root /path/to/taskmanager_frontend/dist;index index.html;location / {try_files $uri $uri/ /index.html;}location /api {proxy_pass http://api.yourdomain.com;}
}
五、项目优化与扩展
5.1 性能优化
前端优化:
-
组件懒加载
const Dashboard = () => import('@/views/Dashboard.vue')
-
路由懒加载
{path: '/dashboard',component: () => import('@/views/Dashboard.vue')
}
-
使用
v-memo
优化大型列表渲染
后端优化:
-
数据库查询优化(select_related/prefetch_related)
-
分页支持
-
缓存常用数据
5.2 功能扩展
-
实时更新:
-
使用WebSocket实现任务实时同步
-
集成Django Channels
-
-
文件上传:
-
实现任务附件功能
-
使用Django的FileField和Vue的文件上传组件
-
-
数据可视化:
-
使用ECharts或Chart.js展示任务统计图表
-
实现任务完成情况的时间线视图
-
5.3 测试策略
-
前端测试:
-
单元测试:Vitest + Vue Test Utils
-
E2E测试:Cypress
-
-
后端测试:
-
单元测试:Django TestCase
-
API测试:DRF APITestCase
-
六、总结与展望
通过本项目的实践,我们完成了:
-
基于Django REST Framework构建了功能完善的RESTful API后端
-
使用Vue 3组合式API开发了响应式的前端SPA应用
-
实现了JWT认证的安全机制
-
掌握了前后端分离架构的开发流程
-
学习了项目部署的基本方法
项目亮点:
-
采用现代化技术栈,符合当前行业趋势
-
完善的认证与权限控制
-
响应式设计,适配多种设备
-
清晰的代码结构与模块化设计
未来改进方向:
-
增加团队协作功能,支持多人任务分配
-
实现任务提醒和通知系统
-
集成第三方登录(Google、GitHub等)
-
开发移动端应用(React Native或Flutter)
希望这篇实战教程能帮助您掌握Vue+Django REST Framework全栈开发技能!如果您在实践过程中遇到任何问题,欢迎在评论区留言讨论。