【用於複習vuex】shopping-cart案例

回顧Vuex的時候就把這個小demo再打一遍

項目地址 https://github.com/bolonotlob...

1. 路由激活高亮顯示

  1. 方法一:vue

    • .router-link-exact-active
    <!--App.vue-->
        <template>
          <div class="app">
            <div class="nav">
              <router-link tag="li" to="/home">Home</router-link>
              <router-link tag="li" to="/market">Market</router-link>
              <router-view></router-view>
            </div>
          </div>
        </template>
        
        <script>
        export default {};
        </script>
        
        <style scoped >
        .nav li.router-link-exact-active { // 高亮
          color: red;
        }
        </style>
  2. 方法二:ios

    • linkActiveClass:個人樣式名git

      //router/index.js
          const routes = [
            { path: '/', redirect: '/home' },
            { path: '/home',component: ()=>import('@/views/Home')},
            { path: '/market',component: ()=> import('@/views/Market')}
          ]
          const router = new VueRouter({
            linkActiveClass: 'myactive', // .myactive 爲路由激活樣式名,在 App.vue 中定義 .myactive{ color: red; }
            routes
          })

2. 配置 devServer 模擬從後端獲取數據

  1. 根目錄/vue.config.jsgithub

    const products = [
            { id: 1, name: 'iphone', price: 800, inventory: 10 },
            { id: 2, name: 'iphone pro', price: 1000, inventory: 10 },
            { id: 3, name: 'iphone max', price: 1200, inventory: 10 },
        ]
        module.exports = {
            devServer: {
                before(app, serve) {
                    app.get('/api/products', (req, res) => {
                        res.json({
                            result: products
                        })
                    })
                }
            }
        }
  2. http://localhost:8080/api/products 訪問數據

3.開啓嚴格模式

const store = new Vuex.Store({
  // ...
  strict: true
})
/*
在嚴格模式下,不管什麼時候發生了狀態變動且不是由 mutation函數引發的,
將會拋出錯誤。這能保證全部的狀態變動都能被調試工具跟蹤到
*/

4. apis 請求接口配置

/*
---apis
    |---apis.js
    |---index.js
*/
// apis.js
export default {
    baseUrl: 'http://localhost:8080/api',
    getAllProducts: '/products'
}

// index.js
import apis from './apis'
import axios from 'axios'

const ajax = axios.create({
    baseURL:apis.baseUrl
})

export const getAllProducts = ()=> ajax.get(apis.getAllProducts)

5. products/actions 獲取商品信息

import * as service from '@/apis/index.js'

 actions: {
        async getAllProducts({ commit }) {
            // 發起請求獲取商品數據
            try {
                const ret = await service.getAllProducts()
                if (ret.status === 200) {
                    const products = ret.data.result
                    commit('SET_PRODUCTS',products)
                }
            } catch (err) {
                console.log(err)
            }
        }
    }

6. 渲染商品信息

7. 加入購物車

  • 這裏咱們只把商品id和數量存入cartList,固然也能夠存整個商品對象(這裏不這麼作)ajax

    //store/modules/products.js
        mutations: {
            ADD_TO_CART: (state, { id, quantity }) => {
                state.cartList.push({ id, quantity })
            },
            INCREMENT_QUANTITY: (state, { id }) => {
                state.cartList.find(v => v.id === id).quantity++
            }
        },
        actions: {
            addToCart({ commit, state }, product) {
                console.log('addToCart')
                // 判斷購物車中是否已經存在
                const cartItem = state.cartList.find(v => v.id === product.id)
                if (cartItem) { // 若是有
                    commit('INCREMENT_QUANTITY', { id: product.id })
                } else { // 沒有
                    commit('ADD_TO_CART', { id: product.id, quantity: 1 })
                }
            }
        }

8.獲取購物車信息在購物車組件顯示

  • 這裏就顯得在購物車cartList存商品對象就方便展現了,但不想這麼搞
//store/modules/cart.js
    getters: {
        getCartListInfo: (state, getters, rootState) => {
            return state.cartList.map(({ id, quantity }) => {
                const product = rootState.products.products.find(v => v.id === id)
                return {
                    id,
                    name: product.name,
                    price: product.price,
                    quantity
                }
            })
        }
    },

9.計算總價格

// store/modules/cart.js
    getTotalPrice: (state, getters) => {
        return getters.getCartListInfo.reduce((total, item) => {
            return total + item.price * item.quantity
        }, 0)
    }

10.減小庫存,庫存爲0時按鈕灰掉

//cart.js 提交 mutations 到 products.js
// 全局命名空間提交 mutations 須要加上 {root: true}
commit('products/DECREMENT_INVENTORY', { id: product.id }, { root: true })

11.全局過濾器

// main.js
Vue.filter('currency', v => '$' + v)

12.數據持久化 這裏存localstorage

  • product.vue 和 cart.vue 裏面都須要分別監聽 products 和 cartList 這兩個變量,發生改變就更新localStorage

13.購物車減小功能

  • 購物車數量減小 ,商品列表庫存增長。沒有事務,怎麼保證要麼同時成功,要麼同時失敗呢

問題記錄

1. 關於觸發store中分模塊後的子模塊的action

  • 描述:當子模塊沒有命名空間時,默認繼承父模塊的命名空間

當觸發 Actions,會把父模塊和子模塊的都觸發vuex

官方: 默認狀況下,模塊內部的 action、mutation 和 getter 是註冊在全局命名空間的——這樣使得多個模塊可以對同一 mutation 或 action 做出響應。
/*
--- store
    |---index.js
    |---modules
        |---products.js
        |---cart.js
*/

// index.js  actions
getAllProducts(){
    console.log(111)
}

// products.js  actions
getAllProducts(){
    console.log(222)
}

// Products.vue 組件 dispatch
created(){
    this.$store.dispatch('getAllProducts')
}

// 結果分析
// 咱們products 沒有使用命名空間(namespaced:true) 時,這兩個都會觸發
// 輸出結果: 
// 111
// 222

// 咱們給 products.js 加上命名空間
// namespaced: true,

// 組件Products.vue dispatch
created(){
    this.$store.dispatch('products/getAllProducts')
}

2. mapState獲取狀態時子模塊覆蓋父模塊 state 同名變量

// store/index.js
state: {
    products: [1,2]
}

// store/modules/products.js
state: {
    prodcuts:[3,4]
}

// 組件中獲取
computed: {
    ...mapState(['products']) // [3,4] 
}
// vuex.esm.js?2f62:721 [vuex] state field "products" was overridden by a module with the same name at "products"
  • mapState 的參數json

    • mapState(namespace?: string, map: Array<string> | Object<string | function>): Object
  • 下面的幾種寫法
// 1
computed: {
     products(){
         return this.$store.state.products.products
     }
}


// 2  函數形式 state做爲形參傳入
computed: mapState({
    /**
    products(state){
       return state.products.products 
    }
    */
    products:state=>state.products.products
})

// 3 對象形式
computed: mapState({
    products:'products'
})

// 4 展開運算 ...mapState
computed: {
    ...mapState('products',['products']) 
    // ...mapState('命名空間',對象/數組)
}

3.dispatch 若是有命名空間時

  • this.$store.dispatch(命名空間/actions中方法名)axios

    • this.$store.dispatch('cart/addToCart')

4. 使用localStorage 持久化時遇到的問題【重點注意】

// cart.vue
 created() {
    // 經過 localStorage 初始化購物車
    this.$store.dispatch("cart/initializeCartList");
  }
  
// products.vue
  created() {
    // 若是localStorage 有就直接從裏面拿,沒有再去服務器取
    this.$store.dispatch("products/getAllProducts");
  },
  
// 出現的問題: 
/*
當時沒注意,products.js 裏面用的是mounted 去 dispatch 的
而 cart.js 裏面是 created() 就 dispatch 了

而 cart.js 的 getters裏面返回商品信息的時候須要根據 cartList 的 id,去查詢
products 裏面商品的price,name 這些屬性。

那這個時候由於 生命週期的順序 created 在 mounted 以前(雖然不一樣組件,
並不能保證 cart組件的 created 必定在 products 組件的 mounted 以前)

cart 組件 created 時拿到了數據更新了 cartList,getters重新開始計算(像計算屬性),
可是這個時候 products 沒有拿回來數據,全部  getCartListInfo 裏面會出現 product的 屬性沒有定義會報錯
error: Cannot read property 'name' of undefined"
*/

// cart.js
getters: {
    getCartListInfo: (state, getters, rootState) => {
        return state.cartList.map(({ id, quantity }) => {
            const product = rootState.products.products.find(v => v.id === id)
            return {
                id,
                name: product.name,
                price: product.price,
                quantity
            }
        })
    }
}
  • 如何解決這個問題呢?後端

    • 就算都在 created() 裏面dispatch 也不必定能保證吧?
    • 也就是 商品列表必定要在購物車列表以前先獲取
let products = JSON.parse(localStorage.getItem('products'))
if (products) {
    commit('SET_PRODUCTS', products)
      // 在這裏去dispatch 
      // 先拿到 products 再去拿 cartList
    dispatch('cart/initializeCartList',null,{root:true})
}

記憶

// 有命名空間時 commit ,dispatch, 全局分發,...mapState
this.$store.dispatch("products/getAllProducts");
  
this.$store.commit("cart/ADD_TO_CART") 
  
commit('products/DECREMENT_INVENTORY', { id: product.id }, { root: true }) //全局分發
  
mapState(namespace?: string, map: Array<string> | Object<string | function>): Object
相關文章
相關標籤/搜索