1KB Bibliothèque de gestion progressive de l'État inspirée par Vuex.
Vous pouvez obtenir cette bibliothèque en tant que module Node.js disponible via le registre NPM:
// With npm
$ npm install dragonbinder
// With yarn
$ yarn add dragonbinder Ou vous pouvez l'utiliser autonome dans le navigateur avec: <script src="https://cdn.jsdelivr.net/npm/dragonbinder"></script>
const Dragonbinder = require ( 'dragonbinder' ) ;
const store = new Dragonbinder ( {
state : {
count : 0
} ,
mutations : {
increment ( state ) {
state . count ++
}
}
} ) ;
store . commit ( 'increment' ) ;
console . log ( store . state . count ) // -> 1DragonBinder utilise des proxys pour créer un état comme une "source unique de vérité" qui ne peut être modifiée que si vous commettez une mutation. Cela signifie que vous ne pouvez pas supprimer, modifier ou ajouter une propriété directement. Cela nous permet de suivre toutes les modifications que nous avons apportées à l'État.
Si vous ne fournissez pas un état initial par la propriété state , DragonBinder en créera un.
const store = new Dragonbinder ( {
state : {
count : 0
} ,
mutations : {
addProperty ( state , value ) {
state . hello = 'world' ;
} ,
modifyProperty ( state ) {
state . count ++
} ,
removeProperty ( state ) {
delete state . count ;
}
}
} ) ;
// This will throw errors
store . state . hello = 'world' ;
store . state . count ++ ;
delete state . count ;
// This will work as expected
store . commit ( 'addProperty' ) ;
store . commit ( 'modifyProperty' ) ;
store . commit ( 'removeProperty' ) ;De plus, si vous souhaitez éviter que les singletons réutilisent la définition initiale de votre magasin, vous pouvez déclarer son état comme une fonction d'usine.
const myStoreDefinition = {
state ( ) {
return {
count : 1
}
} ,
mutations : {
increment ( state , payload ) {
state . count = state . count + payload ;
}
}
} ;
const store1 = new Dragonbinder ( myStoreDefinition ) ;
const store2 = new Dragonbinder ( myStoreDefinition ) ;
store1 . commit ( 'increment' , 5 ) ;
store2 . commit ( 'increment' , 3 ) ;
console . log ( store1 . state . count ) ; // -> 6
console . log ( store2 . state . count ) ; // -> 4Comme pour Vue, avec DragonBinder, vous pouvez créer des Getters pour créer des propriétés calculées en fonction de l'état. Ces Getters recevront l'État comme premier argument et tous les autres Getters en tant que deuxième.
const store = new Dragonbinder ( {
state : {
todos : [
{
content : 'First' ,
completed : false
} ,
{
content : 'Second' ,
completed : true
}
]
} ,
getters : {
completed ( state ) {
return state . todos . filter ( item => item . completed ) ;
} ,
completedCount ( state , getters ) {
return getters . completed . length ;
}
}
} ) ;
console . log ( store . getters . completed ) ; // -> { content: 'Second', completed: true }
console . log ( store . getters . completedCount ) ; // -> 1Les mutations sont le seul moyen de changer l'état et vous devez considérer les points suivants lors de la conception de mutations.
Object.freeze pour éviter les changements directs. Ainsi, lorsque vous modifiez l'état en utilisant une mutation, vous pouvez ajouter, modifier ou supprimer uniquement les propriétés de premier niveau, les propriétés de deuxième niveau seront lues uniquement. const store = new Dragonbinder ( {
state : {
hello : {
name : 'John Doe'
}
} ,
mutations : {
changeNameError ( state , payload ) {
state . hello . name = payload ;
} ,
changeNameOk ( state , payload ) {
state . hello = { ... state . hello , name : payload } ;
} ,
changeNameTo ( state , ... args ) {
state . hello = { ... state . hello , name : args . join ( ' ' ) } ;
}
}
} ) ;
// This will throw an assign to read only property error
store . commit ( 'changeNameError' , 'Jane Doe' ) ;
// This will work as expected
store . commit ( 'changeNameOk' , 'Jane Doe' ) ;
// You can pass any number of arguments as payload
store . commit ( 'changeNameTo' , 'Jane' , 'Doe' ) ;Si vous devez gérer les fonctions asynchrones, vous devez utiliser des actions. Et les actions rendront toujours une promesse à la suite de les appeler.
const store = new Dragonbinder ( {
state : {
count : 0
} ,
mutations : {
increment ( state ) {
state . count ++
}
} ,
actions : {
increment ( state ) {
return new Promise ( ( resolve ) => {
setTimeout ( ( ) => {
store . commit ( 'increment' ) ;
resolve ( ) ;
} , 1000 ) ;
} )
}
}
} ) ;
store . dispatch ( 'increment' ) . then ( ( ) => console . log ( store . state . count ) ) ; // -> 1 after one secondVous pouvez enregistrer / démêler les rappels aux événements.
const store = new Dragonbinder ( {
state : {
count : 0
} ,
mutations : {
increment ( state ) {
state . count ++
}
}
} ) ;
// Add a named listener
let namedListener = ( store , prop , newVal , oldVal ) => console . log ( `The property ${ prop } was changed from ${ oldVal } to ${ newVal } ` ) ;
store . on ( 'set' , namedListener ) ;
// Add an anonymous listener
let removeAnonymousListener = store . on ( 'set' , ( ) => console . log ( 'Anonymous listener triggered' ) ) ;
// Committing increment will trigger the listener
store . commit ( 'increment' ) ;
// $ The property count was changed from 0 to 1
// $ Anonymous listener triggered
// Remove a named listener
store . off ( 'set' , namedListener ) ;
// Remove an anonyous listener
removeAnonymousListener ( ) ;
// Committing increment will do nothing as the listeners are already removed
store . commit ( 'increment' ) ; Tous les événements reçoivent l'instance de magasin comme premier argument.
| Nom de l'événement | C'est appelé quand | Arguments reçus par lieu |
|---|---|---|
| additionner | Un écouteur d'événements est ajouté | Nom de l'événement | L'auditeur ajouté |
| enlever | Un écouteur d'événements est supprimé | Nom de l'événement | Écouteur supprimé |
| ensemble | Une propriété de l'État est ajoutée ou modifiée, également déclenchée lorsqu'un module est enregistré | Nom de la propriété | Nouvelle valeur | Valeur ancienne |
| supprimer | Une propriété de l'État est supprimée, également déclenchée lorsqu'un module n'est pas enregistré | Nom de la propriété | Valeur ancienne |
| être cominé | Commit Méthode appelée et avant d'appliquer la mutation | Nom de mutation | (...) Arguments passés à la mutation |
| commettre | Commit Méthode appelée et après appliquer la mutation | Nom de mutation | (...) Arguments passés à la mutation |
| beforedispatch | Méthode de répartition appelée et avant d'appliquer l'action | Nom d'action | (...) Arguments transmis à l'action |
| expédition | Méthode de répartition appelée et après appliquer l'action | Nom d'action | (...) Arguments transmis à l'action |
| gette | Un getteur s'appelle | Nom de Getter | Valeur du Getter |
| plugin | Un plugin est ajouté | Plugin ajouté | (...) Options transmises au plugin |
| enregistrement | Un module est enregistré | Espace de noms enregistré | Définition du module | Magasin créé avec la définition |
| désinscriptif | Un module n'est pas enregistré | Espace de noms non enregistré | Magasin créé avec la définition |
Comme Vuex, DragonBinder vous permet de diviser votre magasin en modules et chaque module peut contenir sa propre définition de magasin, y compris des modules plus imbriqués.
const moduleA = {
state : { ... } ,
mutations : { ... } ,
actions : { ... } ,
getters : { ... }
}
const moduleB = {
state : { ... } ,
mutations : { ... } ,
actions : { ... } ,
getters : { ... }
modules : {
a : moduleA
}
}
const store = new Dragonbinder ( {
modules : {
b : moduleB
}
} ) ;
console . log ( store . state . b ) // -> `moduleB`'s state
console . log ( store . state [ 'b.a' ] ) // -> `moduleA`'s state De plus, après la création du magasin, vous pouvez enregistrer / désinscrire des modules avec les méthodes registerModule et unregisterModule .
Considérez que lorsque vous désinscrivez un module, seuls ses modules imbriqués initiaux ne seront pas enregistrés avec lui.
const moduleA = {
state : { ... } ,
mutations : { ... } ,
actions : { ... } ,
getters : { ... }
}
const moduleB = {
state : { ... } ,
mutations : { ... } ,
actions : { ... } ,
getters : { ... } ,
modules : {
a : moduleA
}
}
const moduleC = {
state : { ... } ,
mutations : { ... } ,
actions : { ... } ,
getters : { ... }
}
const store = new Dragonbinder ( ) ;
store . registerModule ( 'b' , moduleB ) ;
store . registerModule ( 'b.c' , moduleC ) ;
console . log ( store . state . b ) // -> `moduleB`'s state
console . log ( store . state [ 'b.a' ] ) // -> `moduleA`'s state
console . log ( store . state [ 'b.c' ] ) // -> `moduleC`'s state
store . unregisterModule ( 'b' ) ;
console . log ( store . state . b ) // -> undefined
console . log ( store . state [ 'b.a' ] ) // -> undefined
console . log ( store . state [ 'b.c' ] ) // -> `moduleC`'s state Chaque module se comportera comme n'importe quel autre magasin, mais, contrairement à Vuex, tous les modules DragonBinder sont complétés par conception. Il n'y a pas d'option pour ajouter des mutations racine, des actions ou des getters avec un module. Ainsi, lorsque vous appelez une mutation de module, une action ou un getter, vous devez fournir son espace de noms complet.
Le premier argument pour les mutations et les getters continuera d'être l'État local, et avec des actions, le premier argument sera le contexte / magasin local.
Getters obtiendra l'état racine et les Getters racinaires comme troisième et quatrième arguments.
Les actions accéderont au contexte racine par la propriété rootStore du contexte local.
const moduleA = {
state : {
hello : 'world'
} ,
mutations : {
sayHello ( state , payload ) {
state . hello = payload ;
}
} ,
actions : {
change ( store , payload ) {
store . commit ( 'sayHello' , payload ) ;
store . rootStore . commit ( 'increment' ) ;
}
} ,
getters : {
hello ( state , getters , rootState , rootGetters ) {
return `You have said hello ${ rootState . count } times to ${ state . hello } ` ;
}
}
} ;
const store = new Dragonbinder ( {
state : {
count : 0
} ,
mutations : {
increment ( state ) {
state . count ++ ;
}
} ,
modules : {
a : moduleA
}
} ) ;
store . dispatch ( 'a.change' , 'John Doe' ) ;
console . log ( store . getters [ 'a.hello' ] ) ; // -> You have said hello 1 times to John Doe
console . log ( store . state . count ) // -> 1
console . log ( store . state . a . hello ) // -> John DoeDragonBinder est livré avec un système de plugin simples mais puissant. Vous pouvez étendre sa fonctionnalité de base ou le modifier complètement en utilisant des plugins.
let store = new Dragonbinder ( ) ;
store . use ( myPlugin , ... options ) ; Un plugin DragonBinder est un module qui exporte une seule fonction qui sera appelée avec l'instance de magasin comme premier argument et éventuellement avec les options passées le cas échéant.
const Dragonbinder = require ( 'dragonbinder' ) ;
const myPlugin = ( store , ... options ) => {
Dragonbinder . myGlobalMethod = function ( ) {
// Awesome code here
} ;
Dragonbinder . fn . myPrototypeMethod = function ( ) {
// Awesome code here
} ;
store . myLocalMethod = function ( ) {
// Awesome code here
} ;
} ; Vérifiez les documents à: https://masquerade-ircus.github.io/dragonbinder/?api
Consultez le guide de contribution à: https://masquerade-ircus.github.io/dragonbinder/?content=Contribution
yarn dev pour regarder et compiler la bibliothèque à chaque modification.yarn build pour construire la bibliothèque.yarn test pour exécuter les tests une seule fois.yarn dev:test pour exécuter les tests en regardant les modifications de la bibliothèque et des tests.yarn dev:test:nyc pour exécuter les tests en regardant les modifications et obtenez enfin la couverture du test.yarn docs pour construire la documentation.yarn docs:watch pour regarder et reconstruire la documentation sur chaque modification de la bibliothèque ou des fichiers MD.yarn docs:serve pour voir la documentation générée localement. Auteur: Masquerade Circus. Licence Apache-2.0