Vue & SpringBoot 从零实现博客系统 (四)

本文系Vue & SpringBoot从零实现博客系统第四部分 前端代码编写

前端代码编写

前言

在写这个程序之前,我只是一个知道前端三剑客的前端菜鸟,最多再加上JQuery,但绝对称不上了解,鬼知道我怎么脑抽筋没有clone网上的模板,反而自己用vue写出了一个前端的整个模板。

对于我这个后端狗来说,写前端无疑是非常痛苦的,说70%的时间都用到了前端上都不为过,所以,当你希望完全靠自己的力量,不通过视频或者网上现成的代码定制化自己的需求的时候,你一定要考虑清楚,特别是你对前端并没有深入了解的时候,当你没有deadline或者必须要完成的决心时,放弃的几率非常大,但当你坚持下来后,收获同样很大

推荐工具 & 插件 & 依赖

  • WebStorm (和Intellij IDEA 一样,Jetbrain 家族一员)
  • Vue Cli 3.0 (图形化界面,非常赞)
  • Vue Router (Vue 中强大的路由管理)
  • Vuex (Vue中的状态管理工具,管理全局变量)
  • npm (既然追求前后端分离,使用vue了,就不要再向以前一样直接怼HTML了)
  • semantic UI(这是个类似于Boostrap的CSS+JS库)
  • element UI (饿了吗团队出品,基于Vue的UI框架,写管理界面顺畅的一批)
  • axios (request 接口方面我用的axios)
  • mavon-editor (一款基于vue的markdown显示器,一般美观,但超级容易上手)

其他

  • Vue Cli 3.0 无论是从创建项目还是管理项目,包括检查打包,下载插件,依赖等等都比较简单容易上手,建议以前接触vue cli 2+的可以试一试,如果是后端新手(此处指的是心接触npm包管理工具和vue),可以看我之前写的一篇面向新手的文章

项目

唠叨了这么多,现在开始写项目,

项目结构

在这里插入图片描述

  • dist 是打包之后的文件
  • node_modules 是项目的依赖包
  • public 是整个程序的公共部分,
    • index.html 网站框架
    • icon.ico 网站图标
  • src 是主要编码文件夹
    • api 对后台api发起request并获得response
    • assets 网站中用到图片的静态文件
    • components 项目中能重用的vue组件
    • router 管理整个网站的路由信息
    • store 管理整个项目的全局变量
    • util 工具类
    • view 网站页面,会调用compons来解耦
  • app.vue 整个vue组件的框架
  • main.js js文件入口
  • statis 项目中用到的静态代码段

跨域请求问题

由于vue cli3中没有配置文件,所以我们要新建一个vue.config.js文件,在其中配置跨域问题

module.exports = {

  // All options for webpack-dev-server are supported
  // https://webpack.js.org/configuration/dev-server/
  devServer: {
    open: true,

    host: '127.0.0.1',

    port: 80,

    https: false,

    hotOnly: false,

    proxy: {
      '/api': {
        // 对应自己的接口
        target: 'http://127.0.0.1:8080/',
        changeOrigin: true,
        ws: true,
        // 重写为api
        pathRewrite: {
          '^/api': ''
        }
      }
    },
  },
}

axios发送并接收json数据

这里我是通过在util包下新建了一个request.js文件来设置文件的请求头

import axios from 'axios'
import store from '../store/store'
import router from '../router/router'

const service = axios.create({
  baseURL: '/api', // api的base_url
  timeout: 15000, // 请求超时时间,
  headers: {
    'dataType': 'json',
    'Content-Type': 'application/json',
    'charset': 'UTF-8'
  }
})

export default service

这样,在api中可以这样调用接口

import request from '../util/request'

export default {
  getCategoryInfoByArticleId (id) {
    return request({
      url: '/article/any/getCategoryInfoById?id= ' + id,
      method: 'get'
    })
  },
  postCategoryInfo (name, parentId) {
    return request({
      url: '/categoryInfo/admin/post',
      method: 'post',
      data: JSON.stringify({
        'name': name,
        'parentId': parentId
      })
    })
  },
  deleteCategoryById (id) {
    return request({
      url: '/categoryInfo/admin/' + id,
      method: 'delete'
    })
  }
}

权限问题

这里我用的是token,用户登录以后,后端返回一个token,把这个token储存在vuex中

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    user: {
      role: '',
      name: '',
      token: ''
    }
  },
  mutations: {
    login (state, data) {
      // 变更状态
      this.state.user.token = data.token 
      // 存储用户名
      this.state.user.name = data.name
      this.state.user.role = data.role
    },
    logout (state) {
      this.state.token = ''
      this.state.name = ''
      this.state.roles = ''
    }
  }
})

之后为了验证身份,用户必须要把该token作为请求头传递给后端才可以,用到的知识是axios的request和response拦截器

// request拦截器
service.interceptors.request.use(config => {
  if ( /*此处伪代码 vuex中有token*/) {
    config.headers['token'] = /*伪代码:把token赋值给header*/ // 让每个请求携带自定义token
  }
  return config
}, error => {
  // Do something with request error
  console.log(error) // for debug
  Promise.reject(error).then(r => error)
})

// response拦截器
service.interceptors.response.use(
  response => {
    // console.log(response)
    if (response.data === null || response.data === '') {
      store.commit('logout')
      router.push({
        path: '/login'
      })
      location.reload()
    }
    return response
  }
)

路由问题

主要涉及到路由跳转等,事实上在<template>中,我们可以用<router-link>标签来代替<a>标签,前者要更强大,如

扫描二维码关注公众号,回复: 8750025 查看本文章
<router-link :to="{name:'categoryList', params: {cid:index,type:activity.time}}">
    <div v-if="activity.time === '2019-7'">共更新{{activity.num - 2}}篇博客</div>
    <div v-else>共更新{{activity.num }}篇博客</div>
 </router-link>
import Vue from 'vue'
import Router from 'vue-router'
import categoryList from '../view/categoryList'

Vue.use(Router)

let routes =[
  {
    path: '/category/:type/:cid',
    name: 'categoryList',
    component: categoryList
  }
]
let router = new Router({
  routes: routes,
  mode: 'history'
})
export default router

以及通过路由器设置404页面

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

let routes =[
 {
    path: '*',
    name: 'notfound',
    component: notFound
  }
]
let router = new Router({
  routes: routes,
  mode: 'history'
})
export default router

在router.js文件中设置路由守卫,防止用户没有权限访问admin页面

router.beforeEach((to, from, next) => {
  // 获取用户权限信息,为空即没登录,跳转至登录页
  if (to.path.includes('/admin')) {
    let role = /*获取用户权限*/
    if (role !== 'ADMIN') {
      next('/login')
    }
  }
  next()
})

export default router

背景以及用户头像的设置

  • 网页背景网上有很多,可到这个网站上寻找:background image

  • 用户头像我也是从网上找的,这也结合markdown就避免了文件上传(哈哈此处偷个懒):网站

    因为图片是纯随机的,但是vue组件是加载完毕就渲染完成的,当我们遇到数组的时候,就会面临数组中所有元素的图片是相同的尴尬问题,所以此处应该设置一个函数,每调用一次就加载一次

    <img :src=changeImgSrc() alt="头像">
    
    changeImgSrc () {
       return img[i] = 'https://picsum.photos/id/' + Math.floor(Math.random() * 100) + '/100/100'
    }
    

多级评论和多级分类的设置

关于不同组件间的通信

因为我博客的修改文章和发表文章都在同一个页面,所以我需要进行不同组件间的通信,以来识别是需要载入选中文章进行更新还是要新建一篇文章

后记

事实上,这系列博客的目的和意义在于帮想搭建博客的你入个门,不提供源码的原因一是因为安全问题,而是因为想让读者多思考,不要总是粘贴复制,粘贴复制是学不来东西的

有疑惑的小伙伴可以留言,我会尽快回复的!

发布了100 篇原创文章 · 获赞 142 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/coder_what/article/details/101231041