Continue with previous article https://www.cnblogs.com/chenyingying0/p/12612393.html
Loading components
In api--home.js, add code to delay ajax from displaying a second after it gets the data for the relay map
import axios from 'axios'; import {SUCC_CODE,TIMEOUT} from './config'; //Get slide data ajax export const getHomeSliders=()=>{ // es6 Use promise Replace callback // axios One is returned 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('Failed to get data'); // }).catch(err=>{ // console.log(err); // //error handling // return [{ // linkUrl:'www.baidu.com', // picUrl:require('assets/img/404.png') // }] // }); //Demo timeout error 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('Failed to get data'); }).catch(err=>{ console.log(err); //error handling return [{ linkUrl:'www.baidu.com', picUrl:require('assets/img/404.png') }] }).then(data=>{//Delay one second to show after getting the data of the broadcast map return new Promise(resolve=>{ setTimeout(()=>{ resolve(data); },1000); }) }); }
Create the loading folder under base, which creates 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:{//Filter indicator:{ type:String, default:'on', validator(value){ return ['on','off'].indexOf(value)>-1; } }, text:{ type:String, default:'Loading...' }, inline:{ type:Boolean, default:false } } } </script> <style lang="scss" scoped> @import '~assets/scss/mixins'; .mine-loading{ width:100%; height:100%; @include flex-center(column); //When text is arranged left and right &.me-loading-inline{ flex-direction: row; .mine-loading-indicator ~ .mine-loading-text{ margin-top:0px; margin-left:7px; } } .mine-loading-indicator{ } // existence.mine-loading-indicator and.mine-loading-text time .mine-loading-indicator ~ .mine-loading-text{ margin-top:7px; } } </style>
Place loading.gif under the base-loading folder
Introducing the loading component in home--slider.vue
<template> <div class="slider-wrapper"> <!-- sliders Show when not loaded loading --> <Meloading v-if="!sliders.length"></Meloading> <!-- Separate passes to separate checks, so objects are not passed directly in --> <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:[],//This is read from the server //This is a static write // sliders:[ // { // linkUrl:'www.baidu.com', // picUrl:require('./1.jpg') //Local picture import in js must 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(){ //Generally in created Get remote data from this.getSliders(); }, methods:{ getSliders(){ getHomeSliders().then(data=>{ console.log(data); this.sliders=data; }); } } } </script> <style lang="scss" scoped> // Waves are required before introduction, otherwise errors will be made @import "~assets/scss/mixins"; .slider-wrapper{ width:100%; height:183px; } .slider-link{ display:block; } .slider-link, .slider-img{ width:100%; height:100%; } </style>
The catalog is as follows:
Design sketch
Scrollbar Component
Create scroll directory under base directory, create new 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> // Component initials are capitalized, otherwise errors will be reported import {Swiper,SwiperSlide} from 'vue-awesome-swiper'; export default { name:"MeScroll", components:{ Swiper, SwiperSlide }, props:{//Filter scrollbar:{ type:Boolean, default:true } }, data(){ return { swiperOption:{ direction:'vertical',//vertical direction slidesPerView:'auto',//Show several at a time freeMode:true,//Slide any distance setWrapperSize:true,//Set container size according to content scrollbar:{ el:this.scrollbar?'.swiper-scrollbar':null, hide:true //Scrollbar Auto Hide } } } } } </script> <style lang="scss" scoped> @import '~assets/scss/mixins'; .swiper-container{ width:100%; height:100%; overflow:hidden; & .swiper-slide{ height:auto; } } </style>
Introducing scroll components in home--index.vue
<template> <div class="home"> <header class="g-header-container"> <!-- No content self-closing--> <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> <!-- Need to use when the current page has a secondary page 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> // Waves are required before introduction, otherwise errors will be made @import "~assets/scss/mixins"; .home{ overflow:hidden; width:100%; height:100%; background:$bgc-theme; } </style>
So many multicast maps are added here to increase the height of the next one
Navigation panel
Create a new nav.vue in the home directory
<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:{//Filter }, data(){ return { } }, created(){ //It is not recommended to put this data in data Because data All data in will be added getter and setter,And the data here doesn't need to respond to changes in real time, put in data Inside is a waste of resources 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>
Introducing nav components in index.vue
<template> <div class="home"> <header class="g-header-container"> <!-- No content self-closing--> <home-header/> </header> <me-scroll> <home-slider /> <home-nav></home-nav> </me-scroll> <div class="g-backup-container"></div> <!-- Need to use when the current page has a secondary page 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> // Waves are required before introduction, otherwise errors will be made @import "~assets/scss/mixins"; .home{ overflow:hidden; width:100%; height:100%; background:$bgc-theme; } </style>
Data in config.js
//Expose a constant 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:'Group Purchase' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-2.png'), text:'Group Purchase' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-3.png'), text:'Group Purchase' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-4.png'), text:'Group Purchase' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-5.png'), text:'Group Purchase' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-6.png'), text:'Group Purchase' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-7.png'), text:'Group Purchase' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-8.png'), text:'Group Purchase' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-9.png'), text:'Group Purchase' },{ linkUrl:'www.baidu.com', picUrl:require('./img/nav-item-10.png'), text:'Group Purchase' } ];
Design sketch
Hot Sell Recommendation--jsonp Packaging
Prepare a Taobao interface
Install the jsonp Library
cnpm install --save jsonp
Encapsulate jsonp method
Create jsonp.js under assets--js
import jsonp from 'jsonp'; /*data Format Case { id:1, name:'cyy' } */ const parseParam=param=>{ /*Convert data format to [ [id,1], [name,cyy] ] */ let arr=[]; for(const key in param){ arr.push([key,param[key]]); } /*Convert data format to [ id=1, name=cyy ] */ /*Convert the data format to id=1&name=cyy */ return arr.map(value=>value.join("=")).join('&'); } export default (url,data,options)=>{ // If so?Then url Followed by&;Add if not present? url+=((url.indexOf('?')<0) ? '?' : '&' ) + parseParam(data); return new Promise((resolve,reject)=>{ //jsonp Usage, three parameters: jsonp(url,options,callback) jsonp(url,options,(err,data)=>{ if(err){ reject(err); }else{ resolve(data); } }) }) }
Call jsonp method in api / home.js to get data
import axios from 'axios'; import {SUCC_CODE,TIMEOUT,HOME_RECOMMEND_PAGE_SIZE,JSONP_OPTIONS} from './config'; import jsonp from 'assets/js/jsonp'; //Get slide data ajax export const getHomeSliders=()=>{ // es6 Use promise Replace callback // axios One is returned 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('Failed to get data'); // }).catch(err=>{ // console.log(err); // //error handling // return [{ // linkUrl:'www.baidu.com', // picUrl:require('assets/img/404.png') // }] // }); //Demo timeout error 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('Failed to get data'); }).catch(err=>{ console.log(err); //error handling return [{ linkUrl:'www.baidu.com', picUrl:require('assets/img/404.png') }] }).then(data=>{//Delay one second to show after getting the data of the broadcast map return new Promise(resolve=>{ setTimeout(()=>{ resolve(data); },1000); }) }); } //Get popular recommendation data 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 and frontCatId Is added according to the given Taobao interface } //call jsonp get data return jsonp(url,params,JSONP_OPTIONS).then(res=>{ if(res.code==='200'){ return res; } throw new Error('Failed to get data'); }).catch(err=>{ if(err){ console.log(err); } }).then(res=>{ //Delay one second to return data return new Promise(resolve=>{ setTimeout(()=>{ resolve(res); },1000); }) }) }
Add Constant to api / config.js
//Get a rotation map export const SUCC_CODE=0; export const TIMEOUT=10000; //Get popular recommendations export const HOME_RECOMMEND_PAGE_SIZE=20; export const JSONP_OPTIONS={ param:'callback', timeout:TIMEOUT };
Add code to pages/home/recommend.vue
<template> <div class="recommend"> <h3 class="recommend-title">Hot Sales Recommendations</h3> <div class="loading-container" v-if="!recommends.length"> <!-- The complete notation is inline:inline ,Boolean types, however, can be written directly 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}}Parts sold</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('No more')); getHomeRecommend(this.curPage).then(data=>{ return new Promise(resolve=>{ if(data){ console.log(data); this.curPage++; this.totalPage=data.totalPage; // concat Merge the contents of the array, appending the data each time you get it 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%;// Consistent height and width can be achieved 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>
Design sketch
Update Scrollbar
Because the hot recommendations are loaded asynchronously, and the scrollbar is loaded before the hot recommendations are loaded, the scrollbar cannot get the correct height of the hot recommendation area, causing the scrollbar effect to fail
So when the popular recommendations are loaded, the scrollbar needs to be updated again
1. In recommend.vue, trigger the loaded message and pass recommends data when the popular recommendation has finished loading
2. Receive triggered message loaded, trigger getRecommends function
3. Update recommends data in getRecommends function
4. Let the scrollbar receive recommends data
5. Scrollbars detect data changes and start updating scrollbars
6. The swiper instance is used here and needs to be obtained on the swiper element
7. Scrollbar effect is back!
Lazy loading of pictures
1. Install lazyload plug-in cnpm install --save vue-lazyload
2. Introducing components in main.js
3. In recommend.vue, change:src to v-lazy
Perfect for lazy loading!