Vue.js学习记录-15-Vue去哪儿网项目实战:景点详情页开发-功能点概述 + Detail + Banner(通用组件:Gallery、Fade)
3. 景点详情页开发
-
功能点概述
- 用户首页点击热销推荐景点,即可跳转景点详情页面。详情页面包括三部分内容:顶部图片展示、景点门票详情、隐藏页面头。(拖动至下方会出现)
- 用户点击顶部图片展示,进入图片轮播区域(全屏),可左右滑动进行图片浏览,点击图片区域外进行返回景点详情页,过渡动画的使用,图片轮播区域配置首页返回按钮。
- 用户向下拖动景点详情页,即可看见隐藏页面头部,同时具备返回按钮,可以返回至首页。
- 景点门票详情,针对返回的多层数据,进行组件的递归调用展示。
- 用户首页点击热销推荐景点,即可跳转景点详情页面。详情页面包括三部分内容:顶部图片展示、景点门票详情、隐藏页面头。(拖动至下方会出现)
-
Detail:城市详情父组件
-
路由配置
在该模块中,路由采用了动态参数的形式,进行路由配置,坐标/router/index.js:
import Detail from '@/pages/detail/Detail' { // 绑定动态参数 path: '/detail/:id', name: 'detail', component: Detail }
其中 :id 绑定了动态的URL参数,在组件中可以通过:this.$route.params.id 进行获取
-
组件管理:
- 子组件引入及注册:Banner、Header、List
-
引入
import DetailBanner from './components/Banner' import DetailHeader from './components/Header' import DetailList from './components/List'
-
注册
components: { DetailBanner, DetailHeader, DetailList }
-
- 子组件引入及注册:Banner、Header、List
-
组件交互:三个交互点
-
首页热销推荐景点点击路由跳转
坐标:Home.Recommend,路由跳转时,携带item.id
<!-- 采用router-link进行页面跳转,tag标识标签转换为li,to携带参数进行页面跳转 --> <router-link tag="li" :to="'/detail/' + item.id" class="item border-bottom" v-for="item of list" :key="item.id"> <img class="item-img" :src="item.imgUrl" /> <div class="item-info"> <p class="item-title">{{item.title}}</p> <p class="item-desc">{{item.desc}}</p> <button class="item-button">查看详情</button> </div> </router-link>
-
Banner、Header点击返回按钮路由跳转
坐标:Header,下文详细组件中进行介绍
-
-
数据传递:采用axios进行数据传递
-
axios引入
import axios from 'axios'
-
初始化data
data() { return { sightName: '', bannerImg: '', gallaryImgs: [], list: [] } }
-
数据请求
-
触发数据请求
//页面渲染时触发方法 mounted() { this.getDetailInfo() }
-
数据请求:注意请求拼装URL的方式,前面已经提到了调用动态参数的方法:this.$route.params.id
methods: { getDetailInfo() { axios.get('/api/detail.json?', { params: { id: this.$route.params.id } }).then(this.handleGetDataSucc) }, handleGetDataSucc(res) { res = res.data if (res.ret && res.data) { const data = res.data this.sightName = data.sightName this.bannerImg = data.bannerImg this.gallaryImgs = data.gallaryImgs this.list = data.categoryList } } },
-
-
数据传递:三个数据项传递给Banner组件、一个数据项传递给List组件
<template> <div> <detail-banner :sightName="sightName" :bannerImg="bannerImg" :gallaryImgs="gallaryImgs"></detail-banner> <detail-header></detail-header> <div class="content"> <detail-list :list="list"></detail-list> </div> </div> </template>
-
-
-
Banner:景点图片画廊(通用组件)
写在开头,这一部分要实现的具体功能细节有:
1. 景点主图片、景点名称、画廊图片数目展示
2. 点击主图片进入图片画廊,图片画廊可以左右滑动,点击非图片区域可退回景点详情页
细节1实现:
根据上文可知,父组件Detail以属性绑定的方式向Banner组件传递了三个数据项,分别是:
- sightName:景点名称
- bannerImg:景点主图片
- gallaryImgs:画廊图片集
数据获取
props: { sightName: String, bannerImg: String, gallaryImgs: Array },
数据映射渲染:该DOM上绑定了点击事件:handleBannerClick,细节2中会提到。
<div class="banner" @click="handleBannerClick"> <img class="banner-img" :src="bannerImg" alt=""/> <div class="banner-info"> <div class="banner-title"> {{this.sightName}} </div> <div class="banner-number"> <span class="iconfont banner-icon"></span> {{this.gallaryImgs.length}} </div> </div> </div>
细节2实现:通用组件Gallery、Fade的使用
在该部分的实现中,将Gallery画廊组件以及Fade简单动画组件做了封装,封装为通用组件,也方便其他组件进行调用。
Gallery:画廊组件
关于画廊组件,本质上是Swiper组件,进行了些定制化的配置。比如,铺满全屏,分页器样式,状态检查等。
关于Swiper组件,详情API及使用方法见:https://3.swiper.com.cn/api/index.html
<template>:循环数据项、点击事件:handleGallaryClick,控制是否进入景点图片画廊
<template> <div class="container" @click="handleGallaryClick"> <div class="wrapper"> <swiper :options="swiperOptions"> <!-- slides --> <swiper-slide v-for="(item, index) in imgs" :key="index"> <img class="gallary-img" :src="item"> </swiper-slide> <!-- Optional controls --> <div class="swiper-pagination" slot="pagination"></div> </swiper> </div> </div> </template>
循环数据项:由于Gallery组件为公共组件,调用该组件的即为其父组件(这里是Banner)。Banner组件将gallaryImgs数组传递给Gallery组件,在模板中通过index为索引进行了遍历输出。
props: { imgs: { type: Array, // 默认值为空数组 default() { return [] } } },
点击事件:handleGallaryClick,当点击区域时触发点击事件,向父组件(Banner)触发close事件,关闭通用画廊组件。
methods: { handleGallaryClick() { // 关闭公共画廊事件 this.$emit('close') } },
定制化配置:Swiper3 相关配置 https://3.swiper.com.cn/api/
data() { return { swiperOptions: { pagination: '.swiper-pagination', // 分式分页器 paginationType: 'fraction', // 将observe应用于Swiper的父元素。当Swiper的父元素变化时,例如window.resize,Swiper更新。 observeParents: true, // 启动动态检查器(OB/观众/观看者),当改变swiper的样式(例如隐藏/显示)或者修改swiper的子元素时,自动初始化swiper。 observer: true } } },
官网API说明: https://3.swiper.com.cn/api/
Fade:简单CSS动画组件该通用组件实质上为过渡效果的动画组件,之前文章中也提到过,CSS简单的动画过渡效果,这里同一封装为组件方便其他组件使用。
其他组件在使用Fade组件时候,外层嵌套组件标签即可,组件将以插槽的方式包裹在<transition>标签内部。
<template> <transition> <slot></slot> </transition> </template> <script> export default { name: 'Fade' } </script> <style lang="stylus" scoped> .v-enter, .v-leave-to opacity: 0 .v-enter-active, .v-leave-active transition: opacity .5s </style>
我们回到Banner组件中,引入并使用这两个通用组件。
-
引入并注册通用组件
import CommonGallary from 'common/gallary/Gallary' import FadeAnimation from 'common/fade/Fade' components: { CommonGallary, FadeAnimation }
-
使用通用组件
<template>
<!-- 添加动画效果:渐隐渐现,内部common-gallary组件以插槽的方式嵌入 --> <fade-animation> <!-- 公用图片画廊 父子组件交互 + 展示控制--> <common-gallary :imgs="gallaryImgs" @close="handleGallaryClose" v-show="showGallary"></common-gallary> </fade-animation>
这里可以看到传递给Gallery组件传递给父组件Banner的close的事件:handleGallaryClose,并且该组件上采用v-show做了组件展示的控制,控制变量为showGallary。
默认情况下:
data() { return { showGallary: false } },
点击景点主图片区域时:
handleBannerClick() { this.showGallary = true }
点击通用组件Gallary区域时:
handleGallaryClose() { this.showGallary = false }