前端通用后台登录解决方案(一站式解决封装axios模块、接口请求模块、登录请求动作、本地缓存处理、登录鉴权解决)

目录

封装 axios 模块

封装接口请求模块

封装登录请求动作

触发登录动作

本地缓存处理方案

响应数据的统一处理 

登录鉴权解决方案


封装 axios 模块 💨 

我们希望封装出来的 axios 模块,至少需要具备一种能力,那就是:根据当前模式的不同,设定不同的 BaseUrl ,因为通常情况下企业级项目在 开发状态 和 生产状态 下它的 baseUrl 是不同的。

我们可以在项目中创建两个文件:

  1.  .env.development 
  2.  .env.production 

它们分别对应 开发状态 和 生产状态

我们可以在上面两个文件中分别写入以下代码:

.env.development

# 标志
ENV = 'development'

# base api
VUE_APP_BASE_API = '/api'

.env.production

# 标志
ENV = 'production'

# base api
VUE_APP_BASE_API = '/prod-api'

有了这两个文件之后,我们就可以创建对应的 axios 模块

创建 utils / request.js ,我们在这里配置发送 axios 请求的基本信息,写入如下代码:

import axios from 'axios'

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 5000
})

export default service

这里 process.env.VUE_APP_BASE_API 可以根据当前模式的不同来设定我们前文设置好的 baseURL 

有了 axios 模块之后,接下来我们就可以

  1. 封装接口请求模块
  2. 封装登录请求动

封装接口请求模块 💨 

创建 api 文件夹,我们把所有的接口请求模块都放到这个文件夹里,例如登陆注册是一个模块,请求首页文章信息是一个模块:

创建 sys.js ,我们在这里编写登录的接口请求函数:

import request from '@/utils/request'

/**
 * 登录
 */
export const login = data => {
  return request({
    url: '/sys/login',
    method: 'POST',
    data
  })
}

现在调用这个方法返回的就是一个promise对象 

封装登录请求动作 💨 

什么是登录请求动作呢,就是我们不想在登陆页面中直接调用接口请求方法,而是想把它封装在vuex的模块里我们在 store 下创建 modules 文件夹,创建 user.js 模块,用于处理所有和用户相关的内容(此处需要使用第三方包 md5 )。

modules/user.js:

import { login } from '@/api/sys'
import md5 from 'md5'
export default {
  namespaced: true,
  state: () => ({}),
  mutations: {},
  actions: {
    login(context, userInfo) {
      const { username, password } = userInfo
      return new Promise((resolve, reject) => {
        login({
          username,
          password: md5(password)
        })
          .then(data => {
            resolve()
          })
          .catch(err => {
            reject(err)
          })
      })
    }
  }
}

注意:这里的md5是一种加密方法,用于加密密码,同学们可以 npm install 来安装。在user.js的actions中,我们把登录请求的动作封装好了,我们在返回的promise对象中调用接口请求模块 login,然后把参数传递进去,返回的promise实例我们通过 then 和 catch 进行接收。

然后我们还需要在 store 的 index.js 中完成注册:

import { createStore } from 'vuex'
import user from './modules/user.js'
export default createStore({
  modules: {
    user
  }
})

触发登录动作 💨 

我们先看一下登录界面主要逻辑的代码:

<template>
    <el-form
      class="login-form"
      ref="loginFromRef"
      :model="loginForm"
      :rules="loginRules"
    >
    ...
	    <el-button
            type="primary"
            style="width: 100%; margin-bottom: 30px"
            :loading="loading"
            @click="handleLogin"
            >登录</el-button
         >
    </el-form>
</template>
<script setup>
import { ref } from 'vue'
import { validatePassword } from './rules'
import { useStore } from 'vuex'
...

// 登录动作处理
const loading = ref(false)
const loginFromRef = ref(null)
const store = useStore()
const handleLogin = () => {
  loginFromRef.value.validate(valid => {
    if (!valid) return

    loading.value = true
    store
      .dispatch('user/login', loginForm.value)
      .then(() => {
        loading.value = false
        // TODO: 登录后操作
      })
      .catch(err => {
        console.log(err)
        loading.value = false
      })
  })
}
</script>

当点击按钮的时候,我们触发登陆动作,首先要进行表单验证,因为这里用的是 element-plus ,其中的 form 组件有一个 validate 的验证方法可以把表单中的每一项进行验证,那我们如何给这个元素添加方法呢,首先得获取到它的实例对象,在 vue2直接 this.$refs.xxx ,就能获取到,而在 vue3 中我们通过在元素上添加 ref 属性,然后只需要在 setup 中声明一个 ref 值为空且和想绑定的 ref 值相同的值就行。在本例中 ref 值为 loginFromRef,那我们就在 setup 中定义 loginFromRef 为空,通过 loginFromRef.value 就能获取到实例对象。

然后通过 store.dispatch 来调用 user 模块下的 login 登陆方法,然后把表单的内容作为参数传递进去。通过 then 指定成功后的回调,catch 指定失败后的回调。

但是当我们这样做完以后是有问题的,控制台会报错 404 ,该错误表示,我们当前请求的接口不存在。出现这个问题的原因,是因为我们在前面配置环境变量时指定了 开发环境下,请求的 BaseUrl 为 /api ,所以我们真实发出的请求为:/api/sys/login 。

这样的一个请求会被自动键入到当前前端所在的服务中,所以我们最终就得到了 http://192.168.18.42:8081/api/sys/login 这样的一个请求路径。

而想要处理这个问题,那么可以通过指定的 WebPack DevServer 的形式,代理当前的 url 请求。而指定这个代理非常简单,是一种近乎固定的配置方案。在 vue.config.js 中,加入以下代码:

module.exports = {
  devServer: {
    // 配置反向代理
    proxy: {
      // 当地址中有/api的时候会触发代理机制
      '/api': {
        // 要代理的服务器地址  这里不用写 api
        target: 'https://api.imooc-admin.lgdsunday.club/',
        changeOrigin: true // 是否跨域
      }
    }
  },
  ...
}

 重新启动服务,再次进行请求,即可得到返回数据:

本地缓存处理方案 💨 

在登陆成功后,后台返回给我们的信息一般都有 token ,通常情况下,在获取到 token 之后,我们会把 token 进行缓存,而缓存的方式将会分为两种:

  1. 本地缓存:LocalStorage
  2. 全局状态管理:Vuex

保存在 LocalStorage 是为了方便实现 自动登录功能,保存在 vuex 中是为了后面在其他位置进行使用,那么下面我们就分别来实现对应的缓存方案。

注意:我们一般会将一个对象存入localstorage中,但是localstorage会自动将对象数据转换成字符串形式,这时候我们可以使用JSON.stringify()这个方法,来将数据转换成JSON字符串存入localstorage存入,当读取的时候再使用JSON.parse()方法读取出来。

创建 utils/storage.js 文件,封装四个对应方法: 

/**
 * 存储数据
 */
export const setItem = (key, value) => {
  // 将数组、对象类型的数据转化为 JSON 字符串进行存储
  if (typeof value === 'object') {
    value = JSON.stringify(value)
  }
  window.localStorage.setItem(key, value)
}

/**
 * 获取数据
 */
export const getItem = key => {
  const data = window.localStorage.getItem(key)
  try {
    return JSON.parse(data)
  } catch (err) {
    return data
  }
}

/**
 * 删除数据
 */
export const removeItem = key => {
  window.localStorage.removeItem(key)
}

/**
 * 删除所有数据
 */
export const removeAllItem = key => {
  window.localStorage.clear()
}

因为获取的 token 值是一个常量,所以我们单独建一个文件来存储常量,创建 constant 常量目录 constant/index.js:

export const TOKEN = 'token'

在 vuex 的 user 模块下处理 token 的存储:

import { login } from '@/api/sys'
import md5 from 'md5'
import { setItem, getItem } from '@/utils/storage'
import { TOKEN } from '@/constant'
export default {
  namespaced: true,
  state: () => ({
    token: getItem(TOKEN) || ''
  }),
  mutations: {
    setToken(state, token) {
      state.token = token
      setItem(TOKEN, token)
    }
  },
  actions: {
    login(context, userInfo) {
      ...
          .then(data => {
            this.commit('user/setToken', data.data.data.token)
            resolve()
          })
          ...
      })
    }
  }
}

此时,当点击登陆时,即可把 token 保存至 vuex 与 localStorage 中

响应数据的统一处理  💨 

在上一小节中,我们保存了服务端返回的 token 。但是有一个地方比较难受,那就是在 vuex 的 user 模块中,我们获取数据端的 token 数据,通过 data.data.data.token 的形式进行获取。

一路的 data, 确实让人比较难受,如果有过 axios 拦截器处理经验的同学应该知道,对于这种问题,我们可以通过 axios 响应拦截器 进行处理。

在 utils/request.js 中实现以下代码:

import axios from 'axios'
import { ElMessage } from 'element-plus'

...
// 响应拦截器
service.interceptors.response.use(
  response => {
    const { success, message, data } = response.data
    //   要根据success的成功与否决定下面的操作
    if (success) {
      return data
    } else {
      // 业务错误
      ElMessage.error(message) // 提示错误消息
      return Promise.reject(new Error(message))
    }
  },
  error => {
    // TODO: 将来处理 token 超时问题
    ElMessage.error(error.message) // 提示错误信息
    return Promise.reject(error)
  }
)

export default service

此时,对于 vuex 中的 user 模块就可以进行以下修改了:

this.commit('user/setToken', data.token)

登录鉴权解决方案 💨 

首先我们先去对 登录鉴权 进行一个定义,什么是 登录鉴权 呢?

  • 当用户未登陆时,不允许进入除 login 之外的其他页面。
  • 用户登录后,token 未过期之前,不允许进入 login 页面。

而想要实现这个功能,那么最好的方式就是通过 路由守卫 来进行实现。

那么明确好了 登录鉴权 的概念之后,接下来就可以去实现一下,在路由 index.js 页面进行配置:

// 白名单
const whiteList = ['/login']
/**
 * 路由前置守卫
 */
router.beforeEach(async (to, from, next) => {
  // 存在 token ,进入主页
  if (store.state.user.token) {
    if (to.path === '/login') {
      next('/')
    } else {
      next()
    }
  } else {
    // 没有token的情况下,可以进入白名单
    if (whiteList.indexOf(to.path) > -1) {
      next()
    } else {
      next('/login')
    }
  }
})

配置白名单是因为如果他没有 token ,可能是没有登录,也可能页面是 404 或者 401 状态,我们不应该设定只要没 token 都跳转到登录页面。

  • 30
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 28
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YinJie…

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值