The original idea was spoilered in a video about Vuex 5 draft by the maker (Kia King Ishii). I never liked the current syntax, but this new API made me to want to use it ASAP. So I started to implement it, for fun and for internal use in some hobby project.
It's not supposed to be a replacement of Vuex. It's just a fun project for me.
Currently its pretty lightweight: 1.23 KB minified (619 byte Gzipped) - with stripped asserts
It's supports plugins, app-scoped stores and TypeScript.
Do NOT use it in production environment. It's under continuous refactor, and as soon as Vuex 5 released it's will be useless anyway.
- Vue 3.0 or higher
npm i vue-composable-store
NOTE: There is a "hidden" production build in the dist folder which is ~30% smaller due to stripped asserts.
import { createVueStore } from 'vue-composable-store/dist/index-prod-es.js';
The Options API not supported!
import { readonly, reactive, ref, computed } from 'vue';
import { defineStore } from 'vue-store';
export default defineStore('shop', () => {
// state
const list = reactive([]);
const page = ref(1);
const limit = ref(20);
// actions
const hasNext = computed(() => {
return list.length === limit;
});
async function load(page) {
// ...fetch data into `list`...
page.value = page;
}
// final store object
return {
hasNext,
list: readonly(list), // <---- in case you do not want it to be modifiable from outside
page,
load
};
});
import { readonly, reactive } from 'vue';
import { defineStore, useStore } from 'vue-store';
// imported just in the store/compont which composing,
// so it's can be properly code-splitted/tree-shaken, etc
import cartStore from './stores/cart';
export default defineStore('shop', () => {
// state
const products = reactive([]);
async function load(page) {
// ...fetch data into `list`...
page.value = page;
}
// final store object
return {
cart: useStore(cartStore), // <--- store composition instead of store modules
products,
load
};
});
import { defineComponent } from 'vue';
import { useStore } from 'vue-composable-store';
import productsStore from './stores/products';
export default defineComponent({
name: 'ProductList',
setup() {
// the store lazy initialized on the first use
const { load, list, hasNext } = useStore(productsStore);
load(1);
return {
list,
hexNext
};
}
});
import { createApp } from 'vue';
import { createVueStore } from 'vue-composable-store';
import App from './App.vue';
const store = createVueStore();
const app = createApp(App);
app.use(store);
app.mount('#el');
Plugins can provide utility function or data object to be exposed in the storeContext
.
// api-plugin.js
export default (provide) => {
provide('api', (uri) => fetch(`https://localhost:5000/${uri}`));
}
// app.js
import { createApp } from 'vue';
import { createVueStore } from 'vue-composable-store';
import apiPlugin from './api-plugin.js';
import App from './App.vue';
const options = { plugins: [ apiPlugin ] };
const vueStore = createVueStore(options);
const app = createApp(App);
app.use(vueStore);
// optionally you can pass the `options` at the app.use() too
// in this case the plugin list will be overridden
// app.use(vueStore, options)
// my-store.js
export default defineStore(('my-store', ({ api }) => { // <-- `api` in the context
// you can use your api() plugin here...
return {};
}));
The usable hooks are passed in the second argument of the plugin.
// my-plugin.js
export default function myPlugin(provide, hooks) => { /* plugin code */};
The first two arguments passed to the callback functions are always:
- The
name
of the store which triggered the callback - The
storeInstance
contains the exact object which is returned from the initializer function passed todefineStore()
.
onUse(callback: (storeName, storeInstance, context) => void)
Called every time a store is being accessed by the useStore()
or composed via use()
.
The context
is the same as in the defineStore
, you can use it to access or detect other plugins
Example:
// my-store-logger.js
export default function logger(provide, { onUse }) {
onUse((name, instance, context) => {
console.log(`Used: "${name}"`);
});
}
onInitialized(callback: (storeName, storeInstance, context) => void)
Called once per store per app after the first time usage of useStore()
or composed via use()
// my-store-logger.js
export default function logger(provide, { onInitialized }) {
onInitialized((name, instance, context) => {
console.log(`[Initialized] Store name: "${name}"`);
});
}
onAction(callback: (storeName, storeInstance, actionName, actionArgs, actionResult, context) => void)
Called after a store function invoked.
// my-store-logger.js
export default function logger(provide, { onAction }) {
onAction((name, instance, action, actionArgs, actionResult, context) => {
console.log(`[Action invoked] Store action: "${name}.${action}"`);
console.log(` args: ${actionArgs.map(JSON.stringify).join(', ')}`)
console.log(` return value: ${JSON.stingify(actionResult)}`);
});
}
onMutated(callback: (storeName, storeInstance, stateKey, value, oldValue, context) => void)
Called when any watchable store property mutated (the value changed).
// my-store-logger.js
export default function logger(provide, { onMutated }) {
onMutated((name, instance, key, newValue, oldValue, context) => {
const oldJSON = JSON.encode(oldValue);
const newJSON = JSON.encode(newValue);
console.log(`[State mutated] "${name}.${key}" is being mutated from ${oldJSON} to ${newJSON}`);
});
}
See the CHANGELOG.md
Copyright © 2020 Kövesdi György
Licensed under the MIT License.