vuex 学习之路 —— 核心

8 篇文章 0 订阅

一、state

1、单一状态树

vuex 用一个对象包含全部应用层级状态,它便成为一个“唯一数据源(ssot)”而存在,也意味着每个应用仅包含一个 store 实例

2、在 vue 组件得到 vuex 状态

vuex 通过 vue 的插件系统将 store 实例从根组件中注入到所有的子组件里,子组件便可通过 this.$store 来访问。

// 创建一个 Counter 组件

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

3、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
    }
  })
}

如果想要做局部更改的话

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


二、Getter

有时候,我们 state 中的状态会重复进行一些计算,这里我们将在 store 中定义 getter,要注意,getter 在通过属性访问时,是会作为响应系统的一部分而缓存的。

简例

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) {
      return getters.doneTodos.length
    }
  }
})


// 我们可以通过属性来访问他们
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]
store.getters.doneTodosCount // -> 1

// 也可以在任意组件中使用它
computed: {
  doneTodosCount () {
    return this.$store.getters.doneTodosCount
  }
}

// 也可以通过方法去访问
getters: {
  // ...
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }


三、mutation

之前也提到过,想要更改 store 中的状态的唯一方法就是提交 mutation,而所有的 mutation 都是同步

简例

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

    // 或者

    increment1 (state, payload) {
      state.count += payload.amount
    }
  }
})


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

// 当然也可以这样
store.commit({
  type: 'increment1',
  amount: 10
})

这里有提及到一点就是用常量来替代 mutation 事件类型,简例

// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'



// store.js
import { createStore } from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = createStore({
  state: { ... },
  mutations: {
    // 我们可以使用 ES2015 风格的计算属性命名功能
    // 来使用一个常量作为函数名
    [SOME_MUTATION] (state) {
      // 修改 state
    }
  }
})


this.$store.commit(SOME_MUTATION)

这边说到的好处是以下几点

  1. 使 lint 工具发挥作用(lint 作用其中一点就是代码风格统一,方便大型项目维护)
  2. 另一个就是集合在一个文件上,修改以及方便查阅


四、Action

action 类似于 mutation,不同在于

  1. action 提交的是 mutation, 而不是直接更改状
  2. action 可以包含任意异步操作
const store = createStore({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    increment (context) {
      context.commit('increment')
    }

    increment ({ commit }) {
      commit('increment')
    }
  }
})



// 异步调用
actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

// 以载荷形式分发
store.dispatch('incrementAsync', {
  amount: 10
})

// 以对象形式分发
store.dispatch({
  type: 'incrementAsync',
  amount: 10
})

action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters

通常 action 都是异步的,所以我们可以

store.dispatch('actionA').then(() => {
  // ...
})


// async / await
// 假设 getData() 和 getOtherData() 返回的是 Promise

actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}

PS:一个 store.dispatch 在不同模块中可以出发多个 action 函数。在这种情况下,只有当所有出发函数完成后,返回的 promise 才会执行。


五、Module

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 的状态

以下几点需要注意的是

  1. 默认情况下,模块内部 action 和 mutation 是注册在全局命名空间的,所以尽量不要重名,如果需要更高的封装度和复用性的话,可以添加 namespaced: true,给他设置命名空间。
  2. 如果想要将命名空间里的 action 公开到全局,只要设置 root: true
{
  actions: {
    someOtherAction ({dispatch}) {
      dispatch('someAction')
    }
  },
  modules: {
    foo: {
      namespaced: true,

      actions: {
        someAction: {
          root: true,
          handler (namespacedContext, payload) { ... } // -> 'someAction'
        }
      }
    }
  }
}


// 注册

import { createStore } from 'vuex'

const store = createStore({ /* 选项 */ })

// 注册模块 `myModule`
store.registerModule('myModule', {
  // ...
})

// 注册嵌套模块 `nested/myModule`
store.registerModule(['nested', 'myModule'], {
  // ...
})


store.state.myModule
store.state.nested.myModule

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值