接上篇 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封装
准备一个淘宝接口
安装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

完美实现懒加载!

来源:https://www.cnblogs.com/chenyingying0/p/12623653.html