購(gòu)物街項(xiàng)目(4)

五宣蔚、實(shí)現(xiàn)購(gòu)物車(chē)頁(yè)面功能

5.1 完成購(gòu)物車(chē)頂部導(dǎo)航的功能

  1. 在購(gòu)物頁(yè)面引入頂部導(dǎo)航組件 Navbar.vue 注冊(cè)和使用
  2. 由于頂部導(dǎo)航只有中間部分顯示信息,因此只需要使用中間插槽部分
  3. 在頂部導(dǎo)航顯示購(gòu)物商品數(shù)量
    • 使用 vuex 中的 getters 方法將 cartList 數(shù)據(jù)進(jìn)行計(jì)算長(zhǎng)度后返回
    • 將返回的長(zhǎng)度存儲(chǔ)到 cartListLength
    • 再在購(gòu)物車(chē)組件 Cart.vue 中的 computed 中拿到 getter 中的 cartListLength 返回給 cartLength
    • 再在頁(yè)面上渲染商品數(shù)量的數(shù)據(jù) {{cartLength}}
  4. 完成頂部導(dǎo)航的樣式

5.2 使用 mapGetters 函數(shù)改造5.1中的內(nèi)容

  1. 抽離 getters 中的內(nèi)容到 getters.js
    • 獲取存儲(chǔ)到 statecartList 的數(shù)組長(zhǎng)度 cartLength
    • 獲取存儲(chǔ)到 state 中的商品數(shù)據(jù) cartList
  2. Cart 組件中引入 mapGetters
    import { mapGetters } from 'vuex'
  3. 使用對(duì)象展開(kāi)運(yùn)算符將 getter 混入 computed 對(duì)象中
       computed: {
         ...mapGetters(["cartLength"])
       }
    

5.3 完成購(gòu)物車(chē)列表商品卡片

  1. 使用 Vant 組件中的 Card橙凳,CheckboxCheckboxGroup 來(lái)實(shí)現(xiàn)完整商品卡片
  2. 創(chuàng)建商品列表組件 CartList.vue 并將其引入 Cart 父組件中,注冊(cè)并使用
  3. CartList 組件中使用 mapGetters 來(lái)獲取 vuex 中的商品數(shù)據(jù)
  4. CheckboxGroup 組件將 CheckboxCard 包裹在一起,使用 v-for 進(jìn)行遍歷來(lái)顯示每個(gè)商品卡片横殴,實(shí)現(xiàn)可選擇的商品卡片
  5. 修改并調(diào)試樣式,使其滿(mǎn)足頁(yè)面布局
  6. 實(shí)現(xiàn)商品列表的滾動(dòng)區(qū)域妇智,引入 Scroll 組件滥玷,來(lái)讓替換原生滾動(dòng)
    • CartList 組件中將 scroll 的父標(biāo)簽上設(shè)置高度
    • 再在 scroll 標(biāo)簽上設(shè)置滾動(dòng)范圍氏身,即外層高度減去頭部和底部的高度
    • 由于添加了購(gòu)物車(chē)數(shù)據(jù)后可滾動(dòng)區(qū)域的高度發(fā)生了變化巍棱,因此需要調(diào)用已 scroll 的刷新
           activated() {
             this.$refs.scroll.refresh();
           },
      
    • 由于使用了 keep-alive 保持狀態(tài)的功能,需要在 activated 生命周期函數(shù)中去調(diào)用該刷新方法蛋欣,這樣在每次進(jìn)入購(gòu)物車(chē)頁(yè)面時(shí)航徙,由于滾動(dòng)區(qū)域高度有變化重新刷新計(jì)算一下

5.4 實(shí)現(xiàn)添加購(gòu)物車(chē)商品時(shí),已經(jīng)存在的商品自動(dòng)加一

  1. 先查找之前的購(gòu)物車(chē)列表中是否有該商品
    • 使用 find 函數(shù)查找 cartList 中與商品 iid 相符的數(shù)據(jù)陷虎,并返回該商品信息
         let oldProduct = state.cartList.find(item => item.iid === payload.iid)
      
  2. 然后判斷 oldProduct 是否為空到踏,即 oldProduct 是否為 true
    • 使用 if else 來(lái)判斷,當(dāng) oldProducttrue 時(shí)尚猿,oldProduct.count +=1
    • 否則 payload.count = 1窝稿,并往 cartList 中插入一條新的商品數(shù)據(jù),并且該商品中帶有 count 屬性
         if (oldProduct){
             oldProduct.count += 1
         } else {
             payload.count = 1
             state.cartList.push(payload)
         }
      
  3. 對(duì) vuex 中的 store 進(jìn)行重構(gòu)
    • mutations 中的方法盡可能完成單一的事件
    • actions 中來(lái)完成判斷邏輯復(fù)雜和異步等操作
      • 在添加購(gòu)物車(chē)時(shí)凿掂,采用 dispatch() 方法來(lái)發(fā)送操作
      • 將原本 mutations 中的 addToCartList 方法放到 actions
      • 而且接受一個(gè)與 store 實(shí)例具有相同方法和屬性的 context 對(duì)象
      • 因此將 if 判斷邏輯中的加1操作和push操作通過(guò) commit 提交
         mutations: {
             addCounter(state, payload){
                 payload.count++
             },
             addToCart(state, payload){
                 state.cartList.push(payload)
             }
         }
         actions: {
             addToCartList(context, payload){
                 let oldProduct = constext.state.cartList.find(item => item.iid === payload.iid)
             
                if (oldProduct){
                    context.commit("addCounter", oldProduct)
                } else {
                    payload.count = 1
                    context.commit("addToCart", payload)
                }
             }
         }
      
    • mutations 中的內(nèi)容進(jìn)行抽離放到 mutations.js 文件中
    • actions 中的內(nèi)容進(jìn)行抽離放到 actions.js 文件中

5.5 完成底部提交商品內(nèi)容

  1. 創(chuàng)建組件 CartBottomBar 組件伴榔,并在父組件購(gòu)物車(chē) CartList 中引入、注冊(cè)和使用
  2. 使用UI Vant 組件中的 SubmitBar 提交訂單欄組件
  3. 將添加到購(gòu)物車(chē)的商品價(jià)格計(jì)算總數(shù)顯示在 CartBottomBar 組件上的 :price
    • 在父組件 CartList 中的計(jì)算屬性 computed 中計(jì)算并存儲(chǔ)總價(jià)格 totalPrice
    • 此處通過(guò) reduce 計(jì)算累加踪少,返回一個(gè)累加函數(shù)的結(jié)果
    • 注意:由于 reduce 對(duì)空數(shù)組不執(zhí)行回調(diào),當(dāng)result數(shù)組為空時(shí)糠涛,會(huì)報(bào)錯(cuò)
    • 因此給 result 數(shù)組一個(gè)初始值0
      data() {
        return {
          result: [0]
        };
      },
      computed: {
        totalPrice() {
          // 此處通過(guò) reduce 計(jì)算累加援奢,返回一個(gè)累加函數(shù)的結(jié)果
          // 注意:由于 reduce 對(duì)空數(shù)組不執(zhí)行回調(diào),當(dāng)result數(shù)組為空時(shí)忍捡,會(huì)報(bào)錯(cuò)
          return this.result.reduce((preValue, item) => {
            return preValue + item.price * item.count;
          });
        }
      },
      
    • 再將 totalPrice 通過(guò)父子組件傳值的方式傳給 CartBottomBarprops 中的 totalPrice
    • 最后需要將 totalPrice100 給到price中 :price="totalPrice100"
  4. 實(shí)現(xiàn)全選反選各種場(chǎng)景功能
    • 全選按鈕場(chǎng)景分析:

      1. 全選按鈕為選中時(shí)集漾,所有商品全部選中
      2. 當(dāng)商品全部選中時(shí)切黔,全選按鈕自動(dòng)選中
      3. 全選中后,再次點(diǎn)擊全選按鈕帆竹,所有商品取消選中
    • 在子組件的全選按鈕上綁定 checkAll 方法绕娘,將其發(fā)送給父組件 CartList

    • 再在父組件的 <cart-bottom-bar/> 上綁定發(fā)送過(guò)來(lái)的事件 checkAllChange ,通過(guò)該事件方法觸發(fā)全選和反選效果(實(shí)現(xiàn)了場(chǎng)景1栽连、3)

         checkAllChange() {
           // 通過(guò)判斷 result 數(shù)組的長(zhǎng)度與 cartList 數(shù)組的長(zhǎng)度是否一致來(lái)進(jìn)行取反
           if (this.result.length < this.cartList.length) {
             this.$refs.checkboxGroup.toggleAll(true);
           } else {
             this.$refs.checkboxGroup.toggleAll();
           }
         }
      
    • 在計(jì)算屬性 isTotalchecked 中判斷险领,當(dāng)商品全部選中時(shí),將 isTotalchecked 傳遞給子組件 <cart-bottom-bar/>props 中的 totalChecked秒紧,并在復(fù)選框的 v-model 指令上使用(實(shí)現(xiàn)了場(chǎng)景2)

         // 判斷當(dāng)商品一一勾選后绢陌,全選按鈕自動(dòng)勾選
         getTotalChecked() {
           return this.result.length === this.cartList.length &&
             this.result.length > 0
             ? true
             : false;
         },
      
    • 注意: 1)如果將子組件中 propstotalChecked 直接在 v-model 指令上使用會(huì)出現(xiàn)(第一個(gè)vue的告警),雖然不影響功能
      [Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "totalChecked"

      2) 因此需要采用計(jì)算屬性 computedtotalChecked 的值返回給一個(gè)新屬性值 _totalChecked 在在 v-model 中使用

         _totalChecked() {
           return this.totalChecked;
         }
      

      3)但是這又會(huì)產(chǎn)生新(第二個(gè) vue 的告警)
      [Vue warn]: Computed property "_totalChecked" was assigned to but it has no setter.
      4)因此需要給 _totalChecked 設(shè)置 set 熔恢,之后再在 v-model 中引用就不會(huì)保存

         _totalChecked: {
           get: function() {
             return this.totalChecked;
           },
           set: function() {}
         }
      
  5. 改造第3步中的計(jì)算已勾選商品的價(jià)格總數(shù)
     getTotalPrice() {
           let arr = this.result;
           let total = 0;
           if (arr.length === 0) return 0;
           for (let j = 0; j < arr.length; j++) {
             total += arr[j].price * arr[j].count;
           }
           return total;
         },
    
    • 并將計(jì)算的結(jié)果返回給 totalPrice 計(jì)算屬性
    • 通過(guò)屬性綁定父子組件傳值的方式脐湾,傳遞到子組件的 totalPrice 中,并在界面渲染
  6. 上面的第 4 步使用另外一種方式避免出現(xiàn)第一個(gè)vue告警的情況:
    • CartBottomBar 組件內(nèi)的代碼直接寫(xiě)在父組件 CartList 中叙淌,就會(huì)避免采用父子組件的傳值方式秤掌,也就不會(huì)出現(xiàn)直接使用 props 中的 totalChecked 而導(dǎo)致的第一個(gè)vue告警。
    • 但第二個(gè)告警任然會(huì)出現(xiàn)鹰霍,不過(guò)只需要像注意事項(xiàng)的第 4) 條中設(shè)置 Set 就可以了闻鉴。

5.6 優(yōu)化添加購(gòu)物車(chē)方法,并引入提示

  1. 添加購(gòu)物車(chē)成功后要有 toast 提示茂洒,因此需要進(jìn)行異步回調(diào)孟岛,來(lái)提示不同的內(nèi)容
  2. addToCartList 方法中使用 Promise 函數(shù)進(jìn)行回調(diào)
    • 當(dāng)添加商品后,若是新增商品則回調(diào) resolve('添加新的商品成功')
    • 若是只是商品 +1 則回調(diào) resolve('當(dāng)前的商品數(shù)量+1')
       addToCartList(context, payload) {
         return new Promise((resolve, reject) => {
           // 2. 先查找之前的購(gòu)物車(chē)列表中是否有該商品
           let oldProduct = context.state.cartList.find(item => item.iid === payload.iid)
       
           // 3. 然后判斷 oldProduct 是否為空督勺,即 oldProduct 是否為 true渠羞,
           // 不為空就將原本商品的數(shù)量加1,為空就往 cartList 插入一條帶有 count = 1 屬性的新的數(shù)據(jù)智哀,
           if (oldProduct) {
             context.commit("addCounter", oldProduct)
             resolve('當(dāng)前的商品數(shù)量+1')
           } else {
             payload.count = 1
             context.commit("addToCart", payload)
             resolve('添加新的商品成功')
           }
         })
       }
      
  3. Detail 組件中的 addToCart 方法中對(duì) dispatch 進(jìn)行回調(diào)的內(nèi)容用彈窗 Toast 提示
     this.$store.dispatch("addToCartList", products).then(res => {
             Toast.success(res);
           });
    
    注意: 引入 Toast 組件時(shí)次询,若已經(jīng)在 main.js 中已經(jīng)引入,但直接使用任然會(huì)報(bào)錯(cuò)瓷叫。因此需要在當(dāng)前組件中再引入一次

5.7 優(yōu)化圖片懶加載的功能

  1. 需要使用 vue-lazyload 組件
  2. 引入組件 VueLazyload 并使用
     import VueLazyload from 'vue-lazyload'
     
     Vue.use(VueLazyload, {
       loading: require('./assets/img/common/placeholder.png')
     })
    
  3. 在組件 GoodsItem 組件中的圖片標(biāo)簽中使用 v-lazy 指令屯吊,這樣就可以使用懶加載的圖片了
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市赞辩,隨后出現(xiàn)的幾起案子雌芽,更是在濱河造成了極大的恐慌,老刑警劉巖辨嗽,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件世落,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)屉佳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)谷朝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人武花,你說(shuō)我怎么就攤上這事圆凰。” “怎么了体箕?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵专钉,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我累铅,道長(zhǎng)跃须,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任娃兽,我火速辦了婚禮菇民,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘投储。我一直安慰自己第练,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布玛荞。 她就那樣靜靜地躺著娇掏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪冲泥。 梳的紋絲不亂的頭發(fā)上驹碍,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天壁涎,我揣著相機(jī)與錄音凡恍,去河邊找鬼。 笑死怔球,一個(gè)胖子當(dāng)著我的面吹牛嚼酝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播竟坛,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼闽巩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了担汤?” 一聲冷哼從身側(cè)響起涎跨,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎崭歧,沒(méi)想到半個(gè)月后隅很,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡率碾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年叔营,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屋彪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绒尊,死狀恐怖畜挥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情婴谱,我是刑警寧澤蟹但,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站谭羔,受9級(jí)特大地震影響矮湘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜口糕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一缅阳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧景描,春花似錦十办、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至棠绘,卻和暖如春件相,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氧苍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工夜矗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人让虐。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓紊撕,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親赡突。 傳聞我的和親對(duì)象是個(gè)殘疾皇子对扶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354