美文网首页Vue.js
Vue.js第8课-项目实战 - 旅游网站详情页面开发(part

Vue.js第8课-项目实战 - 旅游网站详情页面开发(part

作者: e20a12f8855d | 来源:发表于2019-06-03 10:04 被阅读49次

    三、实现 Header 渐隐渐显效果

    可以打开 去哪网 看一下城市的详情页,首先他的左上角定位了一个返回图标,当页面向下滑动过的时候,这个返回图标消失,会渐渐出现一个头部,和首页的头部样式一样,所以我们先来完成头部这两个部分的布局样式。

    首先在 details/components 目录下新建一个 header.vue 组件,并在 Details.vue 中引用并使用这个组件,然后编写 header.vue 组件的布局与样式。例:

    detail/header.vue

    <template>
    <div class="detail-header">
        <router-link class="header-abs" to="/">
            <span class="iconfont">&#xe624;</span>
        </router-link>
        <div class="header-fix">
            <div class="header-left">
                <span class="iconfont">&#xe624;</span>
            </div>
            <div class="header-center">北京世界园艺博览会</div>
        </div>
    </div>
    </template>
    
    <script>
    export default {
        name: "DetailHeader"
    };
    </script>
    
    <style lang="stylus" scoped>
    @import '~style/varibles';
    
    .detail-header {
        .header-abs {
            position: absolute;
            top: .2rem;
            left: .2rem;
            width: 0.68rem;
            height: 0.68rem;
            background-color: rgba(0, 0, 0, 0.6);
            border-radius: 50%;
            text-align: center;
            line-height: 0.68rem;
            color #fff
        }
    
        .header-fix {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            background-color: $bgColor;
            overflow: hidden;
            color: #fff;
            padding: 0.2rem;
    
            .header-left {
                position: absolute;
                top: 0.2rem;
                left: 0.2rem;
            }
    
            .header-center {
                text-align: center;
                margin: 0 0.2rem;
            }
        }
    }
    </style>
    

    当前效果图:

    注意,因为当前详情页内容不够长,不能下滑,所以给 Detail.vue 内容设置一个高度,让他能够向下滑动。

    头部的布局样式完成了,接下来就需要实现一些逻辑了。默认打开详情页的时候,导航条是不显示的,只显示返回按钮图标,当页面向下滑动的时候,返回图标隐藏,导航条渐渐显示出来,我么来实现一下这个效果。

    首先在 data 中定义一个变量 showAbs,默认为 true,然后通过 v-show 来决定哪个元素显示:

    <template>
    <div class="detail-header">
        <router-link class="header-abs" to="/" v-show="showAbs">
            <span class="iconfont">&#xe624;</span>
        </router-link>
        <div class="header-fix" v-show="!showAbs">
            <div class="header-left">
                <span class="iconfont">&#xe624;</span>
            </div>
            <div class="header-center">北京世界园艺博览会</div>
        </div>
    </div>
    </template>
    
    <script>
    export default {
        name: "DetailHeader",
        data(){
            return {
                showAbs: true
            }
        }
    };
    </script>
    

    所以这个时候返回图标这个元素是显示的,header 部分不显示,当我们将页面向下滑,高度差不多是 50px 的时候,让返回图标隐藏,header 显示,所以这里要写一个滚动监听的函数,那就用到 activted 这个生命周期函数了。

    在 activted 中,我们给 window 添加一个滚动监听事件 handleScroll,然后在 methods 中编写这个事件方法,通过 document.documentElement.scroll 来获取页面向上滚动的高度,然后做判断,当这个高度大于 50 的时候,也就是差不多大于图标按钮的高度时,图标按钮隐藏,header 部分显示,所以让 this.showAbs 为 false 就可以了,否则让他为 true,这样就实现了当页面的滚动高度大于 50 的时候,返回图标隐藏,header 显示。

    header.vue

    <script>
    export default {
        name: "DetailHeader",
        data(){
            return {
                showAbs: true
            }
        },
        methods:{
            handleScroll(){
                const top = document.documentElement.scrollTop;
                if(top > 50){
                    this.showAbs = false;
                }else{
                    this.showAbs = true;
                }
                console.log(top);
            }
        },
        activated(){
            window.addEventListener("scroll",this.handleScroll);
        }
    };
    </script>
    

    接下来就该实现渐隐渐显的动画效果了,在 if 判断中定义一个 opacity 变量,他等于 top/100,因为 top 值在页面滚动的时候是变化的,所以 opacity 也就是变化的了,然后做一个三元运算,当这个 opacity 大于 1 的时候,我就让他一直等于 1,也就是让他一直显示,否则的话,还是让他等于这个 opacity 变量,然后给 data 中的 opacityStyle 设置值,让他里边的 opacity 等于 opacity。

    header.vue

    <script>
    export default {
        name: "DetailHeader",
        data(){
            return {
                showAbs: true,
                opacitySty:{
                    opacity : 0
                }
            }
        },
        methods:{
            handleScroll(){
                const top = document.documentElement.scrollTop;
                if(top>50){
                    this.showAbs = false;
                    let opacity = top / 100;
                    opacity = opacity > 1 ? 1 : opacity;
                    this.opacitySty.opacity = opacity;
                }else{
                    this.showAbs = true
                }
            }
        },
        activated() {
            window.addEventListener("scroll",this.handleScroll)
        },
    };
    </script>
    

    回到页面,向下滚动,可以看到导航出现了一个渐隐渐显的效果。最后记得给导航中的返回按钮也添加一个返回首页的路由。

    以上就实现了 Header 渐隐渐显效果。

    四、对全局事件的解绑

    先来回顾上一章,给 window 对象添加滚动监听事件这部分,我们在 handleScroll 中打印一些内容,例:

    detail/header.vue

    methods:{
        handleScroll(){
            console.log("scroll");
            // ...
        }
    },
    activated(){
        window.addEventListener("scroll",this.handleScroll);
    }
    

    启动项目服务,打开页面,在详情页滚动页面的时候,可以看到控制台输出了 “scroll” 内容,这没有问题,但是,回到首页,在滚动页面的时候,控制台中也打印出了 “scroll” 内容,也就是说首页也执行了 handleScroll 这个方法,但是这个方法我们是写在 header.vue 中的,为什么首页也会有呢?

    因为之前我们都是用 @ 给元素绑定事件,而且是给当前组件中的元素绑定,他只作用于组件的内部,不会影响外部的组件,但是这里我们是给全局的 window 对象绑定的,所以会造成这种问题,这个滚动事件不仅对这个组件有效果,对其他的组件也产生了影响,那如何去处理这个问题呢?

    当我们对这个组件用了 keep-alive 的时候,这个组件会多出一个 activted 生命是周期函数,这一块内容可以回顾一下 “Vue.js第7课-项目实战-城市列表开发(part04)” 中的第十一章,activted 在每次页面展示的时候会执行,与之对应,他还提供给我们一个新的生命周期函数,叫做 deactivted,他是页面即将被隐藏,或者说页面即将被替换成新的页面的时候,这个组件的 deactivted 这个生命周期函数会被执行,我们在这个函数中将给 window 对象添加的滚动事件给移除掉。

    detail/header.vue

    // ...
    activated(){
        window.addEventListener("scroll",this.handleScroll);
    },
    deactivated(){
        window.removeEventListener("scroll",this.handleScroll);
    }
    // ...
    

    也就你页面展示的时候绑定 scroll 事件,页面隐藏的时候再去对这个 scroll 事件进行解绑。这个时候,回到页面,在详情页滚动的时候,可以看到打印出了 “scroll”,回到首页,滚动页面的时候,并没有打印出 “scroll”,说明并没有执行滚动时间,这样就解决了上一章中留下的 BUG。

    最后记得提交代码并合并分支。

    五、使用递归组件实现详情页列表

    这一章我们来完成详情页的列表部分,可以看一下 去哪网 的详情页列表部分,因为内容比较多,所以我们先简单的实现一部分列表内容,来看一下递归组件如何使用。

    首先还是新建一个 detail-list 分支并切换,在这个分支上进行代码的开发。然后新建 list.vue,编写组件的基本结构,然后到 Detail.vue 中引入并使用这个模板。这次我们先不编写模板的样式,先来看一下对数据的处理,在 Detail.vue 中添加一组数据,例:

    Detail.vue

    data() {
        return {
            list: [{
                title: "非指定日上午场票",
                children: [{
                        title: "[上午场]成人票(早定优惠)"
                    },
                    {
                        title: "[上午场]老人票"
                    }
                ]
            }, {
                title: "指定日上午场票",
                children: [{
                        title: "[上午场]老人票"
                    },
                    {
                        title: "[上午场]学生票"
                    }
                ]
            }, {
                title: "儿童票"
            }, {
                title: "特惠票"
            }]
        }
    }
    

    然后使用属性的方式将 list 传递给子组件 list.vue,list.vue 接收到 list 数据后,我们在模板中将它渲染出来,例:

    list.vue

    <template>
    <div>
        <div class="list-con" v-for="(item,index) of list" :key="index">
            <div class="list-tit">{{item.title}}</div>
        </div>
    </div>
    </template>
    

    此时页面是这样的一个效果:

    之前我们在 list 数据的每一项下还添加了一组子项 children,它里面又包含着几组数据,现在我们将 children 子项页渲染到页面,这个时候就用到递归组件了,递归组件的意思就是在组件自身调用组件自身:

    list.vue

    <template>
    <div>
        <div class="list-con" v-for="(item,index) of list" :key="index">
            <div class="list-tit">{{item.title}}</div>
            <div class="list-children">
                <div v-if="item.children" class="list-children-item">
                    <detail-list :list="item.children"></detail-list>
                </div>
            </div>
        </div>
    </div>
    </template>
    

    上面代码中,我们在 list-children 这个元素下,先做了一个判断,当 item.children 下有值的时候,调用一下自身,也就是 detail-list 这个组件,这个组件也是通过属性的形式,传一个 list,因为在 list.vue 中已经通过 props 接收到 list 了,而且外层已经循环过 list 了,现在我们只是要获取 list 下的 children 中的数据了,所以直接让这个 list 属性等于 item.children 就可以了,打开页面,可以看到这样一个效果:

    虽然样式上有问题,但是我们可以在控制台中看到这些数据是存在层级关系的,说明数据渲染的没有问题,接下来我们对样式做一个调整。

    list.vue

    <template>
    <div>
        <div class="list-con" v-for="(item,index) of list" :key="index">
            <div class="list-tit border-bottom">
                <span class="ticket"></span>
                {{item.title}}
            </div>
            <div class="list-children">
                <div v-if="item.children" class="list-children-item">
                    <detail-list :list="item.children"></detail-list>
                </div>
            </div>
        </div>
    </div>
    </template>
    
    <script>
    export default {
        name: "DetailList",
        props: {
            list: Array
        }
    };
    </script>
    
    <style lang="stylus" scoped>
    .list-con {
        .list-tit {
            position: relative;
            height: 0.88rem;
            line-height: 0.88rem;
            padding: 0 0.2rem 0 0.6rem;
    
            .ticket {
                display: inline-block;
                position: absolute;
                width: 0.36rem;
                height: 0.36rem;
                top: 0.26rem;
                left: 0.2rem;
                background: url('//s.qunarzz.com/piao/image/touch/sight/detail.png') 0 -0.45rem no-repeat;
                background-size: auto;
                margin-right: 0.1rem;
                background-size: 0.4rem 3rem;
            }
        }
    
        .list-children {
            padding: 0 0.2rem;
    
            .list-children-item {
                .ticket {
                    background: none;
                }
            }
        }
    }
    </style>
    

    此时打开页面,可以看到页面是这样一个效果的:

    如果我们将 data 中 list 里的 children 中再添加一组 children 呢?可以试一下:

    Detail.vue

    // ...
    list: [{
        title: "非指定日上午场票",
        children: [{
                title: "[上午场]成人票(早定优惠)",
                children:[{
                    title:"第三级"
                },{
                    title:"第三级"
                }]
            },
            {
                title: "[上午场]老人票"
            }
        ]
    }, {
        title: "指定日上午场票",
        children: [{
                title: "[上午场]老人票"
            },
            {
                title: "[上午场]学生票"
            }
        ]
    }, {
        title: "儿童票"
    }, {
        title: "特惠票"
    }]
    // ...
    

    list.vue 中不需要做任何修改,因为我们在子组件中调用自身的时候,其实就相当于在调用子组件的位置又加了一边 template 中的内容,所以他会找到子数据再循环渲染一次,打开页面可以看一下:

    没有任何问题,记得将这部分代码提交一下。


    长得好看的都会关注我的 o(≧v≦)o~~

    相关文章

      网友评论

        本文标题:Vue.js第8课-项目实战 - 旅游网站详情页面开发(part

        本文链接:https://www.haomeiwen.com/subject/kobqxctx.html