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, computed và methods 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 }
})