Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
在View通过Dispatch触底Action,Action触发Mutation,最后更新State触发View更新,这一流程遵循单向数据流的原则。
import Vuex from 'Vuex'
import Vue from 'vue'
Vue.use(Vuex)
let store = new Vuex.Store({
state: {
num: 1
},
getters: {
numStore(state) {
return state.num + 1
}
},
mutations: {
addOne(state, params) {
state.num = state.num + params.num
}
},
actions: {
addOneAsync({ commit }, params) {
setTimeout(() => {
commit('addOne', params)
}, 1000)
}
}
})
export default store
在View中可以通过this.$store.commit
触发mutation
,或者通过this.$store.dispatch
触发action
。这里不会详细介绍Vuex
怎么去使用,这篇唠嗑主要讲的Vuex
的实现原理,入门的话可以到官网溜一波。
温馨提示:定义好的Vuex就得在Vue实例参数传上Vuex实例
new Vue({
store,
render: h => h(App),
}).$mount('#app')
在自己感觉看着舒服的微信建一个miniVuex.js文件
let Vue
class Store{
constructor(options) {}
}
function install(_Vue){
}
export default { Store, install }
为什么要定义这Store跟install方法?
Store方法应该大家都很好理解,回想一下Vuex
的用法,是不是上来就给它来一个new Vuex.Store
?然后传进去一个Object
?
import Vuex from 'Vuex'
let store = new Vuex.Store({})
再回看自己定义Store
函数,是不是清晰很多了?Store
函数接收一个options实参对象
可能大家对于为什么要定义install
函数有点困惑(如果开发过Vue插件,或者熟悉Vue.use()
Api的大佬,请忽略),我们再回想一下当,导出Vuex
的时候,是不是要Vue.use(Vuex)
一下?
import Vuex from 'Vuex'
Vue.use(Vuex)
因为当Vue.use(Vuex)
Api的时候,Vue.use
会将调用插件(这里是Vuex)
的install
函数,然后将Vue
传进去,并可以在install
函数里通过this
可以访问Vue实例
。所以在开发插件的时候,install
函数是必须要提供的。
在Vuex
中install
函数,主要是拿到Vue
保存起来,然后,通过Vue.mixin
混入将store
挂载到Vue.prototype
上;至于平时在页面上我们为什么能通过this.$store
访问Vuex
实例,就是在这里混入进去的;至于使用Vue.mixin
混入来挂载Vuex实例
到Vue.prototype
,主要是因为,我只想在new Vue
的时候只在beforeCreate
钩子里挂载一次就ok,不想每个页面都去重新挂载一次。
具体代码如下
let Vue
class Store {
constructor(options) {}
}
// 新增install实现
function install(_Vue) {
Vue = _Vue
Vue.mixin({
beforeCreate() {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store
}
}
})
}
export default { Store, install }
实现staet
的双向绑定是件非常简单的事,我们只需要利用Vue
自身的双向绑定机制就就可以实现;我们主要将new Vuex.Store({state:{}})
存放在一个new Vue 实例
上就可以。之所以Vuex官网说Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,就是因为Vuex
内部实现双向绑定的时候,使用的是Vue
自身的双向绑定机制,将Vue
强强的绑在身上。
let Vue
class Store {
constructor(options) {
//新增代码
this.state = new Vue({
data: options.state && options.state || {}
})
}
}
function install(_Vue) {
Vue = _Vue
Vue.mixin({
beforeCreate() {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store
}
}
})
}
export default { Store, install }
实现mutations
只要是将传进来的mutations
保存到实例就行,如果不传就默认为一个空对象;在实现commite
之前,先来看看commit
的用法。
this.$store.commit(type, params)
commit
主要是将传进来的type
,找到对应的mutations
里面的函数,进行触发,并将this.state
传给它的一个参数(因为在定义mutations的时候是不是(可以参数下面第一段示例代码)在第一个参数可以访问state里面的数据?至于为什么可以访问,就是在这个时候传进去的),第二个参数才是额外的参数。
mutations定义示例代码
mutations: {
addOne(state, params) {
state.num = state.num + params.num
}
}
具体代码如下
let Vue
class Store {
constructor(options) {
this.state = new Vue({
data: options.state && options.state || {}
})
//新增代码
this.mutations = options.mutations && options.mutations || {}
}
// 新增代码
commit = (type, params) => {
this.mutations[type]&&this.mutations[type](this.state, params)
}
}
function install(_Vue) {
Vue = _Vue
Vue.mixin({
beforeCreate() {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store
}
}
})
}
export default { Store, install }
至于commit
为什么要使用箭头函数,这里做一个解释,因为在actions里面异地再提交一下commit(如下面示例代码),那么会导致this中的上下文产生变化,不再指向Store实例就报一个mutations of undefined的错误(如下面图片);为了避免这种情况出现,使用了箭头函数。
示例代码
actions: {
addOneAsync({ commit }, params) {
setTimeout(() => {
commit('addOne', params)
}, 1000)
}
}
报错提示
getters
的原理,也不难理解,但是实现就要手动通过(Object.defineProperty)去监听,它的那个getter
别使用了,再触发对应的getter
,getters
是只读,所以在Object.defineProperty监听的时候,只需要提供get
(有传的前提在才会进行Object.defineProperty监听,不传的话,什么也不敢),然后把this.state
传给它的一个参数(因为在定义getter的时候,可以通过第一个参数访问state,可参考下面的示例)。
温馨提示:上面的getters是指整个大的getters,getter是指的getters里面小的各个键值对(比如下面示例中的numStore),我怕有同学会不知道,还是提一下
定义getters示例
getters: {
numStore(state) {
return state.num + 1
}
}
具体实现代码
let Vue
class Store {
constructor(options) {
this.state = new Vue({
data: options.state && options.state || {}
})
this.mutations = options.mutations && options.mutations || {}
//新增代码
options.getters && this.initGetters(options.getters)
}
//新增代码
initGetters(getters) {
this.getters = {}
Object.keys(getters).forEach(key => {
Object.defineProperty(this.getters, key, {
get: () => {
return getters[key](this.state)
}
})
})
}
commit = (type, params) => {
this.mutations[type]&&this.mutations[type](this.state, params)
}
}
function install(_Vue) {
Vue = _Vue
Vue.mixin({
beforeCreate() {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store
}
}
})
}
export default { Store, install }
actions
和dispatch
的实现跟mutations
和commit
非常类似;实现actions
只要是将传进来的actions
保存到实例就行,如果不传就默认为一个空对象;在实现dispatch
之前,先来看看dispatch
的用法。
this.$store.dispacth(type, params)
dispacth
主要是将传进来的type
,找到对应的actions
里面的函数,进行触发,并将一个Object(里面包含commit,dispatch,state)
传给它的一个参数(因为在定义actions的时候是不是在第一个参数可以访问这些(commit,dispatch,state…可以参数下面第一段示例代码)?至于为什么可以访问,就是在这个时候传进去的),第二个参数才是额外的参数。
示例代码
actions: {
addOneAsync({ commit,dispatch,state }, params) {
setTimeout(() => {
commit('addOne', params)
}, 1000)
}
}
具体实现代码
let Vue
class Store {
constructor(options) {
this.state = new Vue({
data: options.state && options.state || {}
})
this.mutations = options.mutations && options.mutations || {}
//新增代码
this.actions = options.actions && options.actions || {}
options.getters && this.initGetters(options.getters)
}
initGetters(getters) {
this.getters = {}
Object.keys(getters).forEach(key => {
Object.defineProperty(this.getters, key, {
get: () => {
return getters[key](this.state)
}
})
})
}
commit = (type, params) => {
this.mutations[type](this.state, params)
}
//新增代码
dispatch(type, params) {
this.actions[type] && this.actions[type]({
commit: this.commit,
state: this.state,
dispatch: this.dispatch
}, params)
}
}
function install(_Vue) {
Vue = _Vue
Vue.mixin({
beforeCreate() {
if (this.$options.store) {
Vue.prototype.$store = this.$options.store
}
}
})
}
export default { Store, install }
好了,以上那段也是整个miniVuex
的完整代码了,里面我们实现了state,getters,mutations,actions,commit,dispatch
;至于怎么使用,就跟之前怎么用Vuex一样,把import的路径换成我们自己的miniVuex路径
使用例子:
import Vuex from './miniVuex'
import Vue from 'vue'
Vue.use(Vuex)
let store = new Vuex.Store({
state: {},
getters: {},
mutations:{},
actions: {}
})
export default store