先放一下这个完结项目的整体效果



下面跟我我一起进行下面项目的进行吧~~~

接下来我们进行的是实现header的渐隐渐显效果,并且点击返回要回到首页

我们先看效果



在处理详情页向下移动过程中,header页出现,我们使用的是监听滚动的方法,然后动态传入样式

//src\pages\detail\components\Header.vue
<template>
<div>
<router-link
class="header-abs"
tag="div"
to="/"
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-icon-back"></div>
</router-link>
景点详情
</div>
</div>
</template>
<script>
export default {
name: 'DetailHeader',
data () {
return {
showAbs: true,
opacityStyle: {
opacity: 0
}
}
},
methods: {
handleScroll () {
console.log('滾動艦艇', document.documentElement.scrollTop)
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)
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl';
.header-abs
position:absolute
left:.2rem
top:.2rem
width:.8rem
height:.8rem
border-radius:.4rem
background:rgba(0,0,0,0.8)
text-align:center
line-height:.8rem
.header-abs-back
color:#fff
font-size:.4rem
.header-fixed
height :$headerHeight
line-height:$headerHeight
overflow:hidden
position:fixed
top:0
left:0
right:0
text-align:center
color:#fff
background:$bgColor
font-size:.32rem
.header-icon-back
width:.64rem
text-align:center
font-size:.4rem
position:absolute
top:0
left:0
color:#fff
</style>
//src\pages\detail\Detail.vue
<template>
<div> <detail-banner></detail-banner>
<detail-header></detail-header>
<div class="container"></div>
</div>
</template>
<script>
import DetailBanner from './components/Banner'
import DetailHeader from './components/Header'
export default {
name: 'Detail',
components: {
DetailBanner,
DetailHeader
}
}
</script>
<style lang="stylus" scoped>
.container
height:50rem
</style>

接下来我们对项目中,全局事件进行解绑

当我们在header.vue组件中使用 window.addEventListener('scroll', this.handleScroll)的时候,

我们会发现,在首页中滚动页面也会调用这个事件



我们使用keep-alive在缓存过程中产生的钩子函数进行解决这个问题

当在这些组件之间切换的时候都会请求一些请求过的数据,每次请求都会导致重复渲染影响性能。这些数据可以存到缓存。此时使用 activate:是在被包裹组件被激活的状态下使用的生命周期钩子,deactivated:在被包裹组件停止使用时调用

我们的header组件中添加



这样就解决了在首页中,也会进行滚动事件的Bug

//header.vue
<template>
<div>
<router-link
class="header-abs"
tag="div"
to="/"
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-icon-back"></div>
</router-link>
景点详情
</div>
</div>
</template>
<script>
export default {
name: 'DetailHeader',
data () {
return {
showAbs: true,
opacityStyle: {
opacity: 0
}
}
},
methods: {
handleScroll () {
console.log('滾動艦艇', document.documentElement.scrollTop)
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)
},
// 移除全局事件的影响
dactivated () {
window.removeEventListener('scroll', this.handleScroll)
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl';
.header-abs
position:absolute
left:.2rem
top:.2rem
width:.8rem
height:.8rem
border-radius:.4rem
background:rgba(0,0,0,0.8)
text-align:center
line-height:.8rem
.header-abs-back
color:#fff
font-size:.4rem
.header-fixed
height :$headerHeight
line-height:$headerHeight
overflow:hidden
position:fixed
top:0
left:0
right:0
text-align:center
color:#fff
background:$bgColor
font-size:.32rem
.header-icon-back
width:.64rem
text-align:center
font-size:.4rem
position:absolute
top:0
left:0
color:#fff
</style>

接下来我们使用递归组件实现详情页的列表

所谓递归组件是什么?就是在组件里面调用自身。

我们先放一下实现的效果图



先在detail页面中定义好数据格式,并将数据传递给子组件

//list.vue
<template>
<div> <detail-banner></detail-banner>
<detail-header></detail-header>
<div class="container">
<detail-list :list="list"></detail-list>
</div>
</div>
</template>
<script>
import DetailBanner from './components/Banner'
import DetailHeader from './components/Header'
import DetailList from './components/List'
export default {
name: 'Detail',
components: {
DetailBanner,
DetailHeader,
DetailList
},
data () {
return {
list: [
{
title: '成人票',
children: [
{
title: '成人一馆联票',
children: [
{
title: '成人一馆联票-宝安连锁店销售'
},
{
title: '成人一馆联票-龙岗连锁店销售'
},
{
title: '成人一馆联票-坂田连锁店销售'
}
]
},
{
title: '成人二馆联票'
},
{
title: '成人三馆联票'
}
]
},
{
title: '学生票',
children: [
{
title: '学生一馆联票'
},
{
title: '学生二馆联票'
},
{
title: '学生三馆联票'
}
]
},
{
title: '儿童票',
children: [
{
title: '儿童一馆联票',
children: [
{
title: '儿童一馆联票-宝安连锁店销售'
},
{
title: '儿童一馆联票-龙岗连锁店销售'
},
{
title: '儿童一馆联票-坂田连锁店销售'
}
]
},
{
title: '儿童二馆联票'
},
{
title: '儿童三馆联票'
}
]
}
]
}
}
}
</script>
<style lang="stylus" scoped>
// .container
// height:50rem
</style>
//list.vue
//src\pages\detail\components\List.vue
<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> export default {
name: 'DetailList',
props: {
list: Array
}
}
</script>
<style lang="stylus" scoped>
.item-title-icon
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;
vertical-align: middle;
.item-title
line-height:0.8rem
font-size:.32rem
padding:0 .2rem
.item-children
padding-left:0.6rem
</style>

接下来我们使用ajax来动态渲染数据

我们在mock数据引入detail.json

{
"ret": true,
"data": {
"sightName": "大连圣亚海洋世界(AAAA景区)",
"bannerImg": "http://img1.qunarzz.com/sight/p0/201404/23/04b92c99462687fa1ba45c1b5ba4ad77.jpg_600x330_bf9c4904.jpg",
"commentsNum": 27,
"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": "特惠票"
}
]
}
}

在detail中引入数据

//src\pages\detail\Detail.vue
<template>
<div> <detail-banner
:sightName="sightName"
:bannerImg="bannerImg"
:bannerImgs="gallaryImgs"
></detail-banner>
<detail-header ></detail-header>
<div class="container">
<detail-list :list="list"></detail-list>
</div>
</div>
</template>
<script>
import DetailBanner from './components/Banner'
import DetailHeader from './components/Header'
import DetailList from './components/List'
import axios from 'axios'
export default {
name: 'Detail',
components: {
DetailBanner,
DetailHeader,
DetailList
},
data () {
return {
sightName: '',
bannerImg: '',
gallaryImgs: [],
list: [
]
}
},
methods: {
getDetailInfo () {
axios.get('/api/detail.json?id=' + this.$route.params, {
params: {
id: this.$route.params.id
}
}).then(this.handleGetDataSucc)
},
handleGetDataSucc (res) {
console.log('res', res)
res = res.data
if (res.ret && res.data) {
const data = res.data
console.log('data', data)
this.sightName = data.sightName
this.bannerImg = data.bannerImg
this.gallaryImgs = data.gallaryImgs
this.list = data.categoryList
}
}
},
mounted () {
this.getDetailInfo()
}
}
</script>
<style lang="stylus" scoped>
// .container
// height:50rem
</style>

将detail中传入的数据,传递给子组件

//src\pages\detail\components\List.vue
<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> export default {
name: 'DetailList',
props: {
list: Array
}
}
</script>
<style lang="stylus" scoped>
.item-title-icon
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;
vertical-align: middle;
.item-title
line-height:0.8rem
font-size:.32rem
padding:0 .2rem
.item-children
padding-left:0.6rem
</style>
//src\pages\detail\components\Banner.vue
<template>
<div>
<div class="banner" @click="handleBannerClick">
<img :src="bannerImg" alt="" class="banner-img">
<div class="banner-info">
<div class="banner-title">
{{this.sightName}}
</div>
<div class="banner-number">
<span class="iconfont"></span>
{{this.bannerImgs.length}}
</div>
</div>
</div>
<common-gallary
:imgs="bannerImgs"
v-show="showGallery"
@close="handlegalleryClose"
></common-gallary>
</div>
</template>
<script>
import CommonGallary from 'common/gallary/Gallary'
export default {
name: 'DetailBanner',
props: {
sightName: String,
bannerImg: String,
bannerImgs: Array
},
data () {
return {
showGallery: false,
imgs: [
// 'http://img1.qunarzz.com/sight/p55/201211/04/fbcab3e5d6479ce893835fbb.jpg_r_800x800_6360f514.jpg',
// 'http://img1.qunarzz.com/wugc/p180/201306/16/7f08e81624346b1693835fbb.jpg_r_800x800_5f03ad73.jpg'
]
}
},
components: {
CommonGallary
},
methods: {
handleBannerClick () {
this.showGallery = true
},
handlegalleryClose () {
this.showGallery = false
}
}
}
</script>
<style lang="stylus" scoped>
.banner
overflow:hidden
height:0
padding-bottom:55%
position:relative
.banner-img{
width:100%
}
.banner-info
display:flex
position:absolute
left:0
right:0
bottom:0
line-height:0.6rem
background-image:linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,0.8))
color:#fff
.banner-title
flex:1
font-size:.32rem
padding:0 .2rem
.banner-number
padding:0 .4rem
height:.32rem
line-height:.4rem
margin-top:.24rem
border-radius:.2rem
background:rgba(0,0,0,.8)
font-size:0.24rem
.iconfont
font-size:.24rem </style>

接下来我们给项目添加一些动画效果,点击我们的banner图片,进入gallery图片,给他一个动画的效果

封装动画组件,transition里面的slot插槽,可以插入各种我们要进行动画处理的组件

//src\common\fade\FadeAnimation.vue
<template>
<div>
<transition>
<slot></slot>
</transition>
</div>
</template>
<script>
export default {
name: 'FadeAnimation'
}
</script>
<style lang="stylus" scoped>
.v-enter,.v-leave-to
opacity:0
.v-enter-active,.v-leave-active
transition: opacity 0.5s
</style>

我们在gallery中使用



效果如下

我已经将我跟着视频做的项目传到了github上面:https://github.com/JserJser/dailyPush/tree/master/travel

恬不知耻向你们求一个star~~~~~

后记:今天接到了一个电话,大抵是以前实习的时候的,问我同学,说是打我同学电话没有打到,然后问我有没有同他联系。有些唏嘘。

想想毕业已经三年了。

不知道自己对自己的未来有什么期许呢~你想成为什么样的人呢?是不是三年前自己想成为的人呢?

好好看视频,好好学习,好好掌握项目吧~我呀, 还差的很远呢~

第十部分主要讲解的是项目接口联调部分,将mock中的数据放入XAMPP中,还有如何手机真机测试,以及如何打包数据,这部分我就是看了而已,没有实际操作。真实数据是与后端联调。视频的老师给了有些学习vue的建议,可以多多研究vue的一些常见组件的源码,还有研究vue的源码,以及,学习vue的ssr服务端的渲染问题等。

还有很多东西不会,要精通,要很厉害啊~~~

跟我一起做一个vue的小项目(APPvue2.5完结篇)的更多相关文章

  1. 跟我一起做一个vue的小项目(二)

    这个vue项目是紧跟着之前的项目跟我一起做一个vue的小项目(一)来的. 我继续后面的开发(写的比较粗糙,边学边记录) 下图是header头部的样式 header组件内容如下 //header.vue ...

  2. 跟我一起做一个vue的小项目(八)

    接下来我们进行的是城市选择页面的路由配置 添加city.vue,使其点击城市,然后跳转到city页面 //router.js import Vue from 'vue' import Router f ...

  3. 跟我一起做一个vue的小项目(七)

    先看下我们所做项目的效果 这些数据都是我们在data中定义的,不是从后端数据中请求的.那么 接下来我们使用axios渲染数据 npm install axios --save 每个组件里面的数据都不相 ...

  4. 跟我一起做一个vue的小项目(五)

    接下来我们要做的是热门推荐页面,我们写一个推荐组件 使用的方法也是前端data中的数据渲染到页面上面,这里对文字过长取省略号的方法不成功使用了一个小技巧 使用了min-width:0 我们来看完整的代 ...

  5. 跟我一起做一个vue的小项目(四)

    接下来我们进行的是轮播页面下面的导航页的开发 我们需要的是实现轮播页下面的图标,并且实现轮播效果 这个话,其实基本思路先是渲染出小图标,然后,我们要对页数进行判断,如果图标的个数展示的就是8个,那个这 ...

  6. 跟我一起做一个vue的小项目(十一)

    接下来我们进行的是详情页动态路由及banner布局 先看页面的效果 下面是代码部分 <template> <div> <div class="banner&qu ...

  7. 跟我一起做一个vue的小项目(十)

    接下来我们对城市列表页面进行优化,除了对数据优化,也会进行节流处理 //src\pages\city\components\Alphabet.vue <template> <ul c ...

  8. 跟我一起做一个vue的小项目(三)

    接下来我们进行轮播的开发 安装插件,选用2.6.7的稳定版本 npm install vue-awesome-swiper@2.6.7 --save 根据其github上面的用法,我们在全局引用,在m ...

  9. 跟我一起做一个vue的小项目(九)

    接下来我们进行的就是城市列表页面数据额动态渲染. 也是在mock数据,进行动态渲染 //city.json { "ret": true, "data":{ &q ...

随机推荐

  1. <Python基础>集合的基本操作

    #小知识点:返回对象的内存地址 i,j = 1,2 print(id(i),id(j)) #集合的基本操作, #相当于没有键值对的字典,里面的元素是无序且不重复的 #一般写法 s = set({1,2 ...

  2. 2018-8-10-win10-uwp-验证输入-自定义用户控件

    title author date CreateTime categories win10 uwp 验证输入 自定义用户控件 lindexi 2018-08-10 19:16:51 +0800 201 ...

  3. UMP系统架构 LVS

  4. 【笔记篇】C#笔记2

    返回目录:目录请戳这里~ C#数组 基本概念不提.. int[] a; bool[] b = new bool[10]; float[] c = {0.5, 57.0, 233.3, 12345.67 ...

  5. AutoIt自动化编程(2)【转】

    注意:窗口标题和窗口文本参数总是对大小写敏感的. 1.等待窗口系列命令/函数 AHK和AU3都提供了用法类似的一组窗口等待命令/函数:WinWait/WinWaitActive/WinWaitClos ...

  6. 不用winio直接用c#函数实现模拟键盘

    原理来自:  http://blog.sina.com.cn/s/blog_71921a8e0100olaw.html /// <summary> /// 导入模拟键盘的方法 /// &l ...

  7. DRF初识

    目录 Web API接口 什么是Web API接口 接口四大特征 接口文档的编写测试 restful接口规范 url链接设计 五大请求方式 响应结果 DRF框架安装 基于原生Django实现十大接口 ...

  8. JAVA 类加载机制学习笔记

    JAVA 类生命周期 如上图所示,Java类的生命周期如图所示,分别为加载.验证.准备.解析.初始化.使用.卸载.其中验证.准备.解析这三个步骤统称为链接. 加载:JVM根据全限定名来获取一段二进制字 ...

  9. arguments介绍(二)

    1.1 将参数从一个函数传递到另一个函数 下面是将参数从一个函数传递到另一个函数的推荐做法. function foo() { bar.apply(this, arguments); } functi ...

  10. Attribute类的使用

    为每个变量设置设置属性 "Description" public class PatternOption { /// <summary> /// 方向图步长 /// & ...