为什么有 Vuex?
也许你已经学习过 Vue 组件通信的方式了,知道父传子通过props
,子传父通过this.$emit
。这两种方式有时确实很好用,也很简单。当然,也许你还了解 事件总线 这种方法,但一般很少用它,在思想层面,事件总线 与 Vuex 是类似的。既然如此,何不直接使用 Vuex 呢?
如果你有使用 vue-router
经验的话,也知道组件的渲染除了直接引入,还有 <router-view>
——路由视图可以渲染组件。
props
和this.$emit
是在直接引入时才能使用。使用vue-router
渲染组件的话便没法再使用上述几种方法传递数据。并且,如果你的数据在应用的许多地方都使用到了,且这种数据的改变牵一发而动全身的话,就必须使用 Vuex。
单向数据流
什么是单向数据流?
想一想,当把一些广泛使用的数据抽离到 vuex 中后,便会有一些数据是从 vuex 流向各个组件的。还有一部分数据保留在 data
和 props
中。这些数据杂糅在一起,本身就已经比较麻烦了。
如果还有数据从 vuex 流向各个组件,这些数据被滥用,那么数据的来源和去向将难以循迹,在维护组件时会非常头疼。
例如,某个数据存储在 state 中,这个数据在异步请求数据下来后需要进行更新,只有在 state 中更新后数据流向全局,不能在某个组件里发起请求就直接在其页面上渲染,这会导致数据的流向变得混乱。
拿父传子、子传父举例,为什么传递一个数据需要使用两种方法?一个使用props
,一个使用this.$emit
?这本身就是一种单项数据流,绝不允许数据在组件间随意传递。
vuex 也是这样,数据只能从其内部流向组件。
Vuex 是什么?
Vuex 其实就是一个集中管理数据状态的地方,官方之所以称之为 状态管理 而不是 数据管理,原因在于这里存储的数据都是动态的,随时改变且发射到全局各个地方。
Vuex 应用的核心在于一个 store,也就是一个仓库。仓库的作用就是用来存放物品的。store 里有五个功能区域:
// 引入 Vue
import Vue from 'vue'
// 引入 Vuex
import Vuex from 'vuex'
// 全局使用
Vue.use(Vuex)
// 定义一个仓库
const store = new Vuex.Store({
state: {},
mutations: {},
actions: {},
getters: {},
modules: {}
})
// 导出这个仓库
export default store
state
第一个功能,是 state,即状态的意思,这里存放所有的全局数据,这里是仓库的唯一存储区域。
const store = new Vuex.Store({
// 数据放在这里
state: {}
})
mutations
第二个功能,是 mutations,这里保存所有更改 state 中数据的方法。也就是说,state 中的数据不能直接改变,所有的改变都要经过 mutations 方法。
例如,state 中有一个 number,值为 0:
const store = new Vuex.Store({
state: {
number: 0
}
})
要想改变这个 number 的值,必须在 mutations 里定义一个方法:
const store = new Vuex.Store({
state: {
number: 0
},
mutations: {
changeNumber (state) {
state.number++
}
}
})
可以看到,这个自定义方法接收 state 对象作为参数,并对 state 内部数据进行更改。
当然,这个自定义方法 changeNumber
需要一个触发条件,即,哪个组件用到了 number,并且希望更改它,就可以触发这个方法。
actions
第三个功能是 actions。actions 存在的意义是为了执行异步的对 state 的更改操作。也就是说,所有通过网络请求改变 state 数据的操作都必须经过 actions。但 actions 的作用只是一个异步请求更改数据的集中,要改变 state 中的数据还是需要提交 mutations 中的方法。也就是说,actions 的作用其实只是将数据更改的同步操作与异步操作区分开而已。这样利于数据的维护与管理,对于那些异步更改的数据可以更快定位。
actions: {
// 执行注册请求,异步更新 token 与 user 信息】
async registerFunc ({ commit }) {
try {
const res = await register()
if (res.status === 200) {
// 这里的 SaveToken 就是定义在 mutations 里的方法,actions 想要改变数据就必须提交这个方法
commit('SaveToken', `Token ${res.data.user.token}`)
commit('SaveUser', res.data.user)
}
return res
} catch (err) {
return Promise.reject(err.response)
}
},
getters
getters 可以看作 vuex 中的计算属性。其实,就是 state 中的衍生状态。例如 state 中有一个数组,在组件中需要用到这个数组的一些过滤项。这时就可以使用 getters 方法,且和 computed 属性一样,这些数据会被缓存,只有更改后才会重新渲染。
getters 接收 state 作为第一个参数:
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
getters 接收其他 getters 作为第二个参数:
getters: {
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
modules 因为比较难,本次不做介绍。
前面提到了这些方法的作用,但没有提到如何在组件中使用他们。其实,因为是直接在全局注册的 Vuex,可以在任何地方使用 Vuex 中的数据。
例如,想要使用 state 中的数据,可以通过:
// 模板中
$store.state.数据
// js 中
this.$store.state.数据
使用 mutations:
this.$store.commit('方法名')
使用 actions:
this.$store.dispatch('方法名')
使用 getters:
this.$store.getters.数据
不仅仅是组件,在 JS 文件中也可以使用 store中的数据。只需要引入即可;
import store from '../store/index'
好了,这就是 Vuex 的简单入门。后面我们会讲到传递参数以及其他的更为进阶的用法。欢迎关注。