Pinia/Vuex ainsi que Redux sont conçus pour être une « source unique de vérité » dans laquelle vous pouvez disposer d'un ou plusieurs magasins pour contenir des données d'application pouvant être obtenues de n'importe où.
La boutique Pinia ressemble à ceci :
export let useProductsStore = defineStore('products', () => { let data = ref(products); function getList (params) { return someSearchStuffForProducts(params); } return {data, getList}; });
peut alors être utilisé comme :
let productsStore = useProductsStore(); console.log(data, data.value); productsStore.getList(params);
Nous pouvons créer plusieurs magasins :
let usersStore = useUsersStore(); let productsStore = useProductsStore(); let basketStore = useBasketStore(); let favoritesStore = useFavoritesStore();
Les magasins peuvent se référencer les uns les autres :
export let useUsersStore = defineStore('users', () => { let productsStore = useProductsStore(); } export let useBasketsStore = defineStore('basket', () => { let productsStore = useProductsStore(); } //Et cetera
Enfin, Pinia/Vuex sont des outils qui offrent la possibilité de récupérer et de manipuler des données stockées dans l'état.
Mais il existe une autre approche plus mature : les cours manager/service.
L'exemple précédent peut être réécrit comme :
//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);
Tout cela peut être facilement saisi dans TypeScript sans avoir besoin de wrappers supplémentaires, ref()
etc.
D'après ce que je peux dire, Pinia ressemble à « réinventer la roue » : écrire la même fonctionnalité de manière maladroite.
De plus, il ne fournit pas d'injection de dépendances : vous ne pouvez pas initialiser les magasins dans la configuration et injecter exactement un magasin dans un autre, vous devez passer useProductsStore()
et ainsi de suite.
L'héritage ou tout autre élément POO n'est pas non plus possible.
Pinia favorise même les dépendances circulaires, ce qui entraîne un code spaghetti avec une mauvaise maintenabilité
Alors, après tout, pourquoi préférer Pinia/Vuex à l'approche POO propre et éprouvée avec des classes de manager ? J'ai passé des dizaines d'heures à écrire un projet de didacticiel de ma propre invention en utilisant Pinia comme "prochaine gestion d'état Vue recommandée", et maintenant je suis tenté de tout réécrire dans des classes de gestionnaire parce que je trouve Pinia maladroit et riche. Je viens de me rappeler qu'il y a quelques années, j'écrivais un autre projet de test - en utilisant Vue2 - et à cette époque j'utilisais la classe manager - et tout s'est bien passé. Ai-je oublié quelque chose ? Y aura-t-il un problème si j'abandonne Pinia ?
Les classes sont des citoyens de seconde zone dans la réactivité de Vue, et il y a quelques pièges. Le fait qu'ils ne puissent pas être liés dans le constructeur
this
,这将导致使用非反应式类实例反应式代理。他们无法有效地使用引用,因为这些引用是在记录但异常的方式。他们无法使用 get/set 访问器来计算引用。这些问题需要显式使用 Vue 反应性 API 以奇怪的方式编写类,或者以受限制的方式设计类,因此reactive(new MyClass)
n'empêche pas celui-ci de fonctionner correctement.Les classes n'ont pas de fonctionnalités dont disposent les magasins, telles qu'une prise en charge étendue des outils de développement Vue, des systèmes de plug-ins, etc.
Les classes ne sont pas non plus sérialisables en JavaScript, donc la sauvegarde et la restauration de l'état nécessitent une logique personnalisée plutôt qu'une simple (dé)sérialisation JSON comme dans le plugin de persistance de stockage.
L'injection de dépendances n'est pas propre aux cours et peut être réalisée de manière adaptée, par exemple pour le magasin Pinia :
Dans de nombreux cas, il est préférable de gérer le stockage des composables par Pinia plutôt que de stocker les instances, car cela résout les dépendances circulaires qui peuvent devenir un problème si les composables sont appelés prématurément. Le même problème peut survenir avec les classes et nécessite l'utilisation d'un conteneur DI au lieu d'utiliser directement des instances de classe.
L'héritage ne pose aucun problème car le code réutilisable peut être géré avec FP au lieu de la POO. Vue n'en fait pas explicitement la promotion, mais rend le premier plus idiomatique et plus confortable à utiliser.
TL;DR : restez fidèle aux objets simples et au FP car c'est le cas principal de la conception réactive de Vue.