Pinia官网地址
Pinia的优势
Pinia是一个全新的Vue状态管理库,是Vuex的代替者,尤雨溪强势推荐
- Vue2 和 Vue3 都能支持
- 抛弃传统的 Mutation ,只有 state, getter 和 action ,简化状态管理库
- 不需要嵌套模块,符合 Vue3 的 Composition api,让代码扁平化
- TypeScript支持
- 代码简洁,很好的代码自动分割
Pinia的基本使用
初始化项目
npm init vite@latest
安装
npm install pinia
yarn add pinia
挂载Pinia
// src/main.ts
import { createPinia } from 'pinia'
const pinia = createPinia()
createApp(App).use(pinia).mount('#app')
创建store
// src/store/index.ts
import { defineStore } from "pinia";
export const mainStore = defineStore("main", {
state: () => {
return {
msg: "hello world",
};
},
getters: {},
actions: {},
});
注:这是官网上提供的常规写法,我个人还是更偏向于下面这种写法:
// src/store/index.ts
import { defineStore } from "pinia";
interface State {
msg: string;
count: number;
}
const defaultState:State = {
msg: "hello world!"
}
export const mainStore = defineStore("main", {
state: () => {
return defaultState;
},
getters: {},
actions: {},
},
});
使用Store
// src/components/HelloWorld.vue
<script setup lang="ts">
import { mainStore } from '../store';
const store = mainStore()
</script>
<template>
<h1>{{store.msg}}</h1>
</template>
<style scoped>
</style>
解构store
当store中的多个参数需要被使用到的时候,为了更简洁的使用这些变量,我们通常采用结构的方式一次性获取所有的变量名
- ES传统方式解构(能获取到值,但是不具有响应性)
// src/components/HelloWorld.vue
<script setup lang="ts">
import { mainStore } from '../store';
const store = mainStore()
const {count} = store
</script>
<template>
<p>正常获取--{{store.count}}</p>
<p>ES传统方式解构,count不会响应式更新--{{count}}</p>
<h1>{{store.msg}}</h1>
</template>
<style scoped>
</style>
点击按钮,count的值+1,可以看到传统方式解构,count不会响应式更新。那要怎么解构呢?Pinia给我们提供了一个解构方法:
- Pinia解构方法:storeToRefs
// src/components/HelloWorld.vue
<script setup lang="ts">
import { mainStore } from '../store';
import {storeToRefs} from 'pinia'
const store = mainStore()
// const {count} = store
const {count} = storeToRefs(store)
</script>
<template>
<p>正常获取--{{store.count}}</p>
<!-- <p>ES传统方式解构,count不会响应式更新--{{count}}</p> -->
<p>Pinia中的storeToRefs方法解构,count可以响应式更新--{{count}}</p>
<h1>{{store.msg}}</h1>
</template>
<style scoped>
Pinia修改数据状态
- 简单数据修改
简单数据直接通过在方法中操作
store.属性名
来修改
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import { mainStore } from './store';
const store = mainStore()
const addOne = ():void => {
store.count++
}
</script>
<template>
<button @click="addOne">点击+1</button>
<HelloWorld/>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
- 多条数据修改
通过基础数据修改方式去修改多条数据也是可行的,但是在
pinia
官网中,已经明确表示$patch
的方式是经过优化的,会加快修改速度,对性能有很大好处,所以在进行多条数据修改的时候,更推荐使用$patch
$patch
方法可以接受两个类型的参数,函数 和 对象
- $patch + 对象
- $patch + 函数: 通过函数方式去使用的时候,函数接受一个 state 的参数,state 就是 store 仓库中的 state
<script setup lang="ts">
import { mainStore } from './store';
const store = mainStore()
// $path + 对象
const onObjClick = ():void => {
store.$patch({
count: store.count + 2,
msg: store.msg === 'hello world!' ? '你好 世界!' : 'hello world!'
})
}
// $patch + 函数
const onFunClick = ():void => {
store.$patch((state) => {
state.count += 2
state.msg = store.msg === 'hello world!' ? '你好 世界!' : 'hello world!'
})
}
</script>
<template>
<div>
<button @click="onObjClick">修改所有状态($patch + 对象)</button>
<button @click="onFunClick">修改所有状态($patch + 函数)</button>
</div>
</template>
- 通过action修改
- Store.actions中添加
changeState
方法
import { defineStore } from "pinia";
import { reactive } from "vue";
interface State {
msg: string;
count: number;
}
const defaultState:State = {
msg: "hello world!",
count: 0,
}
export const mainStore = defineStore("main", {
state: () => {
return defaultState;
},
getters: {},
actions: {
changeState() {
this.count++;
this.msg = this.msg === "hello world!" ? "你好 世界!" : "hello world!";
},
},
});
- 组件方法调用
store.方法名
<script setup lang="ts">
import { mainStore } from './store';
const store = mainStore()
// 通过action修改多个状态
const onActionClick = ():void => {
store.changeState()
}
</script>
<template>
<div>
<button @click="onActionClick">修改多个状态(action)</button>
</div>
</template>
Pinia中的Getters
Pinia 中的 getter 和 Vue 中的计算属性几乎一样,在获取 State值之前做一些逻辑处理。
getter中的值有缓存特性,如果值没有改变,多次使用也只会调用一次
- 添加 getter方法
import { defineStore } from "pinia";
import { reactive } from "vue";
interface State {
msg: string;
count: number;
}
const defaultState = reactive<State>({
msg: "hello world!",
count: 0,
})
export const mainStore = defineStore("main", {
state: () => {
return defaultState;
},
getters: {
getState(state) {
console.log('getter被调用');
return `${state.msg}---${state.msg}`
}
},
actions: {
changeState() {
this.count++;
this.msg = this.msg === "hello world!" ? "你好 世界!" : "hello world!";
},
},
});
- 组件内多次调用
<script setup lang="ts">
import { mainStore } from './store';
const store = mainStore()
</script>
<template>
<div>
<hr />
<h1>Getter 获取数据</h1>
<div>{{store.getState}}</div>
<div>{{store.getState}}</div>
<div>{{store.getState}}</div>
<div>{{store.getState}}</div>
<div>{{store.getState}}</div>
</div>
</template>
- getter 中不仅可以传递 state 直接改变数据状态,还可以使用 this 来改变数据
import { defineStore } from "pinia";
interface State {
msg: string;
count: number;
}
const defaultState: State = {
msg: "hello world!",
count: 0,
}
export const mainStore = defineStore("main", {
state: () => {
return defaultState;
},
getters: {
getState():string {
console.log('getter被调用');
return `${this.msg}---${this.msg}`
}
},
actions: {
changeState() {
this.count++;
this.msg = this.msg === "hello world!" ? "你好 世界!" : "hello world!";
},
},
});
store之间的相互调用
在
Pinia
中,可以在一个store
中import
另外一个store
,然后通过调用引入store
方法的形式,获取引入store
的状态
- 新建 store
// src/store/music.ts
import { defineStore } from "pinia";
interface State {
list: string[];
}
const defaultState: State = {
list: ['晴天', '枫', '以父之名', '手写的从前', '明明就', '半岛铁盒', '稻香', '最长的电影']
};
export const musicStore = defineStore("music", {
state: () => {
return defaultState;
},
getters: {},
actions: {},
});
- 在原 store 中引入 musicStore,并获取 list
// src/store/index.ts
import { defineStore } from "pinia";
import {musicStore} from './music'
interface State {
msg: string;
count: number;
}
const defaultState: State = {
msg: "hello world!",
count: 0,
}
export const mainStore = defineStore("main", {
state: () => {
return defaultState;
},
getters: {
getState():string {
console.log('getter被调用');
return `${this.msg}---${this.msg}`
},
// 另一个Store引用,获取musicStore中的list
getMusicStoreList(): string[] {
return musicStore().list
}
},
actions: {
changeState() {
this.count++;
this.msg = this.msg === "hello world!" ? "你好 世界!" : "hello world!";
},
},
});
- 组件中使用
<script setup lang="ts">
import HelloWorld from './components/HelloWorld.vue'
import { mainStore } from './store';
const store = mainStore()
</script>
<template>
<div>
<hr />
<h1>Store 互相调用</h1>
<ul>
<li v-for="item in store.getMusicStoreList" :key="item">{{item}}</li>
</ul>
</div>
</template>
总结
总得来说,Pinia 就是 Vuex 的替代版,可以更好的兼容 Vue2,Vue3以及TypeScript。
在Vuex的基础上去掉了 Mutation,只保留了 state, getter和action。
Pinia拥有更简洁的语法, 扁平化的代码编排,符合Vue3 的Composition api