Skip to content

Pinia使用

创建项目

bash
npm create vue@latest

只选择pinia即可,删除无用文件

src/main.js

javascript
import { createApp } from "vue";
import { createPinia } from "pinia";

import App from "./App.vue";

const app = createApp(App);

app.use(createPinia());

app.mount("#app");

src/App.vue

vue
<script setup>
import OptComp from './views/OptComp.vue';
import SetupComp from './views/SetupComp.vue';
import OptCompWithSetup from './views/OptCompWithSetup.vue';
</script>

<template>
  <SetupComp />
  <div>=======组件用options api,通过映射辅助函数使用Store</div>
  <OptComp />
  <div>=======组件用options api,使用Setup辅助</div>
  <OptCompWithSetup />
</template>

<style scoped>

</style>

src/store

src/store/counter.js

javascript
import { ref, computed } from "vue";
import { defineStore } from "pinia";
import { useUserStore } from "./user";

// Setup函数的写法(实现高级用法)
// export const useCounterStore = defineStore("counter", () => {
//   const count = ref(0); // ref() 就是 state 属性
//   const doubleCount = computed(() => count.value * 2); // computed() 就是 getters
//   function increment() { // function() 就是 actions
//     count.value++;
//   }

//   return { count, doubleCount, increment };
// });

// 对象的写法
export const useCounterStore = defineStore("counter", {
  // 定义 state  // 为了完整类型推理,推荐使用箭头函数
  state: () => {
    return {
      count: 0,
      num: 1,
      items: [{ name: "lisi" }],
      users: [{ name: "lisi", id: 1 }],
    };
  },
  getters: {
    double: (store) => store.count * 2,
    // 通过this访问getters
    doubleCountPlusOne() {
      return this.double + 1;
    },
    // 像getter传递参数
    getUserById: (state) => {
      return (userId) => state.users.find((user) => user.id === userId);
    },
    otherGetter(state) {
      const userStore = useUserStore();
      return state.count + userStore.user.name;
    },
  },
  actions: {
    increment() {
      this.count++;
    },
    async someRequest() {
      try {
        this.items = await new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve([{ name: "张三" }, { name: "李四" }]);
          }, 1000);
        });
      } catch (error) {
        console.log(error);
      }
    },
    async getUsers() {
      const userStore = useUserStore();
      if (userStore.user) {
        await this.someRequest();
      } else {
        throw new Error("未登录");
      }
    },
  },
});

src/store/user.js

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

// 对象的写法
export const useUserStore = defineStore("user", {
  state: () => {
    return { user: { name: "wtm", age: 18 } };
  },
});

在setup组件中使用

src\views\SetupComp.vue

vue
<template>
  <div>属性 直接通过store访问 {{ counter.count }}</div>
  <div>属性 解构后访问 {{ count }}</div>
  <div>计算属性 直接通过store访问{{ counter.double }} | {{ counter.doubleCountPlusOne }} | {{ counter.getUserById(1) }} | {{ counter.otherGetter }}</div>
  <div>计算属性 解构后{{ double }} | {{ doubleCountPlusOne }} | {{ getUserById(1) }} | {{ otherGetter }} </div>
  <ul>
    <li v-for="item in items">{{ item.name }}</li>
  </ul>
  <button @click="counter.increment">actions 同步操作 直接通过store调用</button>
  <button @click="increment">actions 同步操作 调用解构后的访问</button>
  <button @click="counter.$reset">重置 直接通过store调用</button>
  <button @click="$reset">重置 解构后调用 </button>
  <button @click="someRequest">actions 异步请求</button>
  <button @click="getUsers">actions 异步请求 结合其他store数据</button>
</template>
<script setup>
import {useCounterStore} from '@/stores/counter'
import { storeToRefs } from 'pinia';
const counter = useCounterStore()
const {count, double, getUserById,  doubleCountPlusOne, otherGetter, items} = storeToRefs(counter) // 解构使用 storeToRefs
const {someRequest, getUsers, increment, $reset } = counter // 方法直接解构

// 改变store中state的值
counter.count++ // 1.直接改
counter.$patch({count: counter.count + 1}) // 2.通过$patch
counter.increment() // 3.通过action

</script>
<style scoped>
button {
  display: block;
}
li {
  display: inline-block;
}
</style>

在options api组件中使用setup辅助

src\views\OptCompWithSetup.vue

vue
<template>
  <div>属性 直接通过store访问 {{ counter.count }}</div>
  <div>属性 解构后访问 {{ count }}</div>
  <div>计算属性 直接通过store访问{{ counter.double }} | {{ counter.doubleCountPlusOne }} | {{ counter.getUserById(1) }} | {{ counter.otherGetter }}</div>
  <div>计算属性 解构后{{ double }} | {{ doubleCountPlusOne }} | {{ getUserById(1) }} | {{ otherGetter }} </div>
  <ul>
    <li v-for="item in items">{{ item.name }}</li>
  </ul>
  <button @click="counter.increment">actions 同步操作 直接通过store调用</button>
  <button @click="increment">actions 同步操作 调用解构后的访问</button>
  <button @click="counter.$reset">重置 直接通过store调用</button>
  <button @click="someRequest">actions 异步请求</button>
  <button @click="getUsers">actions 异步请求 结合其他store数据</button>
</template>
<script>
import { defineComponent } from 'vue';
import { storeToRefs} from 'pinia'
import {useCounterStore} from '@/stores/counter'

export default defineComponent({
  setup(){
    const counter = useCounterStore()
    const {count, double, getUserById,  doubleCountPlusOne, otherGetter, items} = storeToRefs(counter) // 解构使用 storeToRefs
const {someRequest, getUsers, increment } = counter // 方法直接解构
    return {counter, count, double, getUserById,  doubleCountPlusOne, otherGetter, items, someRequest, getUsers, increment }
  },
  computed: {

  },
  methods: {

  },
  
})
</script>

<style scoped>
button {
  display: block;
}
li {
  display: inline-block;
}
</style>

在options api组件中使用

src\views\OptComp.vue

vue
<template>
  <div>属性 通过mapStores映射后 直接通过store访问 {{ counterStore.count }}</div>
  <div>属性 通过mapState映射后 访问 {{ count }}</div>
  <div>属性 通过mapState映射后 重命名 {{ myCount }}</div>
  <div>计算属性 通过mapStores映射后  直接通过store访问 {{ counterStore.double }}</div>
  <div>计算属性 通过mapState映射后  访问 {{ double}}</div>  
  <div>计算属性 通过mapState映射后  重命名 {{ myDouble}}</div>  
  <div>计算属性 通过mapState映射后  使用函数获取store中的getters {{ double2 }}</div>  
  <div>计算属性 通过mapState映射后  计算属性同时访问store和this {{ magicValue }}</div>
  <ul>
    <li v-for="item in items">{{ item.name }}</li>
  </ul>
  <button @click="">actions 通过mapStores映射后 直接通过store访问</button>
</template>
<script>
import { defineComponent } from 'vue';
import {mapStores, mapState, mapActions, mapWritableState} from 'pinia'
import {useCounterStore} from '@/stores/counter'
import {useUserStore} from '@/stores/user'


export default defineComponent({
  computed: {
    // 允许访问 this.counterStore 和 this.userStore
    ...mapStores(useCounterStore, useUserStore),
    // 允许读取 this.count 和 this.double
    ...mapState(useCounterStore, ['count', 'double', 'items']),
    // mapState接收对象
    ...mapState(useCounterStore, {
      myCount: 'count', // 重命名
      myDouble: 'double',
      double2: store => store.double * 2, // 函数的方式,通过store访问
      magicValue (store) { // 访问store和this
        return store.count + this.count + this.double
      }
    }),
    // this.num++ 与从 store.num 中读取的数据相同
    ...mapWritableState(useCounterStore, ['num']),
    // 重命名
    ...mapWritableState(useCounterStore, {
      myNum: 'num'
    })
  },
  methods: {
    // 允许读取 this.increment()
    ...mapActions(useCounterStore, ['increment']),
    ...mapActions(useCounterStore, {myIncrement: 'increment'}),
    reset () {
      // 在选项式api中,重置store中的state
      const counter = useCounterStore()
      counter.$reset()
    },
    addNum () {
      const counter = useCounterStore()
      // 更新数据
      // 1.方式1
      // this.num++
      // 2.通过$patch

      // counter.$patch({
      //   num: counter.num + 2,
      //   count: counter.count + 1,
      // })

      //3.$patch函数的方式
      // counter.$patch((state) => {
      //   state.items.push({name: '王五'})
      //   state.count++
      // })
      // 4.替换state 在它内部调用 `$patch()`:
      counter.$state = {count: 1, num: 2, items: [{name: '赵六'}]}
    }
  }
})
</script>

<style scoped>
button {
  display: block;
}
li {
  display: inline-block;
}
</style>