Skip to content

5. pinia 传送门

5.1【准备一个效果】

购物车效果

进货信息
商品名称:
商品单价:
数量:
商品总价: 19.80
显示/隐藏
vue
<!-- 1 定义组件模版 --> 
<template>
    <div class="goodsApp">
        进货信息 
        <fieldset >
            <legend>商品名称:</legend>
            <input type="text" v-model="name" />
        </fieldset>
        <fieldset >
            <legend>商品单价:</legend>
            <input type="text" v-model="price" />
        </fieldset>
        <fieldset >
            <legend>数量:</legend>
           <button type="button" style=";border-radius: 2px; background-color: rgb(144, 156, 166);padding: 5px;" @click="increament(1)">+</button><input type="text" v-model="count" count /><button type="button"  style="border-radius: 2px; background-color: rgb(144, 156, 166);padding: 5px;" @click="increament(-1)">-</button>
        </fieldset>
        <fieldset >
            <legend>商品总价:</legend>
            {{ amount }}
        </fieldset>
    </div>
</template> 

<!-- 2 定义组件逻辑 -->
<script setup lang="ts" name="PiniaDemo1">
import { computed, ref } from 'vue';
//定义数据
let name = ref('魔方法式软面包')
let price = ref(19.8)
let count = ref(1)

//计算属性
let amount = computed(()=>{
    return (price.value *  count.value).toFixed(2)
})


function increament(num){
    count.value += num
}

</script> 
 
<!-- 3 定义样式 -->
<style scoped> 
 .goodsApp{
    width: 500px;
 }
 

 input[count]{
    width: 40px;
    text-align: center;
 }
</style>

5.2【搭建 pinia 环境】

第一步:npm install pinia

第二步:操作src/main.ts

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

/*1  引入createPinia,用于创建pinia */
import { createPinia } from 'pinia'

/*2 创建pinia */
const pinia = createPinia()
const app = createApp(App)

/*3 使用插件 */{}
app.use(pinia)
app.mount('#app')

此时开发者工具中已经有了pinia选项

5.3【存储+读取数据】

  1. Store是一个保存:状态业务逻辑 的实体,每个组件都可以读取写入它。

  2. 它有三个概念:stategetteraction,相当于组件中的: datacomputedmethods

  3. 具体编码:src/store/cart.ts

    ts
    import { defineStore } from "pinia";
    
    
    export const useCart = defineStore('cart', {
    
        //数据状态
        state: () => {
            return {
                name: '鲁花压榨葵花仁油5L',
                price: 19.8,
                count: 1
            }
        }
    })
  4. 组件中使用state中的数据

    vue
    <!-- 1 定义组件模版 --> 
    <template>
        <div class="goodsApp">
            进货信息 
            <fieldset >
                <legend>商品名称:</legend>
                <input type="text" v-model="cartStore.name" />
            </fieldset>
            <fieldset >
                <legend>商品单价:</legend>
                <input type="text" v-model="cartStore.price" />
            </fieldset>
            <fieldset >
                <legend>数量:</legend>
               <button type="button" style=";border-radius: 2px; background-color: rgb(144, 156, 166);padding: 5px;" @click="increament(1)">+</button><input type="text" v-model="cartStore.count" count /><button type="button"  style="border-radius: 2px; background-color: rgb(144, 156, 166);padding: 5px;" @click="increament(-1)">-</button>
            </fieldset>
            <fieldset >
                <legend>商品总价:</legend>
                {{ amount }}
            </fieldset>
        </div>
    </template> 
    
    <!-- 2 定义组件逻辑 -->
    <script setup lang="ts" name="PiniaDemo1">
    import { computed, ref } from 'vue';
    import { useCartStore } from "@/store/Cart";
    let cartStore = useCartStore();
    
    console.log("cartStore ",cartStore)
    
    
    //计算属性
    let amount = computed(()=>{
        return (cartStore.price *  cartStore.count).toFixed(2)
    })
    
    
    function increament(num){
        cartStore.count += num
    }
    
    </script> 
     
    <!-- 3 定义样式 -->
    <style scoped> 
     .goodsApp{
        width: 500px;
     }
     
    
     input[count]{
        width: 40px;
        text-align: center;
     }
    </style>

5.4.【修改数据】(三种方式)

  1. 第一种修改方式,直接修改

    ts
    cartStore.count = 999
  2. 第二种修改方式:批量修改

    ts
    countStore.$patch({
      sum:999,
      school:'atguigu'
    })
  3. 第三种修改方式:借助action修改(action中可以编写一些业务逻辑){传送门}

    js
    import { defineStore } from "pinia";
    
    
    export interface ICartStore{
        name:string,
        price:number,
        count:number
    }
    
    export const useCartStore = defineStore('cart', {
        actions:{
            changeCart({name,price,count}:ICartStore):void{
                this.name = name;
                this.count = count
                this.price = price
            },
            plusCount(){
                this.count++
            },
    
            subCount(){
                if( this.count == 1){
                    return
                }
                this.count--
            }
        },
        //数据状态
        state: () => {
            return {
                name: '鲁花压榨葵花仁油5L',
                price: 19.8,
                count: 1
            }
        }
    })
  4. 组件中调用action即可

    vue
    <!-- 1 定义组件模版 --> 
    <template>
        <div class="goodsApp">
            进货信息
            <fieldset>
                <legend>商品名称:</legend>
                {{ cartStore.name }}
            </fieldset>
            <fieldset>
                <legend>商品单价:</legend>
                <input type="text" v-model="cartStore.price" />
            </fieldset>
            <fieldset>
                <legend>数量:</legend>
                <button type="button" class="increament" @click="increament(1)">+</button>
    
                <input type="text" v-model="cartStore.count" count />
    
                <button type="button" class="increament" @click="increament(-1)">-</button>
                <button type="button" class="increament" @click="patch()">patch补丁</button>
                <button type="button" class="increament" @click="useAction()">使用action</button>
            </fieldset>
            <fieldset>
                <legend>商品总价:</legend>
                {{ amount }}
            </fieldset>
        </div>
    </template> 
    
    <!-- 2 定义组件逻辑 -->
    <script setup lang="ts" name="PiniaDemo1">
    import { computed, ref } from 'vue';
    import { useCartStore } from "@/store/Cart";
    let cartStore = useCartStore();
    
    console.log("cartStore ", cartStore)
    
    
    //计算属性
    let amount = computed(() => {
        return (cartStore.price * cartStore.count).toFixed(2)
    })
    
    
    function increament(num) {
        cartStore.count += num
    }
    
    //批量更新
    function patch() {
    
        // cartStore.$patch(state => state.name = 'newName')
    
        cartStore.$patch((state) => {
            state.name = '[中华特色]高邮馆 高邮咸鸭蛋100克10只简装'
            state.price = 100
            state.count = 5
        })
    
        // cartStore.$patch({
        //     name: '[中华特色]高邮馆 高邮咸鸭蛋50克10只简装',
        //     price: 100,
        //     count: 5
        // })
    }
    
    
    function useAction() {
        // cartStore.changeCart({
        //     name: '[中华特色]高邮馆 高邮咸鸭蛋50克10只简装',
        //     price: 100,
        //     count: 5
        // })
    
        // cartStore.plusCount()
    
        //通用的逻辑 逻辑复用
        cartStore.subCount()
    }
    
    </script> 
     
    <!-- 3 定义样式 -->
    <style scoped> .goodsApp {
         width: 500px;
     }
    
    
     input[count] {
         width: 40px;
         text-align: center;
     }
    
     .increament {
         margin: 0 3px;
     }
    </style>

5.5.【storeToRefs】

  • 借助storeToRefsstore中的数据转为ref对象,方便在模板中使用。
  • 注意:pinia提供的storeToRefs只会将数据做转换,而VuetoRefs会转换store中数据。
vue
<!-- 1 定义组件模版 --> 
<template>
    <div class="goodsApp">
        进货信息
        <fieldset>
            <legend>商品名称:</legend>
            {{ name }}
        </fieldset>
        <fieldset>
            <legend>商品单价:</legend>
            <input type="text" v-model="price" />
        </fieldset>
        <fieldset>
            <legend>数量:</legend>
            <button type="button" class="increament" @click="increament(1)">+</button>

            <input type="text" v-model="count" count />

            <button type="button" class="increament" @click="increament(-1)">-</button>
            <button type="button" class="increament" @click="patch()">patch补丁</button>
            <button type="button" class="increament" @click="useAction()">使用action</button>
        </fieldset>
        <fieldset>
            <legend>商品总价:</legend>
            {{ amount }}
        </fieldset>
    </div>
</template> 

<!-- 2 定义组件逻辑 -->
<script setup lang="ts" name="PiniaDemo1">
import { computed, ref, toRefs } from 'vue';
import { useCartStore } from "@/store/Cart";
let cartStore = useCartStore();

//storeToRefs
console.log(toRefs(cartStore))
let {price ,count,name} = toRefs(cartStore);


console.log("cartStore ", cartStore)


//计算属性
let amount = computed(() => {
    return (price.value * count.value).toFixed(2)
})


function increament(num:number) {
    count.value += num
}

//批量更新
function patch() {

    // cartStore.$patch(state => state.name = 'newName')

    cartStore.$patch((state) => {
        state.name = '[中华特色]高邮馆 高邮咸鸭蛋100克10只简装'
        state.price = 100
        state.count = 5
    })

    // cartStore.$patch({
    //     name: '[中华特色]高邮馆 高邮咸鸭蛋50克10只简装',
    //     price: 100,
    //     count: 5
    // })
}


function useAction() {
    // cartStore.changeCart({
    //     name: '[中华特色]高邮馆 高邮咸鸭蛋50克10只简装',
    //     price: 100,
    //     count: 5
    // })

    // cartStore.plusCount()

    //通用的逻辑 逻辑复用
    cartStore.subCount()
}

</script> 
 
<!-- 3 定义样式 -->
<style scoped> .goodsApp {
     width: 500px;
 }


 input[count] {
     width: 40px;
     text-align: center;
 }

 .increament {
     margin: 0 3px;
 }
</style>

5.6.【getters】

  1. 概念:当state中的数据,需要经过处理后再使用时,可以使用getters配置。

  2. 追加getters配置。

    js
    import { defineStore } from "pinia";
    
    
    export interface ICartStore{
        name:string,
        price:number,
        count:number
    }
    
    export const useCartStore = defineStore('cart', {
        actions:{
            changeCart({name,price,count}:ICartStore):void{
                this.name = name;
                this.count = count
                this.price = price
            },
            plusCount(){
                this.count++
            },
    
            subCount(){
                if( this.count == 1){
                    return
                }
                this.count--
            }
        },
        //数据状态
        state: () => {
            return {
                name: '鲁花压榨葵花仁油5L',
                price: 19.8,
                count: 1
            }
        },
        getters:{
            // amount(state){
            //     return (state.price * state.count).toFixed(2)
            // }
    
            amount():number{
                let res = Number((this.price * this.count).toFixed(2));
                return res
            }
    
        }
    })
  3. 组件中读取数据:

    js
    import { useCartStore } from "@/store/Cart";
    let cartStore = useCartStore();
    
    let {price ,count,name,amount} = toRefs(cartStore);

5.7.【$subscribe】

类似于 Vuex 的 【subscribe方法】,通过 store 的 $subscribe() 方法侦听 state 及其变化

ts
 //数据状态
state: () => {
    const cartStoreState:ICartStore = JSON.parse(localStorage.getItem("cartStoreState") as string)
    let {name,price,count} = cartStoreState
    return {
        name: name,
        price: price,
        count: count
    }
},
ts
//订阅
cartStore.$subscribe((mutation, state)=>{
    localStorage.setItem("cartStoreState",JSON.stringify(state))
})

5.8. 【store组合式写法】

Setup Store 也存在另一种定义 store 的可用语法。与 Vue 组合式 API 的 setup 函数 相似,我们可以传入一个函数,该函数定义了一些响应式属性和方法,并且返回一个带有我们想暴露出去的属性和方法的对象。在 Setup Store 中:

  • ref() 就是 state 属性
  • computed() 就是 getters
  • function() 就是 actions

Setup store 比 Option Store 带来了更多的灵活性,因为你可以在一个 store 内创建侦听器,并自由地使用任何组合式函数

实现一个音乐列表的功能(普通vue组件即可,此处需要组件销毁后数据还在)

显示/隐藏

文件列表如下

ts
import { defineStore } from "pinia";
import { reactive } from 'vue';
import axios from "axios";


export interface IComment {
    name: string,
    url: string,
    picurl: string,
    artistsname: string,
    avatarurl: string,
    nickname: string,
    content: string
}

export const useMusicListStore = defineStore('music', () => {


    //https://api.uomg.com/
    //https://api.uomg.com/doc-comments.163.html
    //https://api.uomg.com/api/comments.163?format=json
    const tableData = reactive<IComment[]>([
        {
            "name": "满足",
            "url": "http://music.163.com/song/media/outer/url?id=1313558538.mp3",
            "picurl": "http://p2.music.126.net/rF6m9HfXqAZptLngigQXJg==/109951163668999283.jpg",
            "artistsname": "肖战",
            "avatarurl": "http://p1.music.126.net/g_JP6rzOkDYNCBXyPOVjZQ==/109951164407826586.jpg",
            "nickname": "三七不是二十一JMQ",
            "content": "记得有一次班上人谈论到你  坐第一排都听见的我很激动转过去说“嗯我知道我知道 那是我男朋友”  然后她们全都不说话看着我后面 我转过去  我们数学老师正认真的盯着我看  我们数学老师五十多啦还是个男老师 以为我真是谈恋爱啊~生日快乐 爱你 一直支持你"
        },
        {
            "name": "红梅赞",
            "url": "http://music.163.com/song/media/outer/url?id=1439975538.mp3",
            "picurl": "http://p2.music.126.net/7kRz_B18IaWapEQPh3B7oA==/109951164898369103.jpg",
            "artistsname": "肖战",
            "avatarurl": "http://p1.music.126.net/voNbpHJ69sN9nNyIAJbZvQ==/109951164904265410.jpg",
            "nickname": "绝甜空気-",
            "content": "遇见美丽中国!希望中国青年都能坚持文化自信,延续梅兰竹菊的气节。支持中国新青年肖战,一起加油[爱心]"
        }
    ])


    async function load() {
        let { data: { data } } = await axios.get('https://api.uomg.com/api/comments.163?format=json')
        tableData.unshift(data)
    }

    return {tableData,load}


})
vue
<template>
    <el-button type="primary" size="small" @click="load()">加载....</el-button>
    <el-table :data="tableData" style="width: 100%" >
        <el-table-column prop="name" label="乐曲名称" width="100" fixed/>
        <el-table-column prop="artistsname" label="歌手" width="100" fixed/>
        <el-table-column prop="nickname" label="评论昵称" width="190"   />
        <el-table-column prop="url" label="头像" width="90">
            <template v-slot="scoped" >
                   <img :src="scoped.row.avatarurl" width="80px"/> 
            </template>
        </el-table-column>
        <el-table-column prop="content" label="评论内容" />
    </el-table>

</template>
  
<script lang="ts" setup name="Music">
    import { useMusicListStore } from "@/store/musicList";
    import { storeToRefs } from "pinia";

    const  musicStore = useMusicListStore()
    const {tableData} = storeToRefs(musicStore)
    const load = musicStore.load
    
</script>
  
<style>

</style>

Released under the MIT License.