Vue 实例实战之 Vue webpack 仿去哪儿网App页面开发(应用中的几个页面简单实现)

目录

Vue 实例实战之 Vue webpack 仿去哪儿网App页面开发(应用中的几个页面简单实现)

一、简单介绍

二、环境

三、效果预览

四、项目的页面结构

五、项目主要插件

六、项目实现过程

七、router路由管理, store vuex 状态管理 说明

八、几个性能优化点说明

九、axios 获取服务端数据说明

十、src/common 的共有 vue

十一、src/assets/styles 存放css 样式、常用的变量样式参数等

十二、该仿去哪儿网的演示项目源码下载


一、简单介绍

Vue 开发的一些知识整理,方便后期遇到类似的问题,能够及时查阅使用。

本节介绍,Vue 开发的实例实战,模仿开发去哪儿网的几个页面 ,体验 Vue 在实战中应用,欢迎指出,或者你有更好的方法,欢迎留言。

二、环境

1、vue  2.5.2

2、vue-router 3.0.1

3、vuex 3.0.1

三、效果预览

四、项目的页面结构

五、项目主要插件

六、项目实现过程

1、环境构建,并且 vue init webpack xxx_工程名,根据提示创建工程

具体环境搭建过程:Web 前端 之 Vue webpack 环境的搭建及工程创建简单整理_仙魁XAN的博客-CSDN博客

2、工程文件目录结构如下

3、安装依赖,如果下面的一些依赖没有安装,可以对应使用 npm install 插件名@版本号 --save 先安装插件包

4、src 开发文件结构说明

5、在 main.js 中引入 reset.css 用作重置浏览器标签的样式表,统一样式,border.css 移动端1像素边框,fastclick 解决移动端click事件延迟300ms和点击穿透问题,vue-awesome-swiper 全局轮播组件设置,babel-polyfill 解决部分手机白屏问题

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
/*
//reset.css是重置浏览器标签的样式表,其作用就是重新定义标签样式,覆盖浏览器的CSS默认属性,也就是指把浏览器提供的默认样式覆盖掉。
//
//在HTML标签在浏览器里有默认的样式,例如 p 标签有上下边距,strong标签有字体加粗样式,em标签有字体倾斜样式。不同浏览器的默认样式之间也会有差别,例如ul默认带有缩进的样式,在IE下,它的缩进是通过margin实现的,而Firefox下,它的缩进是由padding实现的。在切换页面的时候,浏览器的默认样式往往会给我们带来麻烦,影响开发效率。
//
//所以解决的方法就是一开始就将浏览器的默认样式全部去掉,更准确说就是通过重新定义标签样式。“覆盖”浏览器的CSS默认属性。最最简单的说法就是把浏览器提供的默认样式覆盖掉!这就是CSS reset。
*/
import 'styles/reset.css'
import 'styles/iconfont.css'/*
//该css样式用于解决移动端1像素边框问题。问题分析:有些手机的屏幕分辨率较高,是2-3倍屏幕。css样式中border:1px solid red;在2倍屏下,显示的并不是1个物理像素,而是2个物理像素。为了解决这个问题,引入border.css是非常有必要的。
*/
import 'styles/border.css'// fastclick 解决移动端click事件延迟300ms和点击穿透问题
import fastClick from 'fastclick'
// 轮播图插件
import VueAwesomeSwiper from 'vue-awesome-swiper'
import 'swiper/dist/css/swiper.css'import store from './store/index'// 解决部分手机白屏问题
import "babel-polyfill"fastClick.attach(document.body)
Vue.config.productionTip = false
// 全局使用轮播图插件
Vue.use(VueAwesomeSwiper)/* eslint-disable no-new */
new Vue({el: '#app',router,store,components: { App },template: '<App/>'
})

6、App.vue 中的 <router-view/> 用于显示路由切换的界面,<keep-alive exclude="Detail"> 标签,作用是缓存 vue ,执行一次 mounted ,exclude 的 vue 则不做缓存

<template><div id="app"><!--缓存数据,执行一次 mounted  ,exclude 不包括--><keep-alive exclude="Detail"><router-view/></keep-alive></div>
</template><script>
export default {name: 'App'
}
</script><style>
</style>

7、pages 是三个路由界面,以及路由界面里面的拆分页面,拆分的目的也是把复杂的界面简单化

8、 Home 界面,包含 5 个拆分页面,Home.vue 主要功能是,从 store 中获取 城市信息,然后axios.get 获取数据,把数据传递给各个子页面,

<template><div><!--:city='city' 数据向子组件传递--><home-header></home-header><home-swiper :list="swiperList"></home-swiper><home-icons :list="iconsList"></home-icons><home-recommend :list="recommendsList"></home-recommend><home-weekend :list="weekendsList"></home-weekend></div>
</template><script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper.vue'
import HomeIcons from './components/Icons.vue'
import HomeRecommend from './components/Recommend.vue'
import HomeWeekend from './components/Weekend.vue'// 获取网络数据
import axios from 'axios'
import {mapState} from 'vuex'export default {name: 'Home',components: {HomeHeader,HomeSwiper,HomeIcons,HomeRecommend,HomeWeekend},data (){return{swiperList:[],iconsList:[],recommendsList:[],weekendsList:[],lastCity:'',}},
computed:{
...mapState(['city'])
},methods:{getHomeInfo(){axios.get('/api/index.json?city='+this.city).then(this.getHomeInfoSucc)},getHomeInfoSucc(res){console.log(res)const result = res.data.retif(result && res.data.data){const data = res.data.datathis.swiperList = data.swiperListthis.iconsList = data.iconsListthis.recommendsList = data.recommendsListthis.weekendsList = data.weekendsList}},},mounted(){this.lastCity = this.citythis.getHomeInfo()},activated(){if(this.lastCity !== this.city){this.lastCity = this.citythis.getHomeInfo()}},
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style>* {margin: 0px;padding: 0px;}
</style>
9、Home 界面中的 Header ,包含输入框,显示当前城市,点击城市可以跳转到城市选择界面
<template><div class="header"><div class="header-left"><div class="iconfont back-icon"></div></div><div class="header-input"><span class="iconfont"></span>输入城市/景点/游玩主题</div><router-link to="/city"><div class="header-right">{{this.city}}<span class="iconfont arrow-icon"></span></div></router-link></div>
</template><script>// 映射属性import {mapState} from 'vuex'export default {name: 'HomeHeader',
//  接收父组件的数据props:{},computed :{...mapState(['city'])},data: function(){return {}}
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="stylus" scoped>@import "~styles/varibles.styl";.header{display: flex;line-height: $headerHeight;background: $bgColor;color: #ffffff;}.header .header-left{width: 0.64rem;float: left;}.header .header-left .back-icon {text-align: center;font-size: 0.4rem;}.header .header-input{flex: 1;background: #fff;border-radius: 0.1rem;margin-top: 0.12rem;margin-left: 0.2rem;padding-left: 0.2rem;height: 0.64rem;line-height: 0.64rem;color: #ccc;}.header .header-right{min-width: 1.04rem;padding: 0 .1rem;float: right;text-align: center;color:white;}.header .header-right .arrow-icon{margin-left: -0.04rem;font-size: 0.24rem;}</style>
10、Home 界面中的 Swiper 是一个轮播组件,轮播图片
<template><div class="wrapper"><swiper :options="swiperOption" v-if="showSwiper"><swiper-slide v-for="item of list" :key="item.id"><img class="swiper-img" :src="item.url"></swiper-slide><div class="swiper-pagination" slot="pagination"></div></swiper></div>
</template><script>
export default {name: 'HomeSwiper',props:{list:Array},data: function () {return {swiperOption: {// 下面的圆点pagination:'.swiper-pagination',// 循环轮播loop:true},}},computed:{showSwiper(){return this.list.length}}
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="stylus" scoped>// >>> (scoped 阻挡后 >>>穿透 ).wrapper >>> .swiper-pagination-bullet{background: #fff;}.wrapper{width: 100%;height: 0;padding-bottom: 30.45%;background: #eee;}.swiper-img{width: 100%;}
</style>

11、Home 界面中的 Icons ,显示icon图标组

<template><div class="icons"><swiper :options="swiperOption"><swiper-slide v-for="(page, indexPage) of pages" :key="indexPage"><div class="icon" v-for="item of page" :key="item.id"><div class="icon-img"><img class="icon-img-content" :src="item.imgUrl"></div><p class="icon-desc">{{item.desc}}</p></div></swiper-slide></swiper></div></template><script>export default {name: 'HomeIcons',props:{list:Array},data: function () {return {swiperOption:{autoplay:false}}},computed:{pages (){const pages = []this.list.forEach((item, index) =>{const page = Math.floor(index/8)if(pages[page]==null){pages[page]=[]}pages[page].push(item)})return pages}}}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="stylus" scoped>@import "~styles/varibles.styl";@import "~styles/mixins.styl";.icons >>> .swiper-wrapper{height: 0;padding-bottom: 50%;}.icons{margin-top: 0.2rem;}.icons .icon{position: relative;overflow: hidden;float: left;width: 25%;padding-bottom: 25%;}.icons .icon .icon-img{position: absolute;top: 0;left: 0;right: 0;bottom: .44rem;}.icons .icon .icon-img .icon-img-content{height: 90%;display: block;margin: 0 auto;}.icons .icon .icon-desc{position: absolute;left: 0;right: 0;bottom: 0;height: .44rem;line-height: .44rem;color: $darkTextColor;text-align: center;font-size: 0.2rem;/*文字过多,则 ... 显示*/ellipsis()}
</style>
12、Home 界面中的 Recommend,热门推荐,点击可以跳转到热门推荐的详情界面
<template><div><div class="title">热门推荐</div><ul class="item-wrapper"><!--border-bottom 每个下面有线--><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"><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></ul></div>
</template><script>export default {name: 'HomeRecommend',props:{list:Array},data: function () {return {}}}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="stylus" scoped>@import "~styles/mixins.styl";.title{margin-top: 0.2rem;line-height: 0.8rem;background: #eee;text-indent: 0.2rem;}.item-wrapper{padding: 0.15rem;}.item{overflow: hidden;display: flex;height: 1.9rem;}.item .item-info{flex:1;padding: 0.1rem;/*让 ellipsis() 生效*/min-width: 0;}.item .item-img {width: 1.7rem;height: 1.7rem;padding: 0.1rem;}.item .item-info .item-title{line-height: 0.54rem;font-size: .32rem;ellipsis()}.item .item-info .item-desc{line-height: 0.4rem;color: #ccc;ellipsis()}.item .item-info .item-button{line-height: .44rem;margin-top: 0.2rem;background: #ff9300;padding: 0 0.2rem;border-radius: 0.06rem;color: #fff;}
</style>
13、Home 界面中的 Weekend,周末去哪页面
<template><div><div class="title">周末去哪儿</div><ul class="item-wrapper"><!--border-bottom 每个下面有线--><li 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></div></li></ul></div>
</template><script>export default {name: 'HomeWeekend',props:{list:Array},data: function () {return {}}}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="stylus" scoped>@import "~styles/mixins.styl";.title{line-height: 0.8rem;background: #eee;text-indent: 0.2rem;}.item-wrapper{padding: 0.15rem;}.item {overflow: hidden;height: 0;padding-bottom: 47%;}.item .item-img {width: 100%;}.item .item-info .item-title{line-height: 0.54rem;font-size: .32rem;ellipsis()}.item .item-info .item-desc{line-height: 0.4rem;font-size: 0.2rem;color: #ccc;ellipsis()}
</style>

14、 City 界面,包含 4 个拆分页面,City.vue 主要功能是,axios.get 获取数据,以及获取 Alphabet传递的数据,对应把数据传递给各个子页面

<template><div><city-header></city-header><city-search :cities="cities"></city-search><city-list :cities="cities" :hot="hotCities" :letter="letter"></city-list><city-alphabet :cities="cities" @change="handleAlphabetEvent"></city-alphabet></div>
</template><script>import CityHeader from './components/Header.vue'import CitySearch from './components/Search.vue'import CityList from './components/List.vue'import CityAlphabet from './components/Alphabet.vue'import axios from 'axios'export default {name: 'City',components:{CityHeader,CitySearch,CityList,CityAlphabet,},data: function () {return {cities:{},hotCities:[],letter:''}},methods:{getCityInfo(){axios.get('/api/city.json').then(this.getCityInfoSucc)},getCityInfoSucc(res){res = res.dataif(res.ret && res.data){const data = res.datathis.cities = data.citiesthis.hotCities = data.hotCitiesconsole.log(res)}},handleAlphabetEvent(alpha){this.letter = alpha}},mounted(){this.getCityInfo()}}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style></style>
15、City 界面中的 Header,包含标题,以及返回键,点击回到 Home 界面
<template><div class="header">城市选择<router-link to="/"><div class="iconfont back-icon"></div></router-link></div>
</template><script>
export default {name: 'CityHeader',data: function () {return {msg: 'Welcome to Your Vue.js App'}}
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="stylus" scoped>@import "~styles/varibles.styl";.header{position: relative;overflow: hidden;height: $headerHeight;line-height: $headerHeight;background: $bgColor;color: #ffffff;text-align: center;font-size: 0.4rem;}.header .back-icon{position: absolute;top:0;left: 0;width: 0.64rem;text-align: center;font-size: 0.4rem;color: white;}</style>
16、City 界面中的 Search,包含输入框,以及搜索出城市的列表,和没有匹配数据的提示;其中搜索功能是在watch 中监听输入的变化,进行在城市名字和拼音中是否包含,添加到结果中去,从而实现搜索功能
<template><div><div class="search"><input v-model="keyword" class="search-input" placeholder="输入城市"></div><div v-show="keyword" class="search-content" ref="search"><ul><li class="search-item border-bottom"v-for="item of list":ket="item.id"@click="handleClickCity(item.name)">{{item.name}}</li><li class="search-item border-bottom" v-show="hasNoData">没有匹配数据</li></ul></div></div>
</template><script>import BScroll from 'better-scroll'import {mapActions} from 'vuex'
export default {name: 'CitySearch',props:{cities:Object},data: function () {return {keyword:'',list:[],timer:null}},watch:{keyword(){console.log('sdd')// 控制执行频率,提高性能if(this.timer){clearTimeout(this.timer)}this.timer = setTimeout(()=>{if(!this.keyword){this.list = []return}const result = []for(let i in this.cities){this.cities[i].forEach((value)=>{if(value.spell.indexOf(this.keyword) > -1|| value.name.indexOf(this.keyword)> -1){result.push(value)}})}this.list = resultconsole.log('this.scroll ', this.scroll)setTimeout(()=>{this.scroll.refresh()},10)},100)}},computed:{hasNoData(){return this.list.length==0}},methods:{handleClickCity(city){this.changeCity(city)this.$router.push('/')},...mapActions(['changeCity'])},mounted(){this.scroll = new BScroll(this.$refs.search,{click:true})}
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="stylus" scoped>@import "~styles/varibles.styl";.search{height: 0.72rem;padding: 0 0.1rem;background: $bgColor;}.search .search-input{box-sizing: border-box;padding: 0 0.1rem;width: 100%;height: 0.62rem;line-height: 0.62rem;border-radius: 0.06rem;text-align: center;color: #777;}.search-content{z-index: 1;position: absolute;overflow: hidden;top:1.58rem;bottom: 0;right: 0;left: 0;background: #eee;}.search-content .search-item{line-height: 0.62rem;padding-left: 0.2rem;background: #fff;}</style>
17、City 界面中的 List,包含当前城市,热门城市,以及以首字母排列的城市列表,点击热门城市,以及城市列表的城市,都会跳转到对应城市的 Home 界面
<template><div class="list" ref="wrapper"><div><div class="area"><div class="title border-topbottom">当前城市</div><div class="button-list"><div class="button-wrapper"><div class="button">{{this.currentCity}}</div></div></div></div><div class="area"><div class="title border-topbottom">热门城市</div><div class="button-list"><div class="button-wrapper"v-for="item of hot":key="item.id"v-on:click="handleClickCity(item.name)"><div class="button">{{item.name}}</div></div></div></div><div class="area" v-for="(items,key) of cities" :key="key" :ref="key"><div class="title border-topbottom">{{key}}</div><div class="item-list"><div class="item border-bottom"v-for="item of items":key="item.id"v-on:click="handleClickCity(item.name)">{{item.name}}</div></div></div></div></div>
</template><script>import BScroll from 'better-scroll'import {mapState, mapActions} from 'vuex'
export default {name: 'CityList',props:{hot:Array,cities:Object,letter:String},data: function () {return {}},computed:{...mapState({currentCity:'city'})},watch:{letter(){if(!this.letter.isEmpty){// 监听字母点击,跳转console.log(this.letter)const element = this.$refs[this.letter][0]console.log(element)this.scroll.scrollToElement(element)}}},methods:{handleClickCity(city){this.changeCity(city)this.$router.push('/')},...mapActions(['changeCity'])},mounted(){// 城市数据先创建,然后在 scroll 不能可能 scroll 在没有数据的时候构建,从而使得scroll无法滚动setTimeout(()=>{this.scroll = new BScroll(this.$refs.wrapper,{click:true})console.log(' this.scroll ',this.scroll)},100)},
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="stylus" scoped>@import "~styles/varibles.styl";.list{overflow: hidden;position: absolute;top:1.58rem;bottom:0;left:0;right:0;}.border-topbottom::before{border-color: #ccc;}.border-topbottom::after{border-color: #ccc;}.border-bottom::before{border-color: #ccc;}
.title{padding-left: 0.2rem;background: #eee;color: #666;line-height: 0.44rem;font-size: 0.24rem;
}.button-list{overflow: hidden;padding: 0.1rem 0.6rem 0.1rem 0.1rem;}.button-list .button-wrapper{width: 33.33%;float: left;}.button-list .button-wrapper .button{margin: 0.1rem;padding: 0.1rem 0;border: 0.02rem solid #ccc;text-align: center;border-radius: 0.06rem;}.item-list .item{line-height: 0.76rem;padding-left: 0.2rem;}</style>
18、City 界面中的 Alphabet,包含所有城市列表的首字母组成的列表,其中通过touch的位置计算出当前选择的哪个字母,传递给 City,City 在传递给 List ,显示对应首字母城市列表
<template><div class="alphabet"><ul><li class="item"v-for="item of letters"@click="handleOnClick"@touchstart.prevent="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd":ref="item">{{item}}</li></ul></div>
</template><script>
export default {name: 'CityAlphabet',props:{cities:Object},data: function () {return {touchStatus:false,aStartY:0,// 性能优化timer:null}},computed:{letters(){const letters =[]for(let i in this.cities){letters.push(i)}return letters}},updated(){this.aStartY = this.$refs['A'][0].offsetTop},methods:{handleOnClick(e){this.$emit('change', e.target.innerText)},handleTouchStart(){this.touchStatus =true},handleTouchMove(e){if(this.touchStatus){if(this.timer){clearTimeout(this.timer)}// 控制执行频率,从而提升性能this.timer = setTimeout(()=>{// 75 是上面城市 和 输入城市元素的 Y 总和const touchY = e.touches[0].clientY - 75// 20 是 字母 元素的 Y 值const index = Math.floor((touchY - this.aStartY) / 20)if(index >= 0 && index < this.letters.length){this.$emit('change',this.letters[index])}}, 16)}},handleTouchEnd(){this.touchStatus =false},}
}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="stylus" scoped>@import "~styles/varibles.styl";.alphabet{position: absolute;top: 1.58rem;right: 0;bottom: 0;width: 0.4rem;display: flex;flex-direction: column;justify-content: center;
}.alphabet .item{color: $bgColor;text-align: center;line-height: 0.4rem;}</style>

19、 Detail界面,包含 3 个拆分页面,Detail.vue 主要功能是,axios.get 获取数据,对应把数据传递给各个子页面

<template><div class="detail"><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><script>import DetailBanner from './components/Banner.vue'import DetailHeader from './components/Header.vue'import DetailList from './components/List.vue'import axios from 'axios'export default {name: 'Detail',components:{DetailBanner,DetailHeader,DetailList,},data: function () {return {sightName:'',bannerImg:'',gallaryImgs:[],list: []}},methods:{getDetailInfo() {
//        axios.get('/api/detail.json?id='+this.$route.params.id)axios.get('/api/detail.json',{params:{id: this.$route.params.id}}).then(this.handleGetDataSucc)},handleGetDataSucc(res){res = res.dataif(res.ret && res.data){const data = res.datathis.sightName = data.sightNamethis.bannerImg = data.bannerImgthis.gallaryImgs = data.gallaryImgsthis.list = data.categoryList}}},mounted(){this.getDetailInfo()}}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="stylus" scoped>.content{height: 20rem;}
</style>

20、Detail 界面中的 Banner ,包含一个图片展示,以及一个图片集轮播组件(默认隐藏);其中点击图片,就会显示图片集轮播

<template><div><div class="banner" @click="onClickBanner"><img class="banner-img" :src="bannerImg"/><div class="banner-info"><div class="banner-title">{{this.sightName}}</div><div class="banner-number"><span class="iconfont banner-icon"></span>78</div></div></div><fade-animation><common-gallary:imgs="gallaryImgs"@close="handleGallaryClose"v-show="isShowGallary"></common-gallary></fade-animation></div>
</template><script>import CommonGallary from 'common/gallary/Gallary'import FadeAnimation from 'common/fade/FadeAnimation'export default {name: 'DetailBanner',props:{sightName:String,bannerImg:String,gallaryImgs:Array,},components:{CommonGallary,FadeAnimation,},data: function () {return {isShowGallary: false,}},methods:{onClickBanner(){this.isShowGallary = true},handleGallaryClose(){this.isShowGallary = false},},}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="stylus" scoped>.banner{position: relative;overflow: hidden;height: 0;padding-bottom: 50%;background: green;}.banner .banner-img{width: 100%;}.banner .banner-info{display: flex;position: absolute;left: 0;right: 0;bottom: 0;line-height: 0.6rem;color: #fff;background-image: linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,0.8));}.banner .banner-info .banner-title{flex: 1;font-size: 0.32rem;padding: 0 0.2rem;}.banner .banner-info .banner-number{height: 0.32rem;line-height: 0.32rem;padding: 0 0.4rem;margin-top: 0.14rem;border-radius: 0.2rem;background: rgba(0,0,0,0.8);font-size: 0.24rem;}.banner .banner-info .banner-number .banner-icon{font-size: 0.24rem;padding: 0.1rem;}
</style>

21、Detail 界面中的 Header,包含绝对位置的返回按钮,以及一个固定位置的返回按钮标题;根据当前页面的滚动情况,动态切换不同位置的按钮显隐

<template><div class="header"><router-link tag="div" to='/' class="header-abs" v-show="showAbs"><div class="iconfont back-abs-icon"></div></router-link><router-link tag="div" to='/' class="header-fixed" v-show="!showAbs":style="styleOpacity"><div class="iconfont back-fixed-icon"></div>景点详情</router-link></div>
</template><script>export default {name: 'DetailHeader',data: function () {return {showAbs:true,styleOpacity:{opacity:0}}},methods:{handleScroll(){console.log('handleScroll')const top = document.documentElement.scrollTopconsole.warn('top ', top)if (top > 60) {this.showAbs = falseconst opacity = top/140 > 1? 1: top/140this.styleOpacity = {opacity,}} else {this.showAbs = truethis.styleOpacity = {opacity:0,}}},},mounted(){window.addEventListener('scroll',this.handleScroll)},unmounted(){window.removeEventListener('scroll',this.handleScroll)},}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="stylus" scoped>@import "~styles/varibles.styl";.header-abs{position: absolute;left:0.2rem;top:0.2rem;width: 0.8rem;height: 0.8rem;line-height: 0.8rem;text-align: center;border-radius: 0.4rem;background: rgba(0,0,0,0.8);
}.header-abs .back-abs-icon{color: #fff;font-size: 0.4rem;}.header-fixed{position: fixed;z-index: 2;top:0;left:0;right:0;overflow: hidden;height: $headerHeight;line-height: $headerHeight;background: $bgColor;color: #ffffff;text-align: center;font-size: 0.4rem;
}.header-fixed .back-fixed-icon{position: absolute;top:0;left: 0;width: 0.64rem;text-align: center;font-size: 0.4rem;color: white;
}
</style>

22、Detail 界面中的 List,一个简单的列表信息展示

<template><div class="list"><div class="item" v-for="(item,id) of list" :key="id"><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},data: function () {return {msg: 'Welcome to Your Vue.js App'}}}
</script><!-- Add "scoped" attribute to limit CSS to this component only -->
<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: 0.8rem;font-size: 0.32rem;padding: 0 0.2rem;}.item-children{padding: 0 0.4rem;}
</style>

七、router路由管理, store vuex 状态管理 说明

1、router路由管理,三个路由 path(/ 、/city、/detail/id),并且添加路由地址切换回 scroll 都置于顶部处理

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
import Detail from '@/pages/detail/Detail'Vue.use(Router)/*** 路由*/
export default new Router({routes: [{path: '/',name: 'Home',//component: Home// 异步加载组件,避免大量的代码堆积到 app.js 中,()=> import( '@/pages/city/City') 可以拆分代码到其他的projectX.js中component: ()=> import( '@/pages/home/Home')},{path: '/city',name: 'City',//component: Citycomponent: ()=>import('@/pages/city/City')},{//动态路由path: '/detail/:id',name: 'Detail',//component: Detailcomponent: ()=>import('@/pages/detail/Detail')},],//添加路由切换的时候,scroll ,都回到顶部处理scrollBehavior(to, from, savedPosition) {// 回到顶部return { x: 0, y:0 }},})

2、store 添加对某个全局参数的管理

state.js/*** Created by 12722 on 2022/6/13.*/
let defaultCity = '桂林'
try{if (localStorage.city){defaultCity = localStorage.city}
}
catch(e){}export default{city:defaultCity
}action.js
/*** Created by 12722 on 2022/6/13.*/
export default {changeCity(context,city){context.commit('changeCity',city)}
}mutations.js
/*** Created by 12722 on 2022/6/13.*/
export default {changeCity(state, city){state.city = citytry{ // 本地化保存localStorage.city = city}catch(e){}}
}

八、几个性能优化点说明

1、App.vue 中的 <keep-alive exclude="Detail"></keep-alive>

keep-alive 会缓存一些数据,在界面切换后,也不销毁,例如mounted函数中 axios.get数据,从而避免 axios.get 频繁访问

2、setTimeout 函数的使用,例如在 City 界面中 Search.vue 进行输入搜索时,可以控制搜索频率,间接提高性能;以及类似在 City 界面中 Alphabet.vue 进行 Touch 选中 字母的时候,也可控制搜索频率,间接提高性能

九、axios 获取服务端数据说明

1、例如 Home.vue 中 mounted 使用 axios.get 获取服务端数据时

使用 axios.get('/api/index.json?city='+this.city).then(this.getHomeInfoSucc) 中的

/api/index.json?city='+this.city 在浏览器中获取不到数据,为什么 axios.get 可以获得呢

2、其实,在 config/index.js 做代理配置,当遇到 /api 就会进行对应转换,从而使得 axios.get 获取到对应的数据

3、axios.get 获取的数据 static/mock 文件夹下的数据

十、src/common 的共有 vue

1、Gallary.vue 全屏大图滑动轮播图片

只要引入Gallary.vue,对应的添加传输图片列表数据,并且添加对应关闭事件,就可以轻易实现全屏大图滑动轮播图片

参考引用 detail/components/Banner.vue :

2、FadeAnimation.vue 渐隐渐现动画效果

只要引入FadeAnimation.vue 包裹需要渐隐渐现的元素即可

参考引用 detail/components/Banner.vue :

十一、src/assets/styles 存放css 样式、常用的变量样式参数等

1、mixins.styl 功能是:字显示超出范围,则用三个点... 表示,例如 ‘字太多了...’

十二、该房去哪儿网的演示项目源码下载

仅供学习参考使用:

代码运行(最好的运行端口为 8080):

1、npm install

2、npm run start

Vue 实例实战之 Vue webpack 仿去哪儿网App页面开发(应用中的几个页面简单实现)相关推荐

  1. 【vue】二、vue2仿去哪儿网app——首页开发

    文章目录 二.vue2仿去哪儿网app--首页开发 Ⅰ 页面结构 Ⅱ 开发笔记及注意点 1.公共样式抽取 2.路径 --> 绝对路径 3.用padding-bottom实现固定宽高比 4.保证内 ...

  2. 六十五、完成Vue项目去哪儿网App首页开发(来源:慕课网)

    2020/10/21. 周三.今天又是奋斗的一天. @Author:Runsen @Date:2020/10/21 写在前面:我是「Runsen」,热爱技术.热爱开源.热爱编程.技术是开源的.知识是共 ...

  3. 【Android自定义View实战】之仿去哪儿网App图片按压显示指纹并缩放效果TouchFingerImageView

    转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/52986713 [DylanAndroid的csdn博客] 我们发现去哪儿网ap ...

  4. 【Vue项目】二、去哪儿网APP——首页开发

    首页开发中的重难点 重点思想:页面组件化 页面组件化下的组件引入及使用 flex:1 使用iconfont图标 使用vue-awesome-swiper实现轮播效果 实现带有分页器的轮播 实现有分页效 ...

  5. vue项目开发实录--仿去哪儿网App-张鹏-专题视频课程

    vue项目开发实录--仿去哪儿网App-160人已学习 课程介绍         本课程为vue项目开发实录(仿去哪儿网App)其中涉及到知识点有:组件搭建,路由,vuex,axios,webpack ...

  6. 六十四、Vue项目去哪儿网App开发准备

    2020/10/20 . 周二.今天又是奋斗的一天. @Author:Runsen @Date:2020/10/20 写在前面:我是「Runsen」,热爱技术.热爱开源.热爱编程.技术是开源的.知识是 ...

  7. 仿去哪网酒店的地图:POI、定位、国际地图、导航、marker及其自定义infowindow

    Android 博客之路第二弹:关于最近研究地图的总结. 前言:最近App开发酒店信息需要用到地图模块,所以就目前需要的功能研究了一下.虽然以前也有用到,但以前仅限于marker及infowindow ...

  8. 去哪网实习总结:JavaWeb中文传參乱码问题的解决(JavaWeb)

    本来是以做数据挖掘的目的进去哪网的.结构却成了系统开发... 只是还是比較认真的做了三个月.老师非常认同我的工作态度和成果... 实习立即就要结束了,总结一下几点之前没有注意过的变成习惯和问题,分享给 ...

  9. vue中swiper仿去哪儿网相册

    效果图 下载Swiper     版本:" vue-awesome-swiper " : " ^3.1.3 ",用的是 Swiper4.x 配置选项 npm i ...

最新文章

  1. win10 4步快速安装vue
  2. 【c语言】蓝桥杯算法训练 6-2递归求二进制表示位数
  3. 让Linux系统开机速度更快的方法
  4. Cmake的install与file命令的区别
  5. 五.Java IO、NIO、文件、通讯
  6. ICCV 2017《Unsupervised Laerning of Important Objects from First-Person Videos》论文笔记
  7. Android之ActivityManage长用方法总结
  8. Java可靠性测试fit_Java ParagraphVectors.fit方法代码示例
  9. linux curl 特殊字符,遇到问题--curl--- url参数中有+、空格、=、%、、#等特殊符号的导致传参不全 | 易学教程...
  10. 【Flink】Flink 消费kafka报错 AMRMClientAsyncImpl Interrupted while waiting for queue InterruptedException
  11. 输入n求N*N矩阵,规定矩阵沿45度线递增
  12. android 访问本地image url_微信图片无法通过 WXSS 获取,可使用网络图片或 base64或image...
  13. C语言程序——如何是使用TurboC2编程调试
  14. ILSVRC-2015 ILSVRC-2017VID数据集下载
  15. android 色值(全)
  16. 微信 Android 热补丁实践演进之路
  17. 关于找不到nsWindows.nsh的问题
  18. 国庆星期日 1949年的国庆节(10月1日)是星期六。 今年(2012)的国庆节是星期一。
  19. SQL查询,子查询作为结果字段
  20. android获取热点主机ip和连接热点手机ip

热门文章

  1. python顺时针旋转_python——n*n矩阵顺时针旋转90度
  2. JS对XML格式化与美化
  3. ROS(indigo)机器人操作系统学习有趣丰富的Gazebo仿真示例evarobot
  4. 以前端角度出发做好SEO需要考虑什么?
  5. ubuntu18.04 | soundfile安装报错与解决
  6. python基础笔记一
  7. 【LeetCode】括号(有效括号、括号生成、最长有效括号)
  8. Tuxera ntfs读写插件工具v2022版本 支持macOS15系统
  9. 软件测试 | 手把手教你如何使用 Fiddler 抓包工具(电脑+手机端)
  10. Android 使用RecyclerView实现(仿微信)的联系人A-Z字母排序和过滤搜索功能