Use this if you do not want Vuex!
Support Vue 2, Vue 3, even React!
-
You do not need Vuex but only want global state management
-
You think Vuex is not friendly to TypeScript's type inference and IDE reference jumping
-
You are tired of committing mutations and dispatching actions
-
You are using React and @tybys/reactivuety
-
You want to create another Vuex
Use static method VueModel.create(Vue, { state, getters? })
to create a model and use it in template / JSX.
The first argument is the implementation of Vue, or something like IVueImpl
below:
interface IVueImpl {
reactive?: <T extends object> (target: T) => any
computed?: (fn: () => void) => any
extend?: (options: any) => new () => { _data: any; $destroy (): void; [x: string]: any }
[x: string]: any
}
Example:
import * as Vue from 'vue' // Vue 3
// import Vue from 'vue' // Vue 2
// React with @tybys/reactivuety
// import { reactive } from '@vue/reactivity'
// import { computed } from '@tybys/reactivuety'
// const Vue = { reactive, computed }
import { VueModel } from '@tybys/vuemodel'
const model = VueModel.create(Vue, {
state: {
a: { count: 1 }
},
getters: {
computedCount (state) {
return state.a.count * 2
}
}
})
// or
// const model = new VueModel(Vue, { ... })
const Component = Vue.defineComponent({
setup () {
const onClick = () => { model.state.a.count++ }
return () => (
<>
<p>{model.state.a.count} * 2 = {model.getters.computedCount}</p>
<button onClick={onClick}>+</button>
</>
)
}
})
Use VueModel.extend(Vue)
to create a new constructor bound a vue implementation
import * as Vue from 'vue' // Vue 3
import { VueModel } from '@tybys/vuemodel'
const Model = VueModel.extend(Vue)
const model = Model.create({
state: {
a: { count: 1 }
},
getters: {
computedCount (state) {
return state.a.count * 2
}
}
})
// or
// const model = new Model({ ... })
Better type inference support than Vuex!
import * as Vue from 'vue' // Vue 3
import { VueModel } from '@tybys/vuemodel'
import type { IVueModel, ISubscribeOptions, Subscriber } from '@tybys/vuemodel'
interface State {
a: { count: number }
}
class MyStore implements IVueModel<State, any> {
// @override
public get state () {
return this.__model.state
}
// @override
public get getters () {
return this.__model.getters
}
private __model = VueModel.create(Vue, {
state: {
a: { count: 1 }
},
getters: {
computedCount (state) { // <- no return type
return state.a.count * 2
}
}
})
public get count () {
return this.state.a.count
}
public get computedCount () {
return this.getters.computedCount // infer => number
}
// like action
public add (): Promise<void> {
return Promise.resolve().then(() => {
this.state.a.count++
})
}
// public install (appOrVue) {
// vue plugin
// }
}
import * as Vue from 'vue' // Vue 3
import { VueModel } from '@tybys/vuemodel'
interface State {
a: { count: number }
}
const getters = {
computedCount (state: State) { // <- no return type
return state.a.count * 2
}
}
class MyStore extends VueModel<State, typeof getters> {
public constructor () {
super(Vue, {
state: {
a: { count: 1 }
},
getters: getters
})
}
public get count () {
return this.state.a.count
}
public get computedCount () {
return this.getters.computedCount // infer => number
}
// like action
public add (): Promise<void> {
return Promise.resolve().then(() => {
this.state.a.count++
})
}
}
If you prefer Vuex's pattern, you can call Store.prototype.registerMutation
or Store.prototype.registerAction
member method then pass the return value to Store.prototype.commit
or Store.prototype.dispatch
.
import * as Vue from 'vue' // Vue 3
import { Store, createLogger } from '@tybys/vuemodel'
import type { IMutation, IAction } from '@tybys/vuemodel'
interface State {
a: { count: number }
}
// class Store extends VueModel
class MyStore extends Store<State, {}> {
private __addMutation: IMutation<number>
private __addAction: IAction<number, void>
public constructor () {
super(Vue, {
state: {
a: { count: 1 }
},
plugins: [
// createLogger()
]
})
this.__addMutation = this.registerMutation<number>('m_add', (payload: number): void => {
this.state.a.count += payload
})
this.__addAction = this.registerAction<number, void>('a_add', (payload: number): void | Promise<void> => {
this.commit(this.__addMutation, payload)
// return Promise.resolve()
})
}
public add (): Promise<void> {
return this.dispatch(this.__addAction, 1)
}
}