一、 vuex是用来干什么的。
Vuex 作为一个专为 Vue.js 程序开发的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
二、vuex的工作流程
三、提出的问题
那么大家在开发的过程中有没有想过以下几个问题:
1、 Vuex 是怎么挂载到 Vue 上的呢
2、 为什么能够在所有子组件中使用 store
3、 Vuex 的数据响应式是如何实现的
4、 Vuex 的严格模式又是怎么回事
5、 为什么只能通过mutation修改state?
6、怎么实现非mutation修改时报错呢
7、辅助函数 mapState、mapGetters、mapActions、mapMutations作用
8、命名空间的实现
四、解决问题
(1)Vuex 是怎么挂载到 Vue 上的呢
通过new Vue实例化的过程
// main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
store,
render: h => h(App),
}).$mount('#app')
(2) 为什么 state 能够在所有子组件中使用 store
Vue使用use调用插件,默认调用插件内部的install方法, install内部实现使vue每个组件都可以访问到store
// main.js
import Vue from 'vue'
import Vuex from './vuex.js'
Vue.use(Vuex)
// vuex.js
const install = (v) => {
Vue = v // 参数v是 Vue 构造器
// 需要在每一个组件上添加store, this指的是当前的vue实例
// 通过全局混入来添加一些组件选项。
Vue.mixin({
beforeCreate() {
console.log(this.$options, 'sdddd')
if (this.$options && this.$options.store) {
// 根结点,默认有store
this.$store = this.$options.store
} else {
// 非根节点,找到父组件的store,依次下去,保证了每个组件都有store实例
this.$store = this.$parent && this.$parent.$store
}
}
})
}
export default {
install,
}
(3) Vuex 的数据响应式是如何实现的
因为vue的数据是响应式的,所以vuex借助vue响应式原理,实现数据响应式,因为vuex只适用于vue框架
// vuex.js
class Store {
constructor(option) {
// 以vue的方式,使store的state成为响应式的
this.vm = new Vue({
data: {
state: option.state // state是对象,所有是引用类型,所以是响应式的
}
})
}
// state es6
get state() {
return this.vm.state
}
(4) Vuex 的严格模式又是怎么回事
开启严格模式,仅需在创建 store 的时候传入 strict: true:
const store = new Store({
// ...
strict: true
})
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
(5) 为什么只能通过mutation修改state?
每一条 mutation修改需要被记录,devtools 都需要捕捉到前一状态和后一状态的快照。mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的。在 mutation 中混合异步调用会导致你的程序很难调试。例如,当你调用了两个包含异步回调的 mutation 来改变状态,你怎么知道什么时候回调和哪个先回调呢。
mutation中异步修改state也能生效,但不建议,违背追踪原则
(6)怎么实现非mutation修改时报错呢
在mutation修改状态时,执行_withCommitting方法
class Store {
constructor(option) {
// 以vue的方式,使store的state成为响应式的
this.vm = new Vue({
data: {
state: option.state // state是对象,所有是引用类型,所以是响应式的
}
})
this._committing =false // 标识
this.strict = option.strict // 是否是严格模式
if (this.strict) {
// 只要状态一变化会立即执行,在状态变化后同步执行 sync, 此时_committing还是true
// 以vue的方式创建一个监听,监听state的变化,判断其是否是通过mutation修改的,若不是,则报错提示
this.vm.$watch(() => this.vm._data.state, () => {
// console.assert() 断言方法在第一个参数为 false 的情况下会在控制台输出信息。
console.assert(this._committing, '在mutation之外更改了')
}, {deep: true, sync: true})
}
this.mutations = {}
Object.keys(mutations).forEach((mutationName) => {
// 在mutations上定义一个方法
this.mutations[mutationName] = payload => {
this._withCommitting(() => {
mutations[mutationName](this.state, payload) // 这里执行更改state状态
})
}
})
}
// 切片
_withCommitting(fn) {
this._committing = true
fn()
this._committing = false
}
(7) 辅助函数 mapState、mapGetters、mapActions、mapMutations作用
可以快速的通过key拿到对应的值
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
computed: {
...mapState([
'count' // 映射 this.count 为 store.state.count
]),
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
])
},
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
}),
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
(8)module
1、模块默认是全局空间, 即namespaced: false
2、状态不要和模块的名字相同,相同时会优先取模块的
3、默认计算属性直接通过getter获取,即全局空间的getter是自动合并的
4、如果增加namespaced:true,会将这个模块的所有属性都放到这个作用域下,否则会放到父模块作用域下
5、默认会找到当前模块上是否有namespace,并且将父级的namespaced一同算上,做成命名空间