pinia学习
import { ref, computed } from 'vue'; import { defineStore } from 'pinia'; import { useRoute } from 'vue-router'; /** * 一、和vuex区别: * 1.不需要有mutations,在组件直接通过counter.count修改、通过$patch()修改或者在actions里修改,仍然可以被vue-devtools捕获到。 * 2.没有模块的概念,我们可以创建无穷多个store;而 vuex只能创建一个store其他都是模块。 * * 二、 * 1.选项式api使用简单;组合式api使用复杂还要返回store但是更灵活功能强大。 可以视情况一个store使用选项一个store使用组合。 * 2.我的感觉是,每个store都像一个组件一样,有自己的data、computed、methods; * 3.选项式api:可以直接调用$reset()方法;组合式需要自己创建; */ /** * ref() 会被转化为 state 属性 * computed() 会被转化为 getters * function关键字定义的函数 会被转化为 actions */ export const useCounterStore = defineStore('counter', () => { const count = ref(0); const doubleCount = computed(() => count.value * 2); function increment() { const route = useRoute(); console.log('%c [ route1 ]-18', 'font-size:13px; background:pink; color:#bf2c9f;', route); count.value++; } return { count, doubleCount, increment }; }); export const useFlagerStore = defineStore('flager', { // state() { // return { // flag: 0, // }; // }, // 为了完整类型推理,推荐使用箭头函数 state: () => ({ flag: 0, items: [{ name: 'clothes', quantity: 1 }], }), // getters: { // doubleFlag(): number { // return this.flag * 2; // }, // }, //1.使用箭头函数可以自动进行类型推理,如果是上面的字面量增强的function,那么必须要返回一个数据类型。 //2.箭头函数默认会传参state,如果想用this去访问$store实例,那就必须用完整function了。 getters: { doubleFlag: state => state.flag * 2, }, actions: { increment() { const route = useRoute(); console.log('%c [ route2222 ]-18', 'font-size:13px; background:pink; color:#bf2c9f;', route); this.flag++; }, async testFunc() { return Promise.resolve(); // return Promise.reject(); }, }, });
<template> <header> <div>--------state里的基本数据类型--------</div> <div>{{ counter.count }}</div> <div>{{ flager.flag }}</div> <div>--------state里的引用数据类型--------</div> <div>{{ flager.items }}</div> <div>---------getters-------</div> <div>{{ flager.doubleFlag }}</div> <div><button @click="handleClick">修改</button></div> <div><button @click="handleReset">重置</button></div> <div><button @click="handleAction">测试侦听action</button></div> <div class="wrapper"> <HelloWorld msg="You did it!" /> <nav> <RouterLink to="/">Home</RouterLink> <RouterLink to="/about">About</RouterLink> </nav> </div> </header> <RouterView /> </template> <script setup lang="ts"> import { ref, watch } from 'vue'; import { RouterLink, RouterView } from 'vue-router'; import HelloWorld from './components/HelloWorld.vue'; import { useCounterStore, useFlagerStore } from './stores/index'; const counter = useCounterStore(); const flager = useFlagerStore(); function handleClick() { // counter.count++; counter.increment(); // counter.$patch({ count: counter.count + 10 }); // 修改仓库里state的4种方式; flager.flag++; flager.increment(); flager.$patch({ flag: flager.flag + 10 }); flager.$patch(state => { state.flag++; state.items.push({ name: 'shoes', quantity: 100 }); }); } function handleReset() { flager.$reset(); } //侦听的两种方式: //方式一:用pinia的侦听器 flager.$subscribe((mutation, state) => { mutation.type; // 有三种类型,'direct' | 'patch object' | 'patch function';分别对应直接修改、$patch传递一个对象、$patch传递一个函数; mutation.storeId; // 和 flagerStore.$id 一样,表示仓库的唯一id mutation.payload; //只有 mutation.type === 'patch object'的情况下, mutation.payload才可用,并且值是传递给 flagerStore.$patch() 的补丁对象。 console.log('变更后的state', state); }); //方式二:用vuex3的侦听器 watch( () => flager.flag, async (newValue, oldValue) => { console.log('%c [ oldValue ]-63', 'font-size:13px; background:pink; color:#bf2c9f;', oldValue); console.log('%c [ newValue ]-63', 'font-size:13px; background:pink; color:#bf2c9f;', newValue); }, ); watch(flager.$state, async (newValue, oldValue) => { console.log('%c [ oldValue ]-63', 'font-size:13px; background:pink; color:#bf2c9f;', oldValue); console.log('%c [ newValue ]-63', 'font-size:13px; background:pink; color:#bf2c9f;', newValue); }); function handleAction() { flager.testFunc(); } // action的侦听器 const unsubscribe = flager.$onAction( ({ name, // action 名称 store, // store 实例,类似 `someStore` args, // 传递给 action 的参数数组 after, // 在 action 返回或解决后的钩子 onError, // action 抛出或拒绝的钩子 }) => { // 为这个特定的 action 调用提供一个共享变量 const startTime = Date.now(); // 这将在执行 "store "的 action 之前触发。 console.log(`Start "${name}" with params [${args.join(', ')}].`); console.log('%c [ store ]-82', 'font-size:13px; background:pink; color:#bf2c9f;', store, store === flager); // 这将在 action 成功并完全运行后触发。 // 它等待着任何返回的 promise after(result => { console.log(`Finished "${name}" after ${Date.now() - startTime}ms.\nResult: ${result}.`); }); // 如果 action 抛出或返回一个拒绝的 promise,这将触发 onError(error => { console.warn(`Failed "${name}" after ${Date.now() - startTime}ms.\nError: ${error}.`); }); }, ); // 手动删除监听器 // unsubscribe(); </script> <style scoped> header { line-height: 1.5; max-height: 100vh; } .logo { display: block; margin: 0 auto 2rem; } nav { width: 100%; font-size: 12px; text-align: center; margin-top: 2rem; } nav a.router-link-exact-active { color: var(--color-text); } nav a.router-link-exact-active:hover { background-color: transparent; } nav a { display: inline-block; padding: 0 1rem; border-left: 1px solid var(--color-border); } nav a:first-of-type { border: 0; } @media (min-width: 1024px) { header { display: flex; place-items: center; padding-right: calc(var(--section-gap) / 2); } .logo { margin: 0 2rem 0 0; } header .wrapper { display: flex; place-items: flex-start; flex-wrap: wrap; } nav { text-align: left; margin-left: -1rem; font-size: 1rem; padding: 1rem 0; margin-top: 1rem; } } </style> ./stores