接上篇 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