誰偷了我的鳳梨!聊聊意外修改 Pinia 資料問題

鱈魚-avatar-img
發佈於Vue
更新 發佈閱讀 9 分鐘
鱈魚的魚缸搬家了!新家文章皆有重新修訂,歡迎來新家看看喔。(´▽`ʃ♡ƪ)
vocus|新世代的創作平台


相信大家用 Vue 3 後應該都改用 Pinia 了吧?沒用過的人趕快試試看吧。(´,,•ω•,,)


Pinia 最簡單的用法就像這樣:

counter.ts

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

export const useStore = defineStore('counter', () => {
const n = ref(0)

return { n }
})


接著在要使用的地方呼叫 useStore:

App.vue

<script setup lang="ts">
import { useStore } from './counter.ts'

const store = useStore()

function increment() {
store.n++;
}
</script>

<template>
<button @click="increment">
Increment {{ store.n }}
</button>
</template>

其實像這樣直接操作 store.n 的方式,方便歸方便,苓膏龜苓膏


但不是個好做法,這種方式容易讓資料流混亂,想像一下你有多個元件都使用 store.n,然後想改就改。…( ・ิω・ิ)


比較推薦的方式通常為由 store 提供一個修改資料的 function,例如:

counter.ts

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

interface User {
name: string;
price: number;
}

export const useStore = defineStore('counter', () => {
const user = ref<User>({
name: 'cod',
price: 100,
})

function updateUser(data: Partial<User>) {
user.value = {
...user.value,
...data,
}
}

return {
user,
updateUser,
}
})


呼叫的部份改為:

App.vue

<script setup lang="ts">
import { useStore } from './counter.ts'

const store = useStore()

function increment() {
const price = store.user.price + 1;
store.updateUser({ price });
}
</script>

<template>
<button @click="increment">
price: {{ store.user.price }}
</button>
</template>

這樣如果未來要新增邏輯、權限甚至重構,都容易得多。


但是問題來了,有時候流程中會有「確認」的按鈕,也就是要按下確認後,才修改 store 的資料。


假設有一個負責修改 User 資料的元件:

UserCard.vue

<script setup lang="ts">
import { ref } from 'vue'
import { useStore } from './counter.ts'

const store = useStore();
const user = ref(store.user);

function increment() {
user.value.price++;
}
function cancel() {
user.value = store.user;
}
function submit() {
store.updateUser(user.value);
}
</script>

<template>
<div>
<button class="button" @click="increment">
遞增
</button>
<button class="button" @click="submit">
確認
</button>

<div>
current price: {{ user.price }}
</div>
</div>
</template>

<style>
.button {
margin: 0px 4px;
}
</style>


App.vue

<script setup lang="ts">
import UserCard from './UserCard.vue';

import { useStore } from './counter.ts'

const store = useStore();
</script>

<template>
<div>
store price: {{ store.user.price }}
</div>

<hr />

<UserCard />
</template>

目前畫面如下圖。

vocus|新世代的創作平台


這時候你會發現出事啦!╭(°A ,°`)╮


按下遞增的時候,不只元件內的 user 數值發生變化,連 store 的數值也一起變啦!Σ(ˊДˋ;)


熟悉 JS 的朋友們一定都知道發生甚麼事,這是因為直接指派物件是 Call by Reference,所以:

const user = ref(store.user);


這個部分的程式會讓 user 依舊指向 store 的 user,結果就意外改到鳳梨裡面的資料了。(›´ω`‹ )


這時候聰明的讀者們一定也想到解法,在 ref 的時候拷貝一次不就行了?


UserCard.vue

<script setup lang="ts">
...
const user = ref(clone(store.user));

function clone<Data>(data: Data): Data {
return JSON.parse(JSON.stringify(data));
}

...
</script>
...

這時候會發現世界恢復和平了,資料一切正常!◝( •ω• )◟


鱈魚:「但是阿。(´● ω ●`)」

路人:「怎麼那麼多但是?…(›´ω`‹ )」


實際上協作開發的時候難保大家都會注意到這件事情,所以保險起見,可以在 Pinia 提供資料時先拷貝一次。


counter.ts

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

interface User {
name: string;
price: number;
}

function clone<Data>(data: Data): Data {
return JSON.parse(JSON.stringify(data));
}

export const useStore = defineStore('counter', () => {
const user = ref<User>({
name: 'cod',
price: 100,
})

function updateUser(data: Partial<User>) {
user.value = {
...user.value,
...data,
}
}

return {
user: computed(() => clone(user.value)),
updateUser,
}
})


這樣即使元件中使用

const user = ref(store.user);

也不會意外修改 Pinia 中的資料了!✧*。٩(ˊᗜˋ*)و✧*。


當然如果使用 immutable.js 這類保證資料不變性的套件也是沒問題。


就看大家喜歡哪一種了。♪( ◜ω◝و(و


以上程式可以來這裡取得:範例程式

留言
avatar-img
鱈魚的魚缸
17會員
14內容數
各種鱈魚滾鍵盤的雜紀
鱈魚的魚缸的其他內容
2024/07/25
先前提到 Quasar 的 Dialog Plugin 很好用,再讓我補充一個用法。
Thumbnail
2024/07/25
先前提到 Quasar 的 Dialog Plugin 很好用,再讓我補充一個用法。
Thumbnail
2024/07/15
Quasar Dialog 的 Invoking custom component 很好用,但是有些困擾的地方,一起來看看有甚麼辦法吧。
Thumbnail
2024/07/15
Quasar Dialog 的 Invoking custom component 很好用,但是有些困擾的地方,一起來看看有甚麼辦法吧。
Thumbnail
2024/07/10
切換頁面卡卡有很多種原因,這裡舉的例子只針對元件太大的情境。 除了想辦法拆分外,還有一個方法就是利用 Vue 的 Async Component。
Thumbnail
2024/07/10
切換頁面卡卡有很多種原因,這裡舉的例子只針對元件太大的情境。 除了想辦法拆分外,還有一個方法就是利用 Vue 的 Async Component。
Thumbnail
看更多
你可能也想看
Thumbnail
背景:從冷門配角到市場主線,算力與電力被重新定價   小P從2008進入股市,每一個時期的投資亮點都不同,記得2009蘋果手機剛上市,當時蘋果只要在媒體上提到哪一間供應鏈,隔天股價就有驚人的表現,當時光學鏡頭非常熱門,因為手機第一次搭上鏡頭可以拍照,也造就傳統相機廠的殞落,如今手機已經全面普及,題
Thumbnail
背景:從冷門配角到市場主線,算力與電力被重新定價   小P從2008進入股市,每一個時期的投資亮點都不同,記得2009蘋果手機剛上市,當時蘋果只要在媒體上提到哪一間供應鏈,隔天股價就有驚人的表現,當時光學鏡頭非常熱門,因為手機第一次搭上鏡頭可以拍照,也造就傳統相機廠的殞落,如今手機已經全面普及,題
Thumbnail
切換頁面卡卡有很多種原因,這裡舉的例子只針對元件太大的情境。 除了想辦法拆分外,還有一個方法就是利用 Vue 的 Async Component。
Thumbnail
切換頁面卡卡有很多種原因,這裡舉的例子只針對元件太大的情境。 除了想辦法拆分外,還有一個方法就是利用 Vue 的 Async Component。
Thumbnail
本文分析導演巴里・柯斯基(Barrie Kosky)如何運用極簡的舞臺配置,將布萊希特(Bertolt Brecht)的「疏離效果」轉化為視覺奇觀與黑色幽默,探討《三便士歌劇》在當代劇場中的新詮釋,並藉由舞臺、燈光、服裝、音樂等多方面,分析該作如何在保留批判核心的同時,觸及觀眾的觀看位置與人性幽微。
Thumbnail
本文分析導演巴里・柯斯基(Barrie Kosky)如何運用極簡的舞臺配置,將布萊希特(Bertolt Brecht)的「疏離效果」轉化為視覺奇觀與黑色幽默,探討《三便士歌劇》在當代劇場中的新詮釋,並藉由舞臺、燈光、服裝、音樂等多方面,分析該作如何在保留批判核心的同時,觸及觀眾的觀看位置與人性幽微。
Thumbnail
Vue3 學習筆記,專案建立與基礎響應式篇
Thumbnail
Vue3 學習筆記,專案建立與基礎響應式篇
Thumbnail
Vue3 學習筆記,vue-router 篇。
Thumbnail
Vue3 學習筆記,vue-router 篇。
Thumbnail
這是一場修復文化與重建精神的儀式,觀眾不需要完全看懂《遊林驚夢:巧遇Hagay》,但你能感受心與土地團聚的渴望,也不急著在此處釐清或定義什麼,但你的在場感受,就是一條線索,關於如何找著自己的路徑、自己的聲音。
Thumbnail
這是一場修復文化與重建精神的儀式,觀眾不需要完全看懂《遊林驚夢:巧遇Hagay》,但你能感受心與土地團聚的渴望,也不急著在此處釐清或定義什麼,但你的在場感受,就是一條線索,關於如何找著自己的路徑、自己的聲音。
Thumbnail
Vue3 學習筆記,元件與資料傳遞篇
Thumbnail
Vue3 學習筆記,元件與資料傳遞篇
Thumbnail
5 月將於臺北表演藝術中心映演的「2026 北藝嚴選」《海妲・蓋柏樂》,由臺灣劇團「晃晃跨幅町」製作,本文將以從舞台符號、聲音與表演調度切入,討論海妲・蓋柏樂在父權社會結構下的困境,並結合榮格心理學與馮.法蘭茲對「阿尼姆斯」與「永恆少年」原型的分析,理解女人何以走向精神性的操控、毀滅與死亡。
Thumbnail
5 月將於臺北表演藝術中心映演的「2026 北藝嚴選」《海妲・蓋柏樂》,由臺灣劇團「晃晃跨幅町」製作,本文將以從舞台符號、聲音與表演調度切入,討論海妲・蓋柏樂在父權社會結構下的困境,並結合榮格心理學與馮.法蘭茲對「阿尼姆斯」與「永恆少年」原型的分析,理解女人何以走向精神性的操控、毀滅與死亡。
Thumbnail
Vue3 學習筆記,用 Pinia 管理資料與邏輯
Thumbnail
Vue3 學習筆記,用 Pinia 管理資料與邏輯
Thumbnail
前言 Vue 是一個現代開發框架,擁有完尚的生態系,讓我們可以將須多元件客製化,做出組件,並且可重複利用,高擴充性。在開發組件時,每個組件都擁有自己的生命周期,Vue 組件會偵測每個變數值,是否有變,並且更新內容,今天要一個一個了解 Vue 的生命週期,讓大家有更多認識。 Vue 的生命週期
Thumbnail
前言 Vue 是一個現代開發框架,擁有完尚的生態系,讓我們可以將須多元件客製化,做出組件,並且可重複利用,高擴充性。在開發組件時,每個組件都擁有自己的生命周期,Vue 組件會偵測每個變數值,是否有變,並且更新內容,今天要一個一個了解 Vue 的生命週期,讓大家有更多認識。 Vue 的生命週期
Thumbnail
幾天不用,剛剛發現之前的程式碼已經不能使用了,我想可能是因為html結構有所改變,之前的程式碼可以看下面這一篇文章 【程式碼教學】追蹤自己 Vocus文章每日流量,第二版 1027 更新後的程式碼 import csv from bs4 import BeautifulSoup
Thumbnail
幾天不用,剛剛發現之前的程式碼已經不能使用了,我想可能是因為html結構有所改變,之前的程式碼可以看下面這一篇文章 【程式碼教學】追蹤自己 Vocus文章每日流量,第二版 1027 更新後的程式碼 import csv from bs4 import BeautifulSoup
Thumbnail
Vue3 筆記,指令進階篇
Thumbnail
Vue3 筆記,指令進階篇
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News