Vue状态管理

前端每个页面访问后端数据的时候都需要token,那么所有组件都需要依赖token这个数据,所以就需要将token找个一个共享的存储,让其他组件都能访问到它,和服务端的session功能类似,如何实现?

一、共享内存

第一种方式最直接:共享内存,直接开辟一个变量,全局都可以访问到了,类似于后端的全局变量

1、全局注入

使用到 provide() 函数注入到根实例, 从而提供全局变量功能

<script setup>
import { RouterLink, RouterView } from "vue-router";
import HelloWorld from "@/components/HelloWorld.vue";

import { ref, provide } from "vue";
// 如果的变量可以是响应式的
const count = ref(0);
provide(/* 注入名 */ "count", /* 值 */ count);
</script>

通过inject获取父组件注入的变量

<script setup>
import { inject } from "vue";

// 这里也可以获取默认值: inject(<变量名称>, <变量默认值>), 如果获取不到变量 就使用默认值
const count = inject("count");

const doClick = () => {
  count.value++;
};
</script>

<template>
  <button @click="doClick">You clicked me {{ count }} times.</button>
</template>
2、组合式函数

声明一个响应式模块,导出后,提供给所有组件使用

// store/global.js
import { reactive } from "vue";

export const store = reactive({
  count: 0,
});

其他组件通过js的导入语法直接使用,妥妥的全局变量

<script setup>
import { store } from "@/stores/global";

const doClick = () => {
  store.count++;
};
</script>

<template>
  <button @click="doClick">You clicked me {{ store.count }} times.</button>
</template>

总结:

这种其实就是一个简单粗暴的 通过共享内存进行通信的方式, 好在其简单易懂,也许你会喜欢, 但是也有它的缺陷

因为这种方式使用的是内存, 所以页面关闭或者刷新就都没有, 想要就状态持久化 还需要存储

二、浏览器本地存储

这种方式就需要使用到浏览器的存储功能了, 它可供我们存储客户端临时信息 简称 Web Storage

image

  • cookie
  • sessionStorage
  • localStorage
1、cookie

cookie是有可以设置过期时间的,同一个域下的页面都可以访问

cookie在没有设置过期时间时,系统默认浏览器关闭时失效,只有设置了没到期的保存日期时,浏览器才会把cookie作为文件保存在本地上。当expire到期时,cookie不会自动删除,仅在下次启动浏览器或刷新浏览器时,浏览器会检测cookie过期时间,如已过期浏览器则会删除过期cookie

注意:

  • 数据存放大小:4k,因为每次http请求都会携带cookie
  • 浏览器关闭时,cookie会失效
  • 注意cookie可以支持httpOnly,这个时候前端js是修改不了的(也看不到)
// 读取cookie, 注意读取出来的cookie是个字符串
document.cookie
'language=zh; Sub-System=develop; sidebarStatus=1; Current-Namespace=c16mhsddrei91m4ri0jg; Refresh-Token=paBuyTIfsX3BeKrXrCmD8khUla6x8y1g'
// 需要自己处理
document.cookie.split('; ')

// 直接赋值就添加了一个key-value
document.cookie = 'cookieKey=cookieValue'
'language=zh; Sub-System=develop; sidebarStatus=1; Current-Namespace=c16mhsddrei91m4ri0jg; Refresh-Token=paBuyTIfsX3BeKrXrCmD8khUla6x8y1g; cookieKey=cookieValue'

// 当然cookie还有很多选项可以设置, 通过;隔开比如
document.cookie="username=John Doe; expires=Thu, 18 Dec 2043 12:00:00 GMT; path=/";

// 修改cookie和设置cookie一样, 保证key相同就可以
document.cookie = 'cookieKey=cookieValue2'
document.cookie
'language=zh; Sub-System=develop; sidebarStatus=1; Current-Namespace=c16mhsddrei91m4ri0jg; Refresh-Token=paBuyTIfsX3BeKrXrCmD8khUla6x8y1g; cookieKey=cookieValue2'

// 删除cookie时,把expires 设置到过期的时间即可, 比如设置个2019年的时间
document.cookie = `cookieKey=;expires=Mon, 26 Aug 2019 12:00:00 UTC`
document.cookie
'language=zh; Sub-System=develop; sidebarStatus=1; Current-Namespace=c16mhsddrei91m4ri0jg; Refresh-Token=paBuyTIfsX3BeKrXrCmD8khUla6x8y1g'
2、sessionStorage

存储的数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁, 因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储

那么,到底什么是一个会话?多个标签页之间的数据是否会共享呢?

我们可以验证下: 开启2个窗口, 直接通过浏览器修改sessionStorage 进行验证

通过验证我们可以知道 一个标签页 就表示一个回话, 当标签页关闭, 回话就清除, 不通标签页之间不共享数据

// 通过setItem设置key-value
sessionStorage.setItem('key1', 'value1')
sessionStorage['key2']= 'value2'
sessionStorage.key2= 'value2'

// 查询sessionStorage对象
sessionStorage
Storage {key2: 'value2', key1: 'value1', length: 2}

// 通过getItem获取key的值
sessionStorage.getItem('key1')
sessionStorage['key1']
sessionStorage.key1

// 修改
sessionStorage.key1 = 'value11'
sessionStorage['key1'] = 'value11'

// 删除key
sessionStorage.removeItem('key1')

// 清空storage
sessionStorage.clear()
3、localStorage

localStorage生命周期是永久, 除非主动删除数据,否则数据是永远不会过期的

相同浏览器的不同页面间可以共享相同的 localStorage(页面属于相同域名和端口)

我们可以验证下: 开启2个窗口, 直接通过浏览器修改localStorage 进行验证

localStorage的操作方法和sessionStorage完全一样:

// 通过setItem设置key-value
localStorage.setItem('key1', 'value1')
localStorage['key2']= 'value2'
localStorage.key2 = 'value2'

// 查询sessionStorage对象
localStorage
Storage {key2: 'value2', key1: 'value1', length: 2}

// 通过getItem获取key的值
localStorage.getItem('key1')
localStorage['key1']
localStorage.key1

// 修改
localStorage.key1 = 'value11'
localStorage['key1'] = 'value11'

// 删除key
localStorage.removeItem('key1')

// 清空storage
localStorage.clear()

三、Vueuse与本地存储

作为vue3的标准库, vueuse提供了很多实用的工具来实现了状态管理, 其他就有保护浏览器存储的组合式API函数: Vueuse State相关工具

在vueuse中有一个高频使用的函数: useStorage, 就默认就是使用的LocalStorage, 该函数已经讲LocalStroage包装成了响应式了, 因此你可以把他理解为一个带有持久化机制的响应式对象

下面是useStorage的基础用法:

import { useStorage } from '@vueuse/core'

// bind object
const state = useStorage('my-store', { hello: 'hi', greeting: 'Hello' })

// bind boolean
const flag = useStorage('my-flag', true) // returns Ref<boolean>

// bind number
const count = useStorage('my-count', 0) // returns Ref<number>

// bind string with SessionStorage
const id = useStorage('my-id', 'some-string-id', sessionStorage) // returns Ref<string>

// delete data from storage
state.value = null

我们集合前面共享内存的通信方式, 把他们改造成, 结合本地存储基础, 从而避免刷新页面是状态丢失:

  • 注入依赖
  • 组合式函数
注入依赖持久化

直接使用useStorage构造一个响应式持久化变量:

<script setup>
import { RouterLink, RouterView } from "vue-router";
import { useStorage } from '@vueuse/core'
import HelloWorld from "@/components/HelloWorld.vue";

import { provide } from "vue";
// 如果的变量可以是响应式的
const count = useStorage('count', 0);
provide(/* 注入名 */ "count", /* 值 */ count);


// 这里也可以获取默认值: inject(<变量名称>, <变量默认值>), 如果获取不到变量 就使用默认值
// const count = inject("count");
</script>
全局变量持久化

声明一个响应式模块,导出后,提供给所有组件使用

// stroes/app.js
// 保存当前应用程序的状态
import { useStorage } from '@vueuse/core'
// localStorage.setItem('my-store', '{"hello": "nihao"}')
// 将用户的token等信息保存到浏览器的localstorage中
export const state = useStorage(
    'vblog',
    {
        token: {},
        is_login: false,
    },
    localStorage,
    { mergeDefaults: true }, // <--
)

这样登录成功后就可以通过全局变量保存到localstorage中

image

某个组件需要使用

image