一文了解Vue全家桶之Vuex

一文了解Vue全家桶之Vuex

一、vuex 状态管理模式

1 - 普通的数据流管理

  • 单项数据流流向

2 - vuex 状态管理模式

  • 集中式存储管理应用的所有组件的状态
  • 以相应的规则保证状态以一种可预测的方式发生变化
    在这里插入图片描述

二、为什么要使用vuex

  • 当多个组件共享一个状态时,方便状态的管理
  • 当跨层级组件之间通信时,方便数据处理

三、核心概念

1 - State

(1)state 是什么?

  • 是项目中唯一的一个状态树
  • 包含了项目中所有组件的共享状态值

(2)state有哪些特性?

  • 一个项目中只有一个store实例
  • 和 vue实例中的data类似,具有相同的响应式规则

(3)如何使用?

  • 在组件中单独使用
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      // 在组件中获取状态值 count
      return store.state.count
    }
  }
}
  • 注入到根组件中使用
// 在store.js 文件中
Vue.use(Vuex);

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

// 在子组件中
const Counter = {
  template: `<div>{{ count }}</div>`,
  computed: {
    count () {
      return this.$store.state.count
    }
  }
}
  • 使用mapState辅助函数

a - 方式1:使用对象的方式引入

import { mapState } from 'vuex'

export default {
  // ...
  computed: mapState({
    // 01 - 箭头函数的方式
    count: state => state.count,
    
    // 02 - 普通函数需要使用this的方式 -  为了能够使用 `this` 获取局部状态,必须使用常规函数
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
    
    // 03 - 传字符串参数 'count' 等同于 `state => state.count`
    countAlias: 'count',
  })
}

b - 方式2:使用数组的方式引入

computed: mapState([
  // 映射 this.count 为 store.state.count
  'count'
])

c - 方式3:和其他计算属性相融

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

2 - Getter

(1)作用是什么?

  • 对 state 值进行处理,依赖state进行计算,并对计算结果进行缓存
  • 类似于组件中的计算属性

(2)如何使用?

  • 作为普通的函数值计算返回
const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '字段一', done: true },
      { id: 2, text: '字段二', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})
  • 作为函数返回
// 声明一个getter -> getTodoById;返回接受一个id为参数的函数
getters: {
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}

// 使用时
store.getters.getTodoById(2) 

(3)如何访问?

  • 通过store对象访问
// 在非vue组件中
store.getters.doneTodosCount  // -> 1

// 在已全局挂载store实例的vue组件中
this.$store.getters.doneTodosCount
  • 使用辅助函数mapGetters

a - 使用数组的方式访问

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

b - 使用对象的方式访问

...mapGetters({
  // 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
  doneCount: 'doneTodosCount'
})

3 - Mutation

(1)Mutation的作用是什么?

  • 用于对状态树中的state进行修改,
  • 唯一一种修改state状态的方式

(2)含有哪些特性?

  • 对于需要的状态值在一开始声明的时候最好进行一个初始化
  • 注意对对象的处理,给对象动态新增属性时的处理方式,
    • 使用Vue.set处理
    • 返回一个新对象
  • 在Mutation中只能进行同步的操作

(3)如何使用?

  • 直接使用
const store = new Vuex.Store({
  state: {
    count: 1
  },
  // 声明一个 mutation , 第一个参数接受当前实例 / 模块的 state对象
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})


// 组件中唤起一个Mutation
this.$store.commit('increment')
  • 带参数使用
mutations: {
  increment (state, n) {
    state.count += n
  }
}

// 在组件中使用
this.$store.commit('increment',10)
  • 辅助函数使用
import { mapMutations } from 'vuex'

export default {
  // ...
  methods: {
    // 数组的方式使用
    ...mapMutations([
      'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`

      // `mapMutations` 也支持载荷:
      'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
    ]),
    
    // 对象的方式使用
    ...mapMutations({
      add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
    })
  }
}

// 重点说明:使用辅助函数传参时
increment(2) => this.$store.commit('increment',2);
<div @click="increment(2)">点击</div>

(4)相关技巧?

  • 使用常量代替Mutation的名称
// store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

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

4 - Action

(1)作用是什么?

  • 通过在action内部提交commit从而实现改变state的目的
  • 主要用于处理需要异步修改state值的情况

(2)使用方式

声明:action的第一个参数接受一个类似于store实例的对象,包含store实例的方法和函数

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}
  • 直接派发
this.$store.dispatch('incrementAsync')
  • 带参数派发
this.$store.dispatch('incrementAsync',10);
  • 辅助函数派发
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')`
    })
  }
}

(3)进阶使用 - 多个Action组合使用

  • 情景一:Action 返回一个Promise,在Promise中进行判断
actions: {
  actionA ({ commit }) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        commit('someMutation')
        resolve()
      }, 1000)
    })
  }
}

// 在组件中使用时
store.dispatch('actionA').then(() => {
  // ...
})

// 在另外一个Action中使用
actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}
  • 情景二:使用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())
  }
}

5 - Module

(1)产生的原因

  • store 作为唯一的一棵状态树,当项目比较大时,可能会导致store.js 文件过于 臃肿
  • 将 store.js 以业务需求为基准进行切割,每一个文件作为一个模块,每个模块都有自己的state、Mutation、Action进行维护,也可以有自己的子模块进行维护。
const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

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

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

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

(2)模块的命名空间

  • 默认情况下,所有子模块的数据命名是注册到全局的,具有唯一性,不能重复。
  • 设置模块命名空间:
const store = new Vuex.Store({
  modules: {
    account: {
      // 在模块account中开启命名空间
      namespaced: true,

      // 模块内容(module assets)
      state: () => ({ ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
      getters: {
        isAdmin () { ... } // -> getters['account/isAdmin']
      },
      actions: {
        login () { ... } // -> dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> commit('account/login')
      },

      // 嵌套模块
      modules: {
        // 继承父模块的命名空间
        myPage: {
          state: () => ({ ... }),
          getters: {
            profile () { ... } // -> getters['account/profile']
          }
        },

        // 进一步嵌套命名空间
        posts: {
          namespaced: true,

          state: () => ({ ... }),
          getters: {
            popular () { ... } // -> getters['account/posts/popular']
          }
        }
      }
    }
  }
})
  • 在命名空间中访问全局内容:
modules: {
  foo: {
    namespaced: true,

    // 被局部化的getters接受第三个参数和第四个参数:rootState / rootGetters  
    getters: {
      // 在这个模块的 getter 中,`getters` 被局部化了
      // 你可以使用 getter 的第四个参数来调用 `rootGetters`
      someGetter (state, getters, rootState, rootGetters) {
        getters.someOtherGetter // -> 'foo/someOtherGetter'
        rootGetters.someOtherGetter // -> 'someOtherGetter'
      },
      someOtherGetter: state => { ... }
    },

    // 在 action 中,提供 rootState 属性  
    actions: {
      // 在这个模块中, dispatch 和 commit 也被局部化了
      // 他们可以接受 `root` 属性以访问根 dispatch 或 commit
      someAction ({ dispatch, commit, getters, rootGetters }) {
        getters.someGetter // -> 'foo/someGetter'
        rootGetters.someGetter // -> 'someGetter'

        dispatch('someOtherAction') // -> 'foo/someOtherAction'
        dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'

        commit('someMutation') // -> 'foo/someMutation'
        commit('someMutation', null, { root: true }) // -> 'someMutation'
      },
      someOtherAction (ctx, payload) { ... }
    }
  }
}
  • 在命名空间中注册全局内容:root:true + handler
{
  actions: {
    someOtherAction ({dispatch}) {
      dispatch('someAction')
    }
  },
  modules: {
    foo: {
      namespaced: true,

      actions: {
        someAction: {
          root: true,
          handler (namespacedContext, payload) { ... } // -> 'someAction'
        }
      }
    }
  }
}
  • 在辅助函数中使用具有命名空间的模块
// 情景1 - 直接使用
computed: {
  ...mapState({
    a: state => state.some.nested.module.a,
    b: state => state.some.nested.module.b
  })
},
methods: {
  ...mapActions([
    'some/nested/module/foo', // -> this['some/nested/module/foo']()
    'some/nested/module/bar' // -> this['some/nested/module/bar']()
  ])
}

// 情景2 - 传递模块路径为第一个参数
computed: {
  ...mapState('some/nested/module', {
    a: state => state.a,
    b: state => state.b
  })
},
methods: {
  ...mapActions('some/nested/module', [
    'foo', // -> this.foo()
    'bar' // -> this.bar()
  ])
}

// 情景3 - 使用 createNamespacedHelpers
import { createNamespacedHelpers } from 'vuex'

const { mapState, mapActions } = createNamespacedHelpers('some/nested/module')

export default {
  computed: {
    // 在 `some/nested/module` 中查找
    ...mapState({
      a: state => state.a,
      b: state => state.b
    })
  },
  methods: {
    // 在 `some/nested/module` 中查找
    ...mapActions([
      'foo',
      'bar'
    ])
  }
}

四、使用技巧

1 - 在vuex中如何处理v-model的情况

  • 使用value+input
// 双向绑定组件
<input :value="message" @input="updateMessage">
  
// vue中的使用
computed: {
  ...mapState({
    message: state => state.obj.message
  })
},
methods: {
  updateMessage (e) {
    this.$store.commit('updateMessage', e.target.value)
  }
}

// 声明的mutation
mutations: {
  updateMessage (state, message) {
    state.obj.message = message
  }
}
  • 使用含有get和set的计算属性
// 在组件中使用v-model实现双向绑定
<input v-model="message">
  
// 使用含有 get / set 的计算属性
computed: {
  message: {
    get () {
      return this.$store.state.obj.message
    },
    set (value) {
      this.$store.commit('updateMessage', value)
    }
  }
}
  

五、vuex 基础总结

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值