Vite+TS基于Pinia记录用户信息

3,817 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

前言

好家伙,昨天发的Docker运维相关的文档的阅读量只有6,那我今天继续更新Vite系列的文章吧。
上一篇文章写了要编写一个后端接口,但是我的个人电脑没有java相关环境,今天用前端代码实现假登录吧

如何记录登录信息

  • 浏览器缓存 使用浏览器缓存记录用户信息,token信息。大家可以按F12切换到Application查看对应的浏览器缓存
  • Vuex、Pinia 简单来讲就是状态管理器。如果想更好地在组件外部管理状态需要用到这个概念
  • 选择Vuex还是Pinia 建议如果功能比较简单的话使用Pinia,一是打包出来的体积很小,二是入门非常简单。

代码实现

初始化Pinia

执行命令下载Pinia

yarn add pinia@next

全局引用Pinana

在main.ts中引入Pinia,核心代码如下

import { createPinia } from 'pinia'
const pinia = createPinia()
app.use(pinia)

在src下新建stores,先创建一个userInfo.ts,内容如下

import { defineStore } from 'pinia'
export const useUserInfoStore = defineStore('userInfo', {
 state: () => {
   return {
     userInfo: {
       userName: '',
       roleCode: []
     }
   }
 },
 actions: {
   setUserInfo(userInfo) {
     this.userInfo = userInfo
   }
 }
})

简单说明一下:state:s说明记录的userInfo类型的状态。actions:对外开发的方法,执行该方法即可设置将userInfo设置为传入的值。这样就定义好了userInfo的状态管理

设置浏览器缓存记录用户信息

为什么使用了状态管理器之后还需要用缓存呢?很简单,状态管理器在按F5后会失效。如果不使用缓存的话,按F5就要重新登录获取用户等信息这样很不合理,所以使用浏览器缓存记录一下信息。
我们这里使用sessionStorage(会话缓存)来记录缓存。为什么不使用localStorage呢?因为改缓存是一直存储在浏览器中的,假设别人使用你的电脑然后查找历史记录访问对应的地址就会不登陆访问你能看到的所有数据(不考虑token的情况)。而会话缓存只要关闭浏览器就可以清除,很方便
在src下新建/cache/index.ts,内容如下

/**
* window.localStorage 浏览器永久缓存
* @method set 设置永久缓存
* @method get 获取永久缓存
* @method remove 移除永久缓存
* @method clear 移除全部永久缓存
*/
export const Local = {
 // 设置永久缓存
 set(key: string, val: any) {
   window.localStorage.setItem(key, JSON.stringify(val))
 },
 // 获取永久缓存
 get(key: string) {
   let json: any = window.localStorage.getItem(key)
   return JSON.parse(json)
 },
 // 移除永久缓存
 remove(key: string) {
   window.localStorage.removeItem(key)
 },
 // 移除全部永久缓存
 clear() {
   window.localStorage.clear()
 }
}

/**
* window.sessionStorage 浏览器临时缓存
* @method set 设置临时缓存
* @method get 获取临时缓存
* @method remove 移除临时缓存
* @method clear 移除全部临时缓存
*/
export const Session = {
 // 设置临时缓存
 set(key: string, val: any) {
   window.sessionStorage.setItem(key, JSON.stringify(val))
 },
 // 获取临时缓存
 get(key: string) {
   let json: any = window.sessionStorage.getItem(key)
   return JSON.parse(json)
 },
 // 移除临时缓存
 remove(key: string) {
   window.sessionStorage.removeItem(key)
 },
 // 移除全部临时缓存
 clear() {
   window.sessionStorage.clear()
 }
}

考虑到这里可能有奇葩需求需要使用local缓存,我把local缓存的方法也放出来了

重新设计一下登录页面

我这里只放核心代码,写实现逻辑了,如果有兴趣可以查看我的git项目获取源码

//引入缓存
import { Session } from '@/utils/cache/index'
import { useUserInfoStore } from '@/stores/userInfo'
...
const handleLogin = () => {
  loginRef.value.validate((vaild) => {
    if (vaild) {
      //设置加载中防止重复调用接口
      loginLoad.value = true
      Session.set('token', new Date().getTime())
      let username = loginModel.username ? loginModel.username : ''
      let userInfo = { userName: '', roleCode: [] as any[] }
      switch (username) {
        case 'admin':
          userInfo = getAdminInfo(username)
          break
        case 'dept':
          userInfo = getDeptAdminInfo(username)
          break
        default:
          userInfo = getCommonUserInfo(username)
          break
      }
      Session.set('userInfo', userInfo)
      userInfoStore.setUserInfo(userInfo)
      setRouteStoreByUserCode(userInfo.roleCode)
      loginLoad.value = false
      router.push('/')
    }
  })
}
//管理员用户信息
const getAdminInfo = (userName) => {
  let userInfo = {
    userName,
    roleCode: [
      'system',
      'system-user',
      'system-setting',
      'system-user:add',
      'system-user:update',
      'system-user:search'
    ]
  }
  return userInfo
}
//部门负责人用户信息
const getDeptAdminInfo = (userName) => {
  let userInfo = {
    userName,
    roleCode: [
      'system',
      'system-user',
      'system-user:add',
      'system-user:update',
      'system-user:search'
    ]
  }
  return userInfo
}
//普通员工用户信息
const getCommonUserInfo = (userName) => {
  let userInfo = {
    userName,
    roleCode: []
  }
  return userInfo
}

这里模仿请求接口之后获取到的用户信息,注意要先存储到Session再放到状态管理器中

处理刷新后使用状态管理器取值失效的问题

在layout上设计显示用户信息的时候会发现按F5取到的是状态管理器的默认值。我们在路由跳转之前从缓存中获取然后再设置就可以了,代码如下

router.beforeEach((to, _from, next) => {
  let path = to.path
  //白名单不必验证token和权限
  if (whiteNameRouters.indexOf(path) > -1 && path != '/') {
    return next()
  }
  //其他路由如果没有token则跳转到登录页面
  let token = Session.get('token')
  if (!token) {
    ElMessage.warning('请先登录!')
    return next('/login')
  }
  //防刷新处理
  let userInfoStore = useUserInfoStore()
  setUserStore(userInfoStore)
  let needRoleCode = ''
  if (to.meta && to.meta.roleCode) {
    needRoleCode = to.meta.roleCode as string
  }
  //当前路径不需要权限时,执行跳转
  if (needRoleCode === '' || !needRoleCode) {
    return next()
  }

  let roleCodeArr: string[] = userInfoStore.userInfo.roleCode
  roleCodeArr = userInfoStore.userInfo.roleCode
  if (!roleCodeArr || roleCodeArr.length === 0) {
    return next('/login')
  }
  if (roleCodeArr!.indexOf(needRoleCode) > -1) {
    return next()
  } else {
    return next('/login')
  }
})
//防止刷新时丢失用户信息
const setUserStore = (userInfoStore) => {
  let userInfo = userInfoStore.userInfo
  if (!userInfo || !userInfo.userName) {
    userInfoStore.setUserInfo(Session.get('userInfo'))
  }
}

我这里含有部分路由鉴权的代码。因为我设置了userInfo的默认userName的值是空字符串所以要判断一下是否为空。

结语

通过我的这篇文章可以发现,其实Pinia使用十分简单,现在已经实现了记录用户信息了。准备开始渲染路由了。不同角色看到的是不同的目录。
欢迎关注我的掘金账号:juejin.cn/user/261290…
欢迎star我的git项目:gitee.com/liangminghu… 下期预告:动态渲染路由的实现及遇到的坑