vuex 使用说明

2017-06-24  本文已影响0人  萧玄辞

安装

npm

npm install vuex --save

在一个模块化的打包系统中,您必须显式地通过Vue.use()来安装Vuex。

import Vue from 'vue'

import Vuex from 'vuex'

Vue.use(Vuex)

Vuex是一个专为Vue.js应用程序开发 的状态管理模式,集中式存储管理应用的所有组件状态。

状态管理包含以下几个部分状态:

state驱动应用的数据源;

view以生命方式将state映射到视图。

actions响应在view上的用户书输入导致的状态变化。

帮助我们管理共享状态,中大型单页面应用。

state

单一状态树,Vuex使用单一状态树用一个对象就包含了全部的应用层级状态。

在Vue组件中获得Vuex状态。

由于Vuex的状态存储是响应式的,从store实例中读取状态最简单的方法

就是在计算属性中返回某个状态。

创建一个Counter组件

const Counter = {

template: '

{{ count }}'

computed: {

count (){

return store.state.count

}

}

每次store.state.count变化的时候,都会重新求取计算属性,并且触发更

新相关的DOM

Vuex通过store选项,提供了一种机制将状态从根组件『注入』到每一个子组件

中(需调用Vue.use(Vuex)):

const app = new Vue({

el:'#app',

//把store对象提供给“store”选项,这可以把store的实例注入所有的子组件

store,

components: {Counter},

template: '

'

})

通过在根实例中注册store选项,该store实例会注册入到跟组件下的所有

子组件,且子组件能通过this.$store访问到。更新counter的实现:

const Counter = {

template : '

{{ count }}',

computed: {

count this.$store.state.count

}

}

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

}

})

}

当映射的计算属性的名称与state的子节点名称相同时,我们也

可以给mapState传一个字符串数组。

computed: mapState([

//映射this.count为store.state.count

'count'

])

组件仍然保有局部状态。

Getters

有时候我们需要从store中的state中 的state中派生一些状态,列如对列表进

行过滤并计算。

computed: {

doneTodosCount() {

return this.$store.state.todos.filter(todo => todo.done).length

}

}

Vuex允许我们再store中定义getters (可以认为是stroe的计算属性)

Getters接受state作为其第一个参数。

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)

}

}

})

Getters会暴露为store.getters对象:

store.getters.doneTodos // [{id:1,text: '...',done:true}]

Getter也可以接受其他getters作为第二个参数:

getters: {

doneTodosCount: (state,getters) => {

return getters.doneTodos.length

}

}

store.getters.doneTodosCount  // -> 1

我们可很容易的在任何组件中使用

computed: {

doneTodosCount() {

return this.$store.getters.doneTodosCount

}

}

mapGetters辅助函数

mapGetters辅助函数仅仅是store中的getters映射到局部计算属性。

import {mapGetter} form 'vuex'

export default {

computed: {

//使用对象展开运算符将getters混入

...mapGetters([

‘doneTodosCount’,

'anotherGetter'

])

}

}

如果你想讲一个getter属性另取名字,使用对象性时

mapGetters({

//映射this.doneCount为store.getters.doneTodosCount

doneCount: 'doneTodosCount'

})

Mutations

更改Vuex的store中的状态的唯一方式就是提交mutation Vuex中的mutation

非常类似于事件,每个mutation都有一个字符串的 事件类型 和回调函数。这个

回调函数就是我们实际进行状态更改的地方。并且他会接受state作为第一个参数。

const store = new Vue.Store({

state: {

count: 1

},

mutations: {

inctement (state) {

state.count++

}

}

})

当触发一个类型为increment的mutation时,调用此函数。”要唤醒一个

mutation handler,你需要以相应的type调用store.commit方法

store.commit('increment')

提交载荷(Payload)

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

mutations: {

increment (state, n) {

state.count += n

}

}

store.commit('increment', 10)

在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录mutation会更易读。

mutations: {

increment (state,payload) {

state.count += payload.amount

}

}

store.commit('increment', {

amount:10

})

对象风格的提交方式

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

store.commit({

type: 'increment',

amount:10

})

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

mutations: {

increment (state, payload) {

state.count += payload.amount

}

}

Mutations需遵守vue的响应规则

既然Vuex的store中的状态是响应式的,那么当我们变更状态时,监视状态的vue更新,这也意味值Vue中的mutation也需要与使用Vue一样遵守一些注意事项。

1.最好提前在你的store中初始化好所有的所需要的属性。

2.当需要在对象上提交新属性时,你应该使用

Vue.set(obj, 'newProp', 123)

使用新对象代替老对象state.obj= {...state.obj ,newProp: 123}

使用常量替代Mutation事件类型

使用常量替代mutation事件类型在各种Flux实现中是很常见的模式

export const SOME_MUTATION = 'SOME_MUTATION';

import Vuex from 'vuex'

import {SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({

state:{...}

mutations: {

//我们可以使用ES2015风格的计算属性命名功能来使用一个常量作为函数名

[SOME_MUTATION] (state) {

//  mutate state

}

}

})

mutation必须是同步函数

一条重要的原则是记住mutation必须是同步函数。

mutations: {

someMutation (state) {

api.callAsyncMethod(() => {

state.count++

})

}

}

在组件中提交Mutations

你可以在组件中使用this.$store.commit('xxx')提交mutation,或者使使用mapMutations辅助函数将组建中的methods映射为store.commit调用(需要在根节点注入store)

import {mapMutations} from 'vuex'

expor default {

methods: {

mapMutations([

methods: {

mapMutations([

'increment'  //映射this.increment()为this.$store.commit('increment')

]),

mapMutations({

add: 'increment'  //映射this.add()为this.$store.commit('increment')

})

}

])

}

}

Actions

在mutation中混异步调用会导致你的程序很难调试。

Actions

Action类似于mutation,不同在于。

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

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

注册一个简单的action

const store = new Vuex.Store({

state: {

count:0

},

mutations: {

increment (state) {

state.count++

}

},

actions: {

increment (context){

context.commit('increment')

}

}

})

Action函数接受一个与store实例具有相同方法和属性的context对象,因此你可以调用context.commit提交一个mutation,或者通过context.state和

context.getters来获取state和getters当我们在之后介绍到Modules时,

你就知道context对象为什么不是store实例本身了。

actions: {

increment({commit}){

commit('increment')

}

}

分发Action

Action通过store.dispatch方法触发:

store.dispatch('increment')

我们可以在action内部执行异步操作。

actions: {

incrementAsync({commit}){

setTimeout(() => {

commit('increment')

},1000)

}

}

Actions支持同样的载荷方式和对象方式进行分发

//以载荷形式分发

store.dispatch('incrementAsync',{

amount:10

})

//以对象形式分发

store.dispatch({

type: 'incrementAsync',

amount:10

})

在组件中分发Action

你在组件中使用this.$store.dispatch('xxx')分发action,或者使用map Actions辅助函数将组件的methods映射为store.dispatch调用

import {mapActions } from 'vuex'

export default{

methods:([

'increment'  //映射this.increment()为this.$store.dispatch('increment')

])

mapActions({

add: 'inctement'    //映射this.add()为this.$store.dispatch('increment')

})

}

组合Actions

Action通常是异步的,那么如何知道action什么时候结束。

你需要明白store.dispatch可以处理被处触发的action的回调函数返回的Promise

并且store.dispatch仍旧返回Promise

actions: {

actionA({commit}){

return new Promise((resolve)=>{

setTimeout (() => {

commit('someMutation')

resolve()

},1000)

})

}

}

现在你可以

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

//...

})

在另一个action中也可以

actions: {

actionB({dispath,commit}){

return dispatch('actionA').then(() => {

commit('someOtherMutation')

})

}

}

我们利用async/ await

//假设getData()和getOther()返回的是一个Promis

actions:{

async actionA ({commit}){

commit('gotData',await getData())

},

async actionB({dispatch,commit}){

await dispatch('actionA')  //等待actionA完成

commit('goOtherData', await getOtherData())

}

}

Modules

使用单一状态树,当应用变的很大的时候,store对象会变的臃肿不堪。

Vuex允许我们将store分割到模块。每一个模块都有自己的state, mutation,action, getters,甚至是嵌套子模块从上到下进行类似的分割。

const moduleA = {

state: {...},

mutations: {...}

actions: {...}

getters:{...}

}

const moduleA = {

state: {...},

mutations: {...}

actions: {...}

}

const store = new Vuex.Store({

modules: {

a:moduleA,

b:moduleB

}

})

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

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

模块的局部状态

对于模块内部的mutation和getter,接收的第一个参数是模块的局部状态。

const moduleA = {

state: {count:0},

mutations: {

increment (state) {

//  state模块的局部状态

state.count++

}

},

getters: {

doubleCount (state) {

return state.count * 2

}

}

}

同样对于模块内部的action, context.state是局部状态,根节点的窗台石context.rootState:

const moduleA = {

actions: {

incrementIfOddOnRootSum ({state, commit ,rootState}) {

if((state.count + rootState.count) %2 ===1){

commit('increment')

}

}

}

}

对于模块内部的getter,跟节点状态会作为第三个参数:

const  moduleA = {

getters: {

getters: {

sumWithRootCount (state,getters,rootState) {

return state.count + rootState.count

}

}

}

}

命名空间

模块内部的action, mutation ,和getter现在仍然注册在全局命名空间    这样保证了多个模块能够响应同一mutation或action.也可以通过添加前缀 或者 后缀的

方式隔离各个模块,以免冲突。

//定义getter, action ,和mutation的名称为常量,以模块名‘todo’为前缀。

export  const  DONE_COUNT = 'todos/DONE_COUNT'

export  const  FETCH_ALL =  'todos/FETCH_ALL'

export  const TOGGLE_DONE = 'todos/TOGGLE_DONE'

import * as types form '../types'

//使用添加了解前缀的名称定义,getter, action和mutation

const todosModule = {

state : {todo: []},

getters: {

[type.DONE_COUNT]  (state) {

}

}

actions: {

[types.FETCH_ALL] (context,payload) {

}

},

mutations: {

[type.TOGGLE_DONE] (state, payload)

}

}

模块动态注册

在store创建之后,你可以使用store.registerModule方法注册模块。

store.registerModule('myModule',{})

模块的状态将是store.state.myModule.

模块动态注册功能可以使让其他Vue插件为了应用的store附加新模块

以此来分割Vuex的状态管理。

项目结构

Vuex并不限制你的代码结构。但是它规定了一些需要遵守的规则:

1.应用层级的状态应该集中到单个store对象中。

2.提交mutation是更改状态的唯一方法,并且这个过程是同步的。

3.异步逻辑应该封装到action里面。

只要你遵守以上规则,如何组织代码随你便。如果你的store文件太大,

只需将action、mutation、和getters分割到单独的文件

对于大型应用,我们会希望把Vuex相关代码分割到模块中。下面是项目结构示例

├── index.html

├── main.js

├── api │

└── ... #抽取出API请求

├── components

│   ├── App.vue

│   └── ...

└── store

├── index.js     #我们组装模块并导出store的地方

├── actions.js        #根级别的action

├── mutations.js      #根级别的mutation

└── modules

├── cart.js       #购物车模块

└── products.js   #产品模块

上一篇 下一篇

猜你喜欢

热点阅读