项目地址 视频演示地址
详情页动态路由及banner布局 实现点击以下能够进入详情页
src\pages\home\components\Recommend.vue中router-link
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <router-link tag="li" class="item border-bottom" v-for="item of list" :key="item.id" :to="/detail/ + item.id" > <img class ="item-img" :src ="item.imgUrl" alt ="" /> <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 >
src\router\index.js路由配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import Detail from '@/pages/detail/Detail' Vue.use(Router) export default new Router({ routes: [ { path : '/' , name : 'Home' , component: Home },{ path :'/city' , name :'City' , component:City },{ path :'/detail/:id' , name :'Detail' , component:Detail } ] })
src\pages\detail\Detail.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <template > <div > <detail-banner > </detail-banner > <detail-header > </detail-header > </div > </template > <script > import DetailBanner from "./components/Banner"; import DetailHeader from "./components/Header"; export default { name : "Detail" , components: { DetailBanner, DetailHeader } }; </script >
详情页对全局事件解绑 全局事件 详情页绑定了一个全局事件,当我在详情页面中滚动,这个样写没有问题,但是当我去到其他页面,在滚动时,你就会发现,刚刚你绑定在详情页中的滚动事件,在这个页面也被执行了,这肯定是有问题的。
其实在我们使用了keep-alive标签后,会有两个生命周期函数分别是:activated、deactivated
activated:页面展示的时候被执行
deactivated:页面被隐藏或者页面即将被替换成新的页面时被执行
1 2 3 4 5 6 activated () { window.addEventListener('scroll ', this .handleScroll ) }, deactivated () { window.removeEventListener('scroll ', this .handleScroll ) }
这段代码是页面被展示的执行scroll,页面被隐藏的时候移除scroll事件
src\pages\detail\components\Header.vue部分代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <template > <div > <router-link tag ="div" to ="/" class ="header-abs" v-show ="showAbs" > <div class ="iconfont header-abs-back" >  </div > </router-link > <div class ="header-fixed" v-show ="!showAbs" :style ="opacityStyle" > <router-link to ="/" > <div class ="iconfont header-fixed-back" >  </div > </router-link > 景点详情 </div > </div > </template > <script > export default { name: "DetailHeader" , data ( ) { return { showAbs: true , opacityStyle: 0 }; }, methods: { handleScroll ( ) { const top = document .documentElement.scrollTop; if (top > 60) { let opacity = top / 140 ; opacity = opacity > 1 ? 1 : opacity; this .opacityStyle = { opacity }; this .showAbs = false ; } else { this .showAbs = true ; } } }, activated ( ) { window .addEventListener("scroll" , this .handleScroll); }, deactivated ( ) { window .removeEventListener("scroll" , this .handleScroll); } };</script >
递归组件实现详情页 递归组件的意思就是在组件自身调用组件自身。
数据 detail.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 { "ret" : true , "data" : { "sightName" : "大连圣亚海洋世界(AAAA景区)" , "bannerImg" : "http://img1.qunarzz.com/sight/p0/201404/23/04b92c99462687fa1ba45c1b5ba4ad77.jpg_600x330_bf9c4904.jpg" , "gallaryImgs" : ["http://img1.qunarzz.com/sight/p0/201404/23/04b92c99462687fa1ba45c1b5ba4ad77.jpg_800x800_70debc93.jpg" , "http://img1.qunarzz.com/sight/p0/1709/76/7691528bc7d7ad3ca3.img.png_800x800_9ef05ee7.png" ], "categoryList" : [{ "title" : "成人票" , "children" : [{ "title" : "成人三馆联票" , "children" : [{ "title" : "成人三馆联票 - 某一连锁店销售" }] },{ "title" : "成人五馆联票" }] }, { "title" : "学生票" }, { "title" : "儿童票" }, { "title" : "特惠票" }] } }
src\pages\detail\components\List.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 <template > <div > <div class ="item" v-for ="(item, index) of list" :key ="index" > <div class ="item-title border-bottom" > <span class ="item-title-icon" > </span > {{ item.title }} </div > <div v-if ="item.children" class ="item-children" > //使用递归组件 <detail-list :list ="item.children" > </detail-list > </div > </div > </div > </template > <script > import Detail from "../Detail.vue"; export default { components: { Detail }, name: "DetailList", props: { list: Array } }; </script > <style lang ="stylus" scoped > .item-title-icon position: relative left: .06rem top: .06rem display: inline-block width: .36rem height: .36rem background: url(http://s.qunarzz.com/piao/image/touch/sight/detail.png) 0 -.45rem no-repeat margin-right: .1rem background-size: .4rem 3rem .item-title line-height :.8rem font-size: 0.32rem padding : 0 .2rem .item-children padding:0 .2rem </style >
上面代码中,在 list-children 这个元素下,先做了一个判断,当 item.children 下有值的时候,调用一下自身,也就是 detail-list 这个组件,这个组件也是通过属性的形式,传一个 list,因为在 list.vue 中已经通过 props 接收到 list 了,而且外层已经循环过 list 了,现在要获取 list 下的 children 中的数据,所以直接让这个 list 属性等于 item.children 就可以了。因为数据存在层级关系,可以通过添加样式呈现出来,效果如下图:
keep-alive不缓存 在Detail.vue页面中,当我点击了其他景点后,它也是不会发送请求的,那么Detail页面就不会重新渲染了。
可以使用keep-alive的exclude属性,给它默认设置为Detail,用途是每次进入Detail页面都会发送请求
1 2 3 <keep -alive exclude="Detail" > <router-view /> </keep -alive>
页面路由切换回到最顶部
让页面切换后始终回到页面最顶部
1 2 3 scrollBehavior (to , from , savedPosition) { return { x: 0 , y: 0 } }
在项目中加入基础动画 让图片点击时有若影若现的效果
新建一个组件,通过插槽的方式传递 src\common\fade\FadeAnimation.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <template > <transition > <slot > </slot > </transition > </template > <script > export default { name: "FadeAnimation" };</script > <style lang ="stylus" scoped > .v-enter, .v-leave-to opacity :0 .v-enter-activate, .v-leave-activate transition :opacity .5s </style >
src\pages\detail\components\Banner.vue中使用,部分代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <fade-animation > <common-gallary :imgs="bannerImgs" v-show="showGallary" @close="handleGallaryClose" ></common-gallary ></fade-animation > <script > import CommonGallary from "common/gallary/Gallary"; import FadeAnimation from "common/fade/FadeAnimation"; export default { name : "DetailBanner" , components: { CommonGallary, FadeAnimation } }; </script >
至此,项目基本完结…