# 一、特点

  1. 完整的 ts 支持
  2. 三大核心:state(存储的值),getters(计算属性),actions 也可支持同步(改变值的方法,支持同步和异步)
  3. 与 vuex 相比,去除了 mutations(actions 也可支持同步)和 modules(只有 store 之间的互相引用)

# 二、安装

yarn add pinia

# 三、使用

# 1. 创建 pinia 实例

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

import { createPinia } from 'pinia';
const store = createPinia();
export default store;

# 2. 在 main.js 中引用

import { createApp } from 'vue';
import App from './App.vue';
import store from './store';
const app = createApp(App);
app.use(store);
app.mount('#app');

# 3. 创建 store

新建 store/user.ts

import { defineStore } from 'pinia';
//defineStore 第一个参数是 id,必需且值唯一
export const useUserStore = defineStore('user', {
  //state 返回一个函数,防止作用域污染
  state: () => {
    return {
      userInfo: {
        name: 'zhangsan',
        age: 23,
      },
      token: 'S1',
    };
  },
  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;
    },
  },
});

# 4. 使用 store

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

<template>
  <div>
    <div>姓名:<!--swig0--> 年龄:<!--swig1--></div>
    <div>token:<!--swig2--></div>
    <div>getter值:<!--swig3--></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>

# 5. 异步 actions

除了上面展示的同步 action,action 也可以是异步的

新建 store/list.ts

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 */
      }
    },
  },
});

# 6. store 的相互引用

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

新建 store/userSex.ts

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;
    },
  },
});

# 7. 路由钩子中使用 store

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

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

# 四、数据持久化

# 1. 安装插件

yarn add pinia-plugin-persistedstate

# 2. 引用插件

修改 store/index.ts

import { createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const store = createPinia();
store.use(piniaPluginPersistedstate); // 使用持久化插件
export default store;

# 3. 在 store 模块中启用持久化

在 user.ts 中启用:添加 persist 配置项

import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
  state: () => ({
    ...
  }),
  getters: { ... },
  actions: { ... },
    
  // 开始数据持久化
  persist: true,
})

当更新 state 值时,会默认存储到 localStorage 中

# 4. 修改 key 与存储位置

默认存储到 localStorage 的 key 值就是 store 模块 id 值。可以修改 key 值和存储位置

// 将 persist: true, 改为
persist: {
    key: 'storekey', // 修改存储的键名,默认为当前 Store 的 id
    storage: window.sessionStorage, // 存储位置修改为 sessionStorage
},

# 5. 自定义要持久化的字段

默认会将 store 中的所有字段都缓存,可以通过 paths 点符号路径数组指定要缓存的字段

persist: {
    paths: ['userInfo.name'], // 存储 userInfo 的 name
},