Vue状态管理模式:Vuex入门教程

什么是 Vuex ?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

使用 Vue CLI 脚手架安装的 Vue 3.0 已经默认集成了 Vuex,下面记录的是 Vue 2.0 安装使用 Vuex 教程。

安装:

npm:

npm install vuex --save

yarn:

yarn add vuex

通过 Vue.use() 安装:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

状态管理

vuex-flow.png Vue状态管理模式:Vuex入门教程 经验总结

当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏。

  • 多个视图依赖于同一状态。

  • 来自不同视图的行为需要变更同一状态

  • 对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。

  • 对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码

我们可以把组件的共享状态抽取出来,以一个全局单例模式管理。

开始使用:

安装 Vuex 之后,在 /src 目录下 创建一个 store 文件夹,然后在该文件夹内创建一个 index.js 文件。

./src/store/index.js:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

然后在 /main.js 中引入并初始化 sotre

import Vue from 'vue'
import App from './App.vue'
import store from './store'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store: store,
}).$mount('#app')

这样就可以在任意页面调用 store 中的参数了:

console.log(this.$store.state.count)
# 0

核心实现:

每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。Vuex 和单纯的全局对象有以下两点不同:

1、Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

2、不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

最简单的 Store

StateGetterMutationActionModule

export default new Vuex.Store({
  modules: {
    
  },
  state: {
    
  },
  getters: {
    
  },
  mutations: {
    
  },
  actions: {
    
  }
})

State 单一状态树:

单一状态树,用一个对象就包含了全部的应用层级状态,作为一个“唯一数据源 (SSOT)”而存在。因此,每个应用将仅仅包含一个 store 实例。可以让我们直接地定位任一特定的状态片段,在调试过程中轻易地取得整个当前应用状态的快照。

this.$store.state.<name>

最佳实践:使用计算属性获得每次 state 中变化 :

computed: {
  titleName () {
    return this.$store.state.name
  }
},

Getters :

可以认为是 store 的计算属性,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

this.$store.getters.gettersFunc()

storecomputed 可以接受两个参数:stategetters

getters: {
  countAdd: (state) => (num) => {
    return state.count + num
  },
  countRise: (state, getters) => (num) => {
    return state.count * num + getters.countAdd(5)
  }
},

也可以在前端任意位置直接调用:

console.log(this.$store.getters.countAdd(2))
// 2

Mutation、Action Mutation(译:突变):

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type) 和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。

this.$store.commit('mutationFunc')

注意:虽然可以直接用 state 赋值的方式修改 state 的值,但是 Vuex 中只可以用 Mutation 来修改 state 的值。

不要用下面这种方法:

// 错误示范
this.$store.state.count = 3

提交载荷(Payload):

可以向 store.commit 传入额外的参数,即 mutation 的载荷(payload):

state 为要改变的 state 对象,option(payload) 为参数对象(负载对象)

//定义
mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}
//调用
this.$store.commit('increment', {
  amount: 10
})

对象风格的提交方式:

提交 mutation 的另一种方式是直接使用包含 type 属性的对象:

store.commit({
  type: 'increment',
  amount: 10
})

当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变:

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
}

建议:使用常量替代 Mutation 事件类型

mutations: {
  SOME_MUTATION_1 (state, options) {
    //...
  }
  SOME_MUTATION_2 (state, options) {
    //...
  }
}

注意:Mutation 必须是同步函数。

对象展开/辅助函数:

当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用辅助函数帮助我们生成计算属性,节省代码。

例如:我们可以使用 mapState 辅助函数帮助我们生成计算属性:

mapState 辅助函数:

作用:帮助我们生成计算属性

在 computed 中使用 mapState 展开函数,就可以直接用 this.count 来调用了。

这里的 this.count 就等价于 return this.$store.state.count :

import { mapState } from 'vuex'

computed: {
  titleName () {
    return this.count
  },
  ...mapState(['count']) //映射哪些字段就填入哪些字段
},

注意:建议仅将全局使用的属性放入 state,局部变量或组件级变量还是放入局部或组件作用域中。

mapGetters 辅助函数:

store 中的 getter 映射到局部计算属性。

import { mapGetters } from 'vuex'

//默认名称
...mapGetters(['countAdd', 'countRise']),

mapGetters 支持自定义名称:

//自定义名称
...mapGetters({
  myRise: 'countRise'
})

mapMutations 辅助函数:

提交 mutations

直接使用 store 提交:

this.$store.commit('SOME_MUTATION_1')

methods 中使用 mapMutations 辅助函数:

import { mapMutations } from 'vuex'

//methods
methods: {
  //注册
  ...mapMutations(['CHANGE_NAME']),
  //按钮点击
  btnClick: function () {
    this.CHANGE_NAME({ name: 'newName' })
  }
}

Action(译:行动):

Action 类似于 mutation,不同在于:

1、Action 提交的是 mutation,而不是直接变更状态。

2、Action 可以包含任意异步操作。

通过 store.dispatch 方法触发:

this.$store.dispatch('changeNameAction', { name: 'XXX' })

通过 mapActions

import { mapActions } from 'vuex'

...mapActions(['changeNameAction']),
this.changeNameAction({ name: 'WTF' })

组合 Action :

actionA: ({ commit }, option) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      commit('CHANGE_NAME', option)
      console.log('运行了actionA')
      resolve()
    }, 3000)
  })
},
actionB: ({ commit }, option) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      commit('CHANGE_NAME', option)
      console.log('运行了actionB')
      resolve()
    }, 5000)
  })
},
actionC: async ({ commit, dispatch }, option) => {
  await dispatch('actionA', option)
  await dispatch('actionC', { name: 'CCC' })
}
...mapActions(['actionC']),
this.actionC({ name: 'AAA' })
// AAA / CCC

Modules(模块) :

store 为单一对象,内容过多,影响执行效率及代码量过多 解决:分包(模块)

1、创建 modules 目录,以业务区分不同文件进行模块划分:

src
|__store
    |__index.js
    |__modules
        |__sectionA.js
        |__sectionB.js
        |__sectionC.js
        |__...

2、在 store/index.js 中引入:

import sectionA from './modules/sectionA'
import sectionB from './modules/sectionB'
import sectionC from './modules/sectionC'

export default new Vuex.Store({
  modules: {
    sectionA,
    sectionB,
    sectionC
  },
})

模块动态注册

store.registerModule('sectionA', {
  // ...
})

store.registerModule(['sectionB','sectionC'], {
  // ...
})


以上就是 Vuex 入门教程,有任何问题可以在下方进行评论。

未经允许不得转载:Web前端开发资源网 » Vue状态管理模式:Vuex入门教程

推荐阅读:

QQ聊天插件,鼠标划入划出显示隐藏效果。

input[type=file]去掉“未选择任何文件”及样式改进

使用 swiper 轮播插件遇到的问题及解决方法

CSS 选择器 nth-child 的几种用法

腾讯云IIS 百度HTTPS认证失败必读及抓取诊断

赞 (2)
分享到: +

评论 沙发

Avatar

换个身份

  • 昵称 (必填)
  • 邮箱 (选填)