案例实践
iword-one Lv2

在这篇文章中,我们将带领你一步步构建一个简单的博客项目,其中包括用户的注册和登录功能以及文章详情展示。

通过这个项目,你将了解如何利用Django处理后台逻辑,并通过Vue.js构建动态的前端界面。

本项目将分为前后端两部分:Django作为后端,用于处理数据存储、用户认证及API服务;Vue.js作为前端,用于构建用户界面和与后端的交互。前端将通过Axios向后端发出请求并接收响应,完成登录、注册、文章展示等功能。

环境搭建与工具准备

在开始构建项目之前,我们需要搭建开发环境并安装一些必要的工具。本文将使用以下技术栈:

  • Django:作为后端框架,处理数据存储、用户认证及API服务。
  • Vue.js:作为前端框架,负责构建用户界面并与后端交互。

1. 安装 Python 和 Django

首先,我们需要安装 Python,这是运行 Django 所必需的。Django 要求 Python 3.6 及以上版本。

步骤:

  1. 安装 Python

    • 如果你已经安装了 Python 3.x,可以通过以下命令检查版本:
      1
      python --version
    • 如果未安装,可以从 Python官网 下载并安装适合你操作系统的版本。
  2. 创建虚拟环境
    为了确保项目的依赖与系统环境隔离,建议创建一个虚拟环境:

    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 项目:

  1. 创建 Django 项目

使用 django-admin 工具来创建一个新的项目,命名为 blog_project:

1
django-admin startproject blog_project
  1. 运行开发服务器

进入项目目录,并启动开发服务器以确认一切正常:

1
2
cd blog_project
python manage.py runserver

如果你在浏览器中访问 http://127.0.0.1:8000/,应该会看到 Django 的欢迎页面,表示后端环境搭建成功。


3. 安装 Node.js 和 Vue CLI

为了构建前端部分,我们需要安装 Node.js 和 Vue CLI。

步骤:

  1. 安装 Node.js 和 npm
    Node.js 是 JavaScript 运行时环境,npm 是 Node.js 的包管理工具。你可以从 Node.js 官网 下载并安装最新的 LTS 版本。安装完成后,可以通过以下命令检查版本:

    1
    2
    node --version
    npm --version
  2. 安装 Vue CLI
    Vue CLI 是一个标准化的工具,用于快速搭建 Vue.js 项目。可以通过 npm 安装:

    1
    npm install -g @vue/cli

    验证安装是否成功:

    1
    vue --version

要使用 Vite 和 Vue 搭建项目,按照以下步骤进行:

  1. 创建项目:运行以下命令创建一个 Vue 项目:

    1
    npm create vite@latest my-vue-app -- --template vue
  2. 安装依赖

    1
    2
    cd my-vue-app
    npm install

    这一步可能会提示node版本过低,请下载安装nvm(node版本管理器)

    1
    2
    3
    4
    nvm list available  #查看有效版本
    nvm install 20.0.0 #安装指定版本号的node
    nvm use 20.0.0 #切换node版本
    nvm list #查看当前正在使用的node版本
  3. 启动服务器

    1
    npm run dev
  4. 构建生产版本

    1
    npm run build

安装 vue-router

  1. 在项目根目录下运行以下命令安装 vue-router

    1
    npm install vue-router
  2. 在 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
    24
    import { 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中引用

  3. 在 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>
  4. 打开 src/main.js 文件,将 router 添加到 Vue 实例中:

    1
    2
    3
    4
    5
    import { createApp } from 'vue';
    import App from './App.vue';
    import router from './router';

    createApp(App).use(router).mount('#app');
  5. 更新 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>
  6. 启动开发服务器

    1
    npm run dev

要在 Vite 和 Vue 项目中添加 vuex 进行全局状态管理,请按照以下步骤操作:

二次封装Axios请求库

  1. 安装
    1
    npm install axios
  2. 在 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
    69
    import 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. 安装
    1
    npm install vant@3 -S

  1. 然后在main.js文件中引用
    1
    2
    import Vant from 'vant'
    app.use(Vant)

安装 vuex

  1. 在项目根目录下运行以下命令安装 vuex

    1
    npm install vuex@next
  2. 在 src 目录下创建一个名为 store 的文件夹,并在其中创建一个 index.js 文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import { createStore } from 'vuex';
    import user from './user';

    export default createStore({
    state: {

    },
    mutactions: {

    },
    actions: {

    },
    getters: {

    },
    modules: {
    user: user
    },
    })
  3. 再创建一个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;
  4. 使用 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')//获取新用户的信息
     

后端api