在學習了 vue 以後,決定作一個小練習,仿寫了一個有關購物商城的小項目。下面就對項目作一個簡單的介紹。
項目源碼: githubcss
項目的目錄結構
-assets 與項目有關的靜態資源,包括 css,以及一些 images
-common 公共的工具類方法
-components 公共組件
-common 與項目耦合度較低的組件
-content 與項目耦合度較高的組件
-network 網絡請求相關
-router 路由相關
-store vuex 相關
-views 主要展現的頁面
-home 首頁
-childcomps
-detail 詳情頁
-childcomps
-cart 購物車頁面
-childcomps
-profile 我的信息頁面
-childcomps
劃分好目錄結構後就能夠對每一個模塊進行獨立開發vue
主要實現的功能
首頁
- 頂部輪播圖的展現
- 中間選項卡點擊可進行商品的切換以及吸頂效果
- 點擊商品進入商品詳情頁
- 上拉展現更多商品
- 點擊按鈕回到頂部
詳情頁
- 根據首頁中用戶的點擊進行每一個商品的獨立展現
- 點擊商品信息對應的標題跳轉到對應的內容區域
- 滑動頁面過程當中與頂部商品信息的標題聯動效果
- 點擊按鈕回到頂部
- 加入購物車跳出彈窗
購物車頁面
- 展現各類商品的數量
- 底部工具欄展現選中商品的總價格
- 全選與取消全選
- 結算時的兩種彈窗
個人頁面
- 我的信息的展現
- 點擊個人購物車進入到購物車頁面
主要思路介紹
底部 tabbar 的封裝
移動端中比較常見的一種導航欄,須要根據用戶的點擊進行跳轉到相應的頁面,因此直接封裝一個公共的組件。佈局通常是圖片加文字,圖片通常有兩種,須要根據當前是否處於活躍狀態的路由和用戶的點擊來判斷展現哪張。內部預留插槽,用戶根據實際需求選擇底部導航的個數。每一個導航爲其配置路由。ios
頂部導航 navbar 的封裝
頂部的導航基本會出如今每一個頁面中,因此也直接封裝爲一個公共組件,佈局一般是左中右三欄佈局,因此組件內部預留三個插槽,採用 flex 佈局。git
數據請求
對 axios 再封裝爲一個 request.js 文件,讓全部的頁面都基於這個文件發送請求,這樣作能夠避免重複操做,提升了項目後期的可維護性。
在這個項目中將每一個頁面須要發送請求的部分又進行了一層封裝,即每一個頁面都有獨立的與之相對應的發送請求的文件,首頁發送請求只須要面向 home.js,詳情頁發送請求面向 detail.js。github
- 引入 better-scroll 來替換掉頁面的原生滾動,Scroll.vue 是對 better-scroll 的封裝,爲了提升組件的可複用性,可讓 probeType, pullUpLoad,等值從外部傳入(項目中 click 的值爲 true)。
- Scroll.vue 內代碼的封裝:
建立一個 Better-Scroll 對象,傳入 DOM 和 參數( probeType,click,pullUpLoad等),
監聽 scroll 事件,該事件會返回一個 position,
監聽 pullUpLoad 事件,表示該事件觸發的時候是否上拉加載更多,
封裝一個滾動的方法 this.scroll.scrollTo(x,y,time),
封裝一個刷新的方法 this.scroll.refresh(),會從新計算可滾動區域的高度,
封裝完成上拉加載更多的方法 this.scroll.finishPullUp
全局插件 Toast
- 項目中有兩個地方用到了彈窗組件,一個是點擊加入購物車時,一個是點擊結算時,若是想要使用彈窗組件那麼每次都須要導入還要加一些代碼,這樣作有點麻煩,若是有多個地方都須要使用這個彈窗組件那麼此時須要作的重複工做量太大,因此採用 Vue.use() 方法全局註冊了一個插件。
- 須要在 main.js 中 import, 而後 Vue.use() 進行註冊,Vue.use() 須要傳入至少一個參數,該參數只能是 Object 或 Function ,若是是 Object 那麼這個對象須要定義一個 install 方法,若是是 Funcion 那麼這個函數就被當作 install 方法,在 Vue.use() 執行時 install 方法會默認執行,install 方法調用時會將 Vue 做爲參數傳入。Vue.use() 除了傳入第一個參數外,也可繼續傳入一個選項對象。這裏還要注意的是 Vue.use() 必需要在 new Vue() 以前被調用,這是由於在安裝組件時,組件給 Vue 添加全局功能,因此必須寫在 new Vue() 以前,不然建立的 Vue 實例就沒法獲取插件添加的全局功能。
- 因此咱們在項目中使用了 Vue.use() 註冊了全局插件,在任何地方想使用只須要 this.$名稱 便可。
首頁
- 數據處理
banners 數組保存輪播圖的數據,recommends 數組保存推薦的數據。
還有其它的即須要展現的每一條商品的數據經過 goods 來保存。
goods: {
'pop': {page: 0, list: [ ] },
'new': {page: 0, list: [ ] },
'sell': {page: 0, list: [ ] }
}
- 最上面的輪播圖,能夠根據圖片數量自動進行輪播,也可手動滑動圖片,是一個獨立的子組件。
輪播圖下面的推薦部分是一個獨立的子組件。
- 對 goods 中的數據進行展現,封裝 GoodsList 組件,而每個商品又是一個獨立的小組件 GoodsListItem.vue。
- 點擊選項卡進行數據的切換,監聽選項卡的點擊事件,經過 $emit() 發出事件(攜帶 index),在父組件中監聽,根據 index 對應的數據類型(swicth(index)),父組件再經過 props 將 currentType 傳給 GoodsList 進行展現。
- 滑動過程當中選項卡的吸頂效果,原理是複製一個選項卡(tabControl)在頂部,默認隱藏,經過 v-show 來決定什麼時候出現,用一個變量保存原選項卡 (contentControl) 的 offsetTop,在頁面滾動的過程當中不斷獲取滾動的距離,當滾動的距離大於原選項卡 (contentControl) 的 offsetTop時,就顯示 tabControl。
- 上拉加載更多,Scroll 組件中的 pullingUp 一觸發就會發送一個事件,父組件中進行監聽而後當事件觸發的時候去請求數據便可。
- 回到頂部組件 BackTop,默認不顯示,當頁面滾動到設定的距離時顯示。點擊回到頂部須要監聽組件的點擊(@click.native),而後觸發函數 scrollTo(x,y,time) 返回頂部。
詳情頁
- 當用戶點擊商品時進入到詳情頁,首先要監聽每一個 GoodsListItem 的點擊,點擊時進行路由的跳轉,而且攜帶商品的 id,根據 id 請求數據,再將數據進行展現。
- 底部功能欄的封裝也是一個獨立的子組件,當點擊加入購物車時,將點擊事件發送給父組件 detail.vue,父組件中根據商品 id 獲取購物車(cart.vue,與 detail.vue 同級)中須要保存的商品數據,提交到 vuex 進行全局狀態管理,購物車組件中經過 $store.state 拿到數據進行渲染,加入購物車成功後彈出彈窗提示。
- 將商品添加到購物車其實有兩種狀況,一種是 vuex 中尚未這個商品的添加,另外一種是 vuex 中已經有了這個商品,因此爲了使 mutation 裏定義的函數職能單一,這裏將點擊添加購物車這個操做提交到 action 中管理,再由 action 提交到 mutation 來使 vuex 管理的狀態進行更新。
- 點擊聯動效果,商品詳情頁面中共有四個部分組成:商品,參數,評論,推薦,每一個部分都是一個獨立的子組件,當數據請求完成時去得到每一個組件的 offsetTop ($refs.組件綁定的 ref 值.$el.offsetTop) 保存在一個數組中,監聽商品詳情欄中的點擊,根據 index 觸發 scrollTo() 進行跳轉
- 滑動聯動效果,實時監聽滾動位置,經過和上面數組中的值進行比較,在 0 - 參數之間的偏移這個高度時,currentIndex 爲 0,在參數的偏移高度 - 評論的偏移高度時 currentIndex 爲1,以此類推,這樣就能夠實如今滑動的過程當中與頂部標題信息的聯動。
- 回到頂部,與首頁中的同樣。
購物車頁
- 渲染在詳情頁添加到購物車的數據,從 vuex 中拿到數據,封裝 CartList 組件,而每一條商品又是一個獨立的子組件 CartListItem。
- 底部工具欄的封裝,主要包括全選按鈕,選中商品的總價格,去結算時的彈窗。
- 全選按鈕的實現,某商品第一次添加到購物車時,須要給這件商品的數據裏定義一個是否選中 (checked)的屬性,默認爲 true。爲了管理選中的狀態,只能由 mucation 對狀態進行修改,商品最終是否展現也由 mucation 最終修改的狀態爲準,監聽全選按鈕的點擊,根據商品數組中每一個商品的 checked 屬性進行邏輯判斷便可。
- 點擊去結算組件時,先判斷當前購物車的商品列表中是否有數據,若是沒有彈出提示信息,讓用戶選中商品。
個人頁面
- 展現用戶的一些我的信息,點擊「個人購物車」跳轉到購物車頁面,this.$router.push('/cart')
項目中遇到的一些問題
- 引入 better-scroll 以後,帶來了一些問題,好比頁面會劃不動、scrollTo 跳不許、等等,致使這些問題的緣由,多數狀況下是由於請求的數據沒回來,或者拿到數據了可是頁面還未加載完,而 better-scroll 在這以前就須要計算可滾動區域的高度,但因爲圖片還沒加載完因此這個時候計算的高度並非最終的高度,因此致使滾動出現了問題,怎麼解決呢?
- 那麼這個時候能夠在某些資源加載完成是時來個 scroll.refresh(),讓 better-scroll 從新計算可滾動區域的高度,好比項目中咱們能夠監聽圖片加載事件,當有圖片加載完成時就觸發 scroll.refresh()。但這樣的話只要一有圖片加載完成就會 scroll.refresh() ,使得 refresh() 的調用太頻繁了,在此咱們能夠進行一個防抖操做來減小 scroll.refresh() 的調用。
讓首頁的內容保持原來的位置
- 項目中在瀏覽到首頁某個位置時,用戶點擊了商品而後進入了商品詳情頁或者進入了購物車等其它頁面,當再次回到首頁時發現不是以前瀏覽的位置。
- 能夠在首頁的 deactivated() 中記錄下離開時的位置信息,activated() 時再將位置設置爲原來保存的位置。
選項卡 tabControl 的吸頂效果
- 其實實現這個效果的基本思路就是首先獲取 tabControl 的 offsetTop ,怎麼獲取?this.$refs.tabControl.$el.offsetTop ,而後實時監測選項卡的滾動位置,當滾動位置達到 offsetTop 時將選項卡改成固定定位便可,這裏須要注意的是在 mounted() 中獲取的值不必定是正確的,由於可能頂部的輪播圖或者推薦部分的圖片沒有加載完,那麼如何獲取正確的值?監聽輪播圖的圖片加載狀況,加載完畢發出事件,而後在首頁中再去獲取 offsetTop。
- 按照這樣的思路實現之後,發現並無達到預期的效果,而是出現了下面的商品部分會忽然上移,並無實現停留的效果,因此換了另外一種方案,就是上文提到的先在頂部複製一份佔位選項卡,默認不顯示,而後根據滾動位置和 offsetTop 來決定何時顯示,這樣就實現了吸頂的效果。