在这篇文章中,我们将带领你一步步构建一个简单的博客项目,其中包括用户的注册和登录功能以及文章详情展示。
通过这个项目,你将了解如何利用Django处理后台逻辑,并通过Vue.js构建动态的前端界面。
本项目将分为前后端两部分:Django作为后端,用于处理数据存储、用户认证及API服务;Vue.js作为前端,用于构建用户界面和与后端的交互。前端将通过Axios向后端发出请求并接收响应,完成登录、注册、文章展示等功能。
环境搭建与工具准备
在开始构建项目之前,我们需要搭建开发环境并安装一些必要的工具。本文将使用以下技术栈:
- Django:作为后端框架,处理数据存储、用户认证及API服务。
- Vue.js:作为前端框架,负责构建用户界面并与后端交互。
1. 安装 Python 和 Django
首先,我们需要安装 Python,这是运行 Django 所必需的。Django 要求 Python 3.6 及以上版本。
步骤:
安装 Python
- 如果你已经安装了 Python 3.x,可以通过以下命令检查版本:
1
python --version
- 如果未安装,可以从 Python官网 下载并安装适合你操作系统的版本。
- 如果你已经安装了 Python 3.x,可以通过以下命令检查版本:
创建虚拟环境
为了确保项目的依赖与系统环境隔离,建议创建一个虚拟环境:1
python -m venv myenv
然后激活并进入虚拟环境
1
myenv\Scripts\activate
然后安装django djangorestframework django-cors-headers
1 | pip install django djangorestframework django-cors-headers |
你可以通过以下命令检查是否安装成功:
1 | pip list |
2. 创建 Django 项目
接下来,我们将创建一个新的 Django 项目:
- 创建 Django 项目
使用 django-admin 工具来创建一个新的项目,命名为 blog_project:
1 | django-admin startproject blog_project |
- 运行开发服务器
进入项目目录,并启动开发服务器以确认一切正常:
1 | cd blog_project |
如果你在浏览器中访问 http://127.0.0.1:8000/,应该会看到 Django 的欢迎页面,表示后端环境搭建成功。
3. 安装 Node.js 和 Vue CLI
为了构建前端部分,我们需要安装 Node.js 和 Vue CLI。
步骤:
安装 Node.js 和 npm
Node.js 是 JavaScript 运行时环境,npm 是 Node.js 的包管理工具。你可以从 Node.js 官网 下载并安装最新的 LTS 版本。安装完成后,可以通过以下命令检查版本:1
2node --version
npm --version安装 Vue CLI
Vue CLI 是一个标准化的工具,用于快速搭建 Vue.js 项目。可以通过 npm 安装:1
npm install -g @vue/cli
验证安装是否成功:
1
vue --version
要使用 Vite 和 Vue 搭建项目,按照以下步骤进行:
创建项目:运行以下命令创建一个 Vue 项目:
1
npm create vite@latest my-vue-app -- --template vue
安装依赖
1
2cd my-vue-app
npm install这一步可能会提示node版本过低,请下载安装nvm(node版本管理器)
1
2
3
4nvm list available #查看有效版本
nvm install 20.0.0 #安装指定版本号的node
nvm use 20.0.0 #切换node版本
nvm list #查看当前正在使用的node版本启动服务器
1
npm run dev
构建生产版本
1
npm run build
安装 vue-router
在项目根目录下运行以下命令安装
vue-router
:1
npm install vue-router
在 src 目录下创建一个名为 router 的文件夹,并在其中创建一个 index.js 文件。编辑 src/router/index.js 文件,配置基本路由:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24import { createRouter, createWebHistory } from 'vue-router';
import Login from '../views/Login.vue';
import Register from '../views/Register.vue';
const routes = [
{
path: '/login',
name: 'Login',
component: Login,
},
{
path: '/register',
name: 'Register',
component: Register,
},
];
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
});
export default router;//导出路由实例,在main.js中引用在 src/views 目录下创建两个 Vue 组件文件:Login.vue 和 Register.vue
1
2
3
4
5
6
7
8
9
10
11<template>
<div>
login
</div>
</template>
<script>
export default {
name: 'Login',
};
</script>1
2
3
4
5
6
7
8
9
10
11<template>
<div>
register
</div>
</template>
<script>
export default {
name: 'Register',
};
</script>打开 src/main.js 文件,将 router 添加到 Vue 实例中:
1
2
3
4
5import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
createApp(App).use(router).mount('#app');更新 App.vue 文件,添加导航链接和路由出口:
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<template>
<div id="app">
<nav>
<router-link to="/login">login</router-link>
<router-link to="/register">register</router-link>
</nav>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
};
</script>
<style>
nav {
padding: 1rem;
background-color: #f0f0f0;
}
nav a {
margin-right: 1rem;
}
</style>启动开发服务器
1
npm run dev
要在 Vite 和 Vue 项目中添加 vuex
进行全局状态管理,请按照以下步骤操作:
二次封装Axios请求库
- 安装
1
npm install axios
- 在 src 目录下创建一个名为 services 的文件夹,并在其中创建一个 axios.js 文件:
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
69import axios from 'axios';
import { Toast } from 'vant';
// 创建一个 axios 实例
const instance = axios.create({
baseURL: 'http://192.168.31.114:8000/', // 设置默认的 baseURL
timeout: 10000, // 请求超时时间
});
// 请求拦截器
instance.interceptors.request.use(
config => {
// 你可以在这里添加认证令牌或其他请求前的处理
const token = localStorage.getItem('jwt_token'); // 示例:从 localStorage 获取令牌
//let token="8142bd759b8498ccbaab7d70c14c8e3530ae3117"
if (token) {
config.headers.Authorization = `TOKEN ${token}`;//注意这里有个空格
}
return config;
},
error => {
// 请求错误时的处理
Toast.fail('请求失败,请稍后再试');
return Promise.reject(error);
}
);
// 响应拦截器
instance.interceptors.response.use(
response => {
// 响应成功时的处理
return response;
},
error => {
// 响应错误时的处理
if (error.response) {
// 服务器响应了状态码,但状态码超出了 2xx 范围
console.error('Server responded with status:', error.response.status);
console.error('Response data:', error.response.data);
if (error.response.status === 401) {
// 处理未授权错误(例如,令牌过期)
Toast.fail('身份认证失败,请重新登录');
// 你可以在这里执行一些重定向操作,例如:router.push('/login');
} else if (error.response.status === 403) {
// 处理禁止访问错误
Toast.fail('没有权限访问此资源');
} else if (error.response.status === 500) {
// 处理服务器错误
Toast.fail('服务器错误,请稍后再试');
} else {
// 其他错误
Toast.fail('发生错误,请重试');
}
} else if (error.request) {
// 请求已发出,但没有收到响应
console.error('No response received:', error.request);
Toast.fail('服务器无响应,请检查网络');
} else {
// 其他错误
console.error('Error message:', error.message);
Toast.fail('发生错误,请重试');
}
return Promise.reject(error);
}
);
export default instance;Toast为vant组件,可以根据实际情况修改
安装vant UI组件
- 安装
1
npm install vant@3 -S
- 然后在main.js文件中引用
1
2import Vant from 'vant'
app.use(Vant)
安装 vuex
在项目根目录下运行以下命令安装
vuex
:1
npm install vuex@next
在 src 目录下创建一个名为 store 的文件夹,并在其中创建一个 index.js 文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import { createStore } from 'vuex';
import user from './user';
export default createStore({
state: {
},
mutactions: {
},
actions: {
},
getters: {
},
modules: {
user: user
},
})再创建一个user.js:
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//这里存用户的所有信息
import axios from '../services/axios'
const store = createStore({
state:{
id: "",
username: "",
is_login: "",
token: ""
},
mutations: {
update_token(state, token) {
state.token = token
},
update_info(state, data) {
state.username = data.username
state.is_login = true
},
clear_user_info(state) {
state.username = '';
state.is_login = false;
state.token = '';
},
},
actions: {
async login(ctx, data) {
try {
const response = await axios({
method: "POST",
headers: {
'Content-Type': 'application/json'
},
url: "/user/account/login/",
data: {
"username": data.username,
"password": data.password,
}
})
//检查api响应
if (response.data.state === 'success') {
console.log(response.data.jwt_token)
localStorage.setItem("authToken", response.data.jwt_token)
ctx.commit('update_token', response.data.jwt_token)
return true
} else {
Toast.fail(response.data.message)
return false
}
} catch (error) {
// 其他错误
console.error('Error message:', error);
return false
}
},
},
getters: {
},
});
export default store;使用 Vuex 在组件中访问和操作状态
在你的 Vue 组件中,你可以通过以下方式访问和操作全局状态。(比如Login组件中)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<template>
<div class="container">
<div title="登录" class="login-user-card">
<h3 class="title">
<van-space size="2rem">
<router-link :to="{ name: 'login' }" :class="{ 'active-link': $route.name === 'login' }">
<span>登录</span>
</router-link>
<b>.</b>
<router-link :to="{ name: 'register' }" :class="{ 'active-link': $route.name === 'register' }">
<span>注册</span>
</router-link>
</van-space>
</h3>
<van-divider />
<van-form @submit="onSubmit" @failed="onFailed">
<van-cell-group inset>
<van-field v-model="username" name="username" label="用户名" placeholder="用户名"
:rules="[{ required: true, message: '请填写用户名' }]" class="custom-field " />
<van-field v-model="password" type="password" name="password" label="密码" placeholder="密码"
:rules="[{ required: true, message: '请填写密码' }]" />
<van-field style="visibility: hidden;pointer-events: none;" v-model="cpatcha" type="cpatcha" name="验证码"
label="验证码" placeholder="验证码" :rules="[{ required: false, message: '验证码不正确' }]" />
</van-cell-group>
<div style="margin: 16px;">
<van-button round block type="primary" native-type="submit">
登录
</van-button>
<p class="sign-up-msg">点击 “登录” 即表示您同意并愿意遵守<br> <a target="_blank"
href="">用户协议</a> 和 <a target="_blank"
href="">隐私政策</a> 。</p>
<van-divider>还没有账户?
<van-button size="mini" @click="goToRegister" type="success" native-type="submit">
去注册
</van-button>
</van-divider>
</div>
</van-form>
</div>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { ref } from 'vue'
import { Toast } from 'vant'
import { useStore } from 'vuex'
const router = useRouter();
const store = useStore();
const username = ref('');
const password = ref('');
const onSubmit = async (values) => {
try {
const isSuccess = await store.dispatch('login', {
username: username.value,
password: password.value
})
if (isSuccess) {
await store.dispatch('getinfo')//获取新用户的信息
router.push({name:"index"})
}
} catch (error) {
Toast.fail("登陆失败或获取个人信息失败")
}
}
const goToRegister = () => {
router.push({name:"register"})
};
const onFailed = () => {
Toast.fail("请重试")
}
</script>store.dispatch('login', { username: username.value, password: password.value })
store.dispatch('getinfo')//获取新用户的信息