当前位置: 移动技术网 > IT编程>开发语言>JavaScript > Vue webAPP首页开发(二)

Vue webAPP首页开发(二)

2020年04月04日  | 移动技术网IT编程  | 我要评论

接上篇 

 

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

 

 完美实现懒加载!

 

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网