Vue3 使用 pinia

什么是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}
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值