一. vuex介绍
官网介绍:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
我觉得说白了就是方便管理或则获取一些共享的数据,比如多个组件之间甚至是父组件都需要的共享数据,以便集中管理修改并且可以实时响应。 store(仓库)形容的非常体贴,比如东西放在仓库里,所有人都遵循同一个规则便可可以获取到,改变等。
props,data 和 vuex 的区别:(1)props 是 父组件向子组件传递数据时,子组件用 props 接受父数据;父子组件的关系可以总结为 prop 向下传递,事件向上传递;(2)data 是组件自身定义的私有数据;(3)vuex 是一个全局的共享的数据存储区域,相当于一个仓库。
vuex 和单纯的全局对象有以下两点不同:vuex 的状态存储是响应式的。当 vue 组件从vuex中读取状态的时候,若 vuex 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
你不能直接改变 vuex中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
二 . vuex 安装
- 直接下载 / CDN 引用,用 script标签引入。vuex代码下载
<script src="/path/to/vue.js"></script>
- NPM 或 Yarn 引入
npm install vuex --save
yarn add vuex
在一个模块化的打包系统中,您必须显式地通过 Vue.use() 来安装 Vuex:
如 vue-cli中需要在 main.js 的开头里面加上两行这样的代码:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
第一行是用ECMAScript6的import将vuex包导入进来.
第二行是Vue.js本身的插件注入语法,将插件注入Vue.js的目的是方便我们在插件内部调用它。
- 注意:
- vuex 依赖 Promise。如果你支持的浏览器并没有实现 Promise (比如 IE),那么你可以使用一个 polyfill 的库。
- 官网解释:Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。
- 使用 vuex 并不意味着你需要将所有的状态放入Vuex。虽然将所有的状态放到Vuex会使状态变化更显式和易调试,但也会使代码变得冗长和不直观。如果有些状态严格属于单个组件,最好还是作为组件的局部状态。你应该根据你的应用开发需要进行权衡和确定。
3.创建一个 vuex
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
三. vuex的核心概念
1. State:
- Vuex 应用的核心就是 store,用来保存状态数据。vuex 所管理的就是状态,其它的如Actions、Mutations都是来辅助实现对状态的管理的。vue 组件要有所变化,也是直接受到 State 的驱动来变化的。
- 将 state 从根组件注入到每一个子组件中。通过 this.$store.state.valueName 访问 state 的状态。
- 若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交(commit) mutations。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。
- mapState 辅助函数:当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性:
/ 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'
export default {
// ...
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])
mapState 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。但是自从有了对象展开运算符(现处于 ECMAScript 提案 stage-4 阶段),我们可以极大地简化写法:
computed: {
localComputed () { /* ... */ },
// 使用对象展开运算符将此对象混入到外部对象中
...mapState({
// ...
})
}
2. Getters
- Getters 负责对外提供需要处理过的 state 数据,不修改数据。
- Getters本质上是用来对状态进行加工处理。Getters(可以认为是 store 的计算属性),就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计。
- 可以通过this.$store.getters.valueName 对派生出来的状态进行访问。或者直接使用mapGetters 辅助函数,它仅仅是将 store 中的 getter 映射到局部计算属性,要求局部计算属性和getter中定义的方法名一样,类似mapState数组。
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)
}
}
})
3. Mutation
- Mutations的中文意思是“变化”,利用它可以更改状态。事实上,更改 Vuex 的 store 中的状态的唯一方法就是提交 (commit)mutation。不过,mutation触发状态改变的方式有一点特别,所谓 commit 一个mutation,实际是像触发一个事件一样,传入一个mutation的类型以及携带一些数据(称作payload,载荷)。
- mutation 必须是同步函数。
// 参数列表最多只有两个参数 : 参数1:是 state 状态, 参数2:是 commit 提交的数据
mutations: { //放置mutations方法
increment(state, payload) {
//在这里改变state中的数据
state.count = payload.number;
},
// 有时候我们需要再payload传递多个数据参数时,我们可以通过对象的方式传递
changeSearchContentByObject(state,payload){
state.searchContent="通过Object对象来修改数据:"+payload.searchContent
},
// 在组件调用 Mutation
this.$store.commit('increment', 10);
-
其中所包含的,实际上是一个个的mutation处理函数,用于指明收到这个mutation的commit之后,应该做些什么(当然,主要就是改变state,只是改变哪些state值的问题)。
-
那commit一个mutation在代码层面怎么表示呢?
this.$store.commit('increment', {
amount: 10
})
或者这样:
this.$store.commit({
type: 'increment',
amount: 10
})
4. Action
-
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
actions: { //我们会经常用到 ES2015 的 参数解构 来简化代码(特别是我们需要调用 commit 很多次的时候): addAction({ commit }) { commit("add") }, reduce(context) { context.commit('reduce') }, incrementAsync({ commit }) { setTimeout(() => { commit('add') }, 1000) } }
-
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。不过,mutation处理函数中所做的事情是改变state,而action处理函数中所做的事情则是commit mutation。
-
Action 通过 store.dispatch 方法触发:具体的触发方法是 this.$store.dispatch(actionType, payload)。所传的两个参数一个是要触发的action的类型,一个是所携带的数据(payload),类似于上文所讲的 commit mutation时所传的那两个参数。
// 以载荷形式分发
this.$store.dispatch('incrementAsync', {
amount: 10
})
或
// 以对象形式分发
this.$store.dispatch({
type: 'incrementAsync',
amount: 10
})
-
this.$store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 this.$store.dispatch 仍旧返回 Promise:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
现在你可以:
$this.store.dispatch('actionA').then(() => {
// ...
})
5. Module
- 它实际上是对于store的一种切割。由于Vuex使用的是单一状态树,这样整个应用的所有状态都会集中到一个比较大的对象上面,那么,当应用变得非常复杂时,store 对象就很可能变得相当臃肿!为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
- 模块的局部状态
- 对于每个模块内部的 mutation 和 getter,接收的第一个参数就是模块的局部状态对象。
const moduleA = {
state: { count: 0 },
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
- 同样,对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState:
const moduleA = {
// ...
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
- 对于模块内部的 getter,根节点状态会作为第三个参数暴露出来:
const moduleA = {
// ...
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}