Vue开发去哪儿网App(三、首页界面开发代码编写)

前面我们已经将首页部分的开发分为了几个模块,下面我们开始进行编码。

首先看一下我的首页项目组成,我们通过五个vue模块组件最后实现一个Home.vue首页的实现。

一、首先来看一下Home.vue这一总的部分的代码:

<template>
      <div>
            <!-- 父组件通过属性的形式给子组件传值 ,子组件需要接收父组件传过来的值-->
            <!-- 这个地方的city是data中的city,data中的city是methods中通过ajax获得的 -->
            <home-header :city="city"></home-header>
            <home-swipe :list="swiperList"></home-swipe>
            <home-icons :list="iconList"></home-icons>
            <home-recommend :list="recommendList"></home-recommend>
            <home-weekend :list="weekendList"></home-weekend>
            
      </div>
</template>

<script>
import HomeHeader from './components/Header'
import HomeSwipe from './components/Swipe'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
export default {
      name: 'Home',
      //引入了子组件Header,需要声明局部组件
      components:{
            HomeHeader:HomeHeader,
            HomeSwipe:HomeSwipe,
            HomeIcons:HomeIcons,
            HomeRecommend:HomeRecommend,
            HomeWeekend:HomeWeekend
      },
      data: function() {
            return{
                  // data中的city、swiperList等数据都是通过methods中ajax获得的
                  city: '',
                  swiperList: [],
                  iconList: [],
                  recommendList: [],
                  weekendList: []
            }
      },
      mounted (){
            //页面挂载好了之后去执行getHomeInfo函数
            this.getHomeInfo()
      },
      methods: {
            getHomeInfo: function(){
                  //使用axios.get去请求一个url
                  axios.get('/api/index.json')
                        //获取信息成功的返回函数
                        .then(this.getHomeInfoSucc)
            },
            //获取信息成功的返回函数
            getHomeInfoSucc: function (res) {
                  res=res.data
                  if (res.ret && res.data) {
                        this.city=res.data.city
                        this.swiperList=res.data.swiperList   
                        this.iconList=res.data.iconList
                        this.recommendList=res.data.recommendList
                        this.weekendList=res.data.weekendList
                  }
            }

      }
}
</script>

<style>
</style>

我们来逐步分析一下这块代码,我们知道Vue文件主要由三部分组成,分别是:模板template部分、逻辑script部分和样式style部分。

首先我们引入了下面五个子组件和一个与服务端进行通讯的插件axios:

Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。要使用它就必须先在我们的项目中安装它,安装非常简单,只需在控制台输入下面一句代码就可以了:

npm install axios

然后在我们引入了上面五个子组件之后,就需要在我们的组件中声明这五个局部组件

然后我们定义需要向子组件传递的data,代码如下:

然后city、swiperList等data的值我们是从哪儿获得的呢?首先我们使用一个页面挂载钩子函数,等页面挂载完成之后,我们使用axios去请求数据,这里我们在本地模拟了服务器端的数据,新建了一个index.json的文件

index.json里面模拟的数据我也放到这儿:

{     
      "ret": true,
      "data": {
            "city": "北京",
            "swiperList": [{
                  "id": "0001",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1801/1a/94428c6dea109402.jpg_640x200_2cf590d8.jpg"
            },{
                  "id": "0002",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1802/42/7c92b9a381e46402.jpg_640x200_1cdce2a4.jpg"
            },{
                  "id": "0003",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1802/51/e78f936a5b404102.jpg_640x200_c14f0b3a.jpg"
            },{
                  "id": "0004",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1712/91/a275569091681d02.jpg_640x200_0519ccb9.jpg"
            }],
            "iconList": [{
                  "id": "0001",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/54/ace00878a52d9702.png",
                  "desc": "景点门票"
            }, {
                  "id": "0002",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1711/df/86cbcfc533330d02.png",
                  "desc": "滑雪季"
            }, {
                  "id": "0003",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1710/a6/83f636bd75ae6302.png",
                  "desc": "泡温泉"
            }, {
                  "id": "0004",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/35/2640cab202c41b02.png",
                  "desc": "动植园"
            }, {
                  "id": "0005",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/d0/e09575e66f4aa402.png",
                  "desc": "游乐园"
            }, {
                  "id": "0006",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/59/569d3c096e542502.png",
                  "desc": "必游榜单"
            }, {
                  "id": "0007",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/17/4bd370f3eb1acd02.png",
                  "desc": "演出"
            }, {
                  "id": "0008",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/7f/b1ea3c8c7fb6db02.png",
                  "desc": "城市观光"
            }, {
                  "id": "0009",
                  "imgUrl": "http://img1.qunarzz.com/piao/fusion/1611/a9/ffc620dbda9b9c02.png",
                  "desc": "一日游"
            }],
            "recommendList": [{
                  "id": "0001",
                  "imgUrl": "http://img1.qunarzz.com/sight/p0/1409/19/adca619faaab0898245dc4ec482b5722.jpg_140x140_80f63803.jpg",
                  "title": "故宫",
                  "desc": "东方宫殿建筑代表,世界宫殿建筑典范"
            }, {
                  "id": "0002",
                  "imgUrl": "http://img1.qunarzz.com/sight/p0/1511/d2/d2aec2dfc5aa771290.water.jpg_140x140_abb362a7.jpg",
                  "title": "南山滑雪场",
                  "desc": "北京专业级滑雪圣地"
            }, {
                  "id": "0003",
                  "imgUrl": "http://img1.qunarzz.com/sight/p0/1501/f4/f467729126949c3a.water.jpg_140x140_ef235b1c.jpg",
                  "title": "天安门广场",
                  "desc": "我爱北京天安门,天安门上太阳升"
            }, {
                  "id": "0004",
                  "imgUrl": "http://img1.qunarzz.com/sight/p0/1501/40/40b2b6c951b28fdd.water.jpg_140x140_1c863e5c.jpg",
                  "title": "水立方",
                  "desc": "中国的荣耀,阳光下的晶莹水滴"
            }, {
                  "id": "0005",
                  "imgUrl": "http://img1.qunarzz.com/sight/p0/201308/23/b283071686e64dfec8d65eac.jpg_140x140_8c5a7c49.jpg",
                  "title": "温都水城养生馆",
                  "desc": "各种亚热带植物掩映其间仿佛置身热带雨林"
            }],
            "weekendList": [{
                  "id": "0001",
                  "imgUrl": "http://img1.qunarzz.com/sight/source/1510/6e/1ea71e2f04e.jpg_r_640x214_aa6f091d.jpg",
                  "title": "北京温泉排行榜",
                  "desc": "细数北京温泉,温暖你的冬天"
            }, {
                  "id": "0002",
                  "imgUrl": "http://img1.qunarzz.com/sight/source/1505/aa/7baaf8a851d221.jpg_r_640x214_1431200f.jpg",
                  "title": "北京必游TOP10",
                  "desc": "来北京必去的景点非这些地方莫属"
            }, {
                  "id": "0003",
                  "imgUrl": "http://img1.qunarzz.com/sight/source/1505/9e/21df651e19af5d.jpg_r_640x214_3ea5bb38.jpg",
                  "title": "寻找北京的皇城范儿",
                  "desc": "数百年的宫廷庙宇,至今依旧威严霸气"
            }, {
                  "id": "0004",
                  "imgUrl": "http://img1.qunarzz.com/sight/source/1505/ce/bc89bc2f0e33ea.jpg_r_640x214_3e408453.jpg",
                  "title": "学生最爱的博物馆",
                  "desc": "周末干嘛?北京很多博物馆已经免费开放啦"
            }, {
                  "id": "0005",
                  "imgUrl": "http://img1.qunarzz.com/sight/source/1505/b2/fde1bfcd057a52.jpg_r_640x214_bbf3fa44.jpg",
                  "title": "儿童剧场,孩子的乐园",
                  "desc": "带宝贝观看演出,近距离体验艺术的无穷魅力"
            }]
      }
    }

下面我们来看一下我们的页面挂载钩子函数:

页面挂载完成以后我们会去执行一个getHomeInfo()的方法,下面我们再来看一下这个方法:

这里我们使用axios.get去获取数据,里面的/api/index.json就是我们具体拿数据的url,这里我们指向的是我们本地,那么可能有人就要问了,我们本地的index.json不是在static/travel目录下么?其实我们这里是做了一个小的配置,使得url更加规范、整洁。

下面我介绍一下如何配置这个url:打开config->index.js,在dev中做如下配置:

这样我们的/api指向的就是/static/travel了。

下面我们继续介绍axios的用法,我们这里使用的是get的方法,

axios的用法也非常的简单,我们首先需要一个url,然后使用.then返回一个数据成功从服务器获取的函数,下面我们继续看一下这个数据获取成功后的函数:

首先进行判断,如果确实获取成功,并且数据已经存在,那么我们就定义五个变量去获取服务器传过来的五组数据。

实现了上面这些,我们就可以在Home的模板中使用我们已经细分好的五个子组件了,具体使用如下:

我们使用属性的方法将我们通过axios拿到的数据传递给五个子组件,(当然子组件中需要接收各自需要用到的数据)。

这样我们的Home组件就写好了,下面我们来写我们细分好的五个子组件。

二、Header子组件

首先看一下整合Header.vue部分的代码:

<template>
      <div class="header">
            <div class="header-left">
                  <div class="iconfont back-icon">&#xe624;</div>
            </div>
            <div class="header-input">
                  <span class="iconfont">&#xe632;</span>
                  输入城市/景点/游玩主题
            </div>
            <div class="header-right arrow-icon">
                  <!-- 使用父组件传过来的,子组件接收过了的city -->
                  {{this.city}}
                  <span class="iconfont">&#xe64a;</span>
            </div>
      </div>
</template>

<script>
export default {
      name: 'HomeHeader',
      // 子组件接收父组件传过来的city
      props:{
            city: String
      }
}
</script>

<style lang="stylus" scoped>
      @import '~styles/varibales.styl'
      .header
            display : flex
            line-height : 3.1rem
            background : $bgColor
            color : #fff
            .header-left
                  width : 2rem
                  float : left 
                  .back-icon
                        text-align: center
                        font-size: 1.5rem 
            .header-input
                  flex : 1
                  margin-top: .25rem
                  margin-bottom: .25rem
                  margin-left: .5rem
                  padding-left .5rem
                  margin-right: .5rem
                  background : #fff
                  border-radius: .1rem
                  color: #ccc
            .header-right     
                  width: 4rem
                  float : right
                  text-align : center
                  .arrow-icon
                        font-size: .4rem
</style>

也是分为三个部分,首先我们看一下逻辑部分script,

这里我们接收了从父组件通过属性的形式传递过来的数据city,然后定义了city的类型是String,现在这个city就是我们在Home组件中通过axios从服务器(本地模拟)上拿到的数据了。

然后看一下我们的样式,样式我们使用了stylus,是 CSS 的预处理框架,关于sytlus我就不多介绍了,大家可以自行百度,要使用stylus,那么我们首先要在本地安装它,安装也是在控制台执行一句代码:

npm install stylus

这样我们就可以使用stylus了去整理我们的布局了,我们看一下我们的布局代码:

<style lang="stylus" scoped>
      @import '~styles/varibales.styl'
      .header
            display : flex
            line-height : 3.1rem
            background : $bgColor
            color : #fff
            .header-left
                  width : 2rem
                  float : left 
                  .back-icon
                        text-align: center
                        font-size: 1.5rem 
            .header-input
                  flex : 1
                  margin-top: .25rem
                  margin-bottom: .25rem
                  margin-left: .5rem
                  padding-left .5rem
                  margin-right: .5rem
                  background : #fff
                  border-radius: .1rem
                  color: #ccc
            .header-right     
                  width: 4rem
                  float : right
                  text-align : center
                  .arrow-icon
                        font-size: .4rem
</style>

下面我稍微解释一下这一块的代码:

scoped的意思是我们的样式只针对我们这一个文件,这里我们引入了一个样式:

@import '~styles/varibales.styl',这是因为有时候我们需要用到的颜色会有重复,所以可以把一些颜色封装起来,用到的时候直接调用即可,这里我只是简单的封装了两种颜色,varibales.styl的内容如下:

我把这个文件的路径也贴一下:

看到这里你或许会问,这个文件到的路径是:src/assets/styles啊,怎么我引入的是@import '~styles/呢?

其实这里为了路径的简洁与美观我也配置了一下styles的路径,具体配置如下:

打开build->webpack.base.conf.js文件夹下,找到resolve对象,在里面作如下配置:

这样我们就可以用styles来代替src/assets/styles啦。

然后再跟大家解释一下rem单位:rem是CSS3新增的一个相对单位(root em,根em)。这个单位与em有什么区别呢?区别在于使用rem为元素设定字体大小时,仍然是相对大小,但相对的只是HTML根元素。这个单位可谓集相对大小和绝对大小的优点于一身,通过它既可以做到只修改根元素就成比例地调整所有字体大小,又可以避免字体大小逐层复合的连锁反应。目前,除了IE8及更早版本外,所有浏览器均已支持rem。对于不支持它的浏览器,应对方法也很简单,就是多写一个绝对单位的声明。这些浏览器会忽略用rem设定的字体大小。
最后看一下模板部分代码:

这里我们使用了通过axios拿到的地名数据去显示我们的地名数据。

这样,我们Header.vue部分就开发好了。

其他四个子组件的编写也是类似,这里我直接给出其余四个子组件的代码,就不再一一赘述。

Icons.vue代码如下:

<template>
      <div class="icons">
            <swiper :options="swiperOption">
                  <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>
            </swiper>
      </div>
</template>

<script>
export default {
      name: 'HomeIcons',
     props:{
           list: Array
     },
     data(){
           return{
                 //希望轮播图不要自动滚动
                 swiperOption:{
                       autoplay: false

                 }
           }
     },
      computed: {
            pages (){
                  const pages = []
                  this.list.forEach((item, index) => {
                        const page = Math.floor(index/8)
                        if(!pages[page]){
                              pages[page] = []
                        }
                        pages[page].push(item)
                  })
                  return pages
            }
      }
}
</script>

<style lang="stylus" scoped>
      .icons 
            margin-top: .2rem    
            overflow hidden 
            height : 0
            padding-bottom: 50%
            .icon
                  position relative
                  overflow hidden
                  float left
                  width 25%
                  height 0
                  padding-bottom 24%
                  

                  .icon-desc
                        position: absolute
                        left : 1.2rem
                        righ : 0
                        bottom : .15rem
                        height .98rem
                        line-height .78rem
                        text-align: center
                        color : #333
                        overflow: hidden
                        white-space: nowrap
                        text-overflow: ellipsis 


                  .icon-img
                        position: absolute
                        top: 0
                        left :0
                        righ :0
                        bottom : .88rem
                        box-sizing: border-box
                        padding: .8rem

                        .icon-img-content
                              display: block
                              margin: 0 auto 
                              height :100%

</style>


Recommend.vue代码如下:

<template>
      <div>
            <div class="title">热销景点</div>
            <ul>
                  <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>
                              <button class="item-button">查看详情</button>
                        </div>
                  </li>
            </ul>
      </div>
</template>

<script>
export default {
      name: 'HomeRecommend',
      props:{
            list: Array
      }

}
</script>

<style lang="stylus" scoped>
      .title
            margin-top: .2rem
            line-height : 2.5rem
            background: #eee
            text-indent: .9rem

      .item
            overflow hidden
            display: flex
            height : 5.0rem

            .item-img
                  width : 6.5rem
                  height : 4.5rem
                  padding :.3rem
            .item-info
                  flex : 1
                  padding: .2rem
                  .item-title
                        line-height : 1.5rem
                        font-size: .6rem
                  .item-desc
                        line-height : 1.5rem
                        color : #ccc
                  .item-button
                        background: #ff9300
                        padding : 0 .5rem
                        border-radius: .1rem


</style>


Swipe.vue代码如下:

<template>
      <div class="wrapper">
            <swiper :options="swiperOption" v-if="showSwipe">
                  <!-- slides -->
                  <swiper-slide v-for="item of list" :key="item.id">
                        <img 
                              class="swipe-img" 
                              :src="item.imgUrl"
                        />
                  </swiper-slide>
                  <!-- Optional controls -->
                  <div class="swiper-pagination"  slot="pagination"></div>           
            </swiper>
      </div>
</template>

<script>
export default {
      name: 'HomeSwipe',
      props:{
            list: Array
      },
      
      data:function(){
            return {
                  swiperOption: {
                        pagination: '.swiper-pagination',
                        loop: true
                  }
            }
      },
      computed:{
            showSwipe(){
                  return this.list.length
            }
      }
      
}
</script>

<style lang="stylus" scoped>
      .wrapper
            overflow: hidden
            width: 100%
            height: 0
            padding-bottom: 32%
            background : #eee
            .swipe-img
                  width: 100% 

</style>


最后一个Weekend.vue的代码如下:

<template>
      <div>
            <div class="title">周末去哪儿</div>
            <ul>
                  <li class="item" border-bottom v-for="item of list" :key=item.id>
                        <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',
      props:{
            list: Array
      }
}
</script>

<style lang="stylus" scoped>
      .title
            margin-top: .2rem
            line-height : 2.5rem
            background: #eee
            text-indent: .9rem
      .item-img-wrapper
            overflow hidden
            height :0
            padding-bottom: 37.09%

            .item-img
                  width:100%
      .item-info
            padding: .2rem
            .item-title
                  line-height : 1.5rem
                  font-size: .6rem
            .item-desc
                  line-height : 1.5rem
                  color : #ccc


</style>


到此为止,我们已经开发好了整个项目的首页部分内容了,并且使用我们的模拟json数据动态的去渲染我们的界面。

那么我们这一篇文章的内容也就先写到这儿了,下一篇我会介绍关于咱们App城市列表页面的开发!!

没有更多推荐了,返回首页