封装 axios 模块

封装背景

使用axios发起一个请求是比较简单的事情,但是axios没有进行封装复用,项目越来越大,会引起越来越多的代码冗余,让代码变得越来越难维护。所以我们在这里先对 axios 进行二次封装,使项目中各个组件能够复用请求,让代码变得更容易维护。

封装要点

  • 统一 url 配置
  • 统一 api 请求
  • request (请求) 拦截器,例如:带上token等,设置请求头
  • response (响应) 拦截器,例如:统一错误处理,页面重定向等
  • 根据需要,结合 Vuex 做全局的 loading 动画,或者错误处理
  • 将 axios 封装成 Vue 插件使用

文件结构

在 src 目录下,新建一个 http 文件夹,用来存放 http 交互 api 代码。

config.js:axios 默认配置,包含基础路径等信息。
axios.js:二次封装 axios 模块,包含拦截器等信息。
interface.js :请求接口汇总模块,聚合模块 API。
index.js:将 axios 封装成插件,按插件方式引入。

config.js

  1. export default {
  2. method: 'get',
  3. // 基础url前缀
  4. baseURL: 'http://localhost:8080/',
  5. // 请求头信息
  6. headers: {
  7. 'Content-Type': 'application/json;charset=UTF-8'
  8. },
  9. // 参数
  10. data: {},
  11. // 设置超时时间
  12. timeout: 10000,
  13. // 携带凭证
  14. withCredentials: true,
  15. // 返回数据类型
  16. responseType: 'json'
  17. }

axios.js

  1. import axios from 'axios';
  2. import config from './config';
  3. import qs from 'qs';
  4. import Cookies from "js-cookie";
  5. import router from '@/router'
  6.  
  7. // 使用vuex做全局loading时使用
  8. // import store from '@/store'
  9.  
  10. export default function $axios(options) {
  11. return new Promise((resolve, reject) => {
  12. const instance = axios.create({
  13. baseURL: config.baseURL,
  14. headers: {},
  15. transformResponse: [function (data) {
  16. }]
  17. })
  18.  
  19. // request 拦截器
  20. instance.interceptors.request.use(
  21. config => {
  22. let token = Cookies.get('token')
  23. // 1. 请求开始的时候可以结合 vuex 开启全屏 loading 动画
  24. // console.log(store.state.loading)
  25. // console.log('准备发送请求...')
  26. // 2. 带上token
  27. if (token) {
  28. config.headers.accessToken = token
  29. } else {
  30. // 重定向到登录页面
  31. router.push('/login')
  32. }
  33. // 3. 根据请求方法,序列化传来的参数,根据后端需求是否序列化
  34. if (config.method === 'post') {
  35. if (config.data.__proto__ === FormData.prototype
  36. || config.url.endsWith('path')
  37. || config.url.endsWith('mark')
  38. || config.url.endsWith('patchs')
  39. ) {
  40.  
  41. } else {
  42. config.data = qs.stringify(config.data)
  43. }
  44. }
  45. return config
  46. },
  47.  
  48. error => {
  49. // 请求错误时
  50. console.log('request:', error)
  51. // 1. 判断请求超时
  52. if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
  53. console.log('timeout请求超时')
  54. // return service.request(originalRequest);// 再重复请求一次
  55. }
  56. // 2. 需要重定向到错误页面
  57. const errorInfo = error.response
  58. console.log(errorInfo)
  59. if (errorInfo) {
  60. error = errorInfo.data // 页面那边catch的时候就能拿到详细的错误信息,看最下边的Promise.reject
  61. const errorStatus = errorInfo.status; // 404 403 500 ...
  62. router.push({
  63. path: `/error/${errorStatus}`
  64. })
  65. }
  66. return Promise.reject(error) // 在调用的那边可以拿到(catch)你想返回的错误信息
  67. }
  68. )
  69.  
  70. // response 拦截器
  71. instance.interceptors.response.use(
  72. response => {
  73. let data;
  74. // IE9时response.data是undefined,因此需要使用response.request.responseText(Stringify后的字符串)
  75. if (response.data == undefined) {
  76. data = JSON.parse(response.request.responseText)
  77. } else {
  78. data = response.data
  79. }
  80.  
  81. // 根据返回的code值来做不同的处理
  82. switch (data.rc) {
  83. case 1:
  84. console.log(data.desc)
  85. break;
  86. case 0:
  87. store.commit('changeState')
  88. // console.log('登录成功')
  89. default:
  90. }
  91. // 若不是正确的返回code,且已经登录,就抛出错误
  92. // const err = new Error(data.desc)
  93. // err.data = data
  94. // err.response = response
  95. // throw err
  96.  
  97. return data
  98. },
  99. err => {
  100. if (err && err.response) {
  101. switch (err.response.status) {
  102. case 400:
  103. err.message = '请求错误'
  104. break
  105. case 401:
  106. err.message = '未授权,请登录'
  107. break
  108. case 403:
  109. err.message = '拒绝访问'
  110. break
  111. case 404:
  112. err.message = `请求地址出错: ${err.response.config.url}`
  113. break
  114. case 408:
  115. err.message = '请求超时'
  116. break
  117. case 500:
  118. err.message = '服务器内部错误'
  119. break
  120. case 501:
  121. err.message = '服务未实现'
  122. break
  123. case 502:
  124. err.message = '网关错误'
  125. break
  126. case 503:
  127. err.message = '服务不可用'
  128. break
  129. case 504:
  130. err.message = '网关超时'
  131. break
  132. case 505:
  133. err.message = 'HTTP版本不受支持'
  134. break
  135. default:
  136. }
  137. }
  138. console.error(err)
  139. return Promise.reject(err) // 返回接口返回的错误信息
  140. }
  141. )
  142.  
  143. // 请求处理
  144. instance(options).then(res => {
  145. resolve(res)
  146. return false
  147. }).catch(error => {
  148. reject(error)
  149. })
  150. })
  151. }

interface.js

  1. import axios from './axios'
  2.  
  3. /*
  4. * 将所有接口统一起来便于维护
  5. * 如果项目很大可以将 url 独立成文件,接口分成不同的模块
  6. */
  7.  
  8. // 单独导出
  9. export const login = () => {
  10. return axios({
  11. url: '/login',
  12. method: 'get'
  13. })
  14. }
  15.  
  16. export const getUser = () => {
  17. return axios({
  18. url: '/user',
  19. method: 'get'
  20. })
  21. }
  22.  
  23. export const getMenu = data => {
  24. return axios({
  25. url: '/menu',
  26. method: 'post',
  27. data
  28. })
  29. }
  30.  
  31. // 默认全部导出
  32. export default {
  33. login,
  34. getUser,
  35. getMenu
  36. }

index.js

  1. // 导入所有接口
  2. import apis from './interface'
  3.  
  4. const install = Vue => {
  5. if (install.installed)
  6. return;
  7.  
  8. install.installed = true;
  9.  
  10. Object.defineProperties(Vue.prototype, {
  11. // 注意,此处挂载在 Vue 原型的 $api 对象上
  12. $api: {
  13. get() {
  14. return apis
  15. }
  16. }
  17. })
  18. }
  19.  
  20. export default install

安装 js-cookie

上面 axios.js 中,会用到 Cookie 获取 token,所以需要把相关依赖安装一下。

执行以下命令,安装依赖包。

  1. yarn add js-cookie

代码实例

1.引入插件

在 main.js 中以 vue 插件的形式引入 axios,这样在其他地方就可通过 this.$api 调用相关的接口了。

2.编写接口

在 interface.js 中添加 login 接口。

3.调用接口

在登录界面 Login.vue 中,添加一个登录按钮,点击处理函数通过 axios 调用 login 接口返回数据。

成功返回之后,将 token 放入 Cookie 并跳转到主页。

  1. <template>
  2. <div class="page">
  3. <h2>Login Page</h2>
  4. <el-button type="primary" @click="login()">登录</el-button>
  5. </div>
  6. </template>
  7.  
  8. <script>
  9. import mock from '@/mock/mock.js';
  10. import Cookies from "js-cookie";
  11. import router from '@/router'
  12. export default {
  13. name: 'Login',
  14. methods: {
  15. login() {
  16. this.$api.login().then(function(res) {
           alert(res.data.token)
  17. Cookies.set('token', res.data.token) // 放置token到Cookie
  18. router.push('/') // 登录成功,跳转到主页
  19. }).catch(function(res) {
  20. alert(res);
  21. });
  22.  
  23. }
  24. }
  25. }
  26. </script>

4.mock 接口

在 mock.js 中添加 login 接口进行拦截,返回一个 token。

启动测试

浏览器访问:http://localhost:8080/#/login,显示登录界面。

点击登录按钮,首先弹出框,显示返回的 token 信息。

点击确定关掉弹出框后,跳转到主页。点击用户、菜单按钮,接口调用正常。

封装 mock 模块

为了统一可以统一管理和集中控制数据模拟接口,我们对 mock 模块进行了封装,可以方便的定制模拟接口的统一开关和个体开关。

文件结构

在 mock 目录下新建一个 index.js ,创建 modules 目录并在里面创建三个模块 *.js 文件。

index.js:模拟接口模块聚合文件

login.js:登录相关的接口模拟

user.js:用户相关的接口模拟

menu.js:菜单相关的接口模拟

index.js

  1. import Mock from 'mockjs'
  2. import * as login from './modules/login'
  3. import * as user from './modules/user'
  4. import * as menu from './modules/menu'
  5.  
  6. // 1. 开启/关闭[业务模块]拦截, 通过调用fnCreate方法[isOpen参数]设置.
  7. // 2. 开启/关闭[业务模块中某个请求]拦截, 通过函数返回对象中的[isOpen属性]设置.
  8. fnCreate(login, true)
  9. fnCreate(user, true)
  10. fnCreate(menu, true)
  11.  
  12. /**
  13. * 创建mock模拟数据
  14. * @param {*} mod 模块
  15. * @param {*} isOpen 是否开启?
  16. */
  17. function fnCreate (mod, isOpen = true) {
  18. if (isOpen) {
  19. for (var key in mod) {
  20. ((res) => {
  21. if (res.isOpen !== false) {
  22. Mock.mock(new RegExp(res.url), res.type, (opts) => {
  23. opts['data'] = opts.body ? JSON.parse(opts.body) : null
  24. delete opts.body
  25. console.log('\n')
  26. console.log('%cmock拦截, 请求: ', 'color:blue', opts)
  27. console.log('%cmock拦截, 响应: ', 'color:blue', res.data)
  28. return res.data
  29. })
  30. }
  31. })(mod[key]() || {})
  32. }
  33. }
  34. }

login.js

  1. // 登录接口
  2. export function login () {
  3. return {
  4. // isOpen: false,
  5. url: 'http://localhost:8080/login',
  6. type: 'get',
  7. data: {
  8. 'msg': 'success',
  9. 'code': 0,
  10. 'data': {
  11. 'token': '4344323121398'
  12. // 其他数据
  13. }
  14. }
  15. }
  16. }

user.js

  1. // 获取用户信息
  2. export function getUser () {
  3. return {
  4. // isOpen: false,
  5. url: 'http://localhost:8080/user',
  6. type: 'get',
  7. data: {
  8. 'msg': 'success',
  9. 'code': 0,
  10. 'data': {
  11. 'id': '@increment',
  12. 'name': '@name', // 随机生成姓名
  13. 'email': '@email', // 随机生成姓名
  14. 'age|10-20': 12
  15. // 其他数据
  16. }
  17. }
  18. }
  19. }

menu.js

  1. // 获取菜单信息
  2. export function getMenu () {
  3. return {
  4. // isOpen: false,
  5. url: 'http://localhost:8080/menu',
  6. type: 'get',
  7. data: {
  8. 'msg': 'success',
  9. 'code': 0,
  10. 'data': {
  11. 'id': '@increment',
  12. 'name': 'menu', // 随机生成姓名
  13. 'order|10-20': 12
  14. // 其他数据
  15. }
  16. }
  17. }
  18. }

修改引入

Login.vue

Home.vue

启动测试

浏览器访问:http://localhost:8080/#/,按照先前流程走一遍,没有问题。

Vue + Element UI 实现权限管理系统(工具模块封装)的更多相关文章

  1. Vue + Element UI 实现权限管理系统

    Vue + Element UI 实现权限管理系统 前端篇(一):搭建开发环境 https://www.cnblogs.com/xifengxiaoma/p/9533018.html

  2. Vue + Element UI 实现权限管理系统 前端篇(三):工具模块封装

    封装 axios 模块 封装背景 使用axios发起一个请求是比较简单的事情,但是axios没有进行封装复用,项目越来越大,会引起越来越多的代码冗余,让代码变得越来越难维护.所以我们在这里先对 axi ...

  3. Vue + Element UI 实现权限管理系统 前端篇(十三):页面权限控制

    权限控制方案 既然是后台权限管理系统,当然少不了权限控制啦,至于权限控制,前端方面当然就是对页面资源的访问和操作控制啦. 前端资源权限主要又分为两个部分,即导航菜单的查看权限和页面增删改操作按钮的操作 ...

  4. Vue + Element UI 实现权限管理系统 前端篇(十一):第三方图标库

    使用第三方图标库 用过Elment的同鞋都知道,Element UI提供的字体图符少之又少,实在是不够用啊,幸好现在有不少丰富的第三方图标库可用,引入也不会很麻烦. Font Awesome Font ...

  5. Vue + Element UI 实现权限管理系统(第三方图标库)

    使用第三方图标库 用过Elment的同鞋都知道,Element UI提供的字体图符少之又少,实在是不够用啊,幸好现在有不少丰富的第三方图标库可用,引入也不会很麻烦. Font Awesome Font ...

  6. Vue + Element UI 实现权限管理系统 前端篇(十二):用户管理模块

    用户管理模块 添加接口 在 http/moduls/user.js 中添加用户管理相关接口. import axios from '../axios' /* * 用户管理模块 */ // 保存 exp ...

  7. Vue + Element UI 实现权限管理系统 (管理应用状态)

    使用 Vuex 管理应用状态 1. 引入背景 像先前我们是有导航菜单栏收缩和展开功能的,但是因为组件封装的原因,隐藏按钮在头部组件,而导航菜单在导航菜单组件,这样就涉及到了组件收缩状态的共享问题.收缩 ...

  8. Vue + Element UI 实现权限管理系统(搭建开发环境)

    技术基础 开发之前,请先熟悉下面的4个文档 vue.js2.0中文, 优秀的JS框架 vue-router, vue.js 配套路由 vuex,vue.js 应用状态管理库 Element,饿了么提供 ...

  9. Vue + Element UI 实现权限管理系统 前端篇(八):管理应用状态

    使用 Vuex 管理应用状态 1. 引入背景 像先前我们是有导航菜单栏收缩和展开功能的,但是因为组件封装的原因,隐藏按钮在头部组件,而导航菜单在导航菜单组件,这样就涉及到了组件收缩状态的共享问题.收缩 ...

随机推荐

  1. python模块(4)

    re正则 re.match 从头开始匹配 re.search 匹配包含 re.findall 把所有匹配到的字符放到以列表中的元素返回(没有group()方法) re.splitall 以匹配到的字符 ...

  2. hybrid cordova+vue开发APP(一) 环境搭建

    没有选择react-navite,而选择cordova+vue2.x,是因为react-navite有学习成本,并且cordova+vue2.x程序员 可以直接上手,性能上可以满足需求,成本低,开发速 ...

  3. 基于Arcface 免费离线人脸识别 2.0 Demo C#

    本来打算做个C#版demo,但没用成功.使用虹软最新人脸识别技术开发完成 过程如下: 1. 传入一张单人脸照片: 2.调用检测人脸函数ASFDetectFaces,成功返回人脸信息的指针: 3.使用 ...

  4. 阿里云域名+github建立网站

    1.准备工作 ①购买一个阿里云域名,这里测试的域名为 www.cores.vip ②创建一个github账号 (注意:一个github账号只能建立一个username.github.io的网站,不能建 ...

  5. Unity --- 纹理为什么要设置为2的N次方

    1.图片的纹理像素在Unity3D中需要遵循2的N次方,由图形学决定的,只识别2的N次方.   非2的N次方的图片会转化为2的N次方图片(500 x 500 → 512 x 512),是因为转化过程比 ...

  6. MySql安装成功后无法远程登录

    MySql安装成功之后在服务器能够登录,但是在客户端却不能登录.只需要按照如下操作即可 请使用mysql管理工具,如:SQLyog Enterprise 可能是你的帐号不允许从远程登陆,只能在loca ...

  7. 非常好的一个CentOS 6.2 apache 2.4.2 编译教程

    除了以下2点,没有错的. 1)pcre-devel 需要安装 2)apr 和 apr-util 有了新的版本了 How to Install Apache 2.4.2 from Source on C ...

  8. GrindEQ Math Utilities 2015破解版 图文安装和序列号补丁激活教程

    GrindEQ Math Utilities 2015破解版 图文安装和序列号补丁激活教程 https://www.sdbeta.com/mf/2018/1002/226048.html 软件下载: ...

  9. English trip V1 - B 13. Are you a model? 你是模特吗? Teacher:Patrick Key: 单词回顾、词性后缀

    因为这节课本身内容过于So easy~ Patrick给我补充了很多课外内容 课上内容(Lesson) I doesn't work  2层意思 1) 东西坏了,这个东西不能正常工作了.比如钟不走时间 ...

  10. 自己动手编写IPv4地址包含关系测试的小脚本

    工作中需要对地址包含关系进行测试,现有ipaddress标准库和IPy无法满足,于是自己动手编写小脚本,主要实现== , in, <等专用功能,其他功能可以后续用到再补充,例如迭代打印网段内所有 ...