Vue博客搭建(16)个人中心2

450 阅读4分钟

在上一篇文章中,我们完成了个人中心的对应路由设计,接下来 我们就开始写个人中心的页面内容。

基本思路

个人中心也需要若干个子路由,包括“个人信息”、“文章列表”等。因此我们如法炮制,创建对应的子路由即可。这里就不再展示代码了。

个人信息包括用户的用户名、注册邮箱、出生日期、头像和个性签名等。进入之后首先展示的便是用户的个人信息,并且还需要有一个修改按钮,去修改除了用户头像之外的信息(通常来讲用户头像都是用户点击头像时弹出询问)。

首先要在对应的后端中创建一个新的数据表userdata用来存储用户的个人信息,我们会存数字和字符串,但是图片我们应该如何存储?图片本身并不是使用文本格式存储的,而是类似于.exe的二进制文件,虽然MySQL本身提供了二进制文件类型blob,但是这会导致数据库查询速度明显下降,因此我们可以曲线救国,即数据库中只存储图片的URL,前端再根据url获取图片。

HTML5原生提供了FileReader类来完成文件上传的工作。但是首先我们要解决一个问题:一般的生产环境中,这种上传头像使用的都是页面中悬浮的消息弹框,而不是跳转到一个新的页面,下面我们就新建一个uploadAvatar.vue组件来进行弹框的模拟。

<script setup>

</script>

<template>
    <div>
        上传头像
        <img>
        <button> 上传</button>
    </div>

</template>

单元测试

为了单独的一个组件设定一个新的路由不光费时费力还没有必要,因此我们引入软件开发中一个很重要的思维——单元测试。也就是测试vue应用的最小组成单位:组件,是否能够正常工作。vite官方的单元测试包为vitest,搭配Vue3的话会有很好的使用体验。因此我们使用vitest配合Vue官方的组件测试库vue test utils来进行单元测试。安装完vitest之后要在package.json中添加如下内容:

"scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "test": "vitest"
  },

别忘了在vite.config.js也就是vite的配置文件中设定测试环境,我们这里使用的是happy-dom环境来进行测试(所以也要安装happy-dom)。

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  server:{
    // ...
  test:{
    environment:'happy-dom',
  }
})

之后就可以创建测试文件了。vitest会自动识别*.test.js文件并运行。接下来我们可以在根目录下创建对应的tests/uploadAvatar.test.js并进行单元测试了。

import { describe, expect, it } from "vitest";
import { mount } from "@vue/test-utils";

import uploadAvatar from '../src/components/uploadAvatar.vue'

// 描述一组测试,包括名称和包括的测试项目
describe('test uploadAvatar',()=>{
    // 一个测试
    it('should render',()=>{
        // 需要将组件实例化之后才可以进行测试
        const wrapper = mount(uploadAvatar,{
            
        })
        // 看看组件里面包不包括'头像'v这个字符串
        expect(wrapper.text()).contain('头像')
    })
})

当我们运行npm run test的时候,vitest便会自动寻找测试文件并进行运行,测试完成后告诉我们测试结果。

文件的后端读取

HTML5自带了文件上传元素,即<input type ="file"/>,并且有配套的事件,即@change,也就是说当存储的文件有改变时进行对应的上传操作,具体可以看下面的代码。

<script setup>
import axios from 'axios'


function upload(event){
    const file = event.target.files[0];

}

</script>

<template>
    <div>
        上传头像
        <img>
        <input type="file" @change="upload"/>
    </div>

</template>

<style scoped>
</style>

接下来就要先进行后端接收的设计。首先我们要允许后端把一部分的文件URL暴露出来,可以使用app.use('/files/avatars',express.static('files/avatars')); ,其也是一个中间件,可以将files/avatars目录下的文件全部暴露出来(通过server/files/avatars访问)。

不过首先我们要整理一下已经写过的后端代码,因为它有点过长了,阅读起来的观感不好,因此我们要对其进行分离,但是按传统的分离方法对于这种全是定义的后端很难弄,因此Express为我们提供了router组件,官方称之为mini-app,我们以测试接口为例,给大家演示一下他是如何工作的。对于connecton的问题,需要注意一下不要循环引用。

// test.js
const express = require('express')
const router = express.Router();
const connection = require('../index').connection;

router.get('/test',(req, res)=>{
    res.send('1');
})

module.exports = router;

// index.js
const test = require('./gets/test');
app.use(test)
module.exports.connection = connection;

接下来就可以进行正式的后端编写了,使用multer包可以express处理上传的文件(也就是form-data类型)。

const e = require('express');
const express = require('express');
const router = express.Router();
const multer = require('multer');

const storage = multer.diskStorage({
    destination: 'files/avatars',
    // 目标文件夹的相对路径
    filename(req, file, callback){
        callback(null,`${Date.now()}${file.originalname}`);
    }
    // 目标文件名,通过函数回调中的第二个参数来实现
})
const upload = multer({storage: storage});
// 添加配置

const connection = require('../funcitons/sqlConnection');

router.post('/uploadAvatar',upload.single('avatar'),(req, res)=>{
    const id = req.body.id;
    const url = req.file.filename;
    const selectSql = `select * from userInformation where id = '${id}'`;
    connection.query(selectSql,(err, result)=>{
        if(result.length ===0){
            // 如果没有头像那么添加一个新的
            const addSql = `insert into userInformation (id,avatar) values (?,?)`;
            connection.query(addSql, [id,url], (err, result)=>{
                if(err){
                    console.log(err);
                }else{
                    res.send(url);
                }
            })
        }else{
            // 如果有头像的话那么便更新头像的链接
            const updateSql = `update userInformation set avatar = '${url}'`;
            connection.query(updateSql, (err, result)=>{
                if(err){
                    console.log(err);
                }else{
                    res.send(url);
                }
            })
        }
    })
    

})

module.exports = router;