Vue3 丐版 Vuex
import { App, inject, Plugin, reactive } from 'vue'
type StoreType<T = any> = {
state: T,
mutations?: { [key: string]: (state: T, payload?: unknown) => void },
actions?: { [key: string]: (context: StoreType<T>, payload?: unknown) => void },
getters?: { [key: string]: (state: T) => unknown }
}
const storeKey = 'store'
class Store<T = any> {
install: Plugin['install']
private $state: T
private _mutations: { [key: string]: (state: T, payload?: unknown) => void }
private _actions: { [key: string]: (context: StoreType<T>, payload?: unknown) => void }
private _getters: { [key: string]: (state: T) => unknown }
constructor (store: StoreType<T>) {
this.$state = reactive<any>(store.state)
this._mutations = store.mutations || {}
this._actions = store.actions || {}
this._getters = store.getters || {}
}
commit = (type: string, payload?: unknown) => {
const fn = this._mutations[type]
if (fn) {
fn(this.$state, payload)
}
}
dispatch = async (type: string, payload?: unknown) => {
const fn = this._actions[type]
if (fn) {
await fn(this, payload)
}
}
get state (): T {
return this.$state
}
get getters () {
const result = {} as any
Object.keys(this._getters).forEach(key => {
result[key] = this._getters[key](this.$state)
})
return result
}
}
function createStore<T = any> (options: StoreType<T>): Plugin {
return new Store(options) as Plugin
}
Store.prototype.install = function (app: App, injectKey) {
app.config.globalProperties.$store = this
app.provide(injectKey || storeKey, this)
}
function useStore<T = any> (key?: string | null): Store<T> {
if (!key) key = null
const store = inject<Store<T>>(key !== null ? key : storeKey) as Store<T>
return store
}
export default { createStore, useStore }
使用
import { createApp } from 'vue'
import App from './App.vue'
import Store from './common/myVuex'
const store = Store.createStore({
state: {
count: 0
},
mutations: {
add: (state: { count: number }) => {
state.count++
}
},
actions: {
'sync-add': async (context: any) => {
await new Promise<void>((resolve, reject) => {
const timer = setTimeout(() => {
clearTimeout(timer)
context.commit('add')
resolve()
}, 1000)
})
}
},
getters: {
count2: (state) => {
return state.count * 2
}
}
})
createApp(App).use(store).mount('#app')
<!-- src/components/HelloWorld.vue -->
<template>
<div class="hello">
<p>{{ count }}</p>
<button @click="clickHandle">add</button>
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from 'vue'
import Store from '@/common/myVuex'
export default defineComponent({
props: {
msg: String
},
setup () {
const store = Store.useStore<{ count: number }>('aaa')
const count = computed(() => store.getters.count2)
const clickHandle = async () => {
await store.dispatch('sync-add')
console.log(store.state)
}
return {
count,
clickHandle
}
}
})
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>