Using Vue framework to develop the mobile terminal project of qunar tourism network

Chapter VI development of the front page of tourism websites

Home navigation bar

Complete the layout of the page according to the design draft:

Before writing code, let's introduce stylus, which is the same function as less and sass. It can help us to write css code quickly. Use the following command to install:

npm install stylus --save
npm install stylus-loader --save

Start writing the header page:

<template>
  <div class="header">
    <div class="header-left">Return</div>
    <div class="header-input">Enter city attractions/play/theme</div>
    <div class="header-right">City</div>
  </div>
</template>

<script>
export default {
  name: 'HomeHeader'
}
</script>
<!-- To use stylus Use lang Identity, only want to be valid for the current component 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>

If there is a blank space at the top of the page, the solution is to set the value of inner margin and outer margin to 0 in style in App.vue, which will not appear in general unless you do.

<style>
  *{
    padding: 0;
    margin: 0;
  }
</style>

Now let's change "back" to an icon, and we will use the next section.

Use and code optimization of iconfont

First we enter iconfont official website . if you don't have an account, register first. Remember to create a new project (travel) after you register.

If you have already registered, click Icon Library > official Icon Library > select any one, find the icon we need and add it to the shopping cart > click the shopping cart on the right and click Add to item > then download to local

At this point, we will get a package, we only need to use a few of them. We copy the files we need to use into our project.

We can see in the figure that I put the files other than. css in the style folder and other types of files in the new iconfont folder.

At this time, we open the iconfont.css file in the editor. We need to change the path of the file it uses

The following section can be commented out or deleted. According to my own understanding, I choose not to use this method here, because it is written in Chinese pinyin.

Next, we introduce iconfont.js into the entry file.

import './assets/styles/iconfont.css'

Complete the above steps, and if there is no error, we can start using these icons in the page.

Write as follows in the Header.vue file:

<template>
  <div class="header">
    <div class="header-left"><span class="iconfont">&#xe696;</span></div>
    <div class="header-input"><span class="iconfont">&#xe64d;</span>Enter city attractions/play/theme</div>
    <div class="header-right">City<span class="iconfont">&#xe64a;</span></div>
  </div>
</template>

The Unicode code in the above code can be obtained by selecting Unicode in the official project file of iconfont and copying the code. That's why we need to comment on the style code earlier.

There are icons, but we need to do some location optimization:

<template>
  <div class="header">
		<div class="header-left"><div class="iconfont back-icon">&#xe696;</div></div>
		<div class="header-input"><span class="iconfont search-icon">&#xe64d;</span>Enter city attractions/play/theme</div>
		<div class="header-right">City<span class="iconfont arrow-icon">&#xe64a;</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>

After completing the above content, we can see the current effect:

Code optimization:

Because the theme color of our project is green. This color will be used in many components in the future. We can define a variable and directly reference it when necessary.

First of all, you need to create a new file such as variables.style in the src/assets/styles directory, which means it is a file in the style format. And write something in the file. This file can be used to write some variables.

$bgColor = #00bcd4

Next, we will introduce it in the Header.vue file.

<style lang="stylus" scoped>
@import '../../../assets/styles/varibles.styl'
.header
  background: $bgColor
</style>

We found that the file path introduced is too long. What can we do? We can use the @ symbol instead of the prefix before the path.

<style lang="stylus" scoped>
@import '~@/assets/styles/varibles.styl'
.header
  background: $bgColor
</style>

However, paths like assets/styles / are used too often and lengthy. In fact, they can also be named. We need to add something to the webpack-base.conf.js file.

resolve: {
  extensions: ['.js', '.vue', '.json'],
  alias: {
    'vue$': 'vue/dist/vue.esm.js',
    '@': resolve('src'),
    //Add the following sentence
    'styles': resolve('src/assets/styles'),
  }
}

After the modification, we can rewrite the path of assets/styles / to styles. Because the configuration file has been modified, an error will be reported after modification, so you need to restart the service.

main.js

import 'styles/reset.css'
import 'styles/border.css'
import 'styles/iconfont.css'

Header.vue

@import '~styles/varibles.styl'

Finally, of course, don't forget to submit the finished code to the online warehouse. Just use the command. Remember to exit the service before submitting

git add .
git commit -m 'addHeader'
git push

Conclusion:
This paper first introduces how to use iconfont icon library, then simplifies the code through the configuration of webpack, and also introduces how to use stylus and apply stylus variables.

Home page rotation chart

In the enterprise development projects, we are all developing in different branches, and finally merging them into the master branch. So we need to create a branch of the carousel in GitHub.

How to create a branch?

Here we choose to use the command to create

# Create branch
git checkout -b index-swiper
# Update locally created branches to online warehouse
git push origin index-swiper
# View local branch
git branch -r
# View current branch
git status

Write a carousel chart. We choose to use you Third party Library This library can help us quickly build the effect of rotation. To use this library, you need to install it first. Use the following command to install the corresponding version of the library file.

npm install vue-awesome-swiper@2.6.7 --save

After downloading, we need to introduce the following code into the mia.js of our project.

//Import to entry file
import Vue from 'vue'
import VueAwesomeSwiper from 'vue-awesome-swiper'

// require styles
import 'swiper/dist/css/swiper.css'

Vue.use(VueAwesomeSwiper, /* { default global options } */)

Create swiper component: create a new Swiper.vue file in the components folder. And write the following code:

<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>

Here's another question: when the load is very slow, there will be obvious jitter when broadcasting the following content. To solve this problem, we just need to make some changes.

<template>
  <div class="wrapper">   
    //....
  </div>
</template>

<style lang='stylus' scoped>
  .wrapper
    overflow: hidden
    width: 100%
    height: 0
    // 26.67% is the aspect ratio of the picture
    padding-bottom: 26.67%
    background: #eee
    .swiper-image
      width:100%
</style>

Next, we add control points to the rotation chart and optimize it;

<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',  //Show control points
          loop:true //Let the rotation be able to switch circularly
        },
        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>
  // This is to change the default color of the control point, '> >' means penetration will be executed as soon as the following style appears under the wrapper
  .wrapper >>> .swiper-pagination-bullet-active
    background: #fff
  .wrapper
    overflow: hidden
    width: 100%
    height: 0
    // 26.67% is the aspect ratio of the picture
    padding-bottom: 26.67%
    background: #eee
    .swiper-image
      width:100%
</style>

After the above content is completed, the completed code can be submitted to the online warehouse. Then merge the current branch content into the master branch.

# Switch to main branch
git checkout master
# Merge branch
git merge origin/index-swiper
# Final submission
git push

In the actual development process, we follow the above steps.

Layout of icon area

In this section, we also need to create a new branch to develop new content. Creating a branch will not be repeated, just look at the previous chapters.

Create Icons component: create a new Icons.vue file in the components folder. And write the following code:

<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>

Complete Icons.vue Code:

<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">Tickets for scenic spots</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>

Logical implementation of icon area

Requirement: when there are many icons, it can achieve the effect of sliding left and right. And loop render data.

Full code:

<template>
  <div class="icons">
    <swiper :options="swiperOption">
      <!-- Loop render data add swiper-slide Label can slide left and right -->
      <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 {
        // Show control points
        swiperOption: {
          pagination: '.swiper-pagination'
        },
        // Write data in data, view layer and render directly
        iconList:[{
          id:'001',
          imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/95/f3dd6c383aeb3b02.png',
          desc:'Tickets for scenic spots'
        },{
          id:'002',
          imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/47/c2b659e048b11602.png',
          desc:'theme park'
        },{
          id:'003',
          imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/ab/6f7d6e44963c9302.png',
          desc:'Enjoy a hot spring'
        },{
          id:'004',
          imgUrl:'http://mp-piao-admincp.qunarzz.com/mp_piao_admin_mp_piao_admin/admin/20193/a40ee278d67000f2a29d2e20f6a029b3.png',
          desc:'Natural scenery'
        },{
          id:'005',
          imgUrl:'http://img1.qunarzz.com/piao/fusion/1804/5a/13ceb38dcf262f02.png',
          desc:'One-day tour'
        },{
          id:'006',
          imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/97/02f5043b51b2102.png',
          desc:'Gulangyu Islet'
        },{
          id:'007',
          imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/96/c70f1e85ae4a4f02.png',
          desc:'Wuyi Mount'
        },{
          id:'008',
          imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/50/26ffa31b56646402.png',
          desc:'Parent-child travel'
        },{
          id:'009',
          imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/b8/c5dcdb58deec2402.png',
          desc:'Play safe'
        },{
          id:'010',
          imgUrl:'http://img1.qunarzz.com/piao/fusion/1803/b6/37560ece9c62b502.png',
          desc:'City Tourism'
        }]
      }
    },
    // Calculate the number of pages to be rotated by calculating properties
    computed:{
      pages (){
        const pages = []
        this.iconList.forEach((item,index) => {
          //If index=3 is rounded up by page=0, the index will be displayed on the first page. Index > = 8 will be displayed on the second page
          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
      // When the text description is too long, the omitted point is displayed
      overflow:hidden
      white-space:nowrap
      text-overflow:ellipsis
</style>

Submit to online warehouse:

git add .
git commit -m 'desc'
git push --set-upstream origin index-icons
git checkout master
# Merge to master branch
git merge origin/index-icons
git push

Develop recommended components

In the old rule, create the branch "index recommended", and create the recommended component: create the recommended.vue file in the components folder. Remember to reference this component in Home.vue.

Full code:

<template>
  <div>
    <div class="recommend-title">Guess you like it.</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">View details</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:'Minggu shop, Fuzhou',
          desc:'The most interesting place'
        },{
          id:'002',
          imgUrl:'https://imgs.qunarzz.com/sight/p0/1703/9d/9dd5987e5c7701f2a3.water.jpg_200x200_8b16531c.jpg',
          title:'Minggu shop, Fuzhou',
          desc:'The most interesting place'
        },{
          id:'003',
          imgUrl:'https://imgs.qunarzz.com/sight/p0/1703/9d/9dd5987e5c7701f2a3.water.jpg_200x200_8b16531c.jpg',
          title:'Minggu shop, Fuzhou',
          desc:'The most interesting place'
        }]
      }
    }
  }
</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>

Where to go on weekends

It is still developed under the same branch, only a new component 'Weekend.vue' needs to be created.

Full code:

<template>
	<div>
		<div class="recommend-title">Where to go on weekends</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:'Minggu shop, Fuzhou',
					desc:'The most interesting place'
				},{
					id:'002',
					imgUrl:'https://imgs.qunarzz.com/sight/source/1505/68/de862f94e383a6.jpg_r_640x214_f9df927b.jpg',
					title:'Minggu shop, Fuzhou',
					desc:'The most interesting place'
				},{
					id:'003',
					imgUrl:'https://imgs.qunarzz.com/sight/source/1505/68/de862f94e383a6.jpg_r_640x214_f9df927b.jpg',
					title:'Minggu shop, Fuzhou',
					desc:'The most interesting place'
				}]
			}
		}
	}
</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 getting home page data

In the previous chapters, we write the data to death. In this section, we use ajax to dynamically obtain the data.

The first step is to create the branch "index ajax". Sending ajax in vue can be used by many tools, such as fetch, vue resource. Now, axios is officially recommended by vue, because it is very powerful, and it can send requests across platforms. You can send XHR requests in the browser and HTTP requests in the node server. We use axios.

# Install axios
npm install axios --save

In a website, it is usually composed of n components. If each component sends a request, the performance is obviously very low. So we usually write one in the Home component.

When using ajax, as the front-end we no longer get the data file, we are using simulated data. So the requested addresses are usually local files. But in the actual development environment, it is necessary to replace the address, but there are risks in modifying the code before going online. How can we solve some problems?

We can add the following content to the proxyTable attribute in the config/index.js file in the project to indicate that when we request the server address, but the server does not find the file, vue will automatically replace it with our local address file.

//This function is provided for webpack dev server
proxyTable: {
    '/api':{
        target: 'http://localhost:8080',
        pathRewrite:{
          //Jump when access begins with api
          '^/api':'/static/mock'
        }
    }
}

We now create a simulated data locally. We put the simulated data under the static folder. Let's create a new folder, mock, and the index.json file under mock.

Since this file is data created by ourselves, we do not want to submit it to our warehouse, so we can ignore this file in gitignore.

Just add the folder path
static/mock

Home.vue

<script>
//... other component code omitted
import axios from 'axios'
export default {
  name: 'Home',
  components: { 
    HomeHeader, 
    HomeSwiper,
    HomeIcons,
    HomeRecommend,
    HomeWeekend
  },
  methods:{
    getHomeInfo (){
      // Although we write the address at the beginning of the api here, vue has helped us jump to our local json file
      axios.get('/api/index.json')
        .then(this.getHomeInfoSucc)
    },
    getHomeInfoSucc (res){
      //Get local json data successfully
      console.log(res)
    }
  },
  mounted (){
    this.getHomeInfo()
  }
}
</script>

Home page parent-child component data transfer

Next, we wrote the contents of the previous section. Let's write them directly in Home.vue. Define the relevant variables in the data function and pass them to the component

<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>

Then rewrite it in each component: delete all the code we wrote before. Use props to receive the passed value.

<template>
  <div class="wrapper">
    <!-- v-if It is used to solve the problem of the last default display of the carousel,Because before getting the data, the rendering is an empty array. Judge if it is an empty array, it will not be rendered -->
    <swiper :options="swiperOption" v-if="showSwiper">
      <!-- The data of the loop comes from the received array -->
      <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,
          //With autoplay, you can automatically switch the rotation chart every 3 seconds
          autoplay:3000
        }
      }
    },
    computed:{
      showSwiper(){
        return this.list.length
      }
    }
  }
</script>

Other components are the same as above. Just copy and write.

Published 12 original articles, praised 10 and visited 858
Private letter follow

Tags: Vue git axios JSON

Posted on Sun, 12 Jan 2020 23:40:02 -0800 by robkir