Vue基础-Vuex

370 阅读5分钟

写在前面:

1.本文是个人课上学习内容的总结和梳理,主要知识点来自于“开课吧”线上直播课程,以及Vue官方文档。

2.目前尚处于Vue乃至前端入门阶段,因此很多内容理解的不是很透彻,文章更多是用来学习记录而非干货分享。

因此,建议如果需要解决项目问题,还是去看一下其他大佬的文档已经vue官方文档(一手资料)

Vuex

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

这是官网给出的解释,看起来很绕。其实通俗的来讲,Vuex就是跟我们的vue项目提供了一个仓库用来存储所有组件的状态/数据等等,仓库只有一个入口和出口,并且有人看管,出入还需要登记记录,这样我们只要看记录就知道谁取了数据,对数据做了什么事。这样我们就把组件共享的状态都抽离了出来,交给Vuex实例来管理,方便我们管理一个大型项目。所以,Vuex主要是用来解决以下问题:

  • 管理项目所有组件的状态
    • 多个视图依赖于同一状态
    • 来自不同视图的行为需要变更同一状态

通过这样一番操作,我们的组件树就变成了一个单一且庞大的视图,不管在视图树的哪个细枝末节,任何组件都能获取到他的状态和触发行为。并且,通过预设定规则,我们可以强制组件状态间保持独立,代码也就变得更加结构化且易于维护。

安装

npm install vuex --save

使用

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

Vue.use(Vuex) //类似koa,通过这种中间件的方式,使用Vuex

const store = new Vuex.Store({  //实例化一个唯一的Vuex.Store实例
  state: {  //用来存放所有组件依赖的、我们需要统一管理的状态
    count: 0
  },
  mutations: {  //用来存放所有组件中需要用到的、我们需要统一管理的、用来改变状态的行为
    increment (state) {
      state.count++
    }
  }
})

通过这种方式,我们创建了一个最简单的Vuex仓库的实例(一个项目中只能有唯一的Store实例),通过这种单一状态树,我们才能做到强制的管理所有组件的状态。

state

上面我们已经实例化了一个状态仓库store,其中的store.state就是用来存放所有状态的单一状态树,那么我们要如何在组件中获取到需要的状态呢。最简单的办法就是用计算属性computed来获取。

通过计算属性获取

// 创建一个 Counter 组件
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return store.state.count 
    }
  }
}

通过计算属性中的store.state.count我们就拿到了需要的count数据,如果数据发生了变化,也会自动计算count的计算属性值。但是这样,会让每个组件都依赖全局变量store。所以,Vue提供了一种将store注入到所有子组件当中的方式:

const app = new Vue({
  el: '#app',
  // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
  // store:store,
  store,
  components: { Counter },
  template: `
    <div class="app">
      <counter></counter>
    </div>
  `
})

这样,就把store对象注入到了所有子组件内部,这样子组件就可以通过this.$store访问到。

const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}

通过以上两种利用计算属性直接监听store.state的方式,就轻松的取到了我们需要的状态信息。但是试想一下,这是只有一个状态,用计算属性写起来还算清晰,但是如果我们组件里面有20个状态需要获取,难道我们要写20个computed的方法吗?显然,虽然可以这么干,但是太不优雅和美观了。

所以,vuex给我们提供了一种函数方法,优雅的解决这个问题——mapState辅助函数。

通过mapState辅助函数获取

mapState辅助函数返回的是一个对象,通过对象运算展开符...,我们就可以吧这个对象传递给该组件,与本地局部状态一起使用。

computed: {
  // 使用对象展开运算符将此对象混入到外部对象中
  ...mapState({
    'count','...','...'
  })
}

Getters

先回想以下使用computed计算属性的最初原因是什么——是我们需要对响应式数据进行一些派生的处理,比如,count++,列表循环等等。我们把这些处理派生逻辑行为的方法都放到了computed里面,进行集中管理。同理,vuex也给我们提供了一种集中管理state派生状态的方法:Getters

它和计算熟悉一样:

  • 会缓存
  • 会响应数据状态的变化
  • 集中管理所以派生方法

初始化

在初始化store对象的时候,Getters接收state作为第一个参数传入,以下是使用方法:

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
// 初始化getters
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    },
    ...:...,
  }
})

getter通用暴露给了外部一个store.getters的对象进行访问。

通过属性访问

通过属性可以直接获取到我们需要的值store.getters.doneTodes,因此我们就可以在子组件中方便的获取到:

computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodos
    // return store.getter.doneTodos
  }
}

通过方法访问

我们可以让getters返回一个回调函数,实现给getters的传参,这一般在查询数组是非常好使。

getters: {
  // ...
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

通过mapGetters辅助函数获取

同样,Vuex也给我们提供了一个回调函数,优雅的获取getters。

import { mapGetters } from 'vuex'

export default {
  // ...
  computed: {
  // 使用对象展开运算符将 getter 混入 computed 对象中
    ...mapGetters([
      'doneTodosCount',
      'anotherGetter',
      // ...
    ])
  }
}

mutation

mutation给我们提供了唯一一个变更state状态的方法,通过这个我们就可以监控到所有组件变更state的行为。

mutation类似于事件监听,每个matation提供了一个字符串的事件类型(Type)和一个回调函数(Handle)。回调函数是用来改变state状态的地方,并且state将会作为函数的第一个参数传入:

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

调用mutation

我们调用mutation的时候,不能使用对象进行操作:store.mutations.increament,因为Vuex给我们规定了一个唯一的提交更新的入口: store.commit('increament')

提交payload

每个mutation的第二个参数是payload,也就是传入的参数:

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

这样在外部调用的时候,可以通过payload传入参数:store.commit('increament',10),这样就实现了参数的传入。

需要注意的一点是:mutation是一个同步函数,如果需要在处理过程中调用异步方法,那么就需要用到下面讲的:action

action

actionmutation声明和调用的方式都很类似,唯一不同的是:

  • action内部是支持调用异步方法的。
  • action提交的是mutation,而不是直接更改state

声明action

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }
  }
})

同样的,action也支持设置payload来传入参数,这里就不展开来了。

调用action

同样的action提供了dispatch发的方式进行调用:store.dispatch('increment')。或者,可以使用action提供的辅助函数mapActions进行调用:

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')`
    })
  }
}

以上,便是Vuex的基本用法,主要包括:如何初始化、state、getter、mutation、action的用法。

此外,vuex提供了模块化的方法,这个准备单独开一篇文章来写,写好之后会附上链接。 计划在本周末(12月5日或6日)写一个应用到所有功能的demo,到时候会更新上github链接。