Vue仿蘑菇街項目需求記錄(B站codewhy老師)


項目需求分析

1 項目前期准備工作

1. cli搭建項目
2.css樣式導入 公共資源圖片等的導入
3.別名的配置和代碼規則
 <script>
   module.exports = {
 configureWebpack: {
   resolve: {
     alias: {
       'assets': '@/assets',
       'common': '@/common',
       'components': '@/components',
       'network': '@/network',
       'views': '@/views',
    }
  }
}
  </script>
<script>
   root = true
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
</script>

2 底部導航欄封裝

3 Home 頂部導航欄封裝(因我在其他的頁面中也有這個組件)

4 Home組件首頁輪播圖組件導入

5 安裝axios 網絡封裝

 <script>
   import axios from 'axios'
export function request(config) {
 // 1.創建axios的實例
 const instance = axios.create({
   baseURL: 'http://123.207.32.32:8000/api/h8',
   //baseURL: 'http://106.54.54.237:8000/api/v1,
   timeout: 5000
})

 // 2.axios的攔截器
 // 2.1.請求攔截的作用
 instance.interceptors.request.use(config => {
   return config
}, err => {
   // console.log(err);
})

 // 2.2.響應攔截
 instance.interceptors.response.use(res => {
   return res.data
}, err => {
   console.log(err);
})

 // 3.發送真正的網絡請求
 return instance(config)
}

6 在組件剛一創建的時候就發送網絡請求,在created生命周期函數進行網絡請求

輪播圖數據展示/推薦數據展示/流行數據展示(feature)

7 Tab'Cont'rol業務分析 因為在其他頁面還有使用 應該是在業務組件中

8 請求首頁goods數據

點擊3個不同的按鈕請求不同的數據,首頁列表數據請求保存

goods: {
   'pop': {page: 1, list:[]},
   'new': {page: 1, list:[]},
   'sell': {page: 1, list:[]}
}
//發送HomeGoods商品列表數據 需要進行參數傳遞  
export function getHomeGoods(type, page) {
 return request({
   url: '/home/data',
   params: {
     type,
     page
  }
})
}
//請求數據
getHomeGoods('pop',1).then(res => {
     console.log(res);
})
8.1如何點擊不同tabControl 請求不同的數據

 // 2 請求homeGoods商品數據
   this.getHomeGoods('pop')
   this.getHomeGoods('new')
   this.getHomeGoods('sell')

getHomeGoods(type) {
     const page = this.goods[type].page + 1
     getHomeGoods(type,page).then(res => {
     // console.log(res.data.list);
     this.goods[type].list.push(...res.data.list)
     this.goods[type].page += 1
  })
}
8.2對商品列表分析,和業務相關 而不是公共組件了

 

然后把數據傳遞到goodsList goodsListItem

9 引入better-scroll 解決在移動端滾動卡頓問題

需求: 為了減少對better-sroll的依賴,對better-scroll 進行封裝 而且在多數頁面需要用到,在公共組件進行創建創建,將引用插件進行封裝成一個組件,那里需要進行導入這個組件即可,后期維護也方便,邏輯也i清晰

better-scroll安裝:
 1. 終端通過npm安裝: npm install better-scroll --save 
<template>
 <div class="wrapper" ref="wrapper">
   <div class="content">
     <slot></slot>
   </div>
 </div>
</template>

<script>
import BScroll from 'better-scroll'
export default {
 data() {
   return {
     scroll: null
  }
},
 props: {
   probeType: {
     type: Number,
     default() {
       return 0
    }
  }
},
 mounted() {
   //1 創建BScroll實力對象
   this.scroll = new BScroll(this.$refs.wrapper, {
     click: true,
     probeType: this.probeType  //默認為不觸發scroll滾動事件,那個頁面需要進行傳值即可,這樣性能也大大提高
  })

滾動原理:

 

10 點擊backTop按鈕返回Home頂部

需求:

 

需求分析:
  1. 首先該功能會在頁面滾動某個臨界值進行顯示或隱藏,其次不會隨着頁面滾動而滾動,所以簡單方法進行fixed 固定定位

  2. 對頁面需要進行實時監聽滾動位置

  3. 該需求是在Home 首頁組件進行監聽,也就是需要對backTop這個組件進行監聽

下面進行代碼實現:
  1. 新建BackTop組件,引入一張箭頭圖片,如下

    <template>
     <div class="back-top" >
       <img src="~assets/img/common/top.png" alt="">
     </div>
    </template>
    <script>
    export default {
    }
    </script>
    <style scoped>
    .back-top {
     position: fixed;
     right: 8px;
     bottom: 50px;
    }
    .back-top img {
     width: 42px;
     height: 42px;
    }
    </style>
  2. 在Home首頁組件導入並注冊:(需要注意的是這個組件是不需要隨着滾動的)

  3. 對這個組件進行事件點擊監聽 --------組件監聽需要用到事件修飾符native(這里也可以在backTop組件內部進行點擊事件,然后把這個事件發送出來在進行監聽,這樣做不過相對繁瑣,還多寫了代碼)

    ![1587051148799](C:\Users\NB\AppData\Roaming\Typora\typora-user-images\1587051148799.png)

    然后通過ref拿到scroll這個組件調用scrollTo()這個方法

     backTopClick() {
         // console.log(this.$refs.scroll.scroll);
         // this.$refs.scroll.scroll.scrollTo(0, 0, 400);
         this.$refs.scroll.scrollTo(0, 0, 400);
    }

    //scrollTo(x, y, time, easing)
    //參數:
    //{Number} x 橫軸坐標(單位 px)
    //{Number} y 縱軸坐標(單位 px)
    //{Number} time 滾動動畫執行的時長(單位 ms)
    //{Object} easing 緩動函數,一般不建議修改,如果想修改,參考源碼中的 ease.js 里的寫法
    //返回值:無
    //作用:滾動到指定的位置,引用:https://ustbhuangyi.github.io/better-scroll/doc/zh-hans/api.html#scrolltox-y-time-easing
    //this.$refs.scroll.scroll.scrollTo(0, 0, 400)
    //此代碼不方便閱讀,對奇進行封裝,把插件scrollTo()封裝到組件scrollTo中 看代碼吧 這樣更清楚

4.對Home頁面滾動進行實時監聽,而我們封裝的BScroll滾動插件是scroll這個組件,這里需要在scroll組件進行監聽,然后把這個事件發送出了

 mounted() {
   //1 創建BScroll實力對象
   this.scroll = new BScroll(this.$refs.wrapper, {
     click: true,
     probeType: this.probeType
  }),
   //發送事件監聽事件
   this.scroll.on('scroll', (position) => {
     // console.log(position);
     this.$emit('scroll',position)
  })
}

然后在首頁接受這個事件,設置一個變量控制backTop顯示隱藏

 contentScroll(position) {
     console.log(position);
     this.isShow = -position.y > 1300 ? true : false
}

 目前代碼效果:

 

 

11 點擊商品列表進入商品詳情頁

分析: 每個商品都有不同id,所以在點擊每個goodslistitem,我們需要拿到這個唯一的商品id,去后台接口請求對應的商品數據,然后在進行展示,而且從home也跳轉到詳情頁,我們使用路由就可以

1 對goodslistitem進行點擊事件,並將商品id傳過去
goodsItemClick() {
     console.log('111',this.goodsitem.iid);
     this.$router.push('/detail/'+ this.goodsitem.iid)
}

//路由跳轉傳參 可以有2種方式 1 動態路由 2 query方式
goodsItemClick() {
     // console.log('-----',this.goodsitem.iid)
     //動態路由
     this.$router.push('/detail/'+ this.goodsitem.iid)
     //query方式
     this.$router.push({
       path: '/detail',
       query: {
         iid: this.goodsitem.iid
      }
    })
  }

//路由配置方式也不一樣
{
   path: '/detail/:iid', //動態路引
   component: Detail
},
{
   path: '/detail',    //query方式
   component: Detail
}

//獲取iid方式
this.iid = this.$route.params.iid
this.iid = this.$route.query.iid
// 另外在url中顯示也有差異 如下

2 拿到iid在network請求數據,為了方便后期維護 閱讀,我們單獨新建詳情頁網絡請求 dedtail.js
import {request} from './request'

export function getDetail(iid) {
 return request({
   url: '/detail',
   params: {
     iid
  }
})
}
之后在Detail組件剛一創建出來created生命周期函數進行網絡請求

12 上拉加載更多

在封裝好的 BScroll 中 調用上拉加載更多事件,然后把這個事件發送出來,然后在home頁 執行並調用finishPullUp()

 //上拉加載更多
   this.scroll.on('pullingUp',() => {
     // console.log('上拉加載更多');
     this.$emit('pullingUp')
     // this.scroll.finishPullUp()
  })

home頁進行監聽

 //上拉加載更多
   pullUpClick() {
     // console.log('上拉加載');
     this.getHomeGoods(this.currentType)
     this.$refs.scroll.finishPullUp()
  },

13 解決首頁 詳情頁 滾動卡頓問題

  • 分析:
  • 主要原因是圖片加載影響 better-scroll計算可滾動區域問題,不能滾動的問題主要是因為圖片加載完成后,這個時候better-scroll得到可滾動區域height,沒有刷新,所導致

  • 我們對每一張加載圖片進行監聽 ,只要有一張圖片你加載完成,我們就重新計算一次Bscroll的高度

    • 如何監聽圖片加載完成

      • 原生js監聽 img.load = () => {}

      • Vue提供的加載方式 @load=() => {}

    • 然后調用better-scroll的refresh()

      //refresh()
      //參數:無
      //返回值:無
      //作用:重新計算 better-scroll,當 DOM 結構發生變化的時候務必要調用確保滾動的效果正常。
  • 這里涉及到非父子組件事件通信,我們可以使用Vuex或者事件總線 這里我們先使用事件總線

  • 1 事件總線使用方法:
    • main.js入口文件 掛載到Vue原型實例上,全局任何地方都可以使用

    • Vue.prototype.$bus = new Vue()
    • 發送事件

    • this.$bus.$emit('itemImageLoad',params)// 事件函數, 參數
    • 接收事件

    • this.$bus.$on(""callback)  // 事件函數 回調函數 一般是組件運行階段,mounted函數中執行
    • 取消事件

    • this.$bus.$off(""callback)  // 事件函數 回調函數 取消事件是組件是否有keep-alive 有選擇deactivated 否destroyed中
  • 2 使用Vuex進行非父子組件通信
    • 先創建·1一個變量,對這個變量植進行watch監聽,每加載一張圖片,變量植發生改變

14 實現詳情頁上下聯動聯動效果,點擊對應標題滾動到對應的位置和內容滾動顯示正確標題

  • 效果圖:

  • 點擊標題跳轉到對應位置

    • 每給標題對應一個值,只要我們拿到這個值,在點擊標題的時候,調用scrollTo就可以滾 動到對應位置,所以我們新建一個數組,來保存offsetTop

    • 首先對詳情頁 標題 進行監聽

    • 獲取 詳情頁 所有組件的offsetTop

      • 如何獲取 不同組件 的offsetTop

        • 首先我們會想到在mounted中獲取offsetTop,然而獲取的值完全不對,甚至出現了undefined

        • mounted() {
             console.log('-----')
             this.themeTops = [];
             this.themeTops.push(0);
             this.themeTops.push(this.$refs.paramsinfo.$el.offsetTop);
             this.themeTops.push(this.$refs.commentinfo.$el.offsetTop);
             this.themeTops.push(this.$refs.recommend.$el.offsetTop);
             console.log(this.themeTops);
          },
          //[0, undefined, 498, 626, __ob__: Observer]
        • 值獲取不正確,首先想到的是圖片影響,之前我們有對圖片的加載進行監聽,圖片加載完成后,我們再獲取offsetTop值

        • //對詳情頁圖片監聽
             detailImageLoad() {
               //獲取參數 評論 推薦的offsetTop值
               this.themeTops = [];
               this.themeTops.push(0);
               this.themeTops.push(this.$refs.paramsinfo.$el.offsetTop);
               this.themeTops.push(this.$refs.commentinfo.$el.offsetTop);
               this.themeTops.push(this.$refs.recommend.$el.offsetTop);
               console.log(this.themeTops);
                 
               this.$refs.scroll.refresh();
            },
          // 打印結果 [0, 9289, 10031, 10180, __ob__: Observer]
        • 然后調用scrollTo方法

        • titleClick(index) {
              // console.log(index);
              this.$refs.scroll.scrollTo(0, -this.themeTops[index], 300)
          },
    • 內容滾動顯示正確標題

      • 分析: 我們需要實時對頁面滾動進行監聽 ,在better-scroll中把scroll這個事件發送出來,我們在Detail進行接受這個事件

      • 通過獲取到posiiton.y 和之前獲取到 this.themeTops這個數組中4個值進行比較 然后動態改變標題

14 Vue原生上下聯動或左右聯動效果

  • 上下聯動或左右聯動效果非常常見 但是我們平時大多使用封裝好的UI工具庫,直接按需導入安裝,這里我們不使用插件和UI

  • 這里我們主要簡單建立三個組件

  • 1 點擊ShopTitles中小titles 讓活躍title顯示背景顏色 其他不變,這個相對簡單

  • 2 點擊不同title讓右邊的Shop組件跳轉到不同位置

    • 1 首先要監聽SHop組件的滾動,其次調用JS原生滾動方法 scroll

    • 2 對Shop組件添加滾動事件 需要注意的是 addEventListener第三個參數為true  ,

    • 3 父元素 overflow不能是hidden,而是scroll,要不然子元素滾動不了 (當然子元素內容高度高於父元素高度)

//父組件
<template>
 <div class="category">
  <div class="nav-bar" >
    <div class="nav"> 我是頂部的商品展示區域</div>
  </div>
  <shop-titles :titles="titles" @titleClick="titleClick" ref="titles"/>
  <shop ref="shop" @shopOpsitons="shopOpsitons" />
 </div>
</template>

<script>
import Shop from 'components/common/shop/Shop'
import ShopTitles from 'components/common/shop/ShopTitles'
export default {
 name: 'Category',
 data() {
   return {
     titles:['熱賣','特色精品','精選熱菜','熱賣','特色精品','精選熱菜'],
     shopScrollTop:[0, 300, 600, 900, 1200, 1500],
     current: 0
  }
},
 components: {
   Shop,
   ShopTitles
},
 mounted() {
 
},
 methods: {
  titleClick(index) {
    this.$refs.shop.$el.scrollTo({left: 0, top: this.shopScrollTop[index],behavior: 'smooth'})
  },
 shopOpsitons(saveY) {
   // console.log(saveY)
   const Max = Number.MAX_VALUE;
   this.shopScrollTop.push(Max);

   const length = this.shopScrollTop.length;
   for( let i = 0; i < length; i++) {
     if((this.current !== i)&&(saveY >= this.shopScrollTop[i] && saveY < this.shopScrollTop[i+ 1])) {
       this.current = i
       this.$refs.titles.currentIndex = this.current
    }
  }
   
}
}
}
</script>
<style scoped>
.category {
 height: 100vh;
}
.nav-bar {
 width: 100%;
 border-bottom: 2px solid #333;
 height: 160px;
 margin: 20px 0;
 position: fixed;
 z-index: 10;
}
.nav {
 width: 90%;
 height: 160px;
 background-color: #ccc;
 position: fixed;
 top: 10px;
 left: 0;
 right: 0;
 padding: 10px 10px;
 margin: 0 auto;
 border-radius: 12px;
}
</style>
<template>
    <div class="shopitem" ref="shopitem">
    <div class="content">
      <ul>
       <li>請設置內容高度</li>
     </ul>
    </div>
   </div>
</template>
<script>
export default {
 data() {
   return {
     currentIndex: 0,
     saveY: null
  }
},
 mounted() {
   window.addEventListener('scroll', this.shopScroll,true)
},
 methods: {
   shopScroll() {
     let shopScrTop = document.querySelector('.shopitem')
     //獲取滾動出去的距離
     let saveY = shopScrTop.scrollTop
     // console.log(saveY)
     this.$emit('shopOpsitons', saveY)
  },
},
destoroyed() {  window.removeEventListener('scroll', this.shopScroll) } 
}
</script>

<style scoped>
.shopitem {
 width: 75%;
 background-color: #4ca2cd;
 position: fixed;
 top: 200px;
 right: 0;
 bottom: 49px;
 overflow: scroll;
 border-radius: 10px;
}
</style>
<template>
   <div class="titles">
     <div v-for="(item,index) in titles"
         :key="index"
         class="titles-item"
         @click="titlesClick(index)"
         :class="{active: currentIndex === index}"
         >
       <div>{{item}}</div>
     </div>
   </div>
</template>
<script>
export default {
 data() {
   return {
     currentIndex: 0
  }
},
 props: {
   titles: {
     type: Array,
     default() {
       return []
    }
  }
},
 methods: {
   titlesClick(index) {
     this.currentIndex = index
     this.$emit('titleClick',index)
  }
}
}
</script>

<style scoped>
.titles {
 width: 25%;
 position: fixed;
 top: 200px;
 left: 0;
}
.titles-item {
 padding: 10px 6px;
 border-radius: 10px;
}
.active {
 background-color: #4ca2cd;
 color: #fff
}
</style>

效果: 

但是有bug 還沒有找到了。。。。。

15 mixin混入

 

  • 這里主要是對事件監聽代碼進行抽取

  • 定義mixin.js

  • import {debounce} from './Utils'
    export const itemListenerMixin = {
     data(){
       return {
         itemImgListener: null
      }
    },
     mounted() {
       let refresh = debounce(this.$refs.scroll.refresh, 500)
       this.itemImgListener = () => {
        refresh()
      }
       this.$bus.$on('itemImageLoad',  this.itemImgListener)
       // console.log('我是混入內容')
    }
    }
  • 使用方法 先導入

  • import {itemListenerMixin} from 'common/mixin'
    // import Toast from 'components/common/toast/Toast'
    export default {
     name: 'Detail',
     mixins: [itemListenerMixin],
    }

16 把Toast組件封裝成一個插件 (這里主要是通過 Vue.prototype 上實現)

  • 1 在入口文件 main.js

  • //導入toast插件
    import toast from 'components/common/toast'
    //安裝toast插件
    Vue.use(toast)
  • 2 在toast文件(里邊有Toast組件)中,新建index.js文件

  • import Toast from './Toast.vue'
    const obj = {}
    obj.install = function (Vue) {
     console.log('執行了這個函數')
     //1 創建組件構造器 並傳入組件對象
     const toastConstructor = Vue.extend(Toast)
     //2 通過new方式 根據組件構造器,創建出新的組件對象
     const toast = new toastConstructor()
     //3 將組件對象手動掛載在某一元素上
     toast.$mount(document.createElement('div'))
     //4 toast.$el對應是div
     document.body.appendChild(toast.$el)
     //把這個插件掛載到Vue原型上
     Vue.prototype.$toast = toast
    }
    export default obj
  • 使用方法(這里Toast組件已經把方法封裝好了)

  • this.$toast.show(res, 1800)

    17 性能優化 頻繁發生操做,進行防抖節流操做

    完結 

     

視頻地址:https://www.bilibili.com/video/BV15741177Eh?from=search&seid=13265493894652667210


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM