0
点赞
收藏
分享

微信扫一扫

前端通用后台登录解决方案(一站式解决封装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对象 

封装登录请求动作 💨 

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>

然后通过 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 中是为了后面在其他位置进行使用,那么下面我们就分别来实现对应的缓存方案。

创建 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 的形式进行获取。

在 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 都跳转到登录页面。

举报

相关推荐

0 条评论