接上篇 https://www.cnblogs.com/chenyingying0/p/12612393.html Loading組件 在api--home.js中,添加代碼,使ajax獲取到輪播圖數據後,延遲一秒再顯示 import axios from 'axios'; import {SUCC ...
接上篇 https://www.cnblogs.com/chenyingying0/p/12612393.html
Loading組件
在api--home.js中,添加代碼,使ajax獲取到輪播圖數據後,延遲一秒再顯示
import axios from 'axios'; import {SUCC_CODE,TIMEOUT} from './config'; //獲取幻燈片數據 ajax export const getHomeSliders=()=>{ // es6使用promise代替回調 // axios返回的就是一個promise // return axios.get('http://www.imooc.com/api/home/slider').then(res=>{ // console.log(res); // if(res.data.code===SUCC_CODE){ // return res.data.slider; // } // throw new Error('沒有成功獲取到數據'); // }).catch(err=>{ // console.log(err); // //錯誤處理 // return [{ // linkUrl:'www.baidu.com', // picUrl:require('assets/img/404.png') // }] // }); //演示超時錯誤 return axios.get('http://www.imooc.com/api/home/slider',{ timeout:TIMEOUT }).then(res=>{ console.log(res); if(res.data.code===SUCC_CODE){ return res.data.slider; } throw new Error('沒有成功獲取到數據'); }).catch(err=>{ console.log(err); //錯誤處理 return [{ linkUrl:'www.baidu.com', picUrl:require('assets/img/404.png') }] }).then(data=>{//獲取輪播圖數據後,延遲一秒再顯示 return new Promise(resolve=>{ setTimeout(()=>{ resolve(data); },1000); }) }); }
在base下創建loading文件夾,裡面創建index.vue
<template> <div class="mine-loading" :class="{'me-loading-inline':inline}"> <span class="mine-loading-indicator" v-if="indicator==='on'" > <img src="./loading.gif" alt=""> </span> <span class="mine-loading-text" v-if="text">{{text}}</span> </div> </template> <script> export default { name:"MeLoading", props:{//過濾器 indicator:{ type:String, default:'on', validator(value){ return ['on','off'].indexOf(value)>-1; } }, text:{ type:String, default:'載入中...' }, inline:{ type:Boolean, default:false } } } </script> <style lang="scss" scoped> @import '~assets/scss/mixins'; .mine-loading{ width:100%; height:100%; @include flex-center(column); //圖文左右排列時 &.me-loading-inline{ flex-direction: row; .mine-loading-indicator ~ .mine-loading-text{ margin-top:0px; margin-left:7px; } } .mine-loading-indicator{ } // 存在.mine-loading-indicator和.mine-loading-text時 .mine-loading-indicator ~ .mine-loading-text{ margin-top:7px; } } </style>
在base-loading文件夾下放入loadging.gif
在home--slider.vue中引入loading組件
<template> <div class="slider-wrapper"> <!-- sliders沒載入時顯示loading --> <Meloading v-if="!sliders.length"></Meloading> <!-- 分開傳才能分開校驗,因此不直接傳入對象 --> <MeSlider :direction="direction" :loop="loop" :interval="interval" :pagination="pagination" v-else > <swiper-slide v-for="(item,index) in sliders" :key="index"> <a :href="item.linkUrl" class="slider-link"> <img :src="item.picUrl" class="slider-img"> </a> </swiper-slide> </MeSlider> </div> </template> <script> import MeSlider from 'base/slider'; import { SwiperSlide } from 'vue-awesome-swiper'; import { sliderOptions } from './config'; import { getHomeSliders } from 'api/home'; import Meloading from 'base/loading'; export default { name:"HomeSlider", components:{ MeSlider, SwiperSlide, Meloading }, data(){ return{ direction:sliderOptions.direction, loop:sliderOptions.loop, interval:sliderOptions.interval, pagination:sliderOptions.pagination, sliders:[],//這是從伺服器讀取 //這是靜態寫入 // sliders:[ // { // linkUrl:'www.baidu.com', // picUrl:require('./1.jpg') //js中本地圖片引入必須加require // }, // { // linkUrl:'www.baidu.com', // picUrl:require('./2.jpg') // }, // { // linkUrl:'www.baidu.com', // picUrl:require('./3.jpg') // }, // { // linkUrl:'www.baidu.com', // picUrl:require('./4.jpg') // } // ] } }, created(){ //一般在created里獲取遠程數據 this.getSliders(); }, methods:{ getSliders(){ getHomeSliders().then(data=>{ console.log(data); this.sliders=data; }); } } } </script> <style lang="scss" scoped> // 引入前面需要加波浪線,否則會報錯 @import "~assets/scss/mixins"; .slider-wrapper{ width:100%; height:183px; } .slider-link{ display:block; } .slider-link, .slider-img{ width:100%; height:100%; } </style>
目錄如下:
效果圖
滾動條組件
在base目錄下創建scroll目錄,新建index.vue
<template> <swiper :options="swiperOption"> <swiper-slide> <slot></slot> </swiper-slide> <div class="swiper-scrollbar" v-if="scrollbar" slot="scrollbar"></div> </swiper> </template> <script> // 組件首字母大寫,否則會報錯 import {Swiper,SwiperSlide} from 'vue-awesome-swiper'; export default { name:"MeScroll", components:{ Swiper, SwiperSlide }, props:{//過濾器 scrollbar:{ type:Boolean, default:true } }, data(){ return { swiperOption:{ direction:'vertical',//垂直方向 slidesPerView:'auto',//一次顯示幾張 freeMode:true,//任意滑動多少距離 setWrapperSize:true,//根據內容設置容器尺寸 scrollbar:{ el:this.scrollbar?'.swiper-scrollbar':null, hide:true //滾動條自動隱藏 } } } } } </script> <style lang="scss" scoped> @import '~assets/scss/mixins'; .swiper-container{ width:100%; height:100%; overflow:hidden; & .swiper-slide{ height:auto; } } </style>
在home--index.vue中引入scroll組件
<template> <div class="home"> <header class="g-header-container"> <!-- 沒有內容自閉合即可--> <home-header/> </header> <me-scroll> <home-slider></home-slider> <home-slider></home-slider> <home-slider></home-slider> <home-slider></home-slider> <home-slider></home-slider> <home-slider></home-slider> <home-slider></home-slider> <home-slider></home-slider> <home-slider></home-slider> <home-slider></home-slider> <home-slider></home-slider> </me-scroll> <div class="g-backup-container"></div> <!-- 當前頁面存在二級頁面時需要使用router-view --> <router-view></router-view> </div> </template> <script> import MeScroll from 'base/scroll'; import HomeHeader from './header'; import HomeSlider from './slider'; export default { name:"Home", components:{ HomeHeader, HomeSlider, MeScroll } } </script> <style lang="scss" scoped> // 引入前面需要加波浪線,否則會報錯 @import "~assets/scss/mixins"; .home{ overflow:hidden; width:100%; height:100%; background:$bgc-theme; } </style>
這裡添加這麼多組輪播圖是為了增高高度展示下輪播圖效果
導航面板
在home目錄中新建nav.vue
<template> <nav class="nav"> <ul class="nav-list"> <li class="nav-item" v-for="(item,index) in navs" :key="index"> <a :href="item.linkUrl" class="nav-link"> <img :src="item.picUrl" alt="" class="nav-pic"> <span>{{item.text}}</span> </a> </li> </ul> </nav> </template> <script> import {navItems} from './config.js'; export default { name:"HomeNav", components:{ }, props:{//過濾器 }, data(){ return { } }, created(){ //不建議把這個數據放在data里,因為data里的數據都會添加getter和setter,而這裡的數據並不需要實時響應變化,放在data里對資源是一種浪費 this.navs=navItems; } } </script> <style lang="scss" scoped> @import '~assets/scss/mixins'; .nav{ width:100%; margin-top:15px; } .nav-list{ display:flex; flex-wrap:wrap; } .nav-item{ width:20%; } .nav-link{ @include flex-center(column); margin-bottom:15px; } .nav-pic{ width:60%; margin-bottom:7px; } </style>
在index.vue中引入nav組件
<template> <div class="home"> <header class="g-header-container"> <!-- 沒有內容自閉合即可--> <home-header/> </header> <me-scroll> <home-slider /> <home-nav></home-nav> </me-scroll> <div class="g-backup-container"></div> <!-- 當前頁面存在二級頁面時需要使用router-view --> <router-view></router-view> </div> </template> <script> import MeScroll from 'base/scroll'; import HomeHeader from './header'; import HomeSlider from './slider'; import HomeNav from './nav'; export default { name:"Home", components:{ HomeHeader, HomeSlider, MeScroll, HomeNav } } </script> <style lang="scss" scoped> // 引入前面需要加波浪線,否則會報錯 @import "~assets/scss/mixins"; .home{ overflow:hidden; width:100%; height:100%; background:$bgc-theme; } </style>
數據在config.js中
//暴露一個常量 export const sliderOptions={ direction:"horizontal", loop:"loop", interval:1000, pagination:"pagination" } export const navItems=[ { linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-1.png'), text:'團購' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-2.png'), text:'團購' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-3.png'), text:'團購' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-4.png'), text:'團購' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-5.png'), text:'團購' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-6.png'), text:'團購' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-7.png'), text:'團購' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-8.png'), text:'團購' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-9.png'), text:'團購' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-10.png'), text:'團購' } ];
效果圖
熱賣推薦--jsonp封裝
準備一個淘寶介面
https://ju.taobao.com/json/tg/ajaxGetItemsV2.json安裝jsonp的庫
cnpm install --save jsonp
封裝jsonp方法
在assets--js下創建jsonp.js
import jsonp from 'jsonp'; /*data格式案例 { id:1, name:'cyy' } */ const parseParam=param=>{ /*將data格式轉換為 [ [id,1], [name,cyy] ] */ let arr=[]; for(const key in param){ arr.push([key,param[key]]); } /*先將data格式轉換為 [ id=1, name=cyy ] */ /*再將data格式轉換為 id=1&name=cyy */ return arr.map(value=>value.join("=")).join('&'); } export default (url,data,options)=>{ // 如果存在?,則url後面加&;如果不存在則加? url+=((url.indexOf('?')<0) ? '?' : '&' ) + parseParam(data); return new Promise((resolve,reject)=>{ //jsonp用法,三個參數:jsonp(url,options,callback) jsonp(url,options,(err,data)=>{ if(err){ reject(err); }else{ resolve(data); } }) }) }
在api / home.js中調用jsonp方法獲取數據
import axios from 'axios'; import {SUCC_CODE,TIMEOUT,HOME_RECOMMEND_PAGE_SIZE,JSONP_OPTIONS} from './config'; import jsonp from 'assets/js/jsonp'; //獲取幻燈片數據 ajax export const getHomeSliders=()=>{ // es6使用promise代替回調 // axios返回的就是一個promise // return axios.get('http://www.imooc.com/api/home/slider').then(res=>{ // console.log(res); // if(res.data.code===SUCC_CODE){ // return res.data.slider; // } // throw new Error('沒有成功獲取到數據'); // }).catch(err=>{ // console.log(err); // //錯誤處理 // return [{ // linkUrl:'www.baidu.com', // picUrl:require('assets/img/404.png') // }] // }); //演示超時錯誤 return axios.get('http://www.imooc.com/api/home/slider',{ timeout:TIMEOUT }).then(res=>{ //console.log(res); if(res.data.code===SUCC_CODE){ return res.data.slider; } throw new Error('沒有成功獲取到數據'); }).catch(err=>{ console.log(err); //錯誤處理 return [{ linkUrl:'www.baidu.com', picUrl:require('assets/img/404.png') }] }).then(data=>{//獲取輪播圖數據後,延遲一秒再顯示 return new Promise(resolve=>{ setTimeout(()=>{ resolve(data); },1000); }) }); } //獲取熱門推薦數據 export const getHomeRecommend=(page=1,psize=HOME_RECOMMEND_PAGE_SIZE)=>{ const url='https://ju.taobao.com/json/tg/ajaxGetItemsV2.json'; const params={ page, psize, type:0, frontCatId:''//type和frontCatId是根據給定的淘寶介面來添加的 } //調用jsonp獲取數據 return jsonp(url,params,JSONP_OPTIONS).then(res=>{ if(res.code==='200'){ return res; } throw new Error('沒有成功獲取到數據'); }).catch(err=>{ if(err){ console.log(err); } }).then(res=>{ //延遲一秒返回數據 return new Promise(resolve=>{ setTimeout(()=>{ resolve(res); },1000); }) }) }
api / config.js中添加常量
//獲取輪播圖 export const SUCC_CODE=0; export const TIMEOUT=10000; //獲取熱門推薦 export const HOME_RECOMMEND_PAGE_SIZE=20; export const JSONP_OPTIONS={ param:'callback', timeout:TIMEOUT };
在pages/home/recommend.vue中添加代碼
<template> <div class="recommend"> <h3 class="recommend-title">熱賣推薦</h3> <div class="loading-container" v-if="!recommends.length"> <!-- 完整寫法是 inline:inline ,不過布爾值類型可以直接寫 inline --> <me-loading inline /> </div> <ul class="recommend-list"> <li class="recommend-item" v-for="(item,index) in recommends" :key="index"> <router-link class="recommend-link" :to="{name:'home-product',params:{id:item.baseinfo.itemId}}"> <p class="recommend-pic"><img class="recommend-img" :src="item.baseinfo.picUrl" alt=""></p> <p class="recommend-name">{{item.name.shortName}}</p> <p class="recommend-oriPrice"><del>¥{{item.price.origPrice}}</del></p> <p class="recommend-info"> <span class="recommend-price">¥<strong class="recommend-price-num">{{item.price.actPrice}}</strong></span> <span class="recommend-count">{{item.remind.soldCount}}件已售</span> </p> </router-link> </li> </ul> </div> </template> <script> import {getHomeRecommend} from 'api/home'; import MeLoading from 'base/loading'; export default { name:"HomeRecommend", data(){ return { recommends:[], curPage:1, totalPage:1 } }, components:{ MeLoading }, created(){ this.getRecommends(); }, methods:{ getRecommends(){ if(this.curPage>this.totalPage) return Promise.reject(new Error('沒有更多了')); getHomeRecommend(this.curPage).then(data=>{ return new Promise(resolve=>{ if(data){ console.log(data); this.curPage++; this.totalPage=data.totalPage; // concat合併數組內容,每次獲取的數據都追加進來 this.recommends=this.recommends.concat(data.itemList); resolve(); } }) }); } } } </script> <style lang="scss" scoped> @import '~assets/scss/mixins'; .recommend{ position:relative; width:100%; padding:10px 0; font-size:$font-size-l; text-align:center; &:before, &:after{ content:""; display:block; position:absolute; top:50%; width:40%; height:1px; background:#ddd; } &:before{ left:0; } &:after{ right:0; } } .recommend-list{ @include flex-between(); flex-wrap:wrap; } .recommend-title{ margin-bottom:8px; } .recommend-item{ width:49%; background:#fff; box-shadow:0 1px 1px 0 rgba(0,0,0,0.12); margin-bottom:8px; } .recommend-link{ display:block; } .recommend-pic{ position:relative; width:100%; padding-top:100%;// 可以實現高度與寬度一致 margin-bottom:5px; } .recommend-img{ width:100%; position:absolute; top:0; left:0; height:100%; } .recommend-name{ height:40px; padding:0 5px; margin-bottom:8px; line-height:1.5; @include multiline-ellipsis(); text-align:left; } .recommend-oriPrice{ padding:0 5px; margin-bottom:8px; color:#ccc; del{ } } .recommend-info{ @include flex-between(); padding:0 5px; margin-bottom:8px; } .recommend-price{ color:#e61414; &-num{ font-size:20px; } } .recommend-count{ color:#999; } .loading-container{ padding-top:150px; } </style>
src/pages/product.vue
<template> <div class="product"> product </div> </template> <script> export default { name:"Product" } </script> <style lang="scss" scoped> @import '~assets/scss/_mixins'; .product{ overflow:hidden; position:absolute; top:0; left:0; width:100%; height:100%; background:#fff; z-index:$product-z-index; } </style>
效果圖
更新滾動條
由於熱門推薦是非同步載入的,熱門推薦還沒載入完時,滾動條已經載入完畢,因此滾動條無法獲取到正確的熱門推薦區域的高度,導致滾動條效果失效
因此當熱門推薦載入完畢時,需要再次更新滾動條
1、recommend.vue中,熱門推薦載入完成後,觸發loaded消息並傳遞recommends數據
2、接收觸發的消息loaded,觸發getRecommends函數
3、在getRecommends函數中更新recommends數據
4、讓滾動條接收到recommends數據
5、滾動條檢測到數據變化,開始更新滾動條
6、這裡用到了swiper實例,需要在swiper元素上獲取到
7、滾動條效果回來啦!
圖片的懶載入
1、安裝lazyload插件 cnpm install --save vue-lazyload
2、在main.js中引入組件
3、在recommend.vue中將:src改為v-lazy
完美實現懶載入!