第六章 旅游网站首页开发
首页导航栏
根据设计稿完成页面的布局:
在正式编写代码之前,先来介绍一下stylus
这是一种与less、sass
一样的功能,可以帮助我们快速完成css代码的编写。请使用一下命令进行安装:
npm install stylus --save
npm install stylus-loader --save
开始编写header
页面:
<template>
<div class="header">
<div class="header-left">返回</div>
<div class="header-input">输入城市景点/游玩/主题</div>
<div class="header-right">城市</div>
</div>
</template>
<script>
export default {
name: 'HomeHeader'
}
</script>
<!-- 要使用stylus使用lang标识,只想对当前组件有效使用scoped -->
<style lang="stylus" scoped>
.header
display: flex
color: #fff
background: #00bcd4
line-height: .86rem
.header-left
width: .64rem
float: left
.header-input
flex: 1
background: #fff
margin-top: .12rem
height: .64rem
line-height: .64rem
margin-left: .2rem
border-radius: .05rem
color: #ccc
.header-right
width: 1.24rem
float:right
text-align: center
</style>
如果页面出现顶部有空白的情况,解决办法是在App.vue中的style
中设置内边距和外边距的值为0,一般情况下不会有出现,除非你真是出现了。
<style>
*{
padding: 0;
margin: 0;
}
</style>
现在我们向把“返回”换成一个图标的形式,就会用到下一节要说的内容。
iconfont的使用和代码优化
首先我们进入iconfont官网,没有账号的先进行注册,注册好了记得新建一个项目(travel)。
如果你已经注册好,点击图标库>官方图标库>选择任意一个,找到我们的需要的图标并且添加到购物车之中>点击右边的购物车并点击添加至项目>然后下载到本地
这时我们会得到一个包,我们只需要用到其中几个文件就可以。我们把需要用到的文件拷贝到我们项目中。
我们可以看到图中,我把除了.css
的的文件放在了style文件夹中,把其它类型的文件放在了新建的iconfont文件夹中。
这时我们在编辑器中打开iconfont.css
文件,我们需要修改一下它所使用到的文件的路径
下面的这一部分可以注释掉或者删除。根据自己的理解来使用,这里我选择不使用这种方法,因为它写的都是中文的拼音。
接下来我们将iconfont.js
引入到入口文件中。
import './assets/styles/iconfont.css'
完成以上的步骤,如果没有出错的话,我们就可以开始在页面中开始使用这些图标了。
在Header.vue
文件中这样写:
<template>
<div class="header">
<div class="header-left"><span class="iconfont"></span></div>
<div class="header-input"><span class="iconfont"></span>输入城市景点/游玩/主题</div>
<div class="header-right">城市<span class="iconfont"></span></div>
</div>
</template>
上面代码中的Unicode代码可以在iconfont官方你的项目文件中选择Unicode并复制代码,就可以得到了。这也就为什么我们前面要注释那些样式代码的原因了。
图标是有了,但是我们还需要做一些位置的优化:
<template>
<div class="header">
<div class="header-left"><div class="iconfont back-icon"></div></div>
<div class="header-input"><span class="iconfont search-icon"></span>输入城市景点/游玩/主题</div>
<div class="header-right">城市<span class="iconfont arrow-icon"></span></div>
</div>
</template>
<style lang="stylus" scoped>
.header
display: flex
color: #fff
background: #00bcd4
line-height: .86rem
.header-left
width: .64rem
float: left
.back-icon
text-align: center
font-size: .4rem
.header-input
flex: 1
background: #fff
margin-top: .12rem
height: .64rem
line-height: .64rem
margin-left: .02rem
border-radius: .05rem
padding-left: .2rem
color: #ccc
.search-icon
padding-right: .1rem
.header-right
width: 1.24rem
float:right
text-align: center
.arrow-icon
font-size: .24rem
</style>
完成以上的内容我们就可以看到现在的效果:
代码的优化:
由于我们这个项目的主题颜色,是绿色。在以后的很多组件都会用到这个颜色。我们可以定义一个变量,需要的时候直接引用就可以了。
首先需要在src/assets/styles
目录下新建一个varibles.styl
这样的一个文件,表示是一个stylus
格式的文件。并且在文件中写上一下内容。该文件可以用来写一些变量。
$bgColor = #00bcd4
下面就就要在Header.vue
文件中进行引入了。
<style lang="stylus" scoped>
@import '../../../assets/styles/varibles.styl'
.header
background: $bgColor
</style>
我们发现啊,引入的文件路径太长,有什么办法可以解决呢?我们可以使用@
符号来代替路径前面的前缀。
<style lang="stylus" scoped>
@import '~@/assets/styles/varibles.styl'
.header
background: $bgColor
</style>
但是呢,像assets/styles/
这样的是路径我们使用的太频繁且冗长,其实也是可以对它起个命名。我们需要在webpack-base.conf.js
文件中添加一下内容就可以了。
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
//添加下面这一句
'styles': resolve('src/assets/styles'),
}
}
修改完成之后,我们就可以对使用了assets/styles/
这种路径的地方进行改写为styles
就可以了。因为修改了配置文件,修改之后会出现报错的情况,所以需要重启一下服务。
main.js
import 'styles/reset.css'
import 'styles/border.css'
import 'styles/iconfont.css'
Header.vue
@import '~styles/varibles.styl'
最后当然也别忘记将写完的代码提交到线上仓库。使用一下命令就可以了。提交前记得先退出服务
git add .
git commit -m 'addHeader'
git push
总结:
首先介绍了如何去使用iconfont
图标库,然后通过对webpack
的配置对代码进行简化,也介绍了如何使用stylus
及应用stylus
变量。
首页轮播图
在企业级开发项目中,我们都是在不同的branch
分支中进行开发的,最后再合并到master分支上。所以我们需要在GitHub中创建一个轮播图的分支。
这里我们选择使用命令来创建
# 创建分支
git checkout -b index-swiper
# 将本地创建的分支更新到线上仓库
git push origin index-swiper
# 查看本地分支
git branch -r
# 查看当前所在分支
git status
写轮播图我们选择使用那你第三方的库(vueAwesomeswiper
),该库可以帮助我们快速构建轮播效果。要使用该库需要先安装它。使用一下命令即可安装相应版本的库文件。
npm install vue-awesome-swiper@2.6.7 --save
下载好之后,我们需要将以下的代码引入到我们项目之中的mian.js
。
//引入到入口文件
import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper'
// require styles
import 'swiper/dist/css/swiper.css'
Vue.use(VueAwesomeSwiper, /* { default global options } */)
创建swiper
组件:在components文件夹中新建Swiper.vue
文件。并且写上如下的代码:
<template>
<swiper :options="swiperOption">
<img class="swiper-image" src="http://mp-piao-admincp.qunarzz.com/mp_piao_admin_mp_piao_admin/admin/20201/8c458e9fe5b5f4575b1d7ec0489a9ff8.jpg_750x200_f0fbf511.jpg">
</swiper-slide>
<swiper-slide>
<img class="swiper-image" src="http://mp-piao-admincp.qunarzz.com/mp_piao_admin_mp_piao_admin/admin/201912/d6df0db510d7b9aaa3d9ce4cffafeca1.jpg_750x200_abb38f14.jpg">
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
//<div class="swiper-button-prev" slot="button-prev"></div>
//<div class="swiper-button-next" slot="button-next"></div>
//<div class="swiper-scrollbar" slot="scrollbar"></div>
</swiper>
</template>
<script>
export default {
name: 'HomeSwiper',
data(){
return {
swiperOption: {}
}
}
}
</script>
<style lang='stylus' scoped>
.swiper-image
width:100%
</style>
这里再介绍一个问题:就是在加载很慢的时候,轮播以下的内容,会有明显的抖动为题。要解决这个问题只需做一下修改就可以了。
<template>
<div class="wrapper">
//....
</div>
</template>
<style lang='stylus' scoped>
.wrapper
overflow: hidden
width: 100%
height: 0
// 26.67%是图片的宽高比
padding-bottom: 26.67%
background: #eee
.swiper-image
width:100%
</style>
接下来我们我给轮播图加控制点及进行相关的优化;
<template>
<div class="wrapper">
<swiper :options="swiperOption">
<swiper-slide v-for="item of swiperList" :key="item.id">
<img class="swiper-image" :src="item.imgUrl">
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</template>
<script>
export default {
name: 'HomeSwiper',
data(){
return {
swiperOption: {
pagination: '.swiper-pagination', //显示控制点
loop:true //让轮播可以循环的切换
},
swiperList:[{
id:'001',
imgUrl:'http://mp-piao-admincp.qunarzz.com/mp_piao_admin_mp_piao_admin/admin/20201/8c458e9fe5b5f4575b1d7ec0489a9ff8.jpg_750x200_f0fbf511.jpg'
},{
id:'002',
imgUrl:'http://mp-piao-admincp.qunarzz.com/mp_piao_admin_mp_piao_admin/admin/201912/d6df0db510d7b9aaa3d9ce4cffafeca1.jpg_750x200_abb38f14.jpg'
}]
}
}
}
</script>
<style lang='stylus' scoped>
// 这是改变控制点的默认颜色,'>>>'代表穿透 在wrapper下只要出现后面的样式就会执行
.wrapper >>> .swiper-pagination-bullet-active
background: #fff
.wrapper
overflow: hidden
width: 100%
height: 0
// 26.67%是图片的宽高比
padding-bottom: 26.67%
background: #eee
.swiper-image
width:100%
</style>
完成以上内容就可以把完成的代码提交到线上仓库。再将当前的分支内容合并到master
分支中。
# 切换到主分支中
git checkout master
# 合并分支
git merge origin/index-swiper
# 最后提交
git push
在实际开发过程中,都是按照以上的步骤来进行的。
图标区域的布局
同前面讲的操作,这一节我们也要新建一个分支来进行开发新的内容。创建分支就不再累述,直接查看前面章节即可。
创建Icons
组件:在components文件夹中新建Icons.vue
文件。并且写上如下的代码:
<template>
<div>icon</div>
</template>
<script>
export default {
name:'HomeIcons'
}
</script>
<style lang="stylus" scoped>
</style>
Home.vue
<template>
<div>
<home-header></home-header>
<home-swiper></home-swiper>
<home-icons></home-icons>
</div>
</template>
<script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons
}
}
</script>
完整Icons.vue
代码:
<template>
<div class="icons">
<div class="icon">
<div class="icon-img">
<img class="icon-img-content" src="http://img1.qunarzz.com/piao/fusion/1803/95/f3dd6c383aeb3b02.png">
</div>
<p class="icon-desc">景点门票</p>
</div>
</div>
</template>
<script>
export default {
name:'HomeIcons'
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.icons
overflow: hidden
height: 0
padding-bottom: 50%
.icon
position:relative
float: left
width: 25%
height:0
padding-bottom: 25%
.icon-img
position:absolute
top:0
left:0
right:0
bottom:.44rem
box-sizing:border-box
padding:.1rem
.icon-img-content
display:block
margin:0 auto
height:100%
.icon-desc
position:absolute
left:0
right:0
bottom:0
height:.44rem
line-height:.44rem
text-align:center
color:$darkTextColor
</style>
图标区域逻辑实现
需求:当图标很多的时候,可以实现左右滑动的效果。以及循环渲染数据。
完整代码:
<template>
<div class="icons">
<swiper :options="swiperOption">
<!-- 循环渲染数据 添加swiper-slide标签可以实现左右滑动 -->
<swiper-slide v-for="page,index of pages" :key="index">
<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>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</template>
<script>
export default {
name:'HomeIcons',
data (){
return {
// 显示控制点
swiperOption: {
pagination: '.swiper-pagination'
},
// 将数据写在data中 视图层直接循环渲染
iconList:[{
id:'001',
imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/95/f3dd6c383aeb3b02.png',
desc:'景点门票'
},{
id:'002',
imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/47/c2b659e048b11602.png',
desc:'主题乐园'
},{
id:'003',
imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/ab/6f7d6e44963c9302.png',
desc:'泡温泉'
},{
id:'004',
imgUrl:'http://mp-piao-admincp.qunarzz.com/mp_piao_admin_mp_piao_admin/admin/20193/a40ee278d67000f2a29d2e20f6a029b3.png',
desc:'自然风光'
},{
id:'005',
imgUrl:'http://img1.qunarzz.com/piao/fusion/1804/5a/13ceb38dcf262f02.png',
desc:'一日游'
},{
id:'006',
imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/97/02f5043b51b2102.png',
desc:'鼓浪屿'
},{
id:'007',
imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/96/c70f1e85ae4a4f02.png',
desc:'武夷山'
},{
id:'008',
imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/50/26ffa31b56646402.png',
desc:'亲子游'
},{
id:'009',
imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/b8/c5dcdb58deec2402.png',
desc:'玩转贵安'
},{
id:'010',
imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/b6/37560ece9c62b502.png',
desc:'城市观光'
}]
}
},
// 通过计算属性来计算需要轮播的页数
computed:{
pages (){
const pages = []
this.iconList.forEach((item,index) => {
//假如index=3 通过向上取整page=0 则该index展示在第一页 index>=8 则展示在第二页
const page = Math.floor(index/8)
if (!pages[page]) {
pages[page] = []
}
pages[page].push(item)
})
return pages
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.icons >>> .swiper-container
overflow: hidden
height: 0
padding-bottom: 60%
.icon
position:relative
float: left
width: 25%
height:0
padding-bottom: 25%
.icon-img
position:absolute
top:0
left:0
right:0
bottom:.44rem
box-sizing:border-box
padding:.1rem
.icon-img-content
display:block
margin:0 auto
height:100%
.icon-desc
position:absolute
left:0
right:0
bottom:0
height:.44rem
padding:.1rem
line-height:.44rem
text-align:center
color:$darkTextColor
// 当文字描述过长,显示省略点
overflow:hidden
white-space:nowrap
text-overflow:ellipsis
</style>
提交到线上仓库:
git add .
git commit -m 'desc'
git push --set-upstream origin index-icons
git checkout master
# 合并到master分支
git merge origin/index-icons
git push
开发推荐组件
老规矩创建分支"index-recommend",创建Recommend
组件:在components文件夹中新建Recommend.vue
文件。记得要在Home.vue
中引用该组件。
完整代码:
<template>
<div>
<div class="recommend-title">猜你喜欢</div>
<ul>
<li :key='item.id' class="item border-bottom" v-for="item of recommendList">
<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>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'HomeRecommend',
data (){
return {
recommendList:[{
id:'001',
imgUrl:'https://imgs.qunarzz.com/sight/p0/1703/9d/9dd5987e5c7701f2a3.water.jpg_200x200_8b16531c.jpg',
title:'福州明谷行馆',
desc:'最好玩的地方'
},{
id:'002',
imgUrl:'https://imgs.qunarzz.com/sight/p0/1703/9d/9dd5987e5c7701f2a3.water.jpg_200x200_8b16531c.jpg',
title:'福州明谷行馆',
desc:'最好玩的地方'
},{
id:'003',
imgUrl:'https://imgs.qunarzz.com/sight/p0/1703/9d/9dd5987e5c7701f2a3.water.jpg_200x200_8b16531c.jpg',
title:'福州明谷行馆',
desc:'最好玩的地方'
}]
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.recommend-title
margin-top:.2rem
line-height: .8rem
background: #eee
text-indent:.2rem
.item
overflow:hidden
display:flex
height:1.9rem
.item-img
width:1.7rem
height:1.7rem
padding:.1rem
.item-info
flex:1
padding:.1rem
min-width:0
.item-title
line-height:.54rem
font-size:.32rem
ellipsis()
.item-desc
line-height:.4rem
color:#ccc
ellipsis()
.item-button
margin-top:.16rem
color:#fff
line-height:.44rem
background:#ff9300
padding:0 .1rem
border-radius:.06rem
</style>
周末去哪儿
还是在同样的分支下进行开发,只不过需要新建一个组件’Weekend.vue’。
完整代码:
<template>
<div>
<div class="recommend-title">周末去哪儿</div>
<ul>
<li :key='item.id' class="item border-bottom" v-for="item of recommendList">
<div class="item-img-wrapper">
<img class="item-img" :src="item.imgUrl">
</div>
<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',
data (){
return {
recommendList:[{
id:'001',
imgUrl:'https://imgs.qunarzz.com/sight/source/1505/68/de862f94e383a6.jpg_r_640x214_f9df927b.jpg',
title:'福州明谷行馆',
desc:'最好玩的地方'
},{
id:'002',
imgUrl:'https://imgs.qunarzz.com/sight/source/1505/68/de862f94e383a6.jpg_r_640x214_f9df927b.jpg',
title:'福州明谷行馆',
desc:'最好玩的地方'
},{
id:'003',
imgUrl:'https://imgs.qunarzz.com/sight/source/1505/68/de862f94e383a6.jpg_r_640x214_f9df927b.jpg',
title:'福州明谷行馆',
desc:'最好玩的地方'
}]
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.recommend-title
margin-top:.2rem
line-height: .8rem
background: #eee
text-indent:.2rem
.item-img-wrapper
overflow:hidden
height:0
padding-bottom:33.9%
.item-img
width:100%
.item-info
flex:1
padding:.1rem
min-width:0
.item-title
line-height:.54rem
font-size:.32rem
ellipsis()
.item-desc
line-height:.4rem
color:#ccc
ellipsis()
</style>
Ajax获取首页数据
在前面的章节中,我们都是把数据给写死的,这节我们使用ajax动态的获取数据。
第一步还是先创建分支"index-ajax“。在vue中发送ajax可以由很多的工具供我们使用,比如fetch
、vue-resource
、现在vue官方推荐使用axios
,在于它非常的强大,它可以跨平台的发送请求。在浏览器可以帮你发送XHR的请求,在node服务器可以发送HTTP的请求。我们就用axios。
# 安装axios
npm install axios --save
在一个网站中,一般都由n个组件组成,如果每个组件都发送一个请求的话,显然性能是非常的低。所以我们一般都是写在Home组件中统一发送一个。
在使用ajax的时候,作为前端的我们再没有获得数据文件之前,我们都是使用模拟的数据。所以请求的地址一般都是些的本地文件。但是在实际的开发环境中,需要把地址替换一下,但是在上线之前去修改代码时有风险的,那要怎么做才能解决一些问题?
我们可以在项目中的config/index.js
文件中的proxyTable
属性中去添加以下内容,表示但我们请求服务器地址的时候,但时服务器有没有找到该文件,vue则会自动的代替为我们的本地地址文件。
//该功能为webpack-dev-server提供
proxyTable: {
'/api':{
target: 'http://localhost:8080',
pathRewrite:{
//当访问以api开头的就跳转
'^/api':'/static/mock'
}
}
}
我们现在本地创建一个模拟的数据。模拟的数据我们放在static
文件夹之下。我们新建一个文件夹mock
以及mock下的 index.json
文件。
由于该文件是我们自己创建的数据,并不希望把它提交到我们的仓库中去,所以我们可以在gitignore
中把该文件给忽略。
添加该文件夹路径即可
static/mock
Home.vue
<script>
//....省略了其他组件代码
import axios from 'axios'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons,
HomeRecommend,
HomeWeekend
},
methods:{
getHomeInfo (){
// 虽然这里我们写的是api的开头的地址,但是vue已经帮我们跳转到了我们本地的json文件
axios.get('/api/index.json')
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc (res){
//成功获取到本地json数据
console.log(res)
}
},
mounted (){
this.getHomeInfo()
}
}
</script>
首页父子组件数据传递
接着上一节的内容写,我们直接先Home.vue中写。在data函数中定义相关的变量,并将这些变量传递给组件
<template>
<div>
<home-header :city="city"></home-header>
<home-swiper :list="swiperList"></home-swiper>
<home-icons :list="iconsList"></home-icons>
<home-recommend :list="recommendList"></home-recommend>
<home-weekend :list="weenkendList"></home-weekend>
</div>
</template>
<script>
data (){
return {
city:"",
swiperList:[],
iconsList:[],
recommendList:[],
weenkendList:[]
}
}
</script>
再到各组件中改写一下:把我们前面写死的代码都给删除了。使用props
接收一下传递过来的值。
<template>
<div class="wrapper">
<!-- v-if 的作用是解决轮播默认显示的最后一张,因为在没有获取到数据之前,渲染是一个空数组。判断如果是空数组就不进行渲染 -->
<swiper :options="swiperOption" v-if="showSwiper">
<!-- 循环的数据来源于接收到的数组 -->
<swiper-slide v-for="item of list" :key="item.id">
<img class="swiper-image" :src="item.imgUrl">
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</template>
<script>
export default {
name: 'HomeSwiper',
props: {
list:Array
},
data(){
return {
swiperOption: {
pagination: '.swiper-pagination',
loop:true,
//使用autoplay就可以实现间隔3秒自动切换轮播图
autoplay:3000
}
}
},
computed:{
showSwiper(){
return this.list.length
}
}
}
</script>
其它的组件也是同上面的写法一致。模仿着写就可以了。
来源:CSDN
作者:宋承佑
链接:https://blog.csdn.net/weixin_41535944/article/details/103957628