Vue 开发实战生态篇 # 18:Vuex最佳实践

Posted 凯小默

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue 开发实战生态篇 # 18:Vuex最佳实践相关的知识,希望对你有一定的参考价值。

说明

【Vue 开发实战】学习笔记。

核心概念

  • State一this.$store.state.xxx 取值——mapState 取值
  • Getter 一this.$store.getters.xxx 取值——mapGetters 取值
  • Mutation 一this.$store.commit( "xxx" )赋值——mapMutations 赋值
  • Action 一 this.$store.dispatch( "xxx" )赋值——mapActions 赋值
  • Module

使用常量替代Mutation事件类型

Module

  • 开启命名空间 namespaced: true
  • 嵌套模块不要过深,尽量扁平化
  • 灵活应用 createNamespacedHelpers

当使用 mapState、mapGetters、mapActions 和 mapMutations 这些函数来绑定带命名空间的模块时,写起来可能比较繁琐:

computed: 
  ...mapState(
    a: state => state.some.nested.module.a,
    b: state => state.some.nested.module.b
  ),
  ...mapGetters([
    'some/nested/module/someGetter', // -> this['some/nested/module/someGetter']
    'some/nested/module/someOtherGetter', // -> this['some/nested/module/someOtherGetter']
  ])
,
methods: 
  ...mapActions([
    'some/nested/module/foo', // -> this['some/nested/module/foo']()
    'some/nested/module/bar' // -> this['some/nested/module/bar']()
  ])

可以通过使用 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'
    ])
  

实践:购物车例子

完整例子请查看:https://github.com/kaimo313/vue-development-practice/tree/master/vuex-demo2

部分代码:

vuex-demo2\\src\\store\\mutation-types.js

export const CART = 
  PUSH_PRODUCT_TO_CART: 'pushProductToCart',
  INCREMENT_ITEM_QUANTITY: 'incrementItemQuantity',
  SET_CART_ITEMS: 'setCartItems',
  SET_CHECKOUT_STATUS: 'setCheckoutStatus',


export const PRODUCTS = 
  SET_PRODUCTS:'setProducts',
  DECREMENT_PRODUCT_INVENTORY: 'decrementProductInventory'

vuex-demo2\\src\\store\\index.js

import Vue from 'vue'
import Vuex from 'vuex'
import cart from './modules/cart'
import products from './modules/products'

Vue.use(Vuex)
export default new Vuex.Store(
  state: 
    userInfo: 
      email: "xxxxxx@qq.com"
    
  ,
  modules: 
    cart,
    products
  ,
)

vuex-demo2\\src\\store\\modules\\cart.js

import shop from '../../api/shop'
import  CART, PRODUCTS  from '../mutation-types'

// initial state
// shape: [ id, quantity ]
const state = 
  items: [],
  checkoutStatus: null


// getters
const getters = 
  cartProducts: (state, getters, rootState) => 
    return state.items.map(( id, quantity ) => 
      const product = rootState.products.all.find(product => product.id === id)
      return 
        title: product.title,
        price: product.price,
        quantity
      
    )
  ,

  cartTotalPrice: (state, getters) => 
    return getters.cartProducts.reduce((total, product) => 
      return total + product.price * product.quantity
    , 0)
  


// actions
const actions = 
  checkout ( commit, state , products) 
    const savedCartItems = [...state.items]
    commit(CART.SET_CHECKOUT_STATUS, null)
    // empty cart
    commit(CART.SET_CART_ITEMS,  items: [] )
    shop.buyProducts(
      products,
      () => commit(CART.SET_CHECKOUT_STATUS, 'successful'),
      () => 
        commit(CART.SET_CHECKOUT_STATUS, 'failed')
        // rollback to the cart saved before sending the request
        commit(CART.SET_CART_ITEMS,  items: savedCartItems )
      
    )
  ,

  addProductToCart ( state, commit , product, number) 
    commit(CART.SET_CHECKOUT_STATUS, null)
    if (product.inventory > 0) 
      const cartItem = state.items.find(item => item.id === product.id)
      if (!cartItem) 
        commit(CART.PUSH_PRODUCT_TO_CART,  id: product.id, number )
       else 
        commit(CART.INCREMENT_ITEM_QUANTITY, cartItem, number)
      
      // remove number item from stock
      commit(`products/$PRODUCTS.DECREMENT_PRODUCT_INVENTORY`,  id: product.id, number ,  root: true )
    
  


// mutations
const mutations = 
  [CART.PUSH_PRODUCT_TO_CART] (state,  id, number ) 
    state.items.push(
      id,
      quantity: number
    )
  ,

  [CART.INCREMENT_ITEM_QUANTITY] (state,  cartItem: id, number ) 
    const cartItem = state.items.find(item => item.id === id)
    cartItem.quantity += number
  ,

  [CART.SET_CART_ITEMS] (state,  items ) 
    state.items = items
  ,

  [CART.SET_CHECKOUT_STATUS] (state, status) 
    state.checkoutStatus = status
  


export default 
  namespaced: true,
  state,
  getters,
  actions,
  mutations

vuex-demo2\\src\\components\\ShoppingCart.vue

<template>
  <div class="cart">
    <h2>清单</h2>
    <p v-show="!products.length"><i>请添加产品到购物车</i></p>
    <ul>
      <li
        v-for="product in products"
        :key="product.id">
         product.title  -  product.price  x  product.quantity 
      </li>
    </ul>
    <p>合计:  total </p>
    <p><button :disabled="!products.length" @click="checkout(products)">提交</button></p>
    <p v-show="checkoutStatus">提交  checkoutStatus .</p>
  </div>
</template>

<script>
import  mapGetters, mapState  from 'vuex'

export default 
  computed: 
    ...mapState(
      checkoutStatus: state => state.cart.checkoutStatus
    ),
    ...mapGetters('cart', 
      products: 'cartProducts',
      total: 'cartTotalPrice'
    ),
    // ...mapGetters(
    //   products: 'cart/cartProducts',
    //   total: 'cart/cartTotalPrice'
    // )
  ,
  // computed: 
  //   checkoutStatus()
  //     return this.$store.state.cart.checkoutStatus
  //   ,
  //   products() 
  //     return this.$store.getters['cart/cartProducts']
  //   ,
  //   total() 
  //     return this.$store.getters['cart/cartTotalPrice']
  //   
  // ,
  methods: 
    checkout (products) 
      this.$store.dispatch('cart/checkout', products)
    
  ,

</script>

以上是关于Vue 开发实战生态篇 # 18:Vuex最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

Vue 开发实战生态篇 # 15:为什么需要Vuex

Vue 开发实战生态篇 # 17:Vuex核心概念及底层原理

Vue 开发实战生态篇 # 19:Vue Router的使用场景

Vue 开发实战生态篇 # 25:单元测试的重要性及其使用

Vue 开发实战生态篇 # 20:选择何种模式的路由及底层原理

Vue 开发实战生态篇 # 21:Nuxt解决了哪些问题?