B站云E办Vue+SpringBoot前后端分离项目——搭建vue.js项目(一)

项目前端学习笔记目录

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),当我开发前端内容的时候,可以把前端的请求通过前端服务器转发给后端(称为反向代理),这样就能实时观察结果,并且不需要知道后端怎么实现,而只需要知道接口提供的功能。

1.技术架构

 2.前端技术架构

本项目采用前后端分离开发模式,使用Spring Boot构建后端。前端模块分为:登录、职位管理、职称管理、部门管理、操作员历、员工管理、工资账套管理、个人中心、在线聊天

前端使用的技术有:

    项目搭建:Vue-cli

    状态管理:Vuex

    路由管理:VueRouter

    UI界面:ElementUI

    通讯框架:Axios

    前端语法:ES6

    打包:Webpack

    在线聊天:WebSocket

    字体:font-awesome

    文件上传下载:js-file-download

    在线聊天开源项目:vue-chat

3.后端技术架构

后端主流开发框架:SpringBoot+Spring MVC +MyBatisPlus。 使用SpringSecurity做安全认证及权限管理,Redis做缓存,RabbitMq做邮件的发送,使用EasyPOI实现对员工数据的导入和导出,使用WebSocket做在线聊天

    安全框架:SpringSecurity

    令牌:JWT

    图形验证码:Kaptcha

    缓存:redis

    文档导入导出:EasyPOI

    消息队列:RabbitMQ 做异步的处理,邮件发送

    邮件组件:Mail

    在线聊天:WebSocket

    文件服务器:FastDFS

    数据库MySQL+Redis

一、搭建vue.js项目

1.vue介绍

        原生JS虽能实现绝大部分功能,但要么就是过于繁琐,要么就是存在缺陷,故绝大多数开发者都会首选框架开发方案。 在框架方面,生命周期、钩子函数、虚拟DOM、Diff算法这些基本知识是必须要掌握的,其中不同级别组件间通信、组件状态管理、路由跳转、组件性能优化等等,都是应该达到灵活运用的程度。在前端项目开发中,我们可以根据实际情况不同程度地使用 Vue。利用 Vue CLI(或写成 vue-cli,即 Vue 脚手架)搭建出来的项目,是最能体现 Vue 的特性的。这点在接下来的内容中我们可以慢慢感受。

模块化开发就是将大的文件拆分为许多独立的小文件,按需在不同的组件中导入,降低了代码耦合度,提高了代码复用性。vue开发的是单页面应用,只有一个html入口,其余称为组件。真正的入口是main.js。vue:渐进式js框架,逐步实现新特性。如模块化开发、路由、状态管理等。

1.1 axios

前端通信框架。因为vue的边界很明确,就是为了处理DOM,所以不具备通信能力,此时就需要额外使用一个通信框架与服务器交互;当然也可以直接使用jQuery提供的AJAX通信功能。

1.2 Vue-router

Vue Router是Vue.js官方的路由管理器。它和Vue.js的核心深度集成,让构建单页面应用变得易如反掌。包含的 功能有:

嵌套的路由/视图表

模块化的、基于组件的路由配置

路由参数、查询、通配符

基于Vue.js过度系统的视图过渡效果

细粒度的导航控制

带有自动激活的CSS class的连接

HTML5历史模式或hash模式,在IE9中自动降级

自定义的滚动条行为

1.3 webpack

        前端开发和其他开发工作的主要区别,首先是前端基于多语言、多层次的编码和组织工作,其次前端产品的交付是基于浏览器的,这些资源是通过增量加载的方式运行到浏览器端,如何在开发环境组织好这些碎片化的代码和中资源,并且保证他们在浏览器快速、优雅的加载和更新,就需要一个模块化系统。

        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这个主组件下。

1.4 ES6模块

        vue通常用es6来写,用export default导出,其下面可以包含数据data,生命周期(mounted等),方法(methods)等,具体语法请看vue.js文档。ES6标准增加了javascript语言层面的模块体系定义。ES6模块的设计思想,是尽量静态化,使编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西。

1.5 UI框架

Element-UI,饿了么出

1.6 vuex

专门为用程序开发的状态管理模式

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 [email protected]

如果你的vue版本是 3.X ,将vuex升到 4.X.X 就能够解决

npm install --save [email protected] npm install --save [email protected]

2.搭建vue.js项目

2.1环境准备

安装Node.js(>=6.x,首选8.x)本项目是v14.18.0版本

2.2安装 Vue CLI

因为需要使用 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

3.构建前端项目

通用方法直接使用命令行构建项目。

然后执行命令 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[email protected]默认支持的命令;

serve默认是[email protected]及以上版本默认支持的命令。

4.vue项目结构分析

├── 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多的项目,看人家怎么写的

主要文件详解

4.1 src——[项目核心文件]

在vue-cli的项目中,其中src文件夹是必须要掌握的,因为基本上要做的事情都在这个目录里。

4.2 index.html——[主页]

index.html如其他html一样,但一般只定义一个空的根节点,在main.js里面定义的实例将挂载在根节点下,内容都通过vue组件来填充,构建的文件将会被自动注入,也就是说我们编写的其它的内容都将在这个 div 中展示。整个项目只有这一个 html 文件,所以这是一个 单页面应用,当我们打开这个应用,表面上可以有很多页面,实际上它们都只不过在一个 div 中。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>vuedemo</title>
  </head>
  <body>
      <!-- 定义的vue实例将挂载在#app节点下 -->
    <div id="app"></div>
  </body>
</html>

4.3 App.vue——[根组件]

这个文件称为“根组件”,因为其它的组件又都包含在这个组件中。.vue 文件是一种自定义文件类型,在结构上类似 html,一个 .vue 文件即是一个 vue 组件。

一个vue页面通常由三部分组成:模板(template)、js(script)、样式(style)

<!-- 模板 -->
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <router-view></router-view>
  </div>
</template>

<!-- script -->
<script>
export default {
  name: 'app'
}
</script>

<!-- 样式 -->
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

【template】

其中模板只能包含一个父节点,也就是说顶层的div只能有一个(例如上面代码,父节点为#app的div,其没有兄弟节点)。这里也有一句 <div id="app">,但跟 index.html 里的那个是没有关系的。这个id=app 只是跟下面的 css 对应。
<router-view></router-view>是子路由视图,后面的路由页面都显示在此处。打一个比喻吧,<router-view>类似于一个插槽,跳转某个路由时,该路由下的页面就插在这个插槽中渲染显示

【script】

<script>标签里的内容即该组件的脚本,也就是 js 代码,export default 是 ES6 的语法,意思是将这个组件整体导出,之后就可以使用 import 导入组件了。大括号里的内容是这个组件的相关属性。
vue通常用es6来写,用export default导出,其下面可以包含数据data,生命周期(mounted等),方法(methods)等,具体语法请看vue.js文档。

【style】

样式通过style标签包裹,默认是影响全局的,如需定义作用域只在该组件下起作用,需在标签上加scoped.如要引入外部css文件,首先需给项目安装css-loader依赖包,打开cmd,进入项目目录,输入npm install css-loader,回车。安装完成后,就可以在style标签下import所需的css文件,例如:

<style>
    import './assets/css/public.css'
</style>

4.4 main.js——[入口文件]

main.js主要是引入vue框架,根组件及路由设置,并且定义vue实例,下面的 components:{App}就是引入的根组件App.vue。后期还可以引入插件,当然首先得安装插件。
前面我们说 App.vue 里的<div id="app"> 和 index.html 里的<div id="app"> 没有关系,那么这两个文件是怎么建立联系的呢?让我们来看入口文件 main.js 的代码

/*引入vue框架*/
import Vue from 'vue'
/*引入根组件*/
import App from './App'
/*引入路由设置*/
import router from './router'

/*关闭生产模式下给出的提示*/ 
Vue.config.productionTip = false

/*定义实例*/ 
new Vue({
  el: '#app',
  router,
  template: '<App/>',
  components: { App }
})

最上面 import 了几个模块,其中 vue 模块在 node_modules 中,App 即 App.vue 里定义的组件,router 即 router 文件夹里定义的路由。Vue.config.productionTip = false ,作用是阻止vue 在启动时生成生产提示。在这个 js 文件中,我们创建了一个 Vue 对象(实例),el 属性提供一个在页面上已存在的 DOM 元素作为 Vue 对象的挂载目标,这里就通过index.html中的<div id="app"><div>中的id=“app”和这里的“#app”进行挂载。router 代表该对象包含 Vue Router,并使用项目中定义的路由。components 表示该对象包含的 Vue 组件,template 是用一个字符串模板作为 Vue 实例的标识使用,类似于定义一个 html 标签。

5.相关包安装

5.1安装 Element-UI

Element 的官方地址为 http://element-cn.eleme.io/#/zh-CN
1.安装 Element
根据官方文档的描述,在项目文件夹下,执行 npm i element-ui -S 即可
在这里插入图片描述
2.引入 Element
引入分为完整引入和按需引入两种模式,按需引入可以缩小项目的体积,这里我们选择完整引入。
根据文档,我们需要修改 main.js 为如下内容

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

5.2安装axios

进入到项目文件夹中,执行
npm install --save axios 以安装这个模块。

5.3安装Vuex

Vuex,它是专门为 Vue 开发的状态管理方案,我们可以把需要在各个组件中传递使用的变量、方法定义在这里。之前我一直没有使用它,所以在不同组件传值的问题上十分头疼,要写很多多余的代码来调用不同组件的值,所以推荐大家从一开始就去熟悉这种管理方式。

运行 npm install vuex --save

之后,在 src 目录下新建一个文件夹 store,并在该目录下新建 index.js 文件,在该文件中引入 vue 和 vuex,代码如下:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

5.4 安装VueRouter

npm install vue-router --save-dev

vue-router是Vue.js官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用。vue的单页面应用是基于路由和组件的,路由用于设定访问路径,并将路径和组件映射起来。

router文件夹下,有一个index.js,即为路由配置文件。可以设置多个路由,‘/index’,’/list’之类的,当然首先得引入该组件,再为该组件设置路由。

5.5 安装font-awesome

npm install font-awesome

二、登录页面设计

1.页面样式设计

为了设计界面,我们需要关注的地方是 <template> 标签内的 html 和 <style> 标签内的 css。登录框我们一般会用 Form 来做,打开 Element 的组件文档(http://element-cn.eleme.io/#/zh-CN/component/),发现它为我们提供了丰富的 Form 组件,我们可以点击“显示代码”,复制我们需要的部分。

不过这里好像并没有特别符合我们应用场景的表单,或者说这些都是比较复杂的,我们只需要其中的一小部分。把页面再往下拉,可以看到关于这个组件的属性、事件、方法等的文档,根据这个文档,我们可以自己去构建需要的表单。

2.登录页代码——views/Login.vue

验证码通过后端返回图片。表单通过rules绑定规则,通过prop为元素添加属性,在rules里写规则。验证方式:this.$refs.loginForm.validate。

/captcha返回信息

/login登陆返回报文

登陆成功后存储用户 token 到 sessionStorage;在请求拦截器里判断toke是否存在,之后每次请求都会校验token,如果存在则请求携带token,放入Authorization参数中;后端校验token。

前端登陆成功后 通过this.$router.replace('/home') 跳转到home首页。replace方法替换后点击浏览器回退按钮不会跳转到登陆页面。登陆失败后端返回失败原因。

在用户未登陆的情况下,如果用户不是以http://localhost:8080/#/访问登陆页,而是访问某个登陆后才能访问的路由,如http://localhost:8080/#/sys/basic。需要分情况讨论:1.用户可能输入首页地址或错误地址,登陆成功后让他跳到首页;2.否则成功跳转到他输入的地址。

this.$router.replace((path === '/' || path === undefined) ? '/home' : path)

<template>
  <div>
    <el-form
      v-loading="loading"
      element-loading-text="正在登录......"
      element-loading-spinner="el-icon-loading"
      element-loading-background="rgba(0, 0, 0, 0.8)"
      ref="loginForm" :model="loginForm" :rules="rules" class="loginContainer">
      <h3 class="loginTitle">系统登录</h3>
      <el-form-item prop="username">
        <el-input type="text" v-model="loginForm.username" placeholder="请输入用户名"></el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input type="password" v-model="loginForm.password" placeholder="请输入密码"></el-input>
      </el-form-item>
      <el-form-item prop="code">
        <el-input type="text" v-model="loginForm.code" placeholder="点击图片更换验证码"
                  style="width: 250px;margin-right: 5px;"></el-input>
        <img :src="captchaUrl" @click="updateCaptcha">
      </el-form-item>
      <el-button type="primary" style="width: 100%" @click="submitLogin">登录</el-button>
    </el-form>
  </div>
</template>
<script>
  export default {
    name: 'Login',
    components: {},
    props: [],
    data() {
      return {
        // 验证码
        captchaUrl:'api/captcha?time=' + new Date(),//获取响应码后端接口
        loginForm: {
          username: 'admin',
          password: '123',
          code: '',
        },
        loading: false, // 加载中
        checked: true,
        //校验规则,与表单绑定
        rules: {
          username: [{required: true, message: '请输入用户名', trigger: 'blur'}],
          password: [{required: true, message: '请输入密码', trigger: 'blur'}],
          code: [{required: true, message: '请输入验证码', trigger: 'blur'}]
        }
      }
    },
    mounted(){

    },
    methods: {
      // 点击刷新验证码
      updateCaptcha() {
      this.captchaUrl="api/captcha?time="+new Date();

      },
      submitLogin() {
        // 登录
        this.$refs.loginForm.validate((valid) => {
          if (valid) {
            this.loading = true;//准备调登录接口时,出现正在加载
            //第一个参数请求后端的地址,第二个参数,传给后端的数据
            this.postRequest('/login', this.loginForm).then(resp => {
              this.loading = false;//登录成功后关闭
              if (resp) {
                // 存储用户 token 到 sessionStorage
                const tokenStr = resp.obj.tokenHead + resp.obj.token;
                window.sessionStorage.setItem('tokenStr', tokenStr);
                // 跳转到首页
                // this.$router.push('/home') // 路由跳转,可以回退到上一页
                this.$router.replace('/home') // 路径替换,无法回退到上一页

                // 页面跳转
                // 拿到用户要跳转的路径
                let path = this.$route.query.redirect;
                // 用户可能输入首页地址或错误地址,让他跳到首页,否则跳转到他输入的地址
                this.$router.replace((path === '/' || path === undefined) ? '/home' : path)
              }

            })
          } else {
            this.$message.error('请输入所有字段!');
            return false;
          }
        })
      }
    }
  }
</script>
<style>
  .loginContainer {
    border-radius: 15px;
    background-clip: padding-box;
    /*属性规定背景的绘制区域 背景被裁剪到内边距框。 margin: 180 px auto;*/
    margin: 180px auto;
    width: 350px;
    padding: 15px 35px;
    background: #fff;
    border: 1px solid #eaeaea;
    box-shadow: 0 0 25px #cac6c6;
    /*  X轴偏移量 Y轴偏移量 [阴影模糊半径] [阴影扩展] [阴影颜色] [投影方式]; */
  }

  .loginTitle {
    margin: 0 auto 40px auto;
    text-align: center;
  }

  .loginRemember {
    text-align: left;
    margin: 0 0 15px 0;
  }

  /*验证码*/
  .el-form-item__content {
    display: flex;
    align-items: center;
  }
</style>

 SessionStorage.setItem()中,为了使axios做下一次请求时获取token认证,登录之后拿到token放到sessionStrorage里         

    // 存储用户 token 到 sessionStorage

                const tokenStr = resp.obj.tokenHead + resp.obj.token;

                window.sessionStorage.setItem('tokenStr', tokenStr);

3.配置页面路由——router/index.js

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 // 不会被循环遍历出来
  },
  ]
})

4.解决前后端跨域

前端端口默认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自动进行的

4.1前端反向代理

修改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: '<App/>'
})

修改vue.config.js 

修改proxyTable 请求地址经过node.js后代理到后端地址8081


    proxyTable: {
      '/': {
        changeOrigin: true, //跨域
        target: 'http://localhost:8081',
        pathRewrite: {
          // '^/api': ''
        }
      },
    
    },

5.登陆拦截器

拦截器顾名思义就是对请求的拦截,分别为请求拦截器和响应拦截器, 执行顺序: 请求拦截器 -> api请求 -> 响应拦截器。 拦截器的作用:a. 统计api从发起请求到返回数据需要的时间;b. 配置公共的请求头,加载弹窗等;c. 对响应状态码做拦截,比入后端返回400或500的状态码, 返回对应错误信息。

5.1axios请求拦截器request

在vue项目中,我们通常使用axios与后台进行数据交互,axios是一款基于promise封装的库,可以运行在浏览器端和node环境中。请求拦截器request作用:在请求发送前统一执行某些操作,常用在请求头中处理token等 

添加请求拦截器的方法

axios.interceptors.request.use(function (config) {
// 在发送请求之前做些什么
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
})

5.2axios响应拦截器response

返回对象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);
});
}

5.3封装请求

在项目中,我们并不会直接使用 axios,而是会对它进行一层封装。 通过export导出封装的请求,如定义一个postRequest方法接收url和params,然后axios对象。在axios里进行实际接口调用操作。

export const postRequest = (url, params) => {
    return axios({
        method: 'post',
        url: `${base}${url}`,
        data: params
    })
}

 5.4代码实现src/utils/api.js

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
    })
}

5.5main.js全局引入封装请求

通过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

6. 前端路由导航守卫

登录页面的开发似乎已经较为完善了,但其实还没有完,因为这个登录页面其实没有用,别人直接输入首页的网址,就可以绕过登录页面。为了让它发挥作用,我们还需要开发一个拦截器。使用钩子函数判断是否拦截函数及在某些时机会被调用的函数。这里我们使用 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)
      }
  }
})

7.运行项目

下一篇

B站云E办Vue+SpringBoot前后端分离项目——前端动态获取菜单目录

猜你喜欢

转载自blog.csdn.net/qq_36384657/article/details/124525482