接上篇
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
完美实现懒加载!
如对本文有疑问, 点击进行留言回复!!
JavaScript 好题汇总分享(持续更新,看到好题就写)
XMLHttpRequest 2级 &&进度事件&&JSONP
使用递归原生实现拷贝&&最简单的方法实现深拷贝
网友评论