Twlig / issuesBlog

MIT License
3 stars 0 forks source link

Vuex #45

Open Twlig opened 2 years ago

Twlig commented 2 years ago

Vuex

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

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

而Vuex 可以帮助我们管理共享状态。

Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。

State

const store = createStore({
  state: {
    count: 10
  }
})

组件获取state

  1. this.$store

Vuex 通过 Vue 的插件系统将 store 实例从根组件中“注入”到所有的子组件里。且子组件能通过 this.$store 访问到。

// 创建一个 Counter 组件
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}
  1. mapState 辅助函数

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

// 在单独构建的版本中辅助函数为 Vuex.mapState
import { mapState } from 'vuex'

export default {
  // ...
  computed: 
    // 使用对象展开运算符将此对象混入到外部对象中
    ...mapState({
        // 箭头函数可使代码更简练
        count: state => state.count,
        //...
  })
}

Getter

获取从 store 中的 state 中派生出的状态。

const store = createStore({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: (state) => {
      return state.todos.filter(todo => todo.done)
    },
    doneTodosCount (state, getters) {//Getter 也可以接受其他 getter 作为第二个参数
      return getters.doneTodos.length
    },
    getTodoById: (state) => (id) => {
      return state.todos.find(todo => todo.id === id)
    }
  }
})

组件获取getter

  1. 通过属性访问
computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}

getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的。

  1. 通过方法访问
this.$store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }

getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。

  1. mapGetters 辅助函数

mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:

import { mapGetters } from 'vuex'

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

Mutation

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

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

不能直接调用一个 mutation 处理函数。这个选项更像是事件注册:“当触发一个类型为 increment 的 mutation 时,调用此函数。”要唤醒一个 mutation 处理函数,你需要以相应的 type 调用 store.commit 方法:

//1.方法1
this.$store.commit('increment', 10)
//2.方法2
this.$store.commit('increment', {
    num: 10
})
//3.方法3
this.$store.commit({
    type: 'increment',
    num: 10
})

注意: mutation 必须是同步函数

Action

Action 类似于 mutation,不同在于:

const store = createStore({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) { //context与 store 实例具有相同方法和属性
      context.commit('increment')
    }
  }
})

分发Action

Action 通过 store.dispatch 方法触发。

this.$store.dispatch('increment')

组合Action

action中返回Promise对象

actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  },
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}

Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = createStore({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态