vue3学习之Pinia状态管理

发布时间 2023-12-26 16:48:50作者: carol2014

状态管理

 src/views/Pinia.vue

<script setup>
import { ref } from "vue";
import { storeToRefs } from "pinia";
import { useCounterStore } from "../stores/counter";
import { useTodos } from "../stores/todos";

// 可以在组件中的任意位置访问 `store` 变量
const store = useCounterStore();

// const { count, doubleCount } = store; // `count` 和 `doubleCount` 没有响应式
let { count, doubleCount } = storeToRefs(store); // `count` 和 `doubleCount` 是响应式的 ref

// 作为 action 的 increment 可以直接解构
const { increment } = store;

store.count++;
setTimeout(() => {
  count.value++;
}, 2000);
setTimeout(() => {
  increment();
}, 4000);

const todoStore = useTodos();
const text = ref("");

function addTodo() {
  if (text != "") {
    todoStore.addTodo(text.value);
    text.value = "";
  }
}

function updateStatus(item) {
  todoStore.$patch((state) => {
    for (var i in state.todos) {
      if (item == state.todos[i]) {
        state.todos[i]["isFinished"] = !state.todos[i]["isFinished"];
      }
    }
  });
}

function delTodo(item) {
  let tmp = [];
  for (var i in todoStore.todos) {
    if (item != todoStore.todos[i]) {
      tmp.push(todoStore.todos[i]);
    }
  }
  todoStore.$patch({ todos: tmp });
}

todoStore.$subscribe((mutation, state) => {
  // 每当状态发生变化时,将整个 state 持久化到本地存储。
  localStorage.setItem("todos", JSON.stringify(state));
});
</script>
<template>
  <div>
    <div>
      <p>count:{{ count }}</p>
      <p>doubleCount:{{ doubleCount }}</p>
      <p>store.count:{{ store.count }}</p>
      <p>store.doubleCount:{{ store.doubleCount }}</p>
      <!-- 重置 state -->
      <button @click="store.$reset">重置</button>
    </div>

    <div>
      <p><input v-model="text" placeholder="edit me" @keyup.enter="addTodo" /></p>
      <ul>
        <li v-for="item in todoStore.filteredTodos" :key="item.id">
          {{ item.text }}
          <a v-if="item.isFinished">完成</a>
          <a v-else @click="updateStatus(item)">未完成</a>
          <a @click="delTodo(item)">删除</a>
        </li>
      </ul>
      <!-- 变更 state -->
      <button @click="todoStore.$patch({ filter: 'all' })">All</button>
      <button @click="todoStore.$patch({ filter: 'finished' })">Finished</button>
      <button @click="todoStore.$patch({ filter: 'unfinished' })">Unfinished</button>
      <!-- 重置 state -->
      <button @click="todoStore.$reset">重置</button>
    </div>
  </div>
</template>

<style scoped>
span,
a {
  margin-left: 1rem;
}

a {
  cursor: pointer;
}
</style>

 

src/stores/counter.js

import { defineStore } from "pinia";
import { ref, computed } from "vue";

export const useCounterStore = defineStore("counter", () => {
  const count = ref(0);
  function increment() {
    count.value++;
  }
  const doubleCount = computed(() => count.value * 2);

  //在 Setup Stores 中,需要创建自己的 $reset() 方法,Option Store不需要手段创建
  function $reset() {
    count.value = 0;
  }

  return { count, doubleCount, increment, $reset };
});

 

src/stores/todos.js

import { defineStore } from "pinia";

export const useTodos = defineStore("todos", {
  state: () => ({
    /** @type {{ text: string, id: number, isFinished: boolean }[]} */
    todos: [],
    /** @type {'all' | 'finished' | 'unfinished'} */
    filter: "all",
    // 类型将自动推断为 number
    nextId: 0,
  }),
  getters: {
    finishedTodos(state) {
      // 自动补全! ✨
      return state.todos.filter((todo) => todo.isFinished);
    },
    unfinishedTodos(state) {
      return state.todos.filter((todo) => !todo.isFinished);
    },
    /**
     * @returns {{ text: string, id: number, isFinished: boolean }[]}
     */
    filteredTodos() {
      if (this.filter === "finished") {
        // 调用其他带有自动补全的 getters ✨
        return this.finishedTodos;
      } else if (this.filter === "unfinished") {
        return this.unfinishedTodos;
      }
      return this.todos;
    },
  },
  actions: {
    // 接受任何数量的参数,返回一个 Promise 或不返回
    addTodo(text) {
      // 你可以直接变更该状态
      this.todos.push({ text, id: this.nextId++, isFinished: false });
    },
  },
});