1. Vuex
是什么
- Vuex:是一个专门为
vue.js
应用程序开发的状态管理模式,也就是一个插件 - vuex:也集成到
Vue
的官方调试工具devtools extension
,提供了诸如零配置的time-travel
调试、状态快照
导入导出等高级调试功能
2. 状态管理到底是什么
- 其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面
- 然后将这个对象放在顶层的Vue实例中,让其它组件可以使用
- 那么多个组件是不是就可以共享这个对象中所有的变量属性了呢
3. 管理什么状态呢
- 比如用户登录状态,用户名称、头像、地理位置等等可能需要多个页面共享
- 还有比如商品的收藏、购物车中的物品等等
- 这些状态信息我们都可以放在统一的地方,对它们进行保存管理,而且他们还会是响应式的
4. Vuex 安装及使用
-
下载:
npm install vuex --save // 运行时依赖
-
在
src
目录下创建一个文件夹 store (仓库的意思,约定俗成的名称) -
在
store
文件夹中创建 index.js 文件// 1. 引入模块 import Vue from 'vue' import Vuex from 'vuex' // 2. 安装插件 Vue.use(Vuex) // 3. 创建对象 const store = new Vuex.Store({ }) // 4. 导出 export default store //======================================================================================= // store实例中有以下属性: // state状态 state:{ counter:1000 }, // mutations改变 mutations:{ increment(state){ state.counter++ }, decrement(state){ state.counter-- } }, // actions行为过程 actions:{}, // getters getters:{}, // modules模块组件 modules:{}
-
在main.js页面中导入、挂载:
import store from './store' // 注:如果导入的是文件夹,会默认去找index文件 // 把 store 挂载到Vue实例中,并且给Vue原型添加属性 Vue.prototype.$store = store nue Vue({ el:'#app', store, render:h => h(App) })
-
在其他组件中访问 store 里面的属性注意:
this.$store.state.属性 // 访问状态 this.$store.commit(mutation中的方法) // 改变状态 // 注意:组件中需要加this访问,模板中不需要加this
5. Vuex
实例的属性
5.1 state
(状态)
state
是存储数据的,相当于组件中的data
属性// 定义: state:{ counter:1000, // 定义变量 student:[{},{}] // 定义数组、对象 } // 使用: this.$store.state.属性
5.2 mutations
(改变状态)
-
如果组件中需要改变
state
中的数据,就必须在mutations
中定义改变的方法,只有通过mutations
改变的状态,
才能被Vue
的工具监听到是哪个组件改变了state
中的数据// 定义: export default new Vuex.Store({ state: { counter: 1000 }, mutations:{ // 方法一:调用一次固定加一============================== increment(state){ state.counter++ }, //调用:this.$store.commit('increnment') 参数 increnment 是 mutations 中定义的方法 // 方法二:由调用方传递参数过来=========================== incrementCount (state, num) { // 第一个参数是 state ,后面可以接收调用者传递过来的参数 state.counter += num } //调用:this.$store.commit('increnmentCount', 10) } )} // 注:commit 是提交的意思,如果有需要,commit 方法中还可以追加参数 // 因为是更改数据,所以调用也叫提交,以上的提交属于普通提交
-
mutations另一种提交方式(对象)
// 定义 mutations:{ increment (state, payload) { // 第一个参数是 state ,后面可以接收调用者传递过来的参数 } } // 调用、提交 this.$store.commit({ type:'increnment', counter, // 等同于counter:counter }) // 注:这样传递过去之后,increment 方法的参数 payload 就变成了一个对象{'type:'increnment',counter:x} // 在mutations里的increnment函数中用形参来接收然后:payload.counter 就可以取出
-
mutations响应规则
1. Vuex 的 store 中的 state 是响应式的,当 state 中的数据发生变化时,Vue 组件会自动更新 2. 响应式只针对初始化时就定义好的数据,(初始化时会将数据添加到响应式系统中) 3. 如果后面操作添加、修改、删除数据,需要响应式的话: 4. 只能用响应式的方法:数组的 push、splice, Vue的set、delete
-
mutations类型常量
1. 为了避免出错,官方推荐方法名字统一定义成常量 // 在 store 目录下创建一个 js 文件用来存储常量,如:mutations-types.js export const INCREMENT = 'increment' // 在需要使用的页面导入 import {INCREMENT} from './mutations-types.js' //使用:mutations 中定义方法的时候 [INCREMENT](state) { // 把 increment 改成常量,这样哪怕常量页面的'increment'写错了,这里也会跟着变 state.counter++ } //使用:组件中提交 this.$store.commit(INCREMENT)
-
mutations 中的异步函数问题
1. 官方强调尽量不要在mutations定义异步函数,因为异步函数,能改变数据,但是store监控不到 2. 改变的只是用户页面的数据,state的数据改不了 3. 如果有些时候必须使用异步函数,那么就要使用actions了
5.3 actions
(异步操作)
- actions 就是给
mutations
做异步操作的 - 把异步函数定义在
actions
中,方法定义在mutations
中,然后在actions
的异步函数中,调用mutations
的方法// 定义: actions:{ // 参数context上下文的意思,在这里相当于store对象 updateInfo(context){ srtTimeout(() => { context.commit('mutations中需要异步操作的函数') },1000) } } // 调用: $store.dispatch('updateInfo') // dispatch 方法中也可以追加参数 // 注:提交异步的时候使用 dispatch,提交的是 actions 的方法
- mapActions 辅助函数可以将 store 中的 action 映射到组件的 methods 中:
import { mapActions } from 'vuex' export default { methods: { ...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')` }) } }
5.4 getters
(计算属性)
- getters相当于组件中的计算属性,
getters
中定义的方法,有两个默认参数state
、getters
// 示例: export default new Vuex.Store({ state: { students: [ {id:1, name:'张三', age: 18}, {id:2, name:'张三', age: 28}, {id:3, name:'张三', age: 38}, {id:4, name:'张三', age: 48} ] }, getters: { // 方法一:筛选年龄大于 30 的学生============================== filterStu (state) { return state.students.filter(ele => ele.age > 30) }, // 调用:$store.getters.filterStu // 方法二:获取年龄大于 30 学生的人数========================== filterStus (state, getters) { return getters.filterStu.length }, // 调用:$store.getters.filterStus // 方法三:传递年龄参数,动态筛选学生=========================== filterStuAge (state) { return function (age) { return state.filter(ele => ele.age > age) } } // 调用:$store.getters.filterStuAge(18) } }) // 注意:如果需要传递除了state、getters 之外的参数进来,那么 getters 中定义的方法里面必须返回一个函数, // 外面在调用的时候,可以传递参数进来,函数里面是业务代码。实例参考方法三 // 如果不需要传另外的参数的话,直接以 $store.getters.filterStuAge 这样的形式调用
- mapGetters 辅助函数:
mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性中:// 注:一般使用计算属性,是需要获取到计算属性里面通过计算的数据 // 案例:把计算属性中数据渲染到页面中,一般是先把这里的计算属性,放到组件里的 computed 计算属性方法中 // 1. 第一种方式: computed: { filterStuAge () { thsi.$store.getters.filterStuAge } } // 2. 第二种方式: import { mapGetters } from 'vuex' // 通过vuex的导入getter计算属性方式导入(这是固定写法,导入所有的计算属性方法) computed: { // A. 通过数组的形式解构 ...mapGetters(['cartLength','cartList']) //通过数组的方式解构vuex中的计算属性的方法 //cartLength和cartList为解构出来的计算属性,可以直接使用 // B. 通过对象的形式解构 ...mapGetters({ length: 'cartLength', // 对象的形式解构,可以自定义名字解构,使用的时候,如:length 、list 就可以了 list: 'cartList' }) }
5.5 modules
(模块)
Vue
是单一状态树,那么意味着很多的状态交给Vuex
来管理,当应用变的非常复杂时,store
对象就有可能变得相当臃肿。- 为了解决这一问题,
Vuex
允许我们将store
分割成模块,而每个模块拥有自己的state
、mutations
、actions
、getters
等// 定义: modules:{ a:{ state:{ name:'张三' }, mutations:{}, actions:{ fn (context) {} // 方法中也可以访问根模块的数据,如:context.rootState.xxx }, getters:{ fn (state, getters, rootState) {} // 这里面的函数有三个参数,最后一个是根模块的state } } } // 调用: $store.state.a.name // 以这种方式来调用模块中的state.... $store.commit('mutations方法') // 会先从外面的mutations找有没有此方法,没有则进入模块找 $store.getters.xxx // 会先从外面的mutations找有没有此方法,没有则进入模块找
6. store 代码抽离
- 项目复杂的情况下一个index.js文件显得要臃肿,官方推荐的抽离方式:
state不动 mutations 放在 mutations.js文件中 actions 放在 actions.js文件中 getters 放在 getters.js文件中 modules 不动,但是里面的模块要抽离出来,新建一个文件夹,里面一个模块建一个js文件 最后把对应的js页面导入到index.js中,然后放在store中对应的位置