转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥“前端一万小时”两大明星专栏——“从零基础到轻松就业”、“前端面试刷题”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
1 需求
使用 Axios 发送 AJAX 请求,完成页面数据的动态渲染:
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 获取并渲染数据
🔗前置知识:
在城市选择页中,有城市选择列表和城市字母表两个组件需要动态渲染数据:
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>
保存后,返回页面查看,成功获取到数据项 hotCities 和 cities :
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 值不重复即可。)
保存后,返回页面查看,控制台无报错,数据正确渲染:
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!