当前位置: 首页 > news >正文

实战教程:基于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支持
AxiosHTTP客户端Promise API、拦截器支持
Django后端框架ORM强大、Admin后台、安全性高
DRFREST 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 功能扩展

  1. 实时更新:

    • 使用WebSocket实现任务实时同步

    • 集成Django Channels

  2. 文件上传:

    • 实现任务附件功能

    • 使用Django的FileField和Vue的文件上传组件

  3. 数据可视化:

    • 使用ECharts或Chart.js展示任务统计图表

    • 实现任务完成情况的时间线视图

5.3 测试策略

  1. 前端测试:

    • 单元测试:Vitest + Vue Test Utils

    • E2E测试:Cypress

  2. 后端测试:

    • 单元测试:Django TestCase

    • API测试:DRF APITestCase

六、总结与展望

通过本项目的实践,我们完成了:

  1. 基于Django REST Framework构建了功能完善的RESTful API后端

  2. 使用Vue 3组合式API开发了响应式的前端SPA应用

  3. 实现了JWT认证的安全机制

  4. 掌握了前后端分离架构的开发流程

  5. 学习了项目部署的基本方法

项目亮点:

  • 采用现代化技术栈,符合当前行业趋势

  • 完善的认证与权限控制

  • 响应式设计,适配多种设备

  • 清晰的代码结构与模块化设计

未来改进方向:

  1. 增加团队协作功能,支持多人任务分配

  2. 实现任务提醒和通知系统

  3. 集成第三方登录(Google、GitHub等)

  4. 开发移动端应用(React Native或Flutter)

希望这篇实战教程能帮助您掌握Vue+Django REST Framework全栈开发技能!如果您在实践过程中遇到任何问题,欢迎在评论区留言讨论。

相关文章:

  • WPF按钮Radius化
  • 如何选择支持AI接入的开发语言与框架
  • .NET ORM开发手册:基于SqlSugar的高效数据访问全攻略
  • 伴随矩阵 -- 代数余子式矩阵的转置
  • .NET外挂系列:8. harmony 的IL编织 Transpiler
  • Docker 镜像标签(Tag)规范与要求
  • AtCoder AT_abc407_d [ABC407D] Domino Covering XOR
  • JavaScript从入门到精通(一)
  • 深入理解Redis线程模型
  • stable diffusion论文解读
  • RabbitMQ 概述
  • MMaDA——开源首个多模态大扩散语言模型
  • 常见实验室器材采购渠道分享
  • 【软考向】Chapter 2 程序设计语言基础知识
  • DAY 35
  • 【Java Web】3.SpringBootWeb请求响应
  • 软件测试文档怎么写?软件渗透测试报告
  • 【R语言科研编程-散点图】
  • commonmark.js 源码阅读(二) - Inline Parser
  • R基于多元线性回归模型实现汽车燃油效率预测及SHAP值解释项目实战
  • 附近学电脑在哪里报名/重庆seo公司
  • 网站整站html/视频推广渠道有哪些
  • 没有网站可以做seo/什么平台打广告比较好免费的
  • 怎么样评价网站做的好坏/郑州网站优化推广
  • 大名网站建设费用/卡点视频软件下载
  • 做文案的人看什么网站/sem是什么的英文缩写