(13)城市选择页开发——③ AJAX 获取城市选择页数据 | Vue.js 项目实战: 移动端“旅游网站”开发

140 阅读4分钟
转载请注明出处,未经同意,不可修改文章内容。

🔥🔥🔥“前端一万小时”两大明星专栏——“从零基础到轻松就业”、“前端面试刷题”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。

1 需求

使用 Axios 发送 AJAX 请求,完成页面数据的动态渲染:

travel_13-01.gif

2 mock 数据

⚠️城市列表选择页所需的本地模拟数据文件 city.json ,由于内容过多,这边已经准备好了,点击下载 city.json 即可。

1️⃣下载完成后,将 city.json 放入 static 下的 mock 文件夹中:

1️⃣-②:打开 city.json 查看数据内容;

{ /* ❗️仅截取了部分以说明数据内容。 */
  
  "ret": true, /* 1️⃣-③:ret 为 true,表示服务器正确响应了请求; */
  
  "data":{ /* 1️⃣-④:data 中包含一个 hotCities 数组 和一个 cities 对象; */
    
    "hotCities": [{ /*
    								1️⃣-⑤:hotCities 里边是“热门城市”的数据,数组中的每一个对象包含了
    								该热门城市的 id、拼音全拼 spell 和城市名称 name;
                     */
      "id": 1,
      "spell": "beijing",
      "name": "北京"
    }, {
      "id": 3,
      "spell": "shanghai",
      "name": "上海"
    }, {
      "id": 47,
      "spell": "xian",
      "name": "西安"
    }, {
      "id": 239,
      "spell": "sanya",
      "name": "三亚"
    }, {
      "id": 188,
      "spell": "lijiang",
      "name": "丽江"
    }, {
      "id": 125,
      "spell": "guilin",
      "name": "桂林"
    }],
    
    "cities": { /*
    						1️⃣-⑥:cities 中包含多个数组,每个数组包含对应拼音首字母(如 A、B、C 等)的
    						所有城市(如“A”数组包含了所有以“a”为城市名拼音首字母的所有“城市”,每个“城市”为
                一个对象。每个对象中,分别包含对应的 id、拼音全拼 spell 和城市名称 name);
                 */
      "A": [{
        "id": 56,
        "spell": "aba",
        "name": "阿坝"
      }, {
        "id": 57,
        "spell": "akesu",
        "name": "阿克苏"
      }, {
        "id": 58,
        "spell": "alashanmeng",
        "name": "阿拉善盟"
      }, {
        "id": 59,
        "spell": "aletai",
        "name": "阿勒泰"
      }, {
        "id": 60,
        "spell": "ali",
        "name": "阿里"
      }, {
        "id": 61,
        "spell": "ankang",
        "name": "安康"
      }, {
        "id": 62,
        "spell": "anqing",
        "name": "安庆"
      }, {
        "id": 63,
        "spell": "anshan",
        "name": "鞍山"
      }, {
        "id": 64,
        "spell": "anshun",
        "name": "安顺"
      }, {
        "id": 65,
        "spell": "anyang",
        "name": "安阳"
      }]
      /*❗️略。*/
    }
  }
}

3 获取并渲染数据

🔗前置知识:

《Vue 入门——⑧ 列表渲染》

《首页开发——⑤ AJAX 获取首页数据》

在城市选择页中,有城市选择列表和城市字母表两个组件需要动态渲染数据:

3.1 List.vue 的数据渲染

2️⃣打开 city 下的 City.vue 获取数据:

<template>
  <div>
    <city-header></city-header>
    <city-search></city-search>
    <city-list></city-list>
    <city-alphabet></city-alphabet>
  </div>
</template>

<script>
import axios from 'axios' /* 2️⃣-①:引入 Axios; */

import CityHeader from './components/Header'
import CitySearch from './components/Search'
import CityList from './components/List'
import CityAlphabet from './components/Alphabet'

export default {
  name: 'City',
  components: {
    CityHeader,
    CitySearch,
    CityList,
    CityAlphabet
  },
  methods: { // 2️⃣-③:在 methods 中,定义 getCityInfo 方法;

    getCityInfo () { /*
    								 2️⃣-④:getCityInfo 方法获取 api 下的 city.json 文件
    								 (当请求/api/city.json 时,会自动转发到本地的 /static/mock/city.json);
                     请求成功时,执行 getCityInfoSucc 方法;
                      */
      axios.get('/api/city.json')
        .then(this.getCityInfoSucc)
    },

    getCityInfoSucc (res) { // 2️⃣-⑤:getCityInfoSucc 方法打印出获取到的数据。
      console.log(res)
    }
  },
  mounted () { // 2️⃣-②:在 mounted 中,执行获取城市选择页数据的方法 getCityInfo;
    this.getCityInfo()
  }
}
</script>

<style>
</style>

保存后,返回页面查看,成功获取到数据项 hotCitiescities

3️⃣ City.vue 获取到数据后,传递给子组件:

<template>
  <div>
    <city-header></city-header>
    <city-search></city-search>

    <!-- 3️⃣-⑦:通过属性 :hot 和 :cities 传递数据 hotCities 和 cities 给城市选择列表组件
		List.vue; -->
    <city-list :hot="hotCities" :cities="cities"></city-list>

    <city-alphabet></city-alphabet>
  </div>
</template>

<script>
import axios from 'axios'
import CityHeader from './components/Header'
import CitySearch from './components/Search'
import CityList from './components/List'
import CityAlphabet from './components/Alphabet'

export default {
  name: 'City',
  components: {
    CityHeader,
    CitySearch,
    CityList,
    CityAlphabet
  },
  data () { /*
  					3️⃣-①:在 data 中,定义一个 hotCities 变量为空数组,定义一个 cities 变量
  					为空对象;
             */
    return {
      hotCities: [],
      cities: {}
    }
  },
  methods: {
    getCityInfo () {
      axios.get('/api/city.json')
        .then(this.getCityInfoSucc)
    },
    getCityInfoSucc (res) {
      res = res.data // 3️⃣-②:将获取到的数据中的 data 赋值给变量 res;
      
      if (res.ret && res.data) { /*
      													 3️⃣-③:如果 res.ret 为 true(即,后端正确返回了结果),
      													 并且 res 中有对应的数据 data;
                                  */

        const data = res.data // 3️⃣-④:将 res.data 赋值给变量 data;

        this.hotCities = data.hotCities /*
        																3️⃣-⑤:将数据项中的 hotCities 赋值
        																给 this.hotCities;
                                         */

        this.cities = data.cities // 3️⃣-⑥:将数据项中的 cities 赋值给 this.cities;

        /*
        ❗️这里,前、后的 hotCities/cities 同名,只是为了方便,其实不必一定要同名(比如 
        hotCities 也可在 data 中定义为 allHotCities,那么这里的 this.hotCities 改为 
        this.allHotCities 即可)。
         */
      }

    }
  },
  mounted () {
    this.getCityInfo()
  }
}
</script>

<style>
</style>

3️⃣-⑧:打开 city 下 components 中的 List.vue 接收并循环数据;

<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">北京</div>
          </div>
        </div>
      </div>
      <div class="area">
        <div class="title border-topbottom">热门城市</div>
        <div class="button-list">

          <!-- 3️⃣-⑩:保留一个 button-wrapper,通过循环 hotCities 获取“热门城市”,key 值
					为每个循环项的 id; -->
          <div class="button-wrapper" v-for="item of hot" :key="item.id">

            <div class="button">{{item.name}}</div> <!-- 3️⃣-⑪:渲染每个循环项中的 name
																										(即热门城市名称); -->
          </div>

        </div>
      </div>

      <!-- 3️⃣-⑫:保留一个 .area,循环对象 cities,绑定 key 值为循环项的键名
			(即“A”、“B”、“C”等字母,字母也可以作为 key 值); -->
      <div class="area" v-for="(item, key) of cities" :key="key">

        <!-- 3️⃣-⑬:渲染每个区域的标题为 cities 的键名 key; -->
        <div class="title border-topbottom">{{key}}</div>

        <ul class="item-list">

          <!-- 3️⃣-⑭:保留一个 li 标签,对 item 进行循环(这里是进行一个二次循环),
					绑定 key 值为 innerItem 的 id; -->
          <li class="item border-bottom" v-for="innerItem of item" :key="innerItem.id">

            {{innerItem.name}} <!-- 3️⃣-⑮:渲染当前字母对应的城市名为每一项中的 name(即以
															 标题 A、B、C 等为城市拼音首字母的城市名称)。 -->

          </li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script>
import BScroll from 'better-scroll'

export default {
  name: 'CityList',

  props: { /*
  				 3️⃣-⑨:子组件通过 props 接收父组件传递的数据,其中 hot 的类型必须是数组,
  				 cities 的类型则是对象;
            */
    hot: Array,
    cities: Object
  },
  mounted () {
    this.scroll = new BScroll(this.$refs.wrapper)
  }
}
</script>

<style lang="stylus" scoped>
.border-topbottom
  &:before
    border-color: #ccc
  &:after
    border-color: #ccc
.border-bottom
  &:before
    border-color: #ccc
.list
  overflow: hidden
  position: absolute
  top: 1.58rem
  left: 0
  right: 0
  bottom: 0
  .title
    padding-left: .2rem
    line-height: .54rem
    background: #eee
    color: #666
    font-size: .26rem
  .button-list
    overflow: hidden
    padding: .1rem .6rem .1rem .1rem
    .button-wrapper
      float: left
      width: 33.33%
      .button
        margin: .1rem
        padding: .1rem 0
        text-align: center
        border: .02rem solid #ccc
        border-radius: .06rem
  .item-list
    .item
      line-height: .76rem
      padding-left: .2rem
</style>

(⚠️二次循环时,若父级的 key 值和子级的 key 值相同,不会相互影响,保证每个层级自身的 key 值不重复即可。)

保存后,返回页面查看,控制台无报错,数据正确渲染:

travel_13-02.gif

3.2 Alphabet.vue 的数据渲染

4️⃣打开 city 下的 City.vue

<template>
  <div>
    <city-header></city-header>
    <city-search></city-search>
    <city-list :hot="hotCities" :cities="cities"></city-list>

    <!-- 4️⃣-①:通过属性 :cities 传递数据 cities 给字母表组件 Alphabet.vue; -->
    <city-alphabet :cities="cities"></city-alphabet>
  </div>
</template>

<script>
import axios from 'axios'
import CityHeader from './components/Header'
import CitySearch from './components/Search'
import CityList from './components/List'
import CityAlphabet from './components/Alphabet'

export default {
  name: 'City',
  components: {
    CityHeader,
    CitySearch,
    CityList,
    CityAlphabet
  },
  data () {
    return {
      hotCities: [],
      cities: {}
    }
  },
  methods: {
    getCityInfo () {
      axios.get('/api/city.json')
        .then(this.getCityInfoSucc)
    },
    getCityInfoSucc (res) {
      res = res.data
      if (res.ret && res.data) {
        const data = res.data
        this.hotCities = data.hotCities
        this.cities = data.cities
      }
    }
  },
  mounted () {
    this.getCityInfo()
  }
}
</script>

<style>
</style>

4️⃣-②:打开 city 下 components 中的 Alphabet.vue 接收数据;

<template>
  <ul class="list">
    <!-- 4️⃣-④:保留一个 li 标签,循环对象 cities,绑定 key 值为键名; -->
    <li class="item" v-for="(item, key) of cities" :key="key">

      {{key}} <!-- 4️⃣-⑤:渲染 cities 中的键名。 -->
    </li>
  </ul>
</template>

<script>
export default {
  name: 'CityAlphabet',

  props: { // 4️⃣-③:子组件中,通过 props 接收父组件传递过来的数据,数据的类型为对象;
    cities: Object
  }
}
</script>

<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.list
  position: absolute
  top: 1.58rem
  right: 0
  bottom: 0
  width: .4rem
  display: flex
  flex-direction: column
  justify-content: center
  .item
    line-height: .4rem
    text-align: center
    color: $bgColor
</style>

保存后,返回页面查看:

以上,我们完成了城市选择页中数据的动态渲染,所有的内容取决于 AJAX 动态获取到的数据。

祝好,qdywxs ♥ you!