本文系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>
标签,前者要更强大,如
<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' }
多级评论和多级分类的设置
关于不同组件间的通信
因为我博客的修改文章和发表文章都在同一个页面,所以我需要进行不同组件间的通信,以来识别是需要载入选中文章进行更新还是要新建一篇文章
- 参考两篇博客父组件对子组件的通信 子组件对父组件的通信
后记
事实上,这系列博客的目的和意义在于帮想搭建博客的你入个门,不提供源码的原因一是因为安全问题,而是因为想让读者多思考,不要总是粘贴复制,粘贴复制是学不来东西的。
有疑惑的小伙伴可以留言,我会尽快回复的!