Comme beaucoup de développeurs, j'écris mon propre projet
Négation
Exigences de base pour un magasin:
- Les types Typescript devraient fonctionner dans les modules
- Les modules doivent être faciles à utiliser dans les composants, les types d'états, les actions, les mutations et les getters doivent fonctionner
- Ne proposez pas de nouvelle API pour vuex, vous devez vous assurer que les types dactylographiés fonctionnent d'une manière ou d'une autre avec les modules vuex afin que vous n'ayez pas à réécrire l'ensemble de l'application à la fois
- L'appel des mutations et des actions doit être aussi simple et direct que possible
- Le paquet doit être aussi petit que possible
- Je ne veux pas stocker de constantes avec des noms de mutations et d'actions
- Cela devrait fonctionner (et qu'en est-il sans lui)
Il est impossible qu'un projet aussi mature que vuex n'ait pas pris en charge le typage normal. Eh bien, nous avons ouvert
vuex-smart-module
github.com/ktsn/vuex-smart-module
Soundly, très bien. Tout avec moi, mais personnellement, je n'aimais pas le fait que pour les actions, les mutations, les états, les getters, vous deviez créer des classes séparées. Ceci, bien sûr, est du goût, mais c'est moi et mon projet) Et en général, le problème de la frappe n'a pas été entièrement résolu ( fil de commentaire avec une explication du pourquoi ).
Support de Vuex Typescript
vuex-module-decorators
Cela semblait être le moyen idéal pour se faire des amis vuex et dactylographiés. Cela ressemble au vue-property-decorator que j'utilise en développement, vous pouvez travailler avec le module comme avec une classe, en général, super, mais ...
Mais il n'y a pas d'héritage. Les classes de module ne sont pas correctement héritées et le problème est suspendu à ce problème depuis très longtemps! Et sans héritage, il y aura beaucoup de duplication de code. Crêpe…
Colère
Ensuite, ce n'était pas du tout beaucoup, enfin, ou ± la même chose - il n'y a pas de solution idéale. C'est le moment même où vous vous dites: pourquoi ai-je commencé à écrire un projet en vue? Eh bien, vous savez réagir, eh bien, j'écrirais sur react, il n'y aurait pas de tels problèmes! Au travail principal, le projet est en vue et vous devez le mettre à niveau - appuyez sur l'argument. Vaut-il les nerfs passés et les nuits blanches? Asseyez-vous comme tout le monde, écrivez komponentiki, non, vous en avez surtout besoin! Jetez cette vue! Écrivez pour réagir, améliorez-le et payez plus!
À ce moment-là, j'étais prêt à détester la vue pas comme les autres, mais c'était de l'émotion, et l'intelligence était toujours au-dessus de ça. Vue a (à mon avis subjectif) de nombreux avantages sur réagir, mais il n'y a pas de perfection, ainsi que des gagnants sur le champ de bataille. La vue et la réaction sont bonnes à leur manière, et comme une partie importante du projet est déjà écrite en vue, il serait aussi stupide que possible de basculer pour réagir maintenant. J'ai dû décider quoi faire avec vuex.
Bonne affaire
Eh bien, les choses ne vont pas bien. Peut-être alors vuex-smart-module? Ce package semble être bon, oui, vous devez créer beaucoup de classes, mais cela fonctionne très bien. Ou peut-être pourrait-il essayer d'écrire des types pour les mutations et les actions à la main dans les composants et utiliser pure vuex? Là, vue3 avec vuex4 est en route, peut-être qu'ils font mieux avec dactylographié. Alors essayons pure vuex. En général, cela n'affecte pas le travail du projet, cela fonctionne toujours, il n'y a pas de types, mais vous tenez bon. Et tiens bon)
Au début, j'ai commencé à faire ça, mais le code s'avère monstrueux ...
La dépression
Je devais passer à autre chose. Mais où est inconnu. C'était une étape complètement désespérée. J'ai décidé de créer un conteneur d'état à partir de zéro . Le code a été rédigé en quelques heures. Et ça s'est même bien passé. Les types fonctionnent, l'état est réactif et même l'héritage est là. Mais bientôt l'agonie du désespoir a commencé à reculer et le bon sens a commencé à revenir. Dans l'ensemble, cette idée est allée à la poubelle. Dans l'ensemble, c'était le modèle de bus d'événements global. Et ce n'est bon que pour les petites applications. Et en général, écrire votre propre vuex est encore assez exagéré (du moins dans ma situation). Puis j'ai déjà deviné que j'étais complètement épuisé. Mais il était trop tard pour se retirer.
Mais si quelqu'un est intéressé, alors le code est ici: (Probablement en vain ajouté ce fragment, mais le chemin sera)
ne pas avoir l'air nerveux
const getModule = <T>(name:string, module:T) => {
const $$state = {}
const computed: Record<string, () => any> = {}
Object.keys(module).forEach(key => {
const descriptor = Object.getOwnPropertyDescriptor(
module,
key,
);
if (!descriptor) {
return
}
if (descriptor.get) {
const get = descriptor.get
computed[key] = () => {
return get.call(module)
}
} else if (typeof descriptor.value === 'function') {
// @ts-ignore
module[key] = module[key].bind(module)
} else {
// @ts-ignore
$$state[key] = module[key]
}
})
const _vm = new Vue({
data: {
$$state,
},
computed
})
Object.keys(computed).forEach((computedName) => {
var propDescription = Object.getOwnPropertyDescriptor(_vm, computedName);
if (!propDescription) {
throw new Error()
}
propDescription.enumerable = true
Object.defineProperty(module, computedName, {
get() { return _vm[computedName as keyof typeof _vm]},
// @ts-ignore
set(val) { _vm[computedName] = val}
})
})
Object.keys($$state).forEach(name => {
var propDescription = Object.getOwnPropertyDescriptor($$state,name);
if (!propDescription) {
throw new Error()
}
Object.defineProperty(module, name, propDescription)
})
return module
}
function createModule<
S extends {[key:string]: any},
M,
P extends Chain<M, S>
>(state:S, name:string, payload:P) {
Object.getOwnPropertyNames(payload).forEach(function(prop) {
const descriptor = Object.getOwnPropertyDescriptor(payload, prop)
if (!descriptor) {
throw new Error()
}
Object.defineProperty(
state,
prop,
descriptor,
);
});
const module = state as S & P
return {
module,
getModule() {
return getModule(name, module)
},
extends<E>(payload:Chain<E, typeof module>) {
return createModule(module, name, payload)
}
}
}
export default function SimpleStore<T>(name:string, payload:T) {
return createModule({}, name, payload)
}
type NonUndefined<A> = A extends undefined ? never : A;
type Chain<T extends {[key:string]: any}, THIS extends {[key:string]: any}> = {
[K in keyof T]: (
NonUndefined<T[K]> extends Function
? (this:THIS & T, ...p:Parameters<T[K]>) => ReturnType<T[K]>
: T[K]
)
}
Adoption La naissance du vélo qui a dépassé tout le monde. vuexok
Pour les impatients, le code est ici , la courte documentation est ici .
En fin de compte, j'ai écrit une minuscule bibliothèque qui couvre toute la liste de souhaits et même un peu plus que ce qui était requis. Mais tout d'abord.
Le module vuexok le plus simple ressemble à ceci:
import { createModule } from 'vuexok'
import store from '@/store'
export const counterModule = createModule(store, 'counterModule', {
state: {
count: 0,
},
actions: {
async increment() {
counterModule.mutations.plus(1)
},
},
mutations: {
plus(state, payload:number) {
state.count += payload
},
setNumber(state, payload:number) {
state.count = payload
},
},
getters: {
x2(state) {
return state.count * 2
},
},
})
Eh bien, un peu comme vuex, cependant ... qu'est-ce qui est sur la ligne 10?
counterModule.mutations.plus(1)
Whoa! Est-ce légal? Eh bien, avec vuexok - oui, légalement) La méthode createModule retourne un objet qui répète exactement la structure de l'objet du module vuex, uniquement sans la propriété namespaced, et nous pouvons l'utiliser pour appeler des mutations et des actions ou pour obtenir l'état et les getters, tous les types sont préservés. Et de n'importe quel endroit où il peut être importé.
Qu'en est-il des composants?
Et avec eux, tout va bien, car en fait c'est vuex, alors, en principe, rien n'a changé, commit, dispatch, mapState, etc. travailler comme avant.
Mais maintenant, vous pouvez faire fonctionner les types du module dans les composants:
import Vue from 'vue'
import { counterModule } from '@/store/modules/counterModule'
import Component from 'vue-class-component'
@Component({
template: '<div>{{ count }}</div>'
})
export default class MyComponent extends Vue {
private get count() {
return counterModule.state.count // type number
}
}
La propriété state dans un module est réactive, tout comme store.state, donc pour utiliser l'état du module dans les composants Vue, il vous suffit de renvoyer une partie de l'état du module dans une propriété calculée. Il n'y a qu'une seule mise en garde. J'ai délibérément fait de l'état Readonly un type, ce n'est pas bon de changer l'état vuex ainsi.
L'appel d'actions et de mutations est simple à déshonorer et les types de paramètres d'entrée sont également enregistrés
private async doSomething() {
counterModule.mutations.setNumber(10)
// this.$store.commit('counterModule/setNumber', 10)
await counterModule.actions.increment()
// await this.$store.dispatch('counterModule/increment')
}
Voici une telle beauté. Un peu plus tard, j'ai également dû réagir au changement de jwt, qui est également stocké dans le magasin. Et puis la méthode watch est apparue dans les modules. Les observateurs de module fonctionnent de la même manière que store.watch. La seule différence est que l'état et les getters du module sont passés en tant que paramètres de la fonction getter.
const unwatch = jwtModule.watch(
(state) => state.jwt,
(jwt) => console.log(`New token: ${jwt}`),
{ immediate: true },
)
Donc ce que nous avons:
- côté tapé - oui
- les types fonctionnent dans les composants - oui
- api comme dans vuex et tout ce qui était auparavant sur pure vuex ne casse pas - est
- travail déclaratif avec le côté - oui
- petite taille de paquet (~ 400 octets gzip) - oui
- pas besoin de stocker les noms des actions et des mutations dans les constantes - il y a
- ça devrait marcher - c'est
En général, il est étrange qu'une telle fonctionnalité merveilleuse ne soit pas disponible dans vuex hors de la boîte, c'est génial comme c'est pratique!
Quant au support de vuex4 et vue3 - je ne l'ai pas testé, mais à en juger par la documentation, il devrait être compatible.
Les problèmes présentés dans ces articles sont également résolus:
Vuex - résoudre un ancien différend avec de nouvelles méthodes
Vuex rompt l'encapsulation
Rêves humides:
Ce serait formidable de faire en sorte que les mutations et autres actions soient disponibles dans le cadre d'actions.
Comment faire cela dans le contexte des types dactylographiés - la bite le sait. Mais si vous pouviez faire ceci:
{
actions: {
one(injectee) {
injectee.actions.two()
},
two() {
console.log('tada!')
}
}
Que ma joie n'aurait pas de limite. Mais la vie, comme dactylographiée, est une chose dure.
Voici l'aventure avec vuex et dactylographié. Eh bien, j'ai en quelque sorte parlé. Merci pour l'attention.