Vue 实例实战之 Vue webpack 仿去哪儿网App页面开发(应用中的几个页面简单实现)
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页面开发(应用中的几个页面简单实现)相关推荐
- 【vue】二、vue2仿去哪儿网app——首页开发
文章目录 二.vue2仿去哪儿网app--首页开发 Ⅰ 页面结构 Ⅱ 开发笔记及注意点 1.公共样式抽取 2.路径 --> 绝对路径 3.用padding-bottom实现固定宽高比 4.保证内 ...
- 六十五、完成Vue项目去哪儿网App首页开发(来源:慕课网)
2020/10/21. 周三.今天又是奋斗的一天. @Author:Runsen @Date:2020/10/21 写在前面:我是「Runsen」,热爱技术.热爱开源.热爱编程.技术是开源的.知识是共 ...
- 【Android自定义View实战】之仿去哪儿网App图片按压显示指纹并缩放效果TouchFingerImageView
转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/52986713 [DylanAndroid的csdn博客] 我们发现去哪儿网ap ...
- 【Vue项目】二、去哪儿网APP——首页开发
首页开发中的重难点 重点思想:页面组件化 页面组件化下的组件引入及使用 flex:1 使用iconfont图标 使用vue-awesome-swiper实现轮播效果 实现带有分页器的轮播 实现有分页效 ...
- vue项目开发实录--仿去哪儿网App-张鹏-专题视频课程
vue项目开发实录--仿去哪儿网App-160人已学习 课程介绍 本课程为vue项目开发实录(仿去哪儿网App)其中涉及到知识点有:组件搭建,路由,vuex,axios,webpack ...
- 六十四、Vue项目去哪儿网App开发准备
2020/10/20 . 周二.今天又是奋斗的一天. @Author:Runsen @Date:2020/10/20 写在前面:我是「Runsen」,热爱技术.热爱开源.热爱编程.技术是开源的.知识是 ...
- 仿去哪网酒店的地图:POI、定位、国际地图、导航、marker及其自定义infowindow
Android 博客之路第二弹:关于最近研究地图的总结. 前言:最近App开发酒店信息需要用到地图模块,所以就目前需要的功能研究了一下.虽然以前也有用到,但以前仅限于marker及infowindow ...
- 去哪网实习总结:JavaWeb中文传參乱码问题的解决(JavaWeb)
本来是以做数据挖掘的目的进去哪网的.结构却成了系统开发... 只是还是比較认真的做了三个月.老师非常认同我的工作态度和成果... 实习立即就要结束了,总结一下几点之前没有注意过的变成习惯和问题,分享给 ...
- vue中swiper仿去哪儿网相册
效果图 下载Swiper 版本:" vue-awesome-swiper " : " ^3.1.3 ",用的是 Swiper4.x 配置选项 npm i ...
最新文章
- win10 4步快速安装vue
- 【c语言】蓝桥杯算法训练 6-2递归求二进制表示位数
- 让Linux系统开机速度更快的方法
- Cmake的install与file命令的区别
- 五.Java IO、NIO、文件、通讯
- ICCV 2017《Unsupervised Laerning of Important Objects from First-Person Videos》论文笔记
- Android之ActivityManage长用方法总结
- Java可靠性测试fit_Java ParagraphVectors.fit方法代码示例
- linux curl 特殊字符,遇到问题--curl--- url参数中有+、空格、=、%、、#等特殊符号的导致传参不全 | 易学教程...
- 【Flink】Flink 消费kafka报错 AMRMClientAsyncImpl Interrupted while waiting for queue InterruptedException
- 输入n求N*N矩阵,规定矩阵沿45度线递增
- android 访问本地image url_微信图片无法通过 WXSS 获取,可使用网络图片或 base64或image...
- C语言程序——如何是使用TurboC2编程调试
- ILSVRC-2015 ILSVRC-2017VID数据集下载
- android 色值(全)
- 微信 Android 热补丁实践演进之路
- 关于找不到nsWindows.nsh的问题
- 国庆星期日 1949年的国庆节(10月1日)是星期六。 今年(2012)的国庆节是星期一。
- SQL查询,子查询作为结果字段
- android获取热点主机ip和连接热点手机ip
热门文章
- python顺时针旋转_python——n*n矩阵顺时针旋转90度
- JS对XML格式化与美化
- ROS(indigo)机器人操作系统学习有趣丰富的Gazebo仿真示例evarobot
- 以前端角度出发做好SEO需要考虑什么?
- ubuntu18.04 | soundfile安装报错与解决
- python基础笔记一
- 【LeetCode】括号(有效括号、括号生成、最长有效括号)
- Tuxera ntfs读写插件工具v2022版本 支持macOS15系统
- 软件测试 | 手把手教你如何使用 Fiddler 抓包工具(电脑+手机端)
- Android 使用RecyclerView实现(仿微信)的联系人A-Z字母排序和过滤搜索功能