转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥“前端一万小时”两大明星专栏——“从零基础到轻松就业”、“前端面试刷题”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
1 mock 数据
🔗前置知识:
《JavaScript 基础——JS 提供的对象:⑤ JSON》
《Vue 实战准备——② 项目框架源码解析》
在项目开发中,当没有后端的支持时,就需要我们自己模拟数据了。
1️⃣在 static 目录下新建一个 mock 文件夹(用来存放项目中所有的模拟数据),在 mock 里新建一个 index.json 文件(数据是 JSON 格式的):
1️⃣-①:index.json 中的数据,为首页所有小组件所需的数据;
{
"ret": true, /* 1️⃣-②:ret 为 true,代表服务器正确响应了请求; */
"data": {
"city": "北京", /* 1️⃣-③:data 中有一个 city 为 Header.vue 的“城市”,即当前所在城市; */
"swiperList": [{ /* 1️⃣-④:轮播组件的数据 swiperList; */
"id": "0001",
"imgUrl": "https://qdywxs.github.io/travel-images/swiperList01.jpg"
},{
"id": "0002",
"imgUrl": "https://qdywxs.github.io/travel-images/swiperList02.jpg"
},{
"id": "0003",
"imgUrl": "https://qdywxs.github.io/travel-images/swiperList03.jpg"
},{
"id": "0004",
"imgUrl": "https://qdywxs.github.io/travel-images/swiperList04.jpg"
}],
"iconList": [{ /* 1️⃣-⑤:图标区域组件的数据 iconList; */
"id": "0001",
"imgUrl": "https://qdywxs.github.io/travel-images/iconList01.png",
"desc": "景点门票"
}, {
"id": "0002",
"imgUrl": "https://qdywxs.github.io/travel-images/iconList02.png",
"desc": "滑雪季"
}, {
"id": "0003",
"imgUrl": "https://qdywxs.github.io/travel-images/iconList03.png",
"desc": "泡温泉"
}, {
"id": "0004",
"imgUrl": "https://qdywxs.github.io/travel-images/iconList04.png",
"desc": "动植园"
}, {
"id": "0005",
"imgUrl": "https://qdywxs.github.io/travel-images/iconList05.png",
"desc": "游乐园"
}, {
"id": "0006",
"imgUrl": "https://qdywxs.github.io/travel-images/iconList06.png",
"desc": "必游榜单"
}, {
"id": "0007",
"imgUrl": "https://qdywxs.github.io/travel-images/iconList07.png",
"desc": "演出"
}, {
"id": "0008",
"imgUrl": "https://qdywxs.github.io/travel-images/iconList08.png",
"desc": "城市观光"
}, {
"id": "0009",
"imgUrl": "https://qdywxs.github.io/travel-images/iconList09.png",
"desc": "一日游"
}],
"recommendList": [{ /* 1️⃣-⑥:“热销推荐”组件的数据 recommendList; */
"id": "0001",
"imgUrl": "https://qdywxs.github.io/travel-images/recommendList01.jpg",
"title": "故宫",
"desc": "东方宫殿建筑代表,世界宫殿建筑典范"
}, {
"id": "0002",
"imgUrl": "https://qdywxs.github.io/travel-images/recommendList02.jpg",
"title": "南山滑雪场",
"desc": "北京专业级滑雪圣地"
}, {
"id": "0003",
"imgUrl": "https://qdywxs.github.io/travel-images/recommendList03.jpg",
"title": "天安门广场",
"desc": "我爱北京天安门,天安门上太阳升"
}, {
"id": "0004",
"imgUrl": "https://qdywxs.github.io/travel-images/recommendList04.jpg",
"title": "水立方",
"desc": "中国的荣耀,阳光下的晶莹水滴"
}, {
"id": "0005",
"imgUrl": "https://qdywxs.github.io/travel-images/recommendList05.jpg",
"title": "温都水城养生馆",
"desc": "各种亚热带植物掩映其间,仿佛置身热带雨林"
}],
"weekendList": [{ /* 1️⃣-⑦:“周末去哪儿”组件的数据 weekendList。 */
"id": "0001",
"imgUrl": "https://qdywxs.github.io/travel-images/weekendList01.jpg",
"title": "北京温泉排行榜",
"desc": "细数北京温泉,温暖你的冬天"
}, {
"id": "0002",
"imgUrl": "https://qdywxs.github.io/travel-images/weekendList02.jpg",
"title": "北京必游TOP10",
"desc": "来北京必去的景点非这些地方莫属"
}, {
"id": "0003",
"imgUrl": "https://qdywxs.github.io/travel-images/weekendList03.jpg",
"title": "寻找北京的皇城范儿",
"desc": "数百年的宫廷庙宇,至今依旧威严霸气"
}, {
"id": "0004",
"imgUrl": "https://qdywxs.github.io/travel-images/weekendList04.jpg",
"title": "学生最爱的博物馆",
"desc": "周末干嘛?北京很多博物馆已经免费开放啦"
}, {
"id": "0005",
"imgUrl": "https://qdywxs.github.io/travel-images/weekendList05.jpg",
"title": "儿童剧场,孩子的乐园",
"desc": "带宝贝观看演出,近距离体验艺术的无穷魅力"
}]
}
}
2 获取并渲染数据
🔗前置知识:
《JavaScript 基础——JS 提供的对象:② 正则表达式》
《Vue 入门——② Vue 实例的生命周期函数》
《深入理解 Vue 组件——② 父子组件间的数据传递》
在我们的项目中,首页一共有 5 个小组件,而每个小组件都有自己的数据。即,每一个小组件中,都需要发送请求来获取数据。
❌但这就意味着,打开首页时,一次至少需要发送 5 个请求。如此一来,网页的性能就很低。
✅更为合理的方式就是:整个首页,只发送一个 AJAX 请求。
❓在哪里发送这个 AJAX 请求呢?
答:在首页 Home.vue 这个组件中发送 AJAX 请求、获取到数据,然后 Home.vue 将数据传递给其他的小组件。
2️⃣要在项目中使用 Axios,首先需要安装。
2️⃣-①:打开终端,在项目目录下运行命令 npm install --save axios 进行安装;
2️⃣-②:打开 home 下的 Home.vue ,在这里写 AJAX 请求的代码;
<template>
<div>
<home-header></home-header>
<home-swiper></home-swiper>
<home-icons></home-icons>
<home-recommend></home-recommend>
<home-weekend></home-weekend>
</div>
</template>
<script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios' /* 2️⃣-③:引入 Axios; */
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons,
HomeRecommend,
HomeWeekend
},
methods: { /* 2️⃣-⑤:在 methods 中定义 getHomeInfo 方法; */
getHomeInfo () { /* 2️⃣-⑥:请求获取 static 下 mock 中的 index.json 文件; */
axios.get('/static/mock/index.json')
.then(this.getHomeInfoSucc) /*
2️⃣-⑦:Axios 返回的结果是一个 Promise 对象,
所以当请求成功时,执行 getHomeInfoSucc 方法;
*/
},
getHomeInfoSucc (res) {
console.log(res) /* 2️⃣-⑧:getHomeInfoSucc 方法打印出获取到的数据。 */
}
},
mounted () { /* 2️⃣-④:在 mounted 中执行 getHomeInfo 这个获取首页数据的方法; */
this.getHomeInfo()
}
}
</script>
<style>
</style>
保存后,返回网页查看控制台,可以看到我们成功获取到了数据:
❌但,我们的代码里存在一个“隐患”:
我们请求的地址,是本地模拟数据的接口地址。如果项目要发布上线,就不能使用这个地址。也就是说,我们需要在上线前更改这部分的代码(比如,把地址替换为 /api/index.json 这种格式)。但上线之前更改代码,是有“风险”的,非常不建议这样做。
所以,我们可以在 Webpack 的“开发环境”配置中,进行一些配置:通过 proxy 这个代理功能,实现当在开发环境中请求 /api/index.json 时,转发到本地的 mock 文件夹下。
3️⃣打开项目目录下的 config 中的 index.js 文件:
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: { /* 3️⃣-①:在开发环境的 proxyTable 中进行配置; */
'/api': { /* 3️⃣-②:当请求 /api 这个目录时; */
target: 'http://localhost:8080', /*
3️⃣-③:希望它把请求目标,转发到当前服务器
的 8080 端口上;
*/
pathRewrite: { /* 3️⃣-④:但请求的路径要进行替换; */
'^/api': '/static/mock' /*
3️⃣-⑤:当请求的地址是以 /api 为开头的,就把它
替换到本地的 static 中的 mock 文件夹下。
*/
}
}
},
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
cssSourceMap: true
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',
/**
* Source Maps
*/
productionSourceMap: true,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}
3️⃣-⑥:打开 home 下的 Home.vue ;
<template>
<div>
<home-header></home-header>
<home-swiper></home-swiper>
<home-icons></home-icons>
<home-recommend></home-recommend>
<home-weekend></home-weekend>
</div>
</template>
<script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons,
HomeRecommend,
HomeWeekend
},
methods: {
getHomeInfo () {
axios.get('/api/index.json') /* ❗️将 /static/mock 替换为 /api。 */
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc (res) {
console.log(res)
}
},
mounted () {
this.getHomeInfo()
}
}
</script>
<style>
</style>
保存代码,因为更改了 Webpack 配置项,所以需要重启服务器后,再打开页面。可以看到,我们成功从 /api/index.json 路径获取到了数据:
当 Home.vue 获取到数据后,就需要把数据传递给子组件。
我们回忆一下“父组件向子组件传值”的过程:
- 父组件的 data 中定义数据;
- 在父组件的模板中,通过“属性”给子组件传递数据;
- 子组件通过“props”接收数据。
4️⃣打开 home 下的 Home.vue :
<template>
<div>
<home-header :city="city"></home-header> <!-- 4️⃣-②:通过属性 :city,把数据 city 传递
给子组件 Header.vue; -->
<home-swiper></home-swiper>
<home-icons></home-icons>
<home-recommend></home-recommend>
<home-weekend></home-weekend>
</div>
</template>
<script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons,
HomeRecommend,
HomeWeekend
},
data () {
return {
city: '' /* 4️⃣-①:因为 city 里的数据是字符串,所以在 data 中初始化数据 city 为空字符串; */
}
},
methods: {
getHomeInfo () {
axios.get('/api/index.json')
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc (res) {
res = res.data /* 4️⃣-③:将获取到的数据中的 data 赋值给变量 res; */
if (res.ret && res.data) { /*
4️⃣-④:如果 res.ret 为 true(即,后端正确返回了结果),
并且 res 中有对应的数据 data;
*/
const data = res.data /* 4️⃣-⑤:为了方便,可以将 res.data 赋值给变量 data; */
this.city = data.city /* 4️⃣-⑥:将数据项中的 city 赋值给 this.city; */
}
}
},
mounted () {
this.getHomeInfo()
}
}
</script>
<style>
</style>
4️⃣-⑦:打开 home 下 components 中的 Header.vue ,在子组件中接收数据;
<template>
<div class="header">
<div class="header-left">
<span class="iconfont back-icon"></span>
</div>
<div class="header-input">
<span class="iconfont"></span>
输入城市/景点/游玩主题
</div>
<div class="header-right">
{{this.city}} <!-- 4️⃣-⑨:把“城市”替换为 this.city,用插值表达式渲染到页面上。 -->
<span class="iconfont arrow-icon"></span>
</div>
</div>
</template>
<script>
export default {
name: 'HomeHeader',
props: { /*
4️⃣-⑧:在子组件新增 props,里边接收属性 :city 传递的数据项 city,city 的值必须
为字符串;
*/
city: String
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.header
display: flex
line-height: .86rem
color: #fff
background: $bgColor
.header-left
float: left
width: .64rem
.back-icon
display: block
text-align: center
font-size: .56rem
.header-input
flex: 1
margin-top: .12rem
margin-left: .2rem
padding-left: .12rem
height: .64rem
line-height: .64rem
color: #ccc
background: #fff
border-radius: .1rem
.header-right
float: right
width: 1.24rem
text-align: center
.arrow-icon
margin-left: -0.1rem
</style>
保存后,返回页面查看,右上角的文字原本是“城市”,现在被“北京”替换了:
❓为什么会显示“北京”呢?
答:因为 AJAX 请求到的数据中, city 对应的是“北京”。然后,我们把 city 的值,赋值给了 data 函数里返回的 city 。
随后,通过属性 :city ,又将数据 city 传递给了子组件 Header.vue :
子组件中接收了 city :
所以,当 city 发生变化时,子组件中就会跟着变化。这也就是“父组件向子组件传递数据”的具体过程。
理解了这个具体过程,其他组件的数据,我们可以“依葫芦画瓢”来完成了。
5️⃣打开 home 下的 Home.vue :
<template>
<div>
<home-header :city="city"></home-header>
<home-swiper :list="swiperList"></home-swiper> <!-- 5️⃣-②:通过属性 list 把 swiperList
传递给子组件 Swiper.vue; -->
<home-icons></home-icons>
<home-recommend></home-recommend>
<home-weekend></home-weekend>
</div>
</template>
<script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons,
HomeRecommend,
HomeWeekend
},
data () {
return {
city: '',
swiperList: [] /*
5️⃣-①:因为 swiperList 里的数据是数组,所以初始化 swiperList
为空数组;
*/
}
},
methods: {
getHomeInfo () {
axios.get('/api/index.json')
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc (res) {
res = res.data
if (res.ret && res.data) {
const data = res.data
this.city = data.city
this.swiperList = data.swiperList /*
5️⃣-③:一旦获取到数据,就把数据中的 swiperList
赋值给 this.swiperList;
*/
}
}
},
mounted () {
this.getHomeInfo()
}
}
</script>
<style>
</style>
5️⃣-④:打开 home 下 components 中的 Swiper.vue ;
<template>
<div class="wrapper">
<swiper :options="swiperOption">
<swiper-slide v-for="item of list" :key="item.id"> <!-- 5️⃣-⑦:循环的数组改为
接收到的数据 list。 -->
<img class="swiper-img" :src="item.imgUrl">
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
</div>
</template>
<script>
export default {
name: 'HomeSwiper',
props: { /* 5️⃣-⑥:在 props 中接收父组件通过 :list 属性传递过来的数据,它对应的值为数组; */
list: Array
},
data () { /* 5️⃣-⑤:删除 data 中的 swiperList; */
return {
swiperOption: {
pagination: '.swiper-pagination',
loop: true,
autoplay: 3000
}
}
}
}
</script>
<style lang="stylus" scoped>
.wrapper >>> .swiper-pagination-bullet-active
background: #fff
.wrapper
overflow: hidden
width: 100%
height: 0
padding-bottom: 31.25%
background: #eee
.swiper-img
width: 100%
</style>
❌保存后,返回页面查看效果,轮播里的图片正常渲染了。但,轮播中默认显示的第一张图片,实际是数据中的第四张图片:
❓为什么刷新页面后,轮播的第一张图片显示的是数据里的最后一张轮播图片?
答:因为当我们在 Swiper.vue 中使用第三方插件的 <swiper> 时,页面还没有获取 AJAX 数据。这时 props 接收的数据 list 是 Home.vue 中定义的 “swiperList 空数组”。
当 AJAX 获取数据完成后,swiperList 才变成真正的数据项。此时,再传递数据项给 Swiper.vue 时,它才获取到新的数据,重新渲染了新数据中对应的图片。
即, <swiper> 最初创建时,是通过空数组创建的。这导致了刷新页面后,轮播的第一张图片显示的是数据里的最后一张轮播图。
❓如何解决这个问题呢?
答:让 <swiper> 初次创建时,由完整的数据创建,而不是由空数组创建。
6️⃣打开 home 下的 Swiper.vue :
<template>
<div class="wrapper">
<!-- 6️⃣-③:在 swiper 标签上添加 v-if 指令,使用计算属性 showSwiper。 -->
<swiper :options="swiperOption" v-if="showSwiper">
<swiper-slide v-for="item of list" :key="item.id">
<img class="swiper-img" :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: 3000
}
}
},
computed: { /* 6️⃣-①:在 computed 中定义一个 showSwiper 计算属性; */
showSwiper () {
return this.list.length /*
6️⃣-②:showSwiper 返回 this.list.length (即,当 list
是空数组时, list.length 为 false,轮播就不会被创建;当数据
获取到之后, list.length 变为 true,轮播才被创建)。
*/
}
}
}
</script>
<style lang="stylus" scoped>
.wrapper >>> .swiper-pagination-bullet-active
background: #fff
.wrapper
overflow: hidden
width: 100%
height: 0
padding-bottom: 31.25%
background: #eee
.swiper-img
width: 100%
</style>
保存后,返回页面查看。当页面刷新后,轮播正确显示数据中的第一张图片:
7️⃣接下来,传递数据给 Icons.vue 小组件。打开 home 下的 Home.vue :
<template>
<div>
<home-header :city="city"></home-header>
<home-swiper :list="swiperList"></home-swiper>
<home-icons :list="iconList"></home-icons> <!-- 7️⃣-②:把数据 iconList 通过属性 list
传递给子组件 Icons.vue; -->
<home-recommend></home-recommend>
<home-weekend></home-weekend>
</div>
</template>
<script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons,
HomeRecommend,
HomeWeekend
},
data () {
return {
city: '',
swiperList: [],
iconList: [] /* 7️⃣-①:初始化 iconList 为空数组; */
}
},
methods: {
getHomeInfo () {
axios.get('/api/index.json')
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc (res) {
res = res.data
if (res.ret && res.data) {
const data = res.data
this.city = data.city
this.swiperList = data.swiperList
this.iconList = data.iconList /*
7️⃣-③:一旦获取到数据,就把 data.iconList 赋值
给 this.iconList;
*/
}
}
},
mounted () {
this.getHomeInfo()
}
}
</script>
<style>
</style>
7️⃣-④:打开 home 下 components 中的 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: { /* 7️⃣-⑥:在 props 中接收父组件通过属性 :list 传递过来的数据,它对应的值为数组; */
list: Array
},
data () { /* 7️⃣-⑤:删除 data 中的 iconList 数据; */
return {
swiperOption: {
autoplay: false
}
}
},
computed: {
pages () {
const pages = []
/* 7️⃣-⑦:计算属性中循环的数组,改为接收到的数据 list。 */
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>
@import '~styles/varibles.styl'
@import '~styles/mixins.styl'
.icons >>> .swiper-container
height: 0
padding-bottom: 50%
.icons
margin-top: .1rem
.icon
position: relative
overflow: hidden
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
ellipsis()
</style>
保存后,返回页面查看:
8️⃣传递数据给“热销推荐”组件和“周末去哪儿”组件,打开 home 下的 Home.vue :
<template>
<div>
<home-header :city="city"></home-header>
<home-swiper :list="swiperList"></home-swiper>
<home-icons :list="iconList"></home-icons>
<home-recommend :list="recommendList"></home-recommend> <!-- 8️⃣-②:通过属性 list,
把 recommendList 传递给
子组件 Recommend.vue; -->
<home-weekend :list="weekendList"></home-weekend> <!-- 8️⃣-③:通过属性 list,把
weekendList 传递给 Weekend.vue;
-->
</div>
</template>
<script>
import HomeHeader from './components/Header'
import HomeSwiper from './components/Swiper'
import HomeIcons from './components/Icons'
import HomeRecommend from './components/Recommend'
import HomeWeekend from './components/Weekend'
import axios from 'axios'
export default {
name: 'Home',
components: {
HomeHeader,
HomeSwiper,
HomeIcons,
HomeRecommend,
HomeWeekend
},
data () {
return {
city: '',
swiperList: [],
iconList: [],
recommendList: [], /* 8️⃣-①:初始化 recommendList 和 weekendList 为空数组; */
weekendList: []
}
},
methods: {
getHomeInfo () {
axios.get('/api/index.json')
.then(this.getHomeInfoSucc)
},
getHomeInfoSucc (res) {
res = res.data
if (res.ret && res.data) {
const data = res.data
this.city = data.city
this.swiperList = data.swiperList
this.iconList = data.iconList
/* 8️⃣-④:一旦获取到数据,就把 data.recommendList 赋值给 this.recommendList; */
this.recommendList = data.recommendList
/* 8️⃣-⑤:一旦获取到数据,就把 data.weekendList 赋值给 this.weekendList; */
this.weekendList = data.weekendList
}
}
},
mounted () {
this.getHomeInfo()
}
}
</script>
<style>
</style>
8️⃣-⑥:打开 home 下 components 中的 Recommend.vue ,在“热销推荐”组件接收数据;
<template>
<div>
<div class="title">热销推荐</div>
<ul>
<!-- 8️⃣-⑧:li 标签循环的数据,变为传递进来的 list; -->
<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: { /* 8️⃣-⑦:在 props 中接收父组件通过属性 :list 传递过来的数据,它对应的值为数组; */
list: Array
}
/* ❗️Recommend.vue 中的 data 只有 recommendList 一个数据,所以删除 data。 */
}
</script>
<style lang="stylus" scoped>
@import '~styles/mixins.styl'
.title
margin-top: .2rem
background: #eee
line-height: .8rem
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
line-height: .44rem
color: #fff
background: #ff9300
padding: 0 .2rem
border-radius: .06rem
</style>
8️⃣-⑨:打开 home 下 components 中的 Weekend.vue ,在“周末去哪儿”组件接收数据;
<template>
<div>
<div class="title">周末去哪儿</div>
<ul>
<!-- 8️⃣-⑪:li 标签循环的数据,变为传递进来的 list。 -->
<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: { /* 8️⃣-⑩:在 props 中接收父组件通过属性 :list 传递过来的数据,它对应的值为数组; */
list: Array
}
/* ❗️Weekend.vue 中的 data 只有 weekendList 一个数据,所以删除 data。 */
}
</script>
<style lang="stylus" scoped>
@import '~styles/mixins.styl'
.title
background: #eee
line-height: .8rem
text-indent: .2rem
.item-img-wrapper
overflow: hidden
height: 0
padding-bottom: 38%
.item-img
width: 100%
.item-info
padding: .1rem
.item-title
line-height: .54rem
font-size: .32rem
ellipsis()
.item-desc
line-height: .4rem
color: #ccc
ellipsis()
</style>
保存后,返回页面查看效果:
以上,我们就完成了首页开发。
🏆本篇总结:
- 在首页
Home.vue使用 Axios 发送了一次 AJAX 请求,动态获取到首页所需的所有数据; - 然后,通过“父组件向子组件间传值”,把数据分别传递给子组件;
- 最终,使数据正确渲染到页面上。
祝好,qdywxs ♥ you!