在上一篇笔记中:Vuex源码学习笔记 - Vuex开发运行流程(二)
我们通过运行npm run dev
命令来启动webpack,来开发Vuex,并在Vuex的createStore函数中添加了第一个断点。
那么在看源码之前,建议先要熟悉Vuex的使用,如果还不熟练使用,那么在看的时候很容易会发蒙。
我们来复习下Vuex中的几个重要概念和使用
- State - 存储状态
- Getter - 类似计算属性
- Mutation - 更改状态的唯一方法是提交 mutation。
- Action - 可以包含任意异步操作,并且提交 mutation来更改状态。
- Module - 用来分割模块,每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。
下面是计数器应用的Vuex代码
import { createStore } from 'vuex'
const state = {
count: 0
}
const mutations = {
increment (state) {
state.count++
},
decrement (state) {
state.count--
}
}
const actions = {
increment: ({ commit }) => commit('increment'),
decrement: ({ commit }) => commit('decrement'),
incrementIfOdd ({ commit, state }) {
if ((state.count + 1) % 2 === 0) {
commit('increment')
}
},
incrementAsync ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('increment')
resolve()
}, 1000)
})
}
}
const getters = {
evenOrOdd: state => state.count % 2 === 0 ? 'even' : 'odd'
}
export default createStore({
state,
getters,
actions,
mutations
})
在上面的代码中,分别创建了state、mutations、actions、getters,没有module,默认为根。
那么我们在回忆,为什么要使用Vuex呢?它解决了什么问题?
在使用Vue来开发页面或者组件时,我们在每个data函数内定义当前组件所需的状态,那么随着应用月来越大、越来越复杂,经常会遇到多个页面或组件需要共用某个状态,在一开始我们可以用组件间的通信去解决数据的同步问题。但当应用越来越大,组件越来越多,这种方式的弊病也显露出来,过于耦合,并且难以维护,当有新组件需要共享状态时,还有额外的添加代码。
那么基于这个问题思考,我们为什么不可以将Vue中data函数内的状态放在一个地方,然后多个组件都去这同一个地方去操作状态呢。
这就是Vuex。
下面这张图表示了Vuex的工作流程:
首先在左边的绿色的组件中我们可以使用Vuex中State存储的状态;
然后在组件中可以通过Dispatch触发Actions,在Actions中我们可以调用后端的服务,或者其他的异步或同步操作;
然后在Actions中会触发Mutations来修改State中的状态,
由于State是响应式的,State状态被修改,组件的视图也会随之更新;
因为只能使用Mutations来修改状态,所以State状态是可被预测的,我们可以通过Devtools来监测到状态的改变。
Vuex的使用
首先在store.js中通过createStore函数来创建vuex的实例对象
import { createStore } from 'vuex'
export default createStore({
state: { /**...**/ },
getters: { /**...**/ },
actions: { /**...**/ },
mutations: { /**...**/ }
})
然后在app.js,通过app.use
函数来将vuex的实例注入到vue中
import { createApp } from 'vue'
import Counter from './Counter.vue'
import store from './store'
const app = createApp(Counter)
app.use(store)
app.mount('#app')
接着就可以在页面中去使用store了
<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
return {
count: computed(() => store.state.count),
evenOrOdd: computed(() => store.getters.evenOrOdd),
increment: () => store.dispatch('increment'),
decrement: () => store.dispatch('decrement'),
incrementIfOdd: () => store.dispatch('incrementIfOdd'),
incrementAsync: () => store.dispatch('incrementAsync')
}
}
}
</script>
我们前面的文章讲过:如何编写Vue插件。
使用app.use
函数时,如果参数为一个对象那么Vue会自动调用函数内的install方法。
也就是src/store.js中的
import { storeKey } from './injectKey'
import { addDevtools } from './plugins/devtool'
export class Store {
constructor (options = {}) {
//...
}
install (app, injectKey) {
// 第一,将Store对象注入到vue的实例上
app.provide(injectKey || storeKey, this)
app.config.globalProperties.$store = this
// 第二,判断是否添加Vue devtool插件
const useDevtools = this._devtools !== undefined
? this._devtools
: __DEV__ || __VUE_PROD_DEVTOOLS__
if (useDevtools) {
addDevtools(app, this)
}
}
//...
}
可以看到install函数做的事情很简单:
- 第一,将Store对象注入到vue的实例上
- 第二,判断是否添加Vue devtool插件
可以看到有两行代码挂载Store对象,通过provide的方式,主要是使用在setup方式中,通过useStore()来获取store
<script>
import { useStore } from 'vuex'
export default {
setup () {
const store = useStore()
}
}
</script>
useStore函数的源代码在injectKey.js文件中:
import { inject } from 'vue'
export const storeKey = 'store'
export function useStore (key = null) {
return inject(key !== null ? key : storeKey)
}
provide/inject的使用就不多讲了,更多请查看文档。
第二种通过app.config.globalProperties.$store = this
的方式挂载到全局vue实例中,主要是使用在下面的场景,通过this.$store
来获取store的实例。
methods: {
increment() {
this.$store.commit('increment')
console.log(this.$store.state.count)
}
}
总结
今天我们复习了Vuex的使用,以及为什么要使用Vuex,同时看到了Vuex是如何与Vue去结合到一起的。
Vuex通过Vue插件的方式,通过use函数讲Vuex实例对象绑定到Vue中。
可以通过useStore
函数或者this.$store
两种方式在页面中访问store。
明天我们来分析Store类的构造函数都做了哪些事情。
一起学习更多前端知识,微信搜索【小帅的编程笔记】,每天更新