# pinia

# 概念

集中式状态(数据)管理

  • Vue2:vuex
  • Vue3:pinia
  • React:redux

# 测试效果

img

# 搭建 pinia 环境

npm install pinia

main.ts

import { createApp } from "vue";
import App from "./App.vue";
// 引入 createPinia,用于创建 pinia
import { createPinia } from "pinia";
// 创建 pinia
const pinia = createPinia();
const app = createApp(App);
// 使用插件
app.use(pinia);
app.mount("#app");

此时开发者工具中已经有了 pinia 选项

img

# 存储 + 读取数据

  • Store 是一个保存:状态业务逻辑 的实体,每个组件都可以读取写入它。
  • 它有三个概念: stategetteraction ,相当于组件中的: datacomputedmethods
示例

src/store/count.ts

// 引入 defineStore 用于创建 store
import { defineStore } from "pinia";
// 定义并暴露一个 store
export const useCountStore = defineStore("count", {
  // 动作
  actions: {},
  // 状态
  state() {
    return {
      sum: 6
    };
  },
  // 计算
  getters: {}
});

src/store/talk.ts

// 引入 defineStore 用于创建 store
import { defineStore } from "pinia";
// 定义并暴露一个 store
export const useTalkStore = defineStore("talk", {
  // 动作
  actions: {},
  // 状态
  state() {
    return {
      talkList: [
        { id: "1", content: "你今天有点怪,哪里怪?怪好看的!" },
        { id: "2", content: "草莓、蓝莓、蔓越莓,你想我了没?" },
        { id: "3", content: "心里给你留了一块地,我的死心塌地" }
      ]
    };
  },
  // 计算
  getters: {}
});

组件中使用

<template>
  <h2>当前求和为:</h2>
  <select v-model.number="n">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
  </select>
  <button @click="add"></button>
  <button @click="minus"></button>
</template>
<script setup lang="ts" name="Count">
  // 引入对应的 useXxxxxStore
  import { useSumStore } from "@/store/sum";
  // 调用 useXxxxxStore 得到对应的 store
  const sumStore = useSumStore();
</script>
<template>
  <ul>
    <li v-for="item in talkStore.talkList" :key="item.id">
      
    </li>
  </ul>
</template>
<script setup lang="ts" name="Count">
  import axios from "axios";
  import { useTalkStore } from "@/store/talk";
  const talkStore = useTalkStore();
</script>

# 修改数据(三种方式)

方式一:直接修改

countStore.sum = 666;

方式二:批量修改

countStore.$patch({
  sum: 999,
  school: "xxx大学"
});

方式三:借助 action 修改( action 中可以编写一些业务逻辑)

import { defineStore } from "pinia";
export const useCountStore = defineStore("count", {
  ...
  actions: {
    // 加
    increment(value: number) {
      if (this.sum < 10) {
        // 操作 countStore 中的 sum
        this.sum += value;
      }
    },
    // 减
    decrement(value: number) {
      if (this.sum > 1) {
        this.sum -= value;
      }
    }
  }
});

组件中调用

// 使用 countStore
const countStore = useCountStore();
// 调用对应 action
countStore.increment(n.value);

# storeToRefs

借助 storeToRefsstore 中的数据转为 ref 对象,方便在模板中使用

⚠️ 注:pinia 提供的 storeToRefs 只会将数据做转换,而 Vue 的 toRefs 会转换 store 中数据

<template>
  <h2>当前求和为:</h2>
</template>
<script setup lang="ts" name="Count">
  import { useCountStore } from "@/store/count";
  import { storeToRefs } from "pinia";
  const countStore = useCountStore();
  //let {sum, school, address} = toRefs (countStore); //toRefs 会将方法一起包裹
  // 使用 storeToRefs 转换 countStore,随后解构
  //storeToRefs 只会关注 store 中的数据,不会对方法进行 ref 包裹
  const { sum } = storeToRefs(countStore);
</script>

# getters

当 state 中的数据,需要经过处理后再使用时,可以使用 getters 配置

追加 getters 配置

// 引入 defineStore 用于创建 store
import { defineStore } from "pinia";
// 定义并暴露一个 store
export const useCountStore = defineStore("count", {
  // 动作
  actions: {},
  // 状态
  state() {
    return {
      sum: 1,
      school: "xxx大学"
    };
  },
  // 计算
  getters: {
    bigSum: (state): number => state.sum * 10,
    upperSchool(): string {
      return this.school.toUpperCase();
    }
  }
});

组件中读取数据

import { useCountStore } from "@/store/count";
import { storeToRefs } from "pinia";
const countStore = useCountStore();
const { increment, decrement } = countStore;
let { sum, school, bigSum, upperSchool } = storeToRefs(countStore);

# $subscribe

通过 store 的 $subscribe() 方法侦听 state 及其变化

import { useTalkStore } from "@/store/loveTalk";
import { storeToRefs } from "pinia";
const talkStore = useTalkStore();
const { talkList } = storeToRefs(talkStore);
talkStore.$subscribe((mutate, state) => {
  console.log("talkStore数据发生变化了");
  console.log("LoveTalk", mutate, state);
  localStorage.setItem("talk", JSON.stringify(talkList.value));
});

# 选项式写法

import { defineStore } from "pinia";
import axios from "axios";
import { nanoid } from "nanoid";
export const useTalkStore = defineStore("talk", {
  state() {
    return {
      talkList: JSON.parse(localStorage.getItem("talkList") as string) || []
      // [
      //   {id: "1", title: "近朱者赤,近你者甜"},
      //   {id: "2", title: "你知道我的缺点是什么吗?是缺点你"},
      //   {id: "3", title: "只许州官放火,不许你离开我"},
      //   {id: "4", title: "今天我只做两件事呼吸跟想你"}
      // ]
    };
  },
  actions: {
    async getTalk() {
      let {
        data: { content: title }
      } = await axios.get("https://api.uomg.com/api/rand.qinghua?format=json");
      let obj = {
        id: nanoid(),
        title
      };
      this.talkList.unshift(obj);
    }
  }
});

# 组合式写法

import { defineStore } from "pinia";
import axios from "axios";
import { nanoid } from "nanoid"; // 随机生成 id 插件
import { reactive } from "vue";
export const useTalkStore = defineStore("talk", () => {
  //talkList 就是 state
  const talkList = reactive(
    JSON.parse(localStorage.getItem("talkList") as string) || []
  );
  //getATalk 函数相当于 action
  async function getATalk() {
    // 发请求,下面这行的写法是:连续解构赋值 + 重命名
    let {
      data: { content: title }
    } = await axios.get("https://api.uomg.com/api/rand.qinghua?format=json");
    // 把请求回来的字符串,包装成一个对象
    let obj = {
      id: nanoid(),
      title
    };
    // 放到数组中
    talkList.unshift(obj);
  }
  return { talkList, getATalk };
});
更新于

请我喝奶茶⌯oᴗo⌯

呆鸭 微信支付

微信支付

呆鸭 支付宝

支付宝