Vue3+TypeScript+Vite+Express+MongoDB搭建博客管理系统

1,114 阅读5分钟

前言

第一次使用Vue3+TS+Vite完全独立开发一个管理系统,加上也是第一次完全自己独立搭建后端,算得上是一个全栈项目了吧。数据库选择了mongodb,后端框架使用express。这个系列算记录自己踩的坑提醒以后的自己。

后端注意事项

1. 后端搭建过程

命令行输入mongod启动mongodb数据库,配合MongoDBCompass图形化管理工具插入数据。

npm i express 安装 express框架(网上查阅到npm i 在某个版本之后与 npm i -S 的效果是一样的,待验证,目前看来是一样的。)

在后端目录文件夹中创建app.js作为启动项目的入口文件。

const express = require('express')
const app = express()

const categoryRouter = require('./router/category')
const articleRouter = require('./router/articleList')
app.use(categoryRouter).use(articleRouter)
app.listen(8000, ()=>{
    console.log('api server running at 8000 port')
})

在与 app.js同级目录下创建三个目录,分别为用于管理路由的router,用来处理管理路由回调函数的router_handler,以及用于放置初始连接数据库的db目录。

image.png

db目录下创建index文件,暴露如下代码

var MongoClient = require("mongodb").MongoClient;
var url = "mongodb://localhost:27017/myblog";

async function database_conn() {
    let conn = await MongoClient.connect(url);
    const db = conn.db('myblog')
    return db
}

module.exports = database_conn

最终暴露的是一个返回数据库的函数。

在router_handler中创建管理路由回调函数的文件,以获取分类列表为例。
先调用从db目录下获取的创建数据库的函数,const 一个常量db来存储。然后连接集合,使用mongodb的查找API,获取结果,把结果返回给请求。然后分别暴露这些函数,注意此处是使用分别暴露,因为这里需要暴露多个函数。

const database_conn = require('../db/index')


exports.getCategory = async (req, res) => {
    const db = await database_conn()
    
    let result = await db.collection('category').find().toArray()
    
    res.send({
        message: '获取分类列表成功',
        data: result
    })
    console.log('获取分类列表成功了')
}

在router目录下建立管理category的路由
调用express.Router()创建路由,设置路由url对应的回调函数,回调函数从router_handler文件夹下获取

const express = require('express')
const router = express.Router()
const categoryHandler = require('../router_handler/category')
router.get('/categorylist', categoryHandler.getCategory)

module.exports = router

回到前面的app文件中,使用use函数调用路由中间件,完成搭建。

2. axios发送请求与express 接受req参数的坑。

axios发送get请求所携带的参数是params,发送post请求携带的参数应该是data。
express搭建的服务器中,接受get请求发送的params参数是在req.query中。 而使用post发送data参数的时候,express必须配置bodyParser.json()和bodyParser.urlencoded()中间件,才能解析出req.body。

image.png

前端注意事项

1. 前端设置proxy代理遇到的坑

在vite项目中设置proxy代理是在vite.config.ts文件夹下

plugins中,Components字段的用途是按需加载antd组件库
server字段的用途:port设置监听的端口号
proxy字段设置代理。
注意前端在使用axios发请求的时候不要携带http://localhost:8000 这样的域名,这样proxy就不会转发了。 勘误:不是不要携带域名,是携带的域名并非端口8000。
在在vue-cli或者vite项目中,proxy的作用是启动一个代理服务器,这个服务器的端口号跟项目端口号一致,例如前端项目端口号为3000,那么代理服务器的端口号也为3000。前端项目发送请求应该发到代理服务器的url,也就是与自身相同的url而非8000端口号。不写域名的情况下,会自动发送到这个代理服务器,因此不写也是可以的。如果写的话就必须写正确,即 http://localhost:3000。


import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite';
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    Components({
      resolvers: [AntDesignVueResolver()],
    }),
  ],
  server:{
    port: 3000,
    proxy:{
      '/api':{
        target: 'http://localhost:8000',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^/api/, ""),
      }
    }
  }
})

2. 前端sideBar侧边栏目录高亮的坑

使用antd组件的侧边栏,遇到需要通过非点击侧边栏的方式跳转路由的时候,侧边栏的高亮并不会跟随路由变化。

此时可以使用watch监听route,当route改变的时候,改变selectedKeys。

watch(route,(newValue, oldValue)=>{
  switch (route.path) {
    case '/':
      selectedKeys.value = ['1']
      break
    case '/article/list':
      selectedKeys.value = ['2']
      break
    case '/article/write':
      selectedKeys.value = ['3']
      break
    case '/article/category':
      selectedKeys.value = ['4']
      break
    case '/tags':
      selectedKeys.value = ['5']
      break
    default:
      break
  }
})

这里还有个小注意点:Vue的watch侦听器只能侦听响应式变量或者对象,不能直接去侦听响应式对象的具体属性,例如这里不能直接侦听route.path,只能侦听route。

3. 前端编辑文章页面

使用antd组件的时候 要在选择器组件前面加上label,需要把选择器组件用a-form-item组件包裹在一起,然后在a-form-item中加入label属性

<a-form-item
    style="margin-top: 30px"
    label="文章分类"
>
  <a-select
      @change="change"
      style="width: 1200px; "
      @focus="focus"

  >
    <a-select-option v-for="item in categories" :key="item._id" :value="item.cateName">{{
        item.cateName
      }}
    </a-select-option>

  </a-select>
</a-form-item>

注意这里select组件绑定的change函数,这里的change函数是底层定义过的,因此不需要传参。定义好的函数中第一个参数就是获取到的value值。

const change = (value: string) => {
  console.log(value)
}

在这里复习一遍Vue中给绑定事件传参的原则。
如果不需要给绑定事件的函数传参,那么@click后面的只需要写函数名,不需要写括号,在实现的函数中第一个参数就是获取dom元素。
如果需要给绑定的事件传参又需要获取dom元素,那么第一个参数写成 $event,后面传入需要传入的实参,实现的函数中第一个是$event对应的形参,后面的是传入的实参对应的形参。


<a-select @click="click($event, '123')"></a-select>
----------------------------------------------------------------------------
//e对应的就是$event,value对应的就是传入的字符串实参123
const click = (e:HTMLElement, value:string)=>{
  console.log('这是元素dom', e, value)
}

4. Vue3嵌套路由和Vite的坑 && Pinia的使用

在用vite构建项目并使用路由懒加载的情况下,页面的加载可能会较慢。
写嵌套路由的时候,children属性可能没有提示。
在路由定义文件下(src/router/index.ts)使用pinia时,需要把pinia实例初始化在beforeEnter属性中,也就是路由守卫中。