B站云E办Vue+SpringBoot前后端分离项目——搭建vue.js项目
B站云E办Vue+SpringBoot前后端分离项目——前端动态获取菜单目录
B站云E办Vue+SpringBoot前后端分离项目——MVC三层架构搭建后台项目
项目背景:受疫情的影响,许多企业由线上办公转为线下办公。随着线上办公的人数的增多,线上办公的优点逐步凸显:通过实现工作流程的自动化、节省企业办公费用、实现绿色办公,同时提升办公效率。
项目介绍:本项目实现了一个在线办公系统,用来管理日常办公事物的:日常流程审批,新闻,通知,公告,文件信息,财务,人事,费用,资产,行政,项目,移动办公等。通过软件的方式让办公系统根据方便管理,提高整体的管理运营水平。
实现方式:本项目基于Vue+Spring Boot构架一个前后端分离项目。前端使用社区非常活跃的开源框架vue进行构建。简单地说,前后端分离 的核心思想是前端页面通过 ajax 调用后端的 restuful api 进行数据交互,而 单页面应用(single page web application,SPA),就是只有一张页面,并在用户与应用程序交互时动态更新该页面的 Web 应用程序。
前后端之间通过 RESTful API 传递 JSON 数据进行交流。不同于 JSP 之类,后端是不涉及页面本身的内容的。在开发的时候,前端用前端的服务器(Nginx),后端用后端的服务器(Tomcat),当我开发前端内容的时候,可以把前端的请求通过前端服务器转发给后端(称为反向代理),这样就能实时观察结果,并且不需要知道后端怎么实现,而只需要知道接口提供的功能。
本项目采用前后端分离开发模式,使用Spring Boot构建后端。前端模块分为:登录、职位管理、职称管理、部门管理、操作员历、员工管理、工资账套管理、个人中心、在线聊天
前端使用的技术有:
项目搭建:Vue-cli
状态管理:Vuex
路由管理:VueRouter
UI界面:ElementUI
通讯框架:Axios
前端语法:ES6
打包:Webpack
在线聊天:WebSocket
字体:font-awesome
文件上传下载:js-file-download
在线聊天开源项目:vue-chat
后端主流开发框架:SpringBoot+Spring MVC +MyBatisPlus。 使用SpringSecurity做安全认证及权限管理,Redis做缓存,RabbitMq做邮件的发送,使用EasyPOI实现对员工数据的导入和导出,使用WebSocket做在线聊天
安全框架:SpringSecurity
令牌:JWT
图形验证码:Kaptcha
缓存:redis
文档导入导出:EasyPOI
消息队列:RabbitMQ 做异步的处理,邮件发送
邮件组件:Mail
在线聊天:WebSocket
文件服务器:FastDFS
数据库MySQL+Redis
原生JS虽能实现绝大部分功能,但要么就是过于繁琐,要么就是存在缺陷,故绝大多数开发者都会首选框架开发方案。 在框架方面,生命周期、钩子函数、虚拟DOM、Diff算法这些基本知识是必须要掌握的,其中不同级别组件间通信、组件状态管理、路由跳转、组件性能优化等等,都是应该达到灵活运用的程度。在前端项目开发中,我们可以根据实际情况不同程度地使用 Vue。利用 Vue CLI(或写成 vue-cli,即 Vue 脚手架)搭建出来的项目,是最能体现 Vue 的特性的。这点在接下来的内容中我们可以慢慢感受。
模块化开发就是将大的文件拆分为许多独立的小文件,按需在不同的组件中导入,降低了代码耦合度,提高了代码复用性。vue开发的是单页面应用,只有一个html入口,其余称为组件。真正的入口是main.js。vue:渐进式js框架,逐步实现新特性。如模块化开发、路由、状态管理等。
前端通信框架。因为vue的边界很明确,就是为了处理DOM,所以不具备通信能力,此时就需要额外使用一个通信框架与服务器交互;当然也可以直接使用jQuery提供的AJAX通信功能。
Vue Router是Vue.js官方的路由管理器。它和Vue.js的核心深度集成,让构建单页面应用变得易如反掌。包含的 功能有:
嵌套的路由/视图表
模块化的、基于组件的路由配置
路由参数、查询、通配符
基于Vue.js过度系统的视图过渡效果
细粒度的导航控制
带有自动激活的CSS class的连接
HTML5历史模式或hash模式,在IE9中自动降级
自定义的滚动条行为
前端开发和其他开发工作的主要区别,首先是前端基于多语言、多层次的编码和组织工作,其次前端产品的交付是基于浏览器的,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和中资源,并且保证他们在浏览器快速、优雅的加载和更新,就需要一个模块化系统。
webpack是一个现代javaScript应用程序静态模块打包器(module bundler)。当webpack处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个bundle。webpack的思想:万物皆模块,即webpack不仅仅是javaScript模块化,还有CSS、图片、字体等资源也需要模块化。Webpack:模块打包器,主要作用是打包、压缩、合并及按序加载。如将ES6打包成ES5
在webpack的配置文件里,设置了main.js是入口文件,我们的项目默认访问index.html,这个文件里面和App.vue组件里面的容器完美的重合了,也就是把组件挂载到了index页面,然后我们只需要去建设其他组件就好了,在App组件中我们也可以引入,注册,应用其他组件,可以通过路由将其他组件渲染在App组件,这样我们就只需要去关注每个组件的功能完善。就是说vue的默认页面是index.html,index中的挂载了App.vue这个大组件,然后所有的其他子组件(hello.vue等)都归属在App.vue这个主组件下。
vue通常用es6来写,用export default导出,其下面可以包含数据data,生命周期(mounted等),方法(methods)等,具体语法请看vue.js文档。ES6标准增加了javascript语言层面的模块体系定义。ES6模块的设计思想,是尽量静态化,使编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西。
Element-UI,饿了么出
专门为用程序开发的状态管理模式
npm install vuex --save
在src目录新建store目录,新建index.js文件,在main.js中引入
安装vuex 启动 报错 “export ‘watch‘ was not found in ‘vue‘
如果你的vue版本是 2.X ,将vuex升到 3.X.X 就能够解决
npm install --save vuex@3.6.2
如果你的vue版本是 3.X ,将vuex升到 4.X.X 就能够解决
npm install --save vue@3.0.2 npm install --save vuex@4.0.0
安装Node.js(>=6.x,首选8.x)本项目是v14.18.0版本
因为需要使用 npm 安装 Vue CLI,而 npm 是集成在 Node.js 中的,所以第一步我们需要安装 Node.js,访问官网 Node.js,首页即可下载。
下载完成后运行安装包,一路下一步就行。
然后在 cmd 中输入 node -v,检查node是否安装成功。
输入 npm -v 查看npm版本号
输入 npm -g install npm ,将 npm 更新至最新版本。
之后,使用 npm install -g vue-cli 安装脚手架。(本项目使用版本2.9.6)
注意此种方式安装的是 2.x 版本的 Vue CLI,最新版本需要通过 npm install -g @vue/cli 安装。新版本可以使用图形化界面初始化项目,并加入了项目健康监控的内容,但使用新版本创建的项目依赖与这个教程不太相符,折腾起来比较麻烦。
安装Node.js的淘宝镜像加速器cnpm
大部分情况使用npm,遇到安装不了的使用cnpm
npm install cnpm -g
或npm install --registry=https://registry.npm.taobao.org
通用方法直接使用命令行构建项目。
然后执行命令 vue init webpack yeb,这里 webpack 是以 webpack 为模板指生成项目,还可以替换为 pwa、simple 等参数,这里不再赘述。
在程序执行的过程中会有一些提示,可以按照默认的设定一路回车下去,也可以按需修改,比如下图问我项目名称是不是 wj-vue,直接回车确认就行。
这里还会问是否安装 vue-router,一定要选是,也就是回车或按 Y,vue-router 是我们构建单页面应用的关键。
还有是否使用 es-lint,选N。
接下来等待项目构建完成就 OK 了。
workspace 目录下生成了项目文件夹 需要在该文件夹执行 npm install ,npm run build 再执行 npm run dev
访问 http://localhost:8080,查看网页 demo,大工告成!
注:在vue项目中,有的时候需要执行npm run serve启动项目,有的时候需要用npm run dev,具体有什么不一样呢?
区别
dev默认是vue-cli@2.x默认支持的命令;
serve默认是vue-cli@3.x及以上版本默认支持的命令。
├── build --------------------------------- 项目构建(webpack)相关配置文件,配置参数什么的,一般不用动
│ ├── build.js --------------------------webpack打包配置文件
│ ├── check-versions.js ------------------------------ 检查npm,nodejs版本
│ ├── dev-client.js ---------------------------------- 设置环境
│ ├── dev-server.js ---------------------------------- 创建express服务器,配置中间件,启动可热重载的服务器,用于开发项目
│ ├── utils.js --------------------------------------- 配置资源路径,配置css加载器
│ ├── vue-loader.conf.js ----------------------------- 配置css加载器等
│ ├── webpack.base.conf.js --------------------------- webpack基本配置
│ ├── webpack.dev.conf.js ---------------------------- 用于开发的webpack设置
│ ├── webpack.prod.conf.js --------------------------- 用于打包的webpack设置
├── config ---------------------------------- 配置目录,包括端口号等。我们初学可以使用默认的。
│ ├── dev.env.js -------------------------- 开发环境变量
│ ├── index.js ---------------------------- 项目配置文件
│ ├── prod.env.js ------------------------- 生产环境变量
│ ├── test.env.js ------------------------- 测试环境变量
├── node_modules ---------------------------- npm 加载的项目依赖模块
├── src ------------------------------------- 我们要开发的目录,基本上要做的事情都在这个目录里。
│ ├── assets ------------------------------ 静态文件,放置一些图片,如logo等
│ ├── components -------------------------- 组件目录,存放组件文件,可以不用。
│ ├── main.js ----------------------------- 主js
│ ├── App.vue ----------------------------- 项目入口组件,我们也可以直接将组件写这里,而不使用 components 目录。
│ ├── router ------------------------------ 路由
├── static ---------------------------- 静态资源目录,如图片、字体等。
├── .babelrc--------------------------------- babel配置文件
├── .editorconfig---------------------------- 编辑器配置
├── .gitignore------------------------------- 配置git可忽略的文件
├── index.html ------------------------------ 首页入口文件,你可以添加一些 meta 信息或统计代码啥的。
├── package.json ---------------------------- node配置文件,记载着一些命令和依赖还有简要的项目描述信息
├── .README.md------------------------------- 项目的说明文档,markdown 格式。想怎么写怎么写,不会写就参照github上star多的项目,看人家怎么写的
主要文件详解
在vue-cli的项目中,其中src文件夹是必须要掌握的,因为基本上要做的事情都在这个目录里。
index.html如其他html一样,但一般只定义一个空的根节点,在main.js里面定义的实例将挂载在根节点下,内容都通过vue组件来填充,构建的文件将会被自动注入,也就是说我们编写的其它的内容都将在这个 div 中展示。整个项目只有这一个 html 文件,所以这是一个 单页面应用,当我们打开这个应用,表面上可以有很多页面,实际上它们都只不过在一个 div 中。
vuedemo
这个文件称为“根组件”,因为其它的组件又都包含在这个组件中。.vue 文件是一种自定义文件类型,在结构上类似 html,一个 .vue 文件即是一个 vue 组件。
一个vue页面通常由三部分组成:模板(template)、js(script)、样式(style)
【template】
其中模板只能包含一个父节点,也就是说顶层的div只能有一个(例如上面代码,父节点为#app的div,其没有兄弟节点)。这里也有一句
,但跟 index.html 里的那个是没有关系的。这个id=app 只是跟下面的 css 对应。
是子路由视图,后面的路由页面都显示在此处。打一个比喻吧, 类似于一个插槽,跳转某个路由时,该路由下的页面就插在这个插槽中渲染显示
【script】
SessionStorage.setItem()中,为了使axios做下一次请求时获取token认证,登录之后拿到token放到sessionStrorage里
// 存储用户 token 到 sessionStorage
const tokenStr = resp.obj.tokenHead + resp.obj.token;
window.sessionStorage.setItem('tokenStr', tokenStr);
import Vue from 'vue'
import Router from 'vue-router'
import Login from "@/views/Login";
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Login',
component: Login,
hidden: true // 不会被循环遍历出来
},
]
})
前端端口默认8080,假设后端端口是8081,那8080如何访问到8081的数据,我们通过Node.js实现端口自动转发。浏览器的同源策略:两个页面必须具有相同的协议(protocol)主机(host)端口号(port)。
请求一个接口时,出现Access-Control-Allow-Origin等,说明出现请求跨域了。vue中解决跨域的方法:配置vue.config.js文件,如果没有就自行新建一个。
原理:
1.将域名发送给本地的服务器(localhost:8080)
2.再由本地的服务器去请求真正的服务器
3.因为请求是从服务端发出的,所以不存在跨域的问题了。
在vue中是由node.js自动进行的
修改main.js
修改 src\main.js 代码如下:
import Vue from 'vue'
import App from './App'
import router from './router'
// 设置反向代理,前端请求默认发送到 http://localhost:8081/api
var axios = require('axios')
axios.defaults.baseURL = 'http://localhost:8081/api'
// 全局注册,之后可在其他组件中通过 this.$axios 发送数据
Vue.prototype.$axios = axios
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: ''
})
修改vue.config.js
修改proxyTable 请求地址经过node.js后代理到后端地址8081
proxyTable: {
'/': {
changeOrigin: true, //跨域
target: 'http://localhost:8081',
pathRewrite: {
// '^/api': ''
}
},
},
拦截器顾名思义就是对请求的拦截,分别为请求拦截器和响应拦截器, 执行顺序: 请求拦截器 -> api请求 -> 响应拦截器。 拦截器的作用:a. 统计api从发起请求到返回数据需要的时间;b. 配置公共的请求头,加载弹窗等;c. 对响应状态码做拦截,比入后端返回400或500的状态码, 返回对应错误信息。
在vue项目中,我们通常使用axios与后台进行数据交互,axios是一款基于promise封装的库,可以运行在浏览器端和node环境中。请求拦截器request作用:在请求发送前统一执行某些操作,常用在请求头中处理token等
添加请求拦截器的方法
axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
})
返回对象response中有response.status:Http响应码;response.data:后端返回的Json对象,包括response.data.code业务逻辑响应码,response.data.message:后端返回的响应提示信息;
添加响应拦截器方法
axios.interceptors.response.use(function (response) {
// 对响应数据做点什么
return response;
}, function (error) {
// 对响应错误做点什么
return Promise.reject(error);
});
}
在项目中,我们并不会直接使用 axios,而是会对它进行一层封装。 通过export导出封装的请求,如定义一个postRequest方法接收url和params,然后axios对象。在axios里进行实际接口调用操作。
export const postRequest = (url, params) => {
return axios({
method: 'post',
url: `${base}${url}`,
data: params
})
}
import axios from "axios";
import {Message} from "element-ui";
import router from "@/router";
// 请求拦截器
axios.interceptors.request.use(config => {
// 如果存在 token,请求携带这个 token( 登录的时候 把 token 存入了 sessionStorage )
if (window.sessionStorage.getItem("tokenStr")) {
// token 的key : Authorization ; value: tokenStr
config.headers['Authorization'] = window.sessionStorage.getItem('tokenStr')
}
return config;
},error => {
console.log(error)
})
// 响应拦截器 - 统一处理消息提示
axios.interceptors.response.use(success => {
// 业务逻辑错误
if (success.status && success.status === 200) { // 调到接口
// 后端:500 业务逻辑错误,401 未登录,403 无权访问;
if (success.data.code === 500 || success.data.code === 401 || success.data.code === 403) {
Message.error({message: success.data.message})
return
}
if (success.data.message) { // 输出后端 添加成功 之类的信息
Message.success({message: success.data.message})
}
}
return success.data
}, error => { // 没访问到后端接口
if (error.response.code === 504 || error.response.code === 404) {
Message.error({message: '服务器不存在'})
} else if (error.response.code === 403) {
Message.error({message: '权限不足,请联系管理员!'})
} else if (error.response.code === 401) {
Message.error({message: '您还未登录,请登录!'})
router.replace('/') // 路由替换
} else {
if (error.response.data.message) {
Message.error({message: error.response.data.message})
} else {
Message.error({message: '未知错误!'})
}
}
return
})
// 预备前置路径
let base = '';
// 传送 json 格式的 post 请求
export const postRequest = (url, params) => {
return axios({
method: 'post',
url: `${base}${url}`,
data: params
})
}
// 传送 json 格式的 get 请求
export const getRequest = (url, params) => {
return axios({
method: 'get',
url: `${base}${url}`,
data: params
})
}
// 传送 json 格式的 put 请求
export const putRequest = (url, params) => {
return axios({
method: 'put',
url: `${base}${url}`,
data: params
})
}
// 传送 json 格式的 delete 请求
export const deleteRequest = (url, params) => {
return axios({
method: 'delete',
url: `${base}${url}`,
data: params
})
}
通过main.js全局引入然后通过插件的方式使用方法。在具体调用时使用this.putRequest(url,params)形式使用
import {postRequest} from "@/utils/api";
import {putRequest} from "@/utils/api";
import {getRequest} from "@/utils/api";
import {deleteRequest} from "@/utils/api";
Vue.prototype.postRequest = postRequest
Vue.prototype.putRequest = putRequest
Vue.prototype.getRequest = getRequest
Vue.prototype.deleteRequest = deleteRequest
登录页面的开发似乎已经较为完善了,但其实还没有完,因为这个登录页面其实没有用,别人直接输入首页的网址,就可以绕过登录页面。为了让它发挥作用,我们还需要开发一个拦截器。使用钩子函数判断是否拦截函数及在某些时机会被调用的函数。这里我们使用 router.beforeEach(),意思是在访问每一个路由前调用。to 要去的路由; from 来自哪里的路由 ; next() 放行。
通过sessionStorage.getItem('user')获取用户的token,如果token不存在则需要登陆。
在判断是否为if (to.path == '/')登陆页,是的话放行,否则按用户指定的路由登陆;
补充main.js
// 使用 router.beforeEach 注册一个全局前置守卫
router.beforeEach((to, from, next) => {
// to 要去的路由; from 来自哪里的路由 ; next() 放行
// 用户登录成功时,把 token 存入 sessionStorage,如果携带 token,初始化菜单,放行
if (window.sessionStorage.getItem('tokenStr')) {
// 如果用户不存在
//待首页功能部分完善后补充
} else {
if (to.path === '/') {
next()
} else {
next('/?redirect=' + to.path)
}
}
})
下一篇
B站云E办Vue+SpringBoot前后端分离项目——前端动态获取菜单目录