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