Vue3 -- Pinia(Vue.js状态管理库)【谁学谁真香系列3】

状态管理:

集中式状态(数据)管理 例如 redux vuex pinia

搭建pinia环境:

1.终端下载pinia --> npm i pinia
2.在main.ts文件中引入、创建、安装pinia

import { createApp } from 'vue'
import App from './App.vue'

// 第一步:引入pinia
import { createPinia } from 'pinia'

const app = createApp(App)
//第二步:创建pinia
const pinia = createPinia()
//第三步:安装pinia
app.use(pinia)
app.mount('#app')

使用pinia:

存储数据:

src下创建store文件夹,我们创建两个ts文件,用来存储数据
count.ts文件存储求和的数据:

import {defineStore} from "pinia"

export const useCountStore = defineStore('count',{
    state(){
       return{
        sum:6
       } 
    }
})

loveTalk.ts 存储土味情话的数组

import { defineStore } from "pinia"

export const useTalkStore = defineStore('talk', {
    state() {
        return {
            talkList: [
                { id: 'cankm01', title: '今天你有点怪,哪里怪?怪好看的!' },
                { id: 'cankm02', title: '草莓、蓝莓、蔓越莓,你今天想我了没' },
                { id: 'cankm03', title: '心里给你留了一块地,我的死心塌地' },
            ]
        }
    }
})

以上就是我们需要在pinia里面存储的数据啦,将他们分别暴露出来,下面我们来说如何在vue文件里面读取数据

读取数据:

我们在Count.vue里面读取count.ts里面的sum数据:
第一步:引入useCountStore

import { useCountStore } from '@/store/count'

第二步:拿到我们需要的数据:

const conutStore = useCountStore()
//conutStore是一个reactive对象,里面有一个sum是ref对象
// 以下两种方式都可以拿到state中的数据
// console.log(conutStore.sum);
// console.log(conutStore.$state.sum);

问:为什么打印出来的conutStore.sum分明是ref包裹的响应式对象数据,为什么不要conutStore.sum.value来获取呢?

// 举例
let obj = reactive({
   a:1,
   b:2,
   c:ref(3)
})
console.log(obj.a);
console.log(obj.b);
//不需要写obj.c.value;因为外层包裹reactive已经默认解包了,直接用即可
console.log(obj.c);

let x =ref(9)
console.log(x.value);

完整代码:

<template>
    <div class="count">
        <h2>当前求和为:{{ conutStore.sum }}</h2>
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="add"></button>
        <button @click="minus"></button>
    </div>
</template>

<script lang="ts" setup name="Count">
import { ref, reactive } from 'vue';
import { useCountStore } from '@/store/count'

// conutStore是一个reactive对象,里面有一个sum是ref对象
// 所以我们需要的sum是conutStore.sum即可,不需要conutStore.sum.value【下面有举例⬇】
const conutStore = useCountStore()
// 以下两种方式都可以拿到state中的数据
// console.log(conutStore.sum);
// console.log(conutStore.$state.sum);


// 举例
/** let obj = reactive({
   a:1,
   b:2,
   c:ref(3)
})
console.log(obj.a);
console.log(obj.b);
//不需要写obj.c.value;因为外层包裹reactive已经默认解包了,直接用即可
console.log(obj.c);

let x =ref(9)
console.log(x.value);*/

// 数据
let n = ref(1) //用户选择的数据

// 方法
function add() {
    // sum.value += n.value
}
function minus() {
    // sum.value -= n.value
}
</script>

<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.ts里面的数据在LoveTalk.vue里面展示:

<template>
    <div class="talk">
        <button @click="getLoveTalk">获取一句土味情话</button>
        <ul>
            <li v-for="talk in talkStore.talkList" :key="talk.id">{{ talk.title }}</li>
        </ul>
    </div>
</template>

<script lang="ts" setup name="LoveTalk">
import { reactive } from 'vue';
import axios from "axios";
import { nanoid } from "nanoid";
import {useTalkStore} from '@/store/loveTalk'

const talkStore = useTalkStore()


// 方法
async function getLoveTalk() {
    // // 发请求 
    // // 扩展【也可以写 let {data:{content}} 进一步解构 直接使用content 】
    // // 【let {data:{content:titel}} 进一步解构处content之后将它重命名为title】【即连续解构赋值+重命名】
    // let {data} = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
    // //  把请求回来的字符串,包装成一个对象
    // let obj = { id: nanoid(), title: data.content }
    // // 放到数组中 在数组的开头添加
    // talkList.unshift(obj)
}
</script>

<style lang="less" scoped>
.talk {
    background-color: orange;
    padding: 10px;
    border-radius: 10px;
    box-shadow: 0 0 10px;
}
</style>

修改数据的三种方式:

第一种方式:直接用countStore拿到我们需要的sum数据去修改

// 方法
function add() {
    // 第一种修改方式 
    countStore.sum += 1
}

第二种方式:更适合用于批量修改数据,只用$patch批量修改一次

// 方法
function add() {
    // 第二种修改方式【批量修改数据,只用$patch批量修改一次】
     countStore.$patch({
         sum:888,
        school:'复旦大学',
         address:'上海'
     })
}

第三种修改方式:在count.ts里面修改数据,其中actions调用state里面的数据不能直接用state,而是用this,这里的this就是当前的store

import {defineStore} from "pinia"

export const useCountStore = defineStore('count',{
    // actions里面放置的是一个一个的方法,用于相应组件中的“动作”
    actions:{
        increment(value:number){
            // this是当前的store【CountStore】,而不能直接state调用
             this.sum += value  
        }
    },
    // 真正存储数据的地方
    state(){
       return{
        sum:6,
        school:'xx学校',
        address:"北京市"
       } 
    }
})

在vue的方法里调用它:【在处理比较复杂的逻辑的时候可以使用第三种】

// 方法
function add() {
    // 第三种修改方式
    countStore.increment(n.value)
}

对于我们的土味情话的例子,LoveTalk.vue和loveTalk.ts同理,这里使用了第三种方法

import { defineStore } from "pinia"
import axios from "axios";
import { nanoid } from "nanoid";

export const useTalkStore = defineStore('talk', {
    actions: {
        async getATalk() {
            // 发请求 
            // 扩展【也可以写 let {data:{content}} 进一步解构 直接使用content 】
            // 【let {data:{content:titel}} 进一步解构处content之后将它重命名为title】【即连续解构赋值+重命名】
            let { data } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
            //  把请求回来的字符串,包装成一个对象
            let obj = { id: nanoid(), title: data.content }
            // 放到数组中 在数组的开头添加
            this.talkList.unshift(obj)
        }
    },
    state() {
        return {
            talkList: [
                { id: 'cankm01', title: '今天你有点怪,哪里怪?怪好看的!' },
                { id: 'cankm02', title: '草莓、蓝莓、蔓越莓,你今天想我了没' },
                { id: 'cankm03', title: '心里给你留了一块地,我的死心塌地' },
            ]
        }
    }
})
<template>
    <div class="talk">
        <button @click="getLoveTalk">获取一句土味情话</button>
        <ul>
            <li v-for="talk in talkStore.talkList" :key="talk.id">{{ talk.title }}</li>
        </ul>
    </div>
</template>

<script lang="ts" setup name="LoveTalk">
import { useTalkStore } from '@/store/loveTalk'

const talkStore = useTalkStore()


// 方法 
function getLoveTalk() {
    talkStore.getATalk()
}
</script>

<style lang="less" scoped>
.talk {
    background-color: orange;
    padding: 10px;
    border-radius: 10px;
    box-shadow: 0 0 10px;
}
</style>

storeToRefs:

为什么不直接用toRefs包裹呢?
因为如果用toRefs包括将会把countStore所有的数据都用ref包裹, 而用storeToRefs指挥关注store的数据,不会对方法进行ref包裹

<template>
    <div class="count">
        <h2>当前求和为:{{ sum }}</h2>
        <h3>欢迎来到:{{ school }},坐落于:{{ address }}</h3>
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="add"></button>
        <button @click="minus"></button>
    </div>
</template>

<script lang="ts" setup name="Count">
import { ref } from 'vue';
import { storeToRefs } from "pinia";
// 引入useCountStore
import { useCountStore } from '@/store/count'
//使用useCountStore,得到一个专门保存count相关的store
const countStore = useCountStore()
// 为什么不直接用toRefs包裹呢?
// 因为如果用toRefs包括将会把countStore所有的数据都用ref包裹,
// 而用storeToRefs指挥关注store的数据,不会对方法进行ref包裹
const { sum, school, address } = storeToRefs(countStore)

// 数据
let n = ref(1) //用户选择的数据

// 方法
function add() {
    countStore.increment(n.value)
}
function minus() {
    countStore.sum -= n.value
}
</script>

<style scoped>
.count {
    background-color: skyblue;
    padding: 10px;
    border-radius: 10px;
    box-shadow: 0 0 10px;
}

select,
button {
    margin: 0 5px;
    height: 25px;
}
</style>

getters的使用:

this是当前的store!!!!在下面的例子里是【CountStore】!

import { defineStore } from "pinia"
interface CountState {
    sum: number;
    school: string;
    address: string;
}


export const useCountStore = defineStore('count', {
    // actions里面放置的是一个一个的方法,用于相应组件中的“动作”
    actions: {
        increment(value: number) {
            // this是当前的store【CountStore】,而不能直接state调用
            this.sum += value
        }
    },
    // 真正存储数据的地方
    state: (): CountState => ({
        sum: 1,
        school: 'abcd',
        address: "北京市"
    }),
    getters: {
        bigSum(state): number {
            return state.sum * 10
        },
        // 简写
        // bigSum: state => state.sum * 10,
        upperSchool(state): string {
            return state.school.toUpperCase()
        },
        // upperSchool():string {
        //     // 可以直接用this,如果用this不能用箭头函数
        //     return this.school.toUpperCase()
        // }
    }
})

如果在vue里面使用,则直接同state里面的数据一样解析出来;如下代码:

<template>
    <div class="count">
        <h2>当前求和为:{{ sum }},放大十倍后:{{ bigSum }}</h2>
        <h3>欢迎来到:{{ school }},坐落于:{{ address }},大写:{{ upperSchool }}</h3>
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="add"></button>
        <button @click="minus"></button>
    </div>
</template>

<script lang="ts" setup name="Count">
import { ref } from 'vue';
import { storeToRefs } from "pinia";
// 引入useCountStore
import { useCountStore } from '@/store/count'
//使用useCountStore,得到一个专门保存count相关的store
const countStore = useCountStore()
// 为什么不直接用toRefs包裹呢?
// 因为如果用toRefs包括将会把countStore所有的数据都用ref包裹,
// 而用storeToRefs指挥关注store的数据,不会对方法进行ref包裹
const { sum, school, address, bigSum, upperSchool } = storeToRefs(countStore)

// 数据
let n = ref(1) //用户选择的数据

// 方法
function add() {
    countStore.increment(n.value)
}
function minus() {
    countStore.sum -= n.value
}
</script>

<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的使用:

有store我们就可以使用$subscribe【有种watch的感觉】订阅状态及其变化,可以监测到store中数据的变化;两个参数–> mutate:本次修改信息 state:真正信息

talkStore.$subscribe((mutate,state)=>{
    //localStorage存储的话必须存储字符串形式
    localStorage.setItem('talkList',JSON.stringify(state.talkList))
})

LoveTalk.vue完整代码:

<template>
    <div class="talk">
        <button @click="getLoveTalk">获取一句土味情话</button>
        <ul>
            <li v-for="talk in talkList" :key="talk.id">{{ talk.title }}</li>
        </ul>
    </div>
</template>

<script lang="ts" setup name="LoveTalk">
import { useTalkStore } from '@/store/loveTalk'
import { storeToRefs } from "pinia";

const talkStore = useTalkStore()
const {talkList} = storeToRefs(talkStore)

// 订阅状态及其变化,可以监测到store中数据的变化
// 两个参数--> mutate:本次修改信息 state:真正信息
talkStore.$subscribe((mutate,state)=>{
    //localStorage存储的话必须存储字符串形式
    localStorage.setItem('talkList',JSON.stringify(state.talkList))
})


// 方法 
function getLoveTalk() {
    talkStore.getATalk()
}
</script>

<style lang="less" scoped>
.talk {
    background-color: orange;
    padding: 10px;
    border-radius: 10px;
    box-shadow: 0 0 10px;
}
</style>

我们在store里面loveTalk.ts:
在state里面我们也可以将其本地获取,刷新不会丢失:

talkList: JSON.parse(localStorage.getItem('talkList') as string) || []

loveTalk.ts完整代码:

import { defineStore } from "pinia"
import axios from "axios";
import { nanoid } from "nanoid";

export const useTalkStore = defineStore('talk', {
    actions: {
        async getATalk() {
            // 发请求 
            // 扩展【也可以写 let {data:{content}} 进一步解构 直接使用content 】
            // 【let {data:{content:titel}} 进一步解构处content之后将它重命名为title】【即连续解构赋值+重命名】
            let { data } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
            //  把请求回来的字符串,包装成一个对象
            let obj = { id: nanoid(), title: data.content }
            // 放到数组中 在数组的开头添加
            this.talkList.unshift(obj)
        }
    },
    state() {
        return {
            // 本地存储后刷新数据不会丢失
            talkList: JSON.parse(localStorage.getItem('talkList') as string) || []
        }
    }
})

store的组合式写法:

如果选择组合式写法一定要 return{ } 出去!!

import { defineStore } from "pinia"
import axios from "axios";
import { nanoid } from "nanoid";

// 选项式写法
/**export const useTalkStore = defineStore('talk', {
    actions: {
        async getATalk() {
            let { data } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
            let obj = { id: nanoid(), title: data.content }
            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 } = await axios.get('https://api.uomg.com/api/rand.qinghua?format=json')
        let obj = { id: nanoid(), title: data.content }
        talkList.unshift(obj)
    }
    return { talkList, getATalk }
})

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值