vue3中使用Pinia

Pinia 是一个用于 Vue 的状态管理库,类似 Vuex, 是 Vue 的另一种状态管理方案

三大核心:state(存储的值),getters(计算属性),actions也可支持同步(改变值的方法,支持同步和异步)

npm install pinia@next
or
yarn add pinia@next

模块化封装

创建实例

新建 store/index.ts(src目录下新建store文件夹,并创建index.ts文件)

创建 pinia 实例并导出所有模块

import { createPinia } from 'pinia'

const store = createPinia()

export default store
//main.ts
// 引入 pinia
import store from './stores'

const app = createApp(App)
app.use(store)

module

新建store/modules/urer.ts

modules 按模块定义对应的 store, user.ts 表示用户相关数据存储的仓库

// user.ts
import { defineStore } from 'pinia'

// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useUserInfoStore = defineStore('useUserInfoStore', {
  // 其他配置...
  // state: 返回对象的函数
  state: () => {
    return {
      login: false
    }
  },
    getters: {
    newName: (state) => state.userInfo.name + 'vip',
  },
    actions: {
    //更新整个对象
    updateUserInfo(userInfo: { name: string; age: number }) {
      this.userInfo = userInfo;
    },
    //更新对象中某个属性
    updateAge(age: number) {
      this.userInfo.age = age;
    },
    //更新基础数据类型
    updateToken(token: string) {
      this.token = token;
    },
  },
})

constant

constant/index.ts 用来存放定义的一些全局常量信息,暂时不是很多,所以统一放在了一个文件下。

调用store

import { useUiSetStore } from '@/store/modules/uiSettings'
const ui = useUiSetStore()
ui.login = true

or

store 是一个用 reactive 包装的对象,直接解构读取state会失去响应式,因此需要storeToRefs,它将为每一个响应式属性创建引用;需通过 .value 读取解构出来的值

<template>
  <div>
    <div>姓名:{{ userInfo.name }} 年龄:{{ userInfo.age }}</div>
    <div>token:{{ token }}</div>
    <div>getter值:{{ newName }}</div>
    <button @click="handleUser">更新用户</button>
    <button @click="handleAge">更新年龄</button>
    <button @click="handleToken">更新token</button>
  </div>
</template>

<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { useUserStore } from '@/store/user'; //路径别名,引入store

const userStore = useUserStore();

//storeToRefs 会跳过所有的 action 属性
const { userInfo, token, newName } = storeToRefs(userStore);

//action 属性直接解构
const { updateUserInfo, updateAge, updateToken } = userStore;

const handleUser = () => {
  updateUserInfo({ name: 'lisi', age: 24 });
};

const handleAge = () => {
  //userInfo是一个ref响应式引用,需通过.value取值
  updateAge(userInfo.value.age + 1);
};

const handleToken = () => {
  updateToken('23234');
};
</script>

Store

Store是一个保存状态和业务逻辑的实体,可以自由读取和写入,并通过导入后在 setup 中使用

// pinia.ts
import { defineStore } from 'pinia';

// defineStore 调用后返回一个函数,调用该函数获得 Store 实体
export const useStore = defineStore({
  // id: 必须的,在所有 Store 中唯一
  id: 'App',
  // state: 返回对象的函数
  state: () => ({
    count: 1
  })
});

访问state

默认情况下,您可以通过 store 实例访问状态来直接读取和写入状态:

const store = useStore();
store.counter++;

相互引用

每个store可看做一个hook。相互引用即调用不同hook

import { defineStore } from 'pinia';
import { useUserStore } from './user';

enum Sex {
  man = '男人',
  woman = '女人',
}

export const userSexStore = defineStore('user2', {
  state: () => {
    return {
      sex: Sex.man,
    };
  },
  actions: {
    updateSex() {
      const userStore = useUserStore();  // 引用其他store
      if (userStore.userInfo.name !== 'zhangsan') this.sex = Sex.woman;
    },
  },
});

路由钩子中使用store

使用store的前提是保证pinia已被注册

import { useUserStore } from '@/store/user';

router.beforeEach((to, from) => {
  // 这样做是可行的,因为路由器是在其被安装之后开始导航的,
  // 而此时 Pinia 也已经被安装。
  const userStore = useUserStore();
  if (!userStore.token && to.path !== "/login") {
    return "/login";
  }
});

重置状态

您可以通过调用 store 上的 $reset() 方法将状态 重置 到其初始值:

const store = useStore();
store.$reset();

改变状态

除了直接用 store.counter++ 修改 store,你还可以调用 $patch 方法。 它允许您使用部分“state”对象同时应用多个更改:

1.第一种

store.$patch({
  counter: store.counter + 1,
  name: 'Abalam'
});

2.第二种

const handleClick = () => {
  store.count++;
};

3.第三种方式 $patch 传递函数

const handleClickMethod = () => {
  // 这里的state就是指代的仓库中的state
  store.$patch((state) => {
    state.count++;
    state.helloWord = state.helloWord === "jspang" ? "Hello World" : "jspang";
  });
};

4.第四种方式 action

当业务逻辑很复杂的时候,就将方法写在store中的action

actions: {
    changeState() {
      this.count++
      this.helloWord = 'jspang'
    }
  }

解构使用

解构后可以直接省略掉 store,减少了代码量

import { mainStore } from "../store/counter";
import { storeToRefs } from "pinia";
const store = mainStore();
// 进行解构
const { helloWord, count } = storeToRefs(store);

解构必须要用到storeToRefs()函数!

替换state

您可以通过将其 $state 属性设置为新对象来替换 Store 的整个状态:

store.$state = { counter: 666, name: 'Paimon' };

您还可以通过更改 pinia 实例的 state 来替换应用程序的整个状态。 这在 SSR for hydration 期间使用。

pinia.state.value = {}

Getters

Getters 直接在 Store 上读取,形似 Store.xx,就和一般的属性读取一样,相当于 vue 中的计算方法

// 修改 store.js
import { defineStore } from "pinia";
import { otherState } from "@/store/otherState.js";

export const useStore = defineStore({
  id: "myGlobalState",
  state: ()=> ({
    count: 2
  }),
  getters: {
    // 一个基本的 Getter: 计算 count 的平方
    // 使用参数
    countPow2(state) {
      return state.count ** 2;
    },

    // 获取其它 Getter, 直接通过 this
    countPow2Getter() {
      return this.countPow2;
    }

    // 使用其它 Store
    otherStoreCount(state) {
      // 这里是其他的 Store,调用获取 Store,就和在 setup 中一样
      const otherStore = useOtherStore();
      return otherStore.count;
    },
  }
});

// otherState.js
import { defineStore } from "pinia";
export const useStore = defineStore({
  id: "otherState",
  state: ()=> ({
    count: 5
  }),
});

actions

Pinia 没有 Mutations,统一在 actions 中操作 state,通过 this.xx 访问相应状态
虽然可以直接操作 Store,但还是推荐在 actions 中操作,保证状态不被意外改变
action 和普通的函数一样
action 同样可以像 Getter 一样访问其他的 Store,同上方式使用其它 Store,这里不在赘述,详细请移步 官方文档 Actions

action

// store.js
export const useStore({
  state: ()=> ({
    count: 2,
    // ...
  })
  // ...
  actinos: {
    countPlusOne() {
      this.count++;
    },
    countPlus(num) {
      this.count += num;
    }
  }
})

可以自由扩展 官方文档 Plugins

异步 actions

import { defineStore } from 'pinia';

const getData = () => {
  return new Promise<number>((resolve) => {
    setTimeout(() => {
      resolve(Math.random() * 100);
    }, 200);
  });
};

export const useListStore = defineStore('list', {
  state: () => {
    return {
      list: [] as number[],
    };
  },
  actions: {
    async updateList() {
      try {
        const data = await getData();
        this.list.push(data);
      } catch {
        /* empty */
      }
    },
  },
});

数据持久化

由于pinia里没有自带的持久化存储,所以我们需要使用要持久化存储的插件 pinia-plugin-persistedstate npm地址:pinia-plugin-persistedstate 文档入口:pinia-plugin-persistedstate

npm i pinia-plugin-persistedstate
#或者使用 npm
yarn add pinia-plugin-persistedstate

基础使用

  1. 将插件添加到 pinia 实例上
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const store = createPinia()
store.use(piniaPluginPersistedstate) // 使用持久化插件
export default store
  1. 创建 Store 时,将 persist 选项设置为 true
// user.ts
import { defineStore } from 'pinia'

// 你可以对 `defineStore()` 的返回值进行任意命名,但最好使用 store 的名字,同时以 `use` 开头且以 `Store` 结尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一个参数是你的应用中 Store 的唯一 ID。
export const useUserInfoStore = defineStore('userInfo', {
  // 其他配置...
  // state: 返回对象的函数
  state: () => {
    return {
      login: false
    }
  },
  persist: true // 开始数据持久化
})

配置

默认配置如下

修改默认配置

将一个对象传递给 Store 的 persist 属性来配置持久化。

import { defineStore } from 'pinia'

export const useStore = defineStore('main', {
  state: () => ({
    someState: '你好 pinia',
  }),
  persist: {
    // 在这里进行自定义配置
    key: 'my-custom-key', //将被持久化存储在 localStorage 中的 my-custom-key key 中
    storage: sessionStorage, //持久化存储在 sessionStorage中 ,默认localStorage
    paths: ['save.me', 'saveMeToo'], // 只有 save.me 和 saveMeToo 被持久化,而未设置的不会被持久化。
  },
})

问题点

  1. pinia 里存储的用户信息,刷新后数据恢复默认 , 使用持久化插件解决

  2. 在axios中使用 ,报错未挂载

    响应拦截器 中调用 uiSettings = useUiSetStore()

  3. 在 js 或vue hooks中单独使用报错

    错误

     "getActivePinia()" was called but there was no active Pinia. Did you forget to install pinia? 	const pinia = createPinia() 	app.use(pinia) This will fail in production..
    

    解决方式

    1. 为pinia单独新建一个文件store.js

    2. 然后在要使用pinia的js文件中导入

    import { createPinia } from 'pinia'
    import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
    const store = createPinia()
    store.use(piniaPluginPersistedstate)
    export default store
    

    使用

    import pinia from '@store/index'
    import { useUiSetStore } from '@/store/modules/uiSettings'
    const ui = useUiSetStore(pinia)
    
posted @ 2023-11-08 13:51  少年。  阅读(393)  评论(0编辑  收藏  举报