程序员Web前端之路

VUE组件系统极速入门2--vuex

2018-01-30  本文已影响55人  flow__啊

看完了官网vuex一头雾水的,比如vuex中的...mapState、countAlias: 'count'等。没看过官网也没事,希望对你们有帮助。
下文都是我的个人理解,官网的就引用了代码,也许存在着很多错误的描述或者理解,请各位请教,我会改正的。

这个坑之前就深挖了~现在是时候填了。
为什么要有vuex?
它是管理组件与组件通信的库。
子组件与父组件之间通信还是很方便的。但是如果是同级组件,或者是其它的复杂情况,比如:

就是为了解决了类似的问题~
我们把事件想简单点吧,如果你管理那么多组件(这个改变了那个的数据,那个改变了这个的数据,那个组件又多了点什么,这个又少了点什么),你会怎么做呢?就说想法,不用实现。
我的想法是:没有蛀牙 把整个应用的数据全部集中起来,谁想用作显示都无条件同意,而谁想改,改完了一定要通知所以用到这个数据显示的组件,还不是美滋滋?
vuex也是这个意思,它一共分为以下5个部分

1 state

1.1 state 定义

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

然后mutations先提一下怎么用吧(官网的例子,原汁原味 )

store.commit('increment')

console.log(store.state.count) // -> 1

然后要把这货放在组件里,如果放入父组件中,那么子组件也可以使用。通过 this.$store。

//普通的定义
const app = new Vue({
  el: '#app',
  // 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
  store,
  components: {XXX},
  template: `XXX`
})
//*.vue
export default {
  el: '#app',
  store,
  components: {XXX}
}
</script>

1.2 关于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
    }
  })
}

先说说mapState的作用,返回一个对象。
什么样的对象

{    
    cout: xxx //第一个
    coutAlias:xxx //第二个
    countPlusLocalState(state){xxxx}//第三个
}

我尽可能说详细一点,

export default {
  computed: mapState({
   /*根据箭头函数,生成的函数为
   function(state){ return state.count},
   所以count的值最后是state.count。count是个值。*/
    count: state => state.count,
    /* countAlias只是个名字,与上面的count没有一毛钱关系,我们的大前提,
是使用的vuex里的mapstate,所以,只要我们给一个store里的属性名,
mapstate就会把属性名对应的属性返回给我们,
并用我们提供的新名字。所以,countAliae就对应的state里的count。
   */
    countAlias: 'count',
/*注意,我强调一下,如果你只给属性名,不给别名,那么就会按原属性名返回。  比如就是一个孤零零的      'a'    你调用的时候也要调a就完事了,上面的是调countAlias  */

    // 为了能够使用 `this` 获取局部状态,必须使用常规函数
    /*  箭头函数基础,亲,如果你看不懂为什么要这样,请去参考下ES6的箭头函数 */
    countPlusLocalState (state) {
      return state.count + this.localCount
    }
  })
}

接下来讲mapState的第二个作用:偷懒。
突然出现了一个...mapState,这个...就是对象展开运算符,是ES6里的,可关键是为什么搞了这么一个另人费解的东西,有什么用呢?
答:并没有什么卵用还是有点用的,比如,你的compute属性里想用store里返回的东西,一共用5个!就可以用mapstate传5个属性名进去把五个搞出来再添加到compute里。
比如

computed:{
                    a1:function(){xxxxx}
}

可是你需要a2到a6,如果一个一个的写的话,就会这样:

  computed:{
                    a1:function(){xxxxx},
                    a2:function(){return this.$store.a2},
                    a3:function(){return this.$store.a3},
                    .......
}

有了这个...

//注意,我再强调一下,如果你只给属性名,不给别名,
那么就会按原属性名返回。所以可以这么写:
compute:{
          a1:function(){xxxxx},
        ...mapState({'a2','a3','a4','a5','a6'})
}

然后就全有了,和上面的作用一样,看吧,偷懒就完事了。

2 Getter

首先要审明下,做为读书人的我,也是会骂人的!!!!
之前很多程序员总是有些很奇怪的爱好,比如他母亲的Getter。这个Getter一见到名字就是想起了被java支配的恐惧,所以不说了 所以一定要好好理解!
vuex里的getter,是在store里的一个东西,为什么需要它呢?比如,
this.$store.state.todos.filter(todo => todo.done).length这一句分别在十个函数里都出现了,就可以把他考虑做成一个getter函数。
把官网的例子搞了过来

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 会暴露为 store.getters 对象:

store.getters.doneTodos // 运行会得到 [{ id: 1, text: '...', done: true }]

Getter接受其他 getter 作为第二个参数,很奇怪是吧,但是有时候需要用到getters里的别的函数。(这里定义时不支持this直接拿)

getters: {
  // ...
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}
store.getters.doneTodosCount // 得到1

在任何组件中(只要是上一级组件有store的)都可以使用:

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

getter 为什么好好的要返回一个函数?因为你返回了一个函数,还可以给函数传值,让他查些什么玩意,难道你们没有发现上面的例子都只是没有传我们自定义的参数的?现在发现了就行~

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

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

还有剩下的mapGetters,嘿嘿,和上面的mapState一样,返回属性的规则也一样:1 别名 2 偷懒

3 Mutation

如果 1与2都是获取数据展示的话,那么3与4就是修改完通知了~
搞一手官网:

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

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})
//使用:
store.commit('increment')
//注意,这里的increment 只能通过store.commit('increment')才能触发

载荷,传事件的时候同时传个参数

既然能传一个字符串的事件类型的increment,那么一定会有别的更复杂的使用方法。妥妥的,commit里还能搞个别的东西传过去,这个就叫:载荷(payload)(我觉得可以理解成: 装备)

mutations: {
  increment (state, payload) {
    state.count += payload.amount
  }
//提交的风格有两种(还是从官网上搞的)
//风格1:
store.commit('increment', {
  amount: 10
})
//风格2:(个人觉得这个逼格更高一点点)
store.commit({
  type: 'increment',
  amount: 10
})

使用payload也是有条件的,

使用更短的名字使用Mutation&在组件中使用Mutation

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

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

然后官网也提到了使用mutations的注意事项
不能使用异步函数(等待时间不能太长的函数,什么网络连接,读写文件,都不是异步函数)

如果你的程序中,有很多'increment'这种事件,推荐用常量保存后放在一个文件中,就像这样:

//文件名: mutation-types.js
export const GO_TO_PICS = 'GO_TO_PICS'
export const GO_TO_NEWS = 'GO_TO_NEWS'
export const GO_TO_WE = 'GO_TO_WE'

4 Action

离4这个标题不远处,你依稀可以看到,mutations不能使用异步函数,
Action就是提交异步事件的~
Action 提交的是 mutation,Action提交的是Action ,提交的是 mutation。重要的事件要说三遍。所以,Action是在mutation外又加了一层,看一个简单的例子。

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

context是什么玩意?有什么用?
答:他是和 store 实例具有相同方法和属性的 context 对象。作用是[个人猜测,未验证]context对象作为store的辅助,也许是store的一些操作一定要通过context进行。

调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。

少写一点(引用自网站,纯语法):

实践中,我们会经常用到 ES2015 的 参数解构 来简化代码(特别是我们需要调用 commit 很多次的时候):

actions: {
//这是ES6的语法
/*举个例子:{ blowUp } = { blowUp: 10 }; 结果:{blowUp:10}
从blowUp中取一个叫blowUp的属性。
参数本来是传过来的context,从context中取一个commit的东西,得到  { commit : context.commit };
挺另人头大的。
*/
  increment ({ commit }) {
    commit('increment')
  }
}

使用action
store.dispatch('increment')
但是最主要是异步调用:

actions: {
  incrementAsync ({ commit }) {
    setTimeout(() => {
      commit('increment')
    }, 1000)
  }
}

同样的支持载荷(payload,装备)

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

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

官网上的一个更复杂的例子-购物车:

actions: {
// es6的语法,从context中取commit与state
  checkout ({ commit, state }, products) {
    // 把当前购物车的物品备份起来 ...对象展开
    const savedCartItems = [...state.cart.added]
    // 发出结账请求,然后乐观地清空购物车
    commit(types.CHECKOUT_REQUEST)
    // 购物 API 接受一个成功回调和一个失败回调
    shop.buyProducts(
      products,
      // 成功操作
      () => commit(types.CHECKOUT_SUCCESS),
      // 失败操作
      () => commit(types.CHECKOUT_FAILURE, savedCartItems)
    )
  }
}

上面只是定义,还有在组件中使用action的方法,不要忘记这个。
关于mapActions,作用和定义你们懂得,下面用的官网的例子:

你在组件中使用 this.$store.dispatch('xxx') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store):

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

组合 Action(全盘搬的官网,这个部分很好理解)

Action 通常是异步的,那么如何知道 action 什么时候结束呢?更重要的是,我们如何才能组合多个 action,以处理更加复杂的异步流程?

首先,你需要明白 store.dispatch 可以处理被触发的 action 的处理函数返回的 Promise,并且 store.dispatch 仍旧返回 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,我们可以如下组合 action:

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

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

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

5 Module

这个部分以后再说吧,想了解的可以去官网看看先。

上一篇下一篇

猜你喜欢

热点阅读