likes
comments
collection
share

基于Vue3做一套适合自己的状态管理(七)实践:当前登录用户的状态(严谨版)

作者站长头像
站长
· 阅读数 15

计划章节

  1. 基类:实现辅助功能
  2. 继承:充血实体类
  3. 继承:OptionApi 风格的状态
  4. Model:正确的打开方式
  5. 组合:setup 风格,更灵活
  6. 注册状态的方法、以及局部状态和全局状态
  7. 实践:当前登录用户的状态
  8. 实践:列表页面需要的状态

做一个更严谨的登录用户状态

网站一般都需要知道现在访问的人是谁,以及相应的使用权限,所以需要用户登录,向后端提交用户名和密码,后端验证通过后,返回用户信息和权限(如果需要区分不同的权限的话)。

安全性 用户信息比较关键和敏感,只有在登录和退出的时候才可以变更,其他时候(组件)只能获取信息,不能变更信息。

那么在代码层面能不能做得严格一点呢?

一般流程

  • 打开登录表单,录入用户名和密码,提交后端。
  • 后端验证信息,如果通过,则返回用户信息。
  • 前端得到用户信息后,可以记录用户信息,以便于各个组件使用。
  • 前端提供一个安全退出的功能,用户点击后可以退出网站。

创建当前登录用户的状态

我们先设计一个这样的局部状态:

  • ./store-nf/state-user
import { reactive, readonly } from 'vue'

import {
  defineStore, // 创建状态
  useStoreLocal // 获取局部状态
} from '@naturefw/nf-state'

export type User = {
  name: string
  isLogin: boolean
  power: {
    modules: Array<number | string>
  }
}

export type UserState = {
  user: User
}

// 区分局部状态的标记
const flag = Symbol('User') as InjectionKey<string>

/**
 * 注册局部的当前登录用户的状态
 * @returns 操作方法和用户状态
 */
export const regUserState = () => {
  // 定义内部成员
  // 内部使用的用户状态
  const user = reactive<User>({
    name: '没有登录',
    isLogin: false,
    power: {
      modules: []
    }
  })
  
  // 登录用的函数,仅示意,不涉及细节
  const login = () => {
    // 模拟登录
    user.name = '张三'
    user.isLogin = true
    user.power.modules.length = 0
    user.power.modules = [...[1,2,3]]
  }
  
  // 模拟退出,仅示意
  const logout = () => {
    // 模拟退出
    user.name = '已经退出'
    user.isLogin = false
    user.power.modules.length = 0
  }
  
  // 定义用户的状态
  const state = defineStore<UserState>(flag, {
    user: readonly(user)
  })

  // 返回用户的状态和操作方式
  return {
    login,
    logout,
    state
  }
}

/**
 * 子组件获取状态的函数
 * @returns 当前登录的用户状态(只读副本)
 */
export const getUserState = (): UserState => {
  return useStoreLocal<UserState>(flag)
}
  • User、UserState 定义两个个简单的类型,仅作为示例。

  • Symbol 使用 Symbol 作为标识,不用担心重名问题。

  • 私有成员 用户状态(user)和操作方法(login和logout)都是私有的形式,不通过状态返回。 操作方法通过创建状态的函数返回给父组件,父组件决定交给具体执行的组件,这样可以控制使用范围。

  • 用户状态的只读副本 使用 defineStore 的 “自定义” 方式创建了一个状态,传入 readonly 只读副本,这样其他组件获取状态的时候,只能读取信息,无法改变状态,提高安全性。 状态返回的是 user 的只读副本(readonly(user)),这样设置状态结构,安全性可以更高一些,当然不可能绝对安全,只是尽力而为。

  • 明确创建和获取 使用 regUserState() 创建状态;使用 getUserState() 获取状态。

父组件里面引入

这里的父组件指的是 App.vue ,这是仅次于 main 的层级,在这里创建的状态,下面的组件都可以访问到。在 App.vue 里面创建状态,是为了得到操作函数后,可以交给子组件,而在 main 里面无法访问子组件。

然后在App.vue 里面加载用户相关的组件,比如显示用户的组件、登录表单组件等,把相应的操作函数传入对应的组件。

  • App.vue
  // 创建状态
  import { regUserState } from './store-nf/state-user'

  // 用户组件
  import user from './components/user.vue'

  // 创建用户状态,获取操作方法
  const {
    login,
    logout
  } = regUserState()

然后在template 里面加载需要的组件,传入需要的函数。

<!--用户组件-->
<user :login="login" :logout="logout"></user>

登录组件里使用

设置props,接收 App.vue 传入的函数,显示需要的信息。

  • ./components/user.vue
  // 获取状态
  import { getUserState } from '../store-nf/state-user'

  const props = defineProps<{
    login: () => void,
    logout: () => void
  }>()

  // 头像网址
  const circleUrl = 'xxx'

  // 只读的用户状态
  const { user } = getUserState()
  • 定义 props,接收父组件传入的函数,以便于执行登录、退出的功能。
  • 获取状态,只读的不能修改,强行修改的话会被拦截,同时给警告。
  <span v-if="user.isLogin">
    <!--已经登录,显示头像和退出按钮-->
    <el-avatar size="" shape="square" :src="circleUrl" />
    <br>  
     欢迎 {{ user.name }} 
    <hr>
    <el-button type="" @click="logout">退出</el-button>
  </span>
  <span v-else>
    <!--没有登录-->
    <el-button type="" @click="login">登录</el-button>
  </span>

一般放在网站的右上角,没有登录的时候,显示“登录”按钮,登录后切换为用户头像和相关信息,以及退出按钮。这里只是做一个简单示例。

其他组件里面使用

其他组件只能获得用户状态的只读的副本,安全可靠不用担心用户信息被意外修改。

  // 获取状态
  import { getUserState } from '../store-nf/state-user'
  
  // 只读的用户状态
  const { user } = getUserState()
  // 不能直接修改,会被拦截
  // user.name = 'ddd'

源码

gitee.com/naturefw-co…

在线演示

naturefw-code.gitee.io/nf-rollup-s…