Pinia/Vuex 以及 Redux 被設計為“單一事實來源”,您可以擁有一個或多個儲存空間來保存可從任何地方獲取的應用程式資料。
Pinia 商店如下所示:
export let useProductsStore = defineStore('products', () => { let data = ref(products); function getList (params) { return someSearchStuffForProducts(params); } return {data, getList}; });
然後可以用作:
let productsStore = useProductsStore(); console.log(data, data.value); productsStore.getList(params);
我們可以創建多個商店:
let usersStore = useUsersStore(); let productsStore = useProductsStore(); let basketStore = useBasketStore(); let favoritesStore = useFavoritesStore();
商店可以互相引用:
export let useUsersStore = defineStore('users', () => { let productsStore = useProductsStore(); } export let useBasketsStore = defineStore('basket', () => { let productsStore = useProductsStore(); } //Et cetera
最後,Pinia/Vuex 是提供檢索和操作儲存在狀態中的資料的能力的工具。
但是還有另一種成熟的方法:管理員/服務類別。
前面的例子可以改寫為:
//Define the "single source of truth" let store = { products: { /* ... */}, currentUser: { /* ... */}, userBasket: { /* ... */}, userFavorites: { /* ... */}, }; //Here goes manager classes class ProductsManager { constructor (params) { this.state = params.state; //... } getList (params) { return someSearchStuffForProducts(params); } } class UsersManager { constructor (params) { this.state = params.state; //Products manager is injected as a dependency this.productsManager = params.productsManager; //... } } class BasketManager { constructor (params) { this.state = params.state; //Products manager is injected as a dependency this.productsManager = params.productsManager; //... } } //Some config/initialization script export let DIC = {}; //Container for manager instances DIC.productsManager = new ProductsManager({state: store.products}); DIC.usersManager = new usersManager({ state: store.currentUser, productsManager: DIC.productsManager, }); DIC.basketManager = new BasketManager({ state: store.userBasket, productsManager: DIC.productsManager, }); //Usage import {DIC} from './config'; DIC.productsManager.getList(); DIC.basketManager.add(someProductId); DIC.basketManager.changeCount(someProductId, 3);
所有這些都可以輕鬆地在 TypeScript 中輸入,無需額外的包裝器、ref()
等。
據我所知,Pinia 看起來就像「重新發明輪子」:以笨拙的方式編寫相同的功能。
此外,它不提供依賴項注入:您無法在配置中初始化存儲並將一個存儲準確地註入另一個存儲,您必須通過 useProductsStore()
等等。
繼承或任何其他 OOP 內容也是不可能的。
Pinia 甚至提倡循環依賴,導致義大利麵條式程式碼,可維護性很差
那麼,畢竟,為什麼人們應該更喜歡 Pinia/Vuex 而不是久經考驗的、帶有管理器類的乾淨的 OOP 方法呢?我已經花了幾十個小時編寫我自己發明的教程項目,使用Pinia 作為“下一個推薦的Vue 狀態管理”,現在我很想將所有內容重寫為管理器類,因為我發現Pinia 笨拙且豐富。我只是記得幾年前我正在編寫另一個測試專案 - 使用 Vue2 - 當時我使用了管理器類別 - 一切都很順利。我是否忽略了什麼?如果我放棄 Pinia 會有問題嗎?
類別在 Vue 反應性中是二等公民,並且存在一些陷阱。它們無法在建構函式中綁定
this
,這將導致使用非反應式類別實例反應式代理程式。他們無法有效地使用引用,因為這些引用是在記錄但異常的方式。他們無法使用 get/set 存取器來計算引用。這些問題需要明確使用 Vue 反應性 API 以奇怪的方式編寫類,或以受限制的方式設計類,因此reactive(new MyClass)
不會阻止它工作正確。類別不具備商店所具有的功能,例如對 Vue 開發工具、插件系統等的廣泛支援。
類別在 JavaScript 中也無法序列化,因此保存和復原狀態需要自訂邏輯,而不是像儲存持久性插件中那樣進行簡單的 JSON(反)序列化。
依賴注入並不是類別所獨有的,並且可以以合適的方式執行,例如對於 Pinia 商店:
在許多情況下,最好處理 Pinia 儲存可組合項而不是儲存實例,因為這可以解決循環相依關係,如果過早呼叫可組合項,循環相依性可能會成為問題。類別也可能出現同樣的問題,並且需要使用 DI 容器而不是直接使用類別實例。
繼承沒有問題,因為可重複使用程式碼可以用 FP 而不是 OOP 來處理。 Vue 沒有明確推廣它,但使前者更慣用且使用起來更舒適。
TL;DR:堅持使用普通物件和 FP,因為這是 Vue 反應性設計的主要情況。