一、useState - Nuxt3中的全局状态管理
1. 介绍
这是Nuxt3为我们准备的全局状态管理,且支持响应式,可以无需使用pinia等,直接在项目中使用。
官网文档:useState · Nuxt Composables 、 State Management · Get Started with Nuxt
注意事项:
· 由于会把State存储为JSON,所以请不要存储不能被JSON化的数据,比如类、函数、Symbol等
· 为了让我们的状态管理能在任何地方使用(不止在setup中),最好统一使用函数的写法,见下
2. 基本使用
· 创建文件
在根目录下的 /composables 文件夹中新建文件state.ts (没有这个文件夹就新建,为什么放在这个文件夹,是因为这个文件夹里导出的函数,Nuxt会帮我们自动引入,官网文档: composables/ · Nuxt Directory Structure)
· 创建需要全局共享的变量并导出
统一一下命名:导出的函数的格式为 useXxx
讲解一下 useState 函数的用法
第一个参数是全局唯一的键值(就比如你这个变量想叫什么名字),如下方代码中的 ‘color’和‘people’(千万不要出现重复的键值)
第二个参数是该状态的值,用函数形式表达,比如下方的 ()=>‘red’,实际的值就是 ‘red’ 。
然后整个useState 作为一个箭头函数的返回值,赋值给 useXxx (为了能在任意地方都能使用state)
1// state.ts
2
3/**简单数据类型示例 */
4export const useColor = () => useState<string>('color', () => 'red')
5
6
7/**复杂数据类型示例 */
8interface test{
9 /**姓名 */
10 name: string,
11 /**年龄 */
12 age: number
13 /**数组 */
14 arr: number[]
15}
16export const usePeople = () => useState<test>('people',()=>({ //箭头函数return对象时的简写
17 name: '小明',
18 age: 18,
19 arr: [1,2]
20}))
· 使用(展示)
在任意想使用全局状态的文件中(不止.vue文件),输入 const color = useColor()
在JS / TS代码中, color.value即可获取color真正的值 (和vue3中的ref数据使用方法一模一样)
在template模板中,直接 {{ color }} 即可,无需 .value (和vue3中的ref数据使用方法一模一样)
1<template>
2 <div>{{ color }} </div>
3 <div>{{ people.name}} </div>
4</template>
5
6<script setup lang='ts'>
7//直接使用下面这俩函数,无需导入,因为Nuxt帮我们导入了
8const color = useColor()
9const people = usePeople()
10
11//在js代码中使用
12const func = ()=>{
13 if(color.value === 'red'){
14 console.log('是红色')
15 }
16}
17
18</script>
· 使用(修改)
在任意地方都能修改(不止.vue文件)。 示例见下**: (和vue3中的ref数据使用方法一模一样)**
下面的修改方法都能触发页面的响应式
1//直接使用下面这俩函数,无需导入,因为Nuxt帮我们导入了
2const color = useColor()
3const people = usePeople()
4
5//在js代码中修改
6color.value = 'black' //简单数据类型直接.value修改
7people.value.name = '小黑' //复杂数据类型可以像这样一个个属性修改
8people.value = { //也可以像这样直接赋值新对象,反正都能响应式
9 name: '小黑',
10 age: 10,
11 arr: [1,2,3,4]
12}
二、持久化(按需)
上面的数据只是存储在内存中的,当浏览器刷新或者关闭后,重新打开就恢复原样了。很多时候我们需要对数据进行持久化(保存在localStorage中)
注意:由于Nuxt是服务端渲染,localStorage只能在挂载后才能使用!!
下面是我自己写的一种实现持久化的方法,大家也可以自己发挥。只要知道在onMounted后使用就行。
因为不是所有数据都需要持久化,所以我做了一些适配:
只有需要的数据才会持久化,根据函数名自动存储为对应的键放在localStorage中。(比如useColor会变为color存储在localStorage中,usePeople会变为people。但是多驼峰的也会全变为小写,比如useAaaBbb,会变为aaabbb存储,你可以根据需要修改,反正不影响代码运行)
持久化时机:在app挂载时,从localStorage获取数据赋值到State中,在页面将要关闭/刷新时,把State数据放到localStorage中。
1// state.ts
2
3/**简单数据类型示例 */
4export const useColor = () => useState<string>('color', () => 'red')
5/**复杂数据类型示例 */
6interface test{
7 /**姓名 */
8 name: string,
9 /**年龄 */
10 age: number
11 /**数组 */
12 arr: number[]
13}
14export const usePeople = () => useState<test>('people',()=>({
15 name: '小明',
16 age: 18,
17 arr: [1,2]
18}))
19/** 不需要持久化的数据示例 */
20export const useXxx = () => useState<string>('Xxx', () => '我不需要持久化')
21
22// 下面是持久化相关的代码
23
24
25/**需要进行持久化的数据:把需要持久化的数据放在下面这个对象中,才会持久化,不需要持久化的数据就不用放到这里了。 */
26const enduring: { [key: string]: () => Ref<any> } = {
27 useColor, usePeople
28}
29//下面的俩函数在app.vue的onMounted中统一调用,或者在其它情况挂载后单独调用。
30/**把所有指定数据保存到本地存储
31 * @param key 要保存的数据名。不填的话就是保存全部(一般不填,统一在页面关闭时保存。如果是特别重要的数据,就时不时单独保存一下即可。)
32 */
33export const setLocal = (key?: string) => {
34 if (key) {
35 console.log('只保存', key);
36 const useKey = 'use' + key.slice(0, 1).toUpperCase() + key.slice(1).toLowerCase();//首字母大写,其它全部转小写
37 const func = enduring[useKey]
38 if (!func) {
39 console.log('没有找到', useKey, '对应的函数');
40 return
41 }
42 localStorage.setItem(key, JSON.stringify(func().value))
43 } else {
44 console.log('正在保存全部数据');
45 for (const key in enduring) {
46 if (Object.prototype.hasOwnProperty.call(enduring, key)) {
47 const element = enduring[key];
48 const setKey = key.toLowerCase().substring(3)//去掉前面的use ,其它全部转小写
49 try {
50 localStorage.setItem(setKey, JSON.stringify(element().value))
51 } catch (error) {
52 console.log(`在设置${setKey}的数据时出现错误`, error);
53 }
54 }
55 }
56 }
57}
58/**从本地存储获取数据到state中 */
59export const getLoacl = () => {
60 for (const key in enduring) {
61 if (Object.prototype.hasOwnProperty.call(enduring, key)) {
62 const element = enduring[key];
63 const setKey = key.toLowerCase().substring(3)//去掉前面的use ,其它全部转小写
64 try {
65 const localData = localStorage.getItem(setKey) || ''
66 if (localData) {
67 element().value = JSON.parse(localData)
68 console.log(setKey, '的本地存储数据获取成功', element().value);
69 }
70 } catch (error) {
71 console.log(`在获取${setKey}的数据时出现错误`, error);
72 }
73 }
74 }
75}
然后在app.vue中,进行下面的操作: 下面的俩函数无需引入,Nuxt自动帮我们引入了
1<template>
2 <div class="App">
3 <!-- 这里是APP.vue !!!!! -->
4 <NuxtPage />
5 </div>
6</template>
7<script setup lang="ts">
8//挂载钩子
9onMounted(() => {//必须在onMounted的时候才能用local和window
10 getLoacl()
11 window.onbeforeunload = () => {//离开页面时保存数据,由于可能突发情况,所以重要数据请手动调用setLocal函数
12 setLocal() //如果需要调试本地存储数据,记得把这个注释一下
13 }
14})
15</script>
以上就实现了按需持久化数据。注:window.onbeforeunload 是会在页面关闭或刷新时执行,但是意外关闭的浏览器不会执行这个函数,所以重要的数据记得时不时单独调用一下 setLocal(‘xxx’) 函数,保证数据持久化(可以利用watch监听,或者简单粗暴的定时器)