一、初始化配置
1.创建项目
使用 NPM:
$ npm create vite@latest
使用 Yarn:
$ yarn create vite
使用 PNPM:
$ yarn create vite
2.安装 Pinia
使用你喜欢的包管理器安装pinia
yarn add pinia
# or with npm
npm install pinia
二、基本使用
1.创建 Pinia 实例并挂载
src/main.js
import { createApp } from 'vue';
import './style.css';
import App from './App.vue';
import { createPinia } from 'pinia';
// 创建 Pinia 实例
const pinia = createPinia();
// 挂载到Vue根实例
createApp(App).use(pinia).mount('#app');
如果使用的是Vue2,还需要安装一个插件,并将创建一个pinia注入到应用的root:
import { createPinia, PiniaVuePlugin } from 'pinia'
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
new Vue({
el: '#app',
// 其他选项...
// ...
// 注意同一个pinia实例可以在多个Vue应用中使用
pinia,
})
2.定义 Store
store 是使用defineStore() 定义的,第一个参数是整个应用中store的唯一名称(id) 建议: 可以为defineStore()的返回值任意命名,但是最好使用use加上store的名称和Store,例如:useUserStore、useCartStore、useProductStore
1.在src目录下,创建store目录,创建user.ts 在里面写入
import { defineStore } from 'pinia'
export const useStore = defineStore('main', {
// 具体代码...
})
3.Store中的选项
类似于Vue的选项API,也可以传递一个带有state、actions和getters属性的选项对象
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0, name: 'Eduardo' }),
getters: {
doubleCount: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
然而,state就类似于组件的 data ,用来存储全局状态的,getters就类似于组件的 computed,用来封装计算属性,有缓存功能,actions类似于组件的 methods,用来封装业务逻辑,修改 state。
4.基本使用
如果你要在组件中使用,就需要先将store引入进来,并在setup()中声明调用
import { useMainStore } from '../store';
export default ({
setup(){
const mainStore = useMainStore();
console.log(mainStore.count); // 这样就可以在组件中获取到Store中的count了
},
})
接下来就是在模板中使用了,想必不用说都能猜出来怎么写 那么,这样就会产生一个问题,每次都需要mainStore,这样就很麻烦。
import { useMainStore } from '../store';
export default ({
setup(){
const mainStore = useMainStore();
console.log(mainStore.count); // 这样就可以在组件中获取到Store中的count了
},
})
问题来了怎么解决?如果你对ES6了解的话可能会想到解构出来。但是这样取出来的数据是有问题的,它已经丢失了响应式,也就是一次性的。
// Pinia 其实就是把 state 数据都做了 reactive 处理了
const { count, foo } = mainStore;
就像上面这段代码,解构出来的数据就已经失去了响应式,如果之后对数据的修改Vue是无法监测到数据变化的。 解决办法:这里就需要使用Pinia为我们提供的storeToRefs()API这就类似Vue3中的toRefs()
import { storeToRefs } from 'pinia'
export default ({
setup(){
const mainStore = useMainStore();
const { count, foo } = storeToRefs(mainStore);
return {
count,
foo,
}
},
})
5.状态更新和Actions
Actions相当于组件中的方法。它们可以使用defineStore()中的actions属性来定义,并且它们非常适合定义业务逻辑 那么接下来怎么修改数据呢?这里有四种方法来修改。 例如:这里我们需要修改state中的count、foo、arr
import { defineStore } from 'pinia';
export const useMainStore = defineStore('main', {
state: () => ({
count: 100,
foo: 'bar',
arr: [1, 2, 3],
}),
...
}
<template>
<p>{{ count }}</p>
<p>{{ foo }}</p>
<p>{{ arr }}</p>
<hr />
<p>
<button @click="handleChangeState">修改数据</button>
</p>
</template>
<script setup>
...
const handleChangeState = () =>{
...
}
</script>
方法一:最简单的方式修改
mainStore.count++;
mainStore.foo = 'hello';
方法二:如果需要修改多个数据,建议使用 $patch批量更新
mainStore.$patch({
count: mainStore.count + 1,
foo: 'hello',
// 由于是以对象形式传递的,显然如果要给数组追加元素不是一个很好的选择
arr: [...mainStore.arr, 4],
});
方法三:更好的批量更新的方法:$patch也可以传递一个函数
mainStore.$patch((state) => {
// 这里接收的形参就是state
state.count++;
state.foo = 'hello';
state.arr.push(4);
});
方法四:逻辑比较多的时候可以封装到 actions 里面
mainStore.changeState(); // 在修改数据的方法中可以直接调用这个封装在actions里面的函数
import { defineStore } from 'pinia';
export const useMainStore = defineStore('main', {
...
actions: {
// 注意:不能使用箭头函数定义,因为使用箭头函数会导致 this 指向错误
changeState(num) {
this.count += num;
this.foo = 'hello';
this.arr.push(4);
// this.$patch({}) // 这里如果批量更新和方法二、三一样
// this.$patch((state) => {});
},
},
}
6.Getters使用
Getters完全等同于Store state的计算值。可以使用defineStore()中的getters属性来定义它们,并且它们将state作为第一个参数接收,以鼓励使用箭头函数。如果你使用的是普通函数的话,这个参数是可选的不接收也可以使用this,
export const useMainStore = defineStore('main', {
state: () => ({
count: 100,
}),
getters: {
// 函数接收一个可选的参数,是 state 对象
/* count10(state) {
console.log('count10 被调用了');
return state.count * 10;
}, */
// 🔴 如果是在ts中的话,this的类型是推导不出来的,所以需要手动指定
/* count10() {
console.log('count10 被调用了');
return this.count * 10;
}, */
count10: (state) => state.count * 10,
},
},
src/store/index.js
import { defineStore } from 'pinia';
// 1、定义容器
// 参数1:容器名称 ID ,必须唯一,将来 Pinia 会把所有的容器挂载到根容器
// 参数2:选项对象
// 返回值:一个函数,调用得到容器实例
export const useMainStore = defineStore('main', {
/**
* 类似于组件的 data,用来存储全局状态的
* 1、必须是函数:这样是为了在服务端渲染的时候避免交叉请求导致数据的状态污染
* 2、必须是箭头函数:这是为了更好的 TS 类型推导
*/
state: () => ({
count: 100,
foo: 'bar',
arr: [1, 2, 3],
}),
/**
* 类似于组件的 computed,用来封装计算属性,有缓存功能
*/
getters: {
// 函数接收一个可选的参数,是 state 对象
/* count10(state) {
console.log('count10 被调用了');
return state.count * 10;
}, */
// 🔴 如果是在ts中的话,this的类型是推导不出来的,所以需要手动指定
/* count10() {
console.log('count10 被调用了');
return this.count * 10;
}, */
count10: (state) => state.count * 10,
},
/**
* 类似于组件的 methods,用来封装业务逻辑,修改 state
*/
actions: {
// 🔴 注意:不能使用箭头函数定义,因为使用箭头函数会导致 this 指向错误
changeState(num) {
this.count += num;
this.foo = 'hello';
this.arr.push(4);
// this.$patch({})
// this.$patch((state) => {});
},
},
});
// 2、使用容器中的 state
// 3、修改 state
// 4、容器中的 action 的使用
src/components/HelloWord.vue
<template>
<p>{{ mainStore.count }}</p>
<p>{{ mainStore.foo }}</p>
<p>{{ mainStore.arr }}</p>
<p>{{ mainStore.count10 }}</p>
<p>{{ mainStore.count10 }}</p>
<p>{{ mainStore.count10 }}</p>
<hr />
<p>{{ count }}</p>
<p>{{ foo }}</p>
<hr />
<p>
<button @click="handleChangeState">修改数据</button>
</p>
</template>
<script setup>
import { storeToRefs } from 'pinia';
import { useMainStore } from '../store';
const mainStore = useMainStore();
console.log(mainStore.count);
// 这是有问题的,因为这样拿到的数据不是响应式的,是一次性的
// Pinia 其实就是把 state 数据都做了 reactive 处理了
// const { count, foo } = mainStore;
// 解决办法就是使用 storeToRefs
// 把解构出来的数据做 ref 响应式代理
const { count, foo } = storeToRefs(mainStore);
const handleChangeState = () => {
// 方法一:最简单的方式就是这样
// mainStore.count++;
// mainStore.foo = 'hello';
// 方法二:如果需要修改多个数据,建议使用 $patch 批量更新
/* mainStore.$patch({
count: mainStore.count + 1,
foo: 'hello',
arr: [...mainStore.arr, 4],
}); */
// 方法三 更好的批量更新的方法:$patch 也可以传入一个函数
/* mainStore.$patch((state) => {
state.count++;
state.foo = 'hello';
state.arr.push(4);
}); */
// 方法四:逻辑比较多的时候可以封装到 actions 里面
mainStore.changeState(10);
};
</script>