什么是Pinia
Pinia是 Vue 的存储库,它允许您跨组件/页面共享状态,与vuex功能一样。
准备
安装
npm install pinia
或者
yarn add pinia
使用
首先修改main.ts文件
main.ts
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
const app = createApp(App)
const pinia = createPinia()
// 使用pinia
app.use(pinia)
// 挂载到 #app
app.mount('#app')
现在若有多个组件要使用count组件中的 count 值 和 LoveTalk 组件中的 talkList 值,可使用 Pinia 将这些值存储到一个文件里,使用时直接导入提取即可。
首先在 src 文件夹下创建 store 文件夹用于存放相关ts文件,然后创建useCountStore.ts 和 useTalkListStore.ts文件。
useCountStore.ts
import { defineStore } from "pinia";
export const useCountStore = defineStore("count", {
actions:{
increment(value: number){
if (this.count < 10){
this.count += value;
}
}
},
state(){
return {
count: 0,
school:'atguigu',
address:'宏福科技园'
}
},
});
useTalkListStore
import axios from "axios";
import { nanoid } from "nanoid";
import { defineStore } from "pinia";
export const useTalkListStore = defineStore("talkList", {
actions:{
async getATalk(){
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json');
let obj = {id: nanoid(), title}
this.talkList.unshift(obj);
}
},
state(){
return {
talkList: [
{id:'ftrfasdf01',title:'今天你有点怪,哪里怪?怪好看的!'},
{id:'ftrfasdf02',title:'草莓、蓝莓、蔓越莓,今天想我了没?'},
{id:'ftrfasdf03',title:'心里给你留了一块地,我的死心塌地'}
]
}
},
});
使用定义在store中的数据。
Count.vue
<script lang="ts" setup name="Count">
import { ref } from 'vue';
import { useCountStore } from '@/store/useCountStore';
import { storeToRefs } from 'pinia';
let countStore = useCountStore();
let n = ref(1);
let { count, address, school } = storeToRefs(countStore);
function add() {
//第一种修改数据方式
// countStore.count += n.value;
// countStore.address = '北京市海淀区';
// countStore.school = '清华大学';
//第二种修改数据方式
// countStore.$patch({
// count: countStore.count + n.value,
// address: '北京市海淀区',
// school: '清华大学'
// });
//第三种修改数据方式
countStore.increment(n.value);
}
function minus() {
}
</script>
<template>
<div class="count">
<!-- <h1>Count: {{ countStore.count }}</h1>
<h2>地址: {{ countStore.address }}, 名称: {{ countStore.school }}</h2> -->
<h1>Count: {{ count }}</h1>
<h2>地址: {{ address }}, 名称: {{ school }}</h2>
<button @click="add">加</button>
<button @click="minus">减</button>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
</template>
<style scoped>
.count {
background-color: skyblue;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
select,button {
margin: 0 5px;
height: 25px;
}
</style>
LoveTalk.vue
<script lang="ts" setup name="LoveTalk">
import axios from 'axios';
import { reactive } from 'vue';
import { nanoid } from 'nanoid';
import { useTalkListStore } from '@/store/useTalkListStore';
let talkListStore = useTalkListStore();
function addTalk() {
talkListStore.getATalk();
}
</script>
<template>
<div class="talk">
<h2>LoveTalk</h2>
<ul>
<li v-for="talk in talkListStore.talkList" :key="talk.id"> {{ talk.title }} </li>
</ul>
<button @click="addTalk">获取一条土味情话</button>
</div>
</template>
<style scoped>
.talk {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
上面两个组件实现了一个计数器和展示土味情话功能。Count组件中 select 标签中若不使用.number将字符串转化为数字,在点击 “加”按钮时会使用字符串拼接方式,例如选择2后,点击“加”后,数字就变成了字符串12。
实现效果
getters
useCountStore.ts
如果对 state 中的数据不满意,想要添加数据时,可以用getters方式,修改useCountStore.ts代码
import { defineStore } from "pinia";
export const useCountStore = defineStore("count", {
actions:{
increment(value: number){
if (this.count < 10){
this.count += value;
}
},
decrement(value: number){
if (this.count > 0){
this.count -= value;
}
}
},
state(){
return {
count: 0,
school:'atguigu',
address:'宏福科技园'
}
},
// 通过 getters 添加新数据
getters:{
// 需要接受 state 作为参数
bigSum(state){
return state.count * 2;
},
smallSum: state => state.count - 1,
upperSchool(state){
return state.school.toUpperCase();
}
}
});
Count.vue
修改 Count.vue
<script lang="ts" setup name="Count">
import { ref } from 'vue';
import { useCountStore } from '@/store/useCountStore';
import { storeToRefs } from 'pinia';
let countStore = useCountStore();
let n = ref(1);
// 通过 storeToRefs 得到 getters 添加到的数据
let { count, address, school, bigSum, smallSum, upperSchool } = storeToRefs(countStore);
function add() {
//第一种修改数据方式
// countStore.count += n.value;
// countStore.address = '北京市海淀区';
// countStore.school = '清华大学';
//第二种修改数据方式
// countStore.$patch({
// count: countStore.count + n.value,
// address: '北京市海淀区',
// school: '清华大学'
// });
//第三种修改数据方式
countStore.increment(n.value);
}
function minus() {
countStore.decrement(n.value);
}
</script>
<template>
<div class="count">
<!-- <h1>Count: {{ countStore.count }}</h1>
<h2>地址: {{ countStore.address }}, 名称: {{ countStore.school }}</h2> -->
<h1>Count: {{ count }}</h1>
<h2>地址: {{ address }}, 名称: {{ school }}</h2>
<h2>bigSum: {{ bigSum }}</h2>
<h2>smallSum: {{ smallSum }}</h2>
<h2>upperSchool: {{ upperSchool }}</h2>
<button @click="add">加</button>
<button @click="minus">减</button>
<select v-model.number="n">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
</template>
<style scoped>
.count {
background-color: skyblue;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
select,button {
margin: 0 5px;
height: 25px;
}
</style>
实现效果
$subscribe
Vue中可以用watch来监听数据的变化
同理,Pinia的store中的数据发生变化,可以用$subscribe来监听
这一篇,$subscribe+localStorage实现数据存储的案例,来介绍这个API的功能
LoveTalk.vue
<script lang="ts" setup name="LoveTalk">
import axios from 'axios';
import { reactive } from 'vue';
import { nanoid } from 'nanoid';
import { useTalkListStore } from '@/store/useTalkListStore';
let talkListStore = useTalkListStore();
//这个API的作用,类似于Vue中的watch,监听store中的数据变化
talkListStore.$subscribe((mutate, state) => {
localStorage.setItem('talkList', JSON.stringify(state.talkList));
})
function addTalk() {
talkListStore.getATalk();
}
</script>
<template>
<div class="talk">
<h2>LoveTalk</h2>
<ul>
<li v-for="talk in talkListStore.talkList" :key="talk.id"> {{ talk.title }} </li>
</ul>
<button @click="addTalk">获取一条土味情话</button>
</div>
</template>
<style scoped>
.talk {
background-color: orange;
padding: 10px;
border-radius: 10px;
box-shadow: 0 0 10px;
}
</style>
useTalkListStore.ts
import axios from "axios";
import { nanoid } from "nanoid";
import { defineStore } from "pinia";
export const useTalkListStore = defineStore("talkList", {
actions:{
async getATalk(){
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json');
let obj = {id: nanoid(), title}
this.talkList.unshift(obj);
}
},
state(){
return {
talkList: JSON.parse(localStorage.getItem('talkList') as string || '[]')
}
},
});
将useTalkListStore.ts修改为组合式写法
import axios from "axios";
import { nanoid } from "nanoid";
import { defineStore } from "pinia";
// export const useTalkListStore = defineStore("talkList", {
// actions:{
// async getATalk(){
// let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json');
// let obj = {id: nanoid(), title}
// this.talkList.unshift(obj);
// }
// },
// state(){
// return {
// talkList: JSON.parse(localStorage.getItem('talkList') as string || '[]')
// }
// },
// });
import {reactive} from 'vue'
export const useTalkStore = defineStore('talk',()=>{
// talkList就是state
const talkList = reactive(
JSON.parse(localStorage.getItem('talkList') as string) || []
)
// getATalk函数相当于action
async function getATalk(){
// 发请求,下面这行的写法是:连续解构赋值+重命名
let {data:{content:title}} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
// 把请求回来的字符串,包装成一个对象
let obj = {id:nanoid(),title}
// 放到数组中
talkList.unshift(obj)
}
return {talkList,getATalk}
})