Chi tiết về Pinia trong Vue3

Pinia là thư viện quản lý trạng thái nhẹ do nhóm phát triển Vue tạo ra, nhằm cung cấp giải pháp quản lý trạng thái đơn giản và mô-đun cho ứng dụng Vue.

Store là một thực thể lưu trữ: ''trạng thái'' và ''logic kinh doanh'', mỗi thành phần có thể ''đọc'' và ''ghi'' vào nó. Nó bao gồm ba khái niệm: state, getter, action, tương đương với data, computedmethods trong thành phần.

Tạo Pinia

Tạo thư mục store trong src và các tệp .ts cụ thể bên trong thư mục store

import { defineStore } from 'pinia'

// Tạo một store tính tổng, tên biến có thể tùy ý
export const useCounterStore = defineStore('counter',{
    // Vị trí lưu trữ dữ liệu thực tế, state phải trả về dưới dạng hàm
    state(){
        return{
            total: 6
        }
    }
})

Truy xuất dữ liệu Pinia

<template>
    <div> {{counterStore.total}} </div>
    <button>Tăng</button>
    <button>Giảm</button>
</template>

<script setup lang="ts">
    
    import { useCounterStore } from '@/store/counter' // Nhập store đã tạo
    
    let counterStore = useCounterStore()

    // counterStore.total là một ref, bạn có thể nghĩ rằng dùng counterStore.total.value để lấy giá trị
    // Thực tế có một lỗi cần lưu ý, sẽ nói sau
    console.log(counterStore.total)// Lấy giá trị
    console.log(counterStore.$state.total)// Lấy giá trị
    
</script>
let total = reactive({
    x:1,
    y:3,
    z:ref(4)
})

total.z.value // undefined
total.z   // 4


let value = ref(4)

value.value  //4

Thay đổi dữ liệu Pinia

<template>
    <div> {{counterStore.total}} </div>
    <button @click="increment">Tăng</button>
    <button>Giảm</button>
</template>

<script setup lang="ts">
    import { ref } from 'vue'
    import { useCounterStore } from '@/store/counter' // Nhập store đã tạo
    
    let counterStore = useCounterStore()

    function increment(){
        // Cách 1: Thay đổi trực tiếp, dữ liệu trong store cũng được cập nhật đồng bộ
        // Khác với Vue2 cần action và mutation                                   
        counterStore.total +=1 

        // Cách 2: Phù hợp khi thay đổi nhiều dữ liệu, thay vì viết từng dòng
        //counterStore.total +=1;counterStore.xx +=1;counterStore.yy +=1;
    
        counterStore.$patch({
            total:2,
            xx:3,
            yy:4
        })

        // Cách 3: Sử dụng action, cần cấu hình trong store, xem cách cấu hình sau
        let n = ref(2)
        counterStore.updateValue(n.value)
        
    }
    
</script>

Cấu hình action cho Pinia

import { defineStore } from 'pinia'

export const useCounterStore = defineStore('counter',{
    // Actions chứa các phương thức xử lý sự kiện từ thành phần
    actions: {
        updateValue(input){ // input là dữ liệu truyền vào
            // this.total truy cập state, this là store hiện tại, tức là CounterStore
            this.total += input
        }
    },
    state(){
        return{
            total: 6
        }
    }
})

storeToRefs

Lấy dữ liệu từ store một cách tinh tế

<template>
    <!-- Truy xuất counterStore.total không thực sự tinh tế -->
   <!--<div> {{counterStore.total}} </div>-->
    <!--<div> {{counterStore.xx}} </div>-->

    <div> {{total}} </div>
    <div> {{xx}} </div>
</template>

<script setup lang="ts">
    import { ref } from 'vue'
    import { useCounterStore } from '@/store/counter' // Nhập store đã tạo

    import { storeToRefs } from 'pinia' // Nhập storeToRefs
    
    let counterStore = useCounterStore()

    let { total,xx } = storeToRefs(counterStore)

    // Nếu dùng toRefs sẽ lấy toàn bộ dữ liệu của counterStore, chi phí cao
    // Trong khi storeToRefs chỉ tập trung vào dữ liệu store, không bọc ref các phương thức

</script>

Sử dụng getters

Khi dữ liệu trong state cần xử lý trước khi sử dụng, có thể dùng getters để cấu hình

Cấu hình getters

import { defineStore } from 'pinia'

// Tạo một store tính tổng, tên có thể tùy ý
export const useCounterStore = defineStore('counter',{
    // Vị trí lưu trữ dữ liệu thực tế, state phải trả về dưới dạng hàm
    state(){
        return{
            total: 6
        }
    },
    getters:{
        scaledTotal(state){
            return state.total * 10
        },
        // scaledTotal:state => state.total * 10

        // scaledTotal():number{
            // return state.total * 10
        //}
    }
})

Sử dụng getters

<template>
    <div> {{total}} nhân 10 là {{ scaledTotal }}</div>
    <div> {{xx}} </div>
</template>

<script setup lang="ts">
    import { ref } from 'vue'
    import { useCounterStore } from '@/store/counter' // Nhập store đã tạo

    import { storeToRefs } from 'pinia' // Nhập storeToRefs
    
    let counterStore = useCounterStore()

    let { total,xx,scaledTotal } = storeToRefs(counterStore)

</script>

Sử dụng $subscribe

Dùng phương thức $subscribe() của store để theo dõi sự thay đổi trạng thái

<template>
    <div> {{counterStore.total}} </div>
    <button @click="increment">Tăng</button>
    <button>Giảm</button>
</template>

<script setup lang="ts">
    import { ref } from 'vue'
    import { useCounterStore } from '@/store/counter' // Nhập store đã tạo
    
    let counterStore = useCounterStore()

    counterStore.$subscribe((mutation,currentState)=>{
        // Tham số mutation và currentState giống như watch
    }) 

    function increment(){                                  
        counterStore.total +=1 
    }
    
</script>

Viết store theo phong cách composition

import { defineStore } from 'pinia'
import { ref } from 'vue'

export const useCounterStore = defineStore('counter',()=>{
    let total = ref(0)

    updateValue(input){ // input là dữ liệu truyền vào
       total.value += input
    }
    return { total,updateValue } 
})

Thẻ: Vue3 pinia state-management store Getter

Đăng vào ngày 7 tháng 7 lúc 04:50