目标: 使用Ajax动态获取首页的数据
准备阶段:
1. 创建分支index-ajax
2. vue中发送ajax有很多工具, 比如 浏览器自带的fetch, vue-resource, axios, 我们选用axios
3. 安装axios: npm install axios --save
4. 如果发现,昨天写的代码并没有合并到主分支上,会发现今天访问的时候,昨天的内容都不见了, 是因为,今天的分支是从主分支上拉取过来的,所以就不会有昨天的代码。首先要先合并一下代码,在index-ajax分支下:`git merge index-recommend`
需要思考的问题
1. 如何发送ajax请求? 已经写过5个子组件了,每一个组件都有自己的数据,需要每一组件都发送一个ajax请求吗? 首页的5个组件都发送ajax请求的话,首页就要发送5次请求,将会极大影响网站的性能。
2. 能否整个首页只发送一个ajax请求? 如果只发送一个,应该从哪里发送? 答: 由Home.vue来获取ajax数据,获取到之后,由它将数据分别传递给各个子组件。
首页发送ajax请求
- 引入 axios
- 借助生命周期函数的mounted, 让页面挂载好之后去执行getHomeInfo这个函数(函数定义在methods中)
export default {
name: 'Home',
components: {...此处省略子组件的名字},
methods: {
getHomeInfo () {
axios.get('/api/index.json')
.then(this.getHomeInfoSucc)
},
getHomInfoSucc (res) {
console.log(res)
}
},
mounted () {
this.getHomeInfo()
}
}
注意: axios返回的结果是一个Promise对象,所以可以then, then里面是一个函数,不是函数的调用,表示在resolve的时候执行这个函数。页面一挂载完成,就去请求ajax数据,数据获取成功,就把数据打印出来。
3. 这个时候会报错,表示找不到这个json文件,在没有后端支持的情况下,如何实现数据的模拟?
- 在项目中有一个static目录,存放的是静态文件,创建一个名为mock的目录,在里面新建一个index.json文件。
- 将数据文件存放在这里的原因: 因为只有static目录下的文件才能被外部访问到。就类似于在NodeJS阶段开放的public资源目录。
- 如果不希望将这个本地的数据文件提交到线上,可以在文件夹下的.gitignore文件,在里面添加一项: static/mock ,表示在进行代码提交的时候不会被提交到本地或者线上仓库中。
- 此时要想访问这个json数据,路径就得改了:
axios.get('/static/mock/index.json')
, 但是我们使用的是本地模拟的接口地址,如果代码上线,写这样的地址是获取不到的, 就需要重新将地址格式修改成axios.get('/api/index.json')
这种格式,但是在上线之前改动代码是有风险的,不建议这样做,如何解决? 思路: 是否有一个转发机制,能够帮助我们把对api下面所有的json文件的请求转发到本地的mock文件夹下? - 上述问题的具体操作: vue中提供了一个proxy代理的功能,通过这个功能就可以解决上述问题。 打开config目录下的index.js文件在开发环境中,官方提供了一个proxyTable的一个配置项,进行如下配置:
proxyTable: {
'/api': {
target: 'http://localhost:8081',
pathRewrite: {
'^/api': '/static/mock'
}
}
}
实际上这个功能是由webpack-dev-server提供的。表示的是当用户请求路径/api开头的时候,它会定位到/static/mock对应的目录,就可以请求里面的json数据了。(记得修改配置项文件之后都需要重启服务器)
至此,就获取到了ajax数据。
首页父子组件的数据传递
-
在Home中创建一个data数据函数,存储需要的数据。
-
首先, data函数返回的数据对象中有一个city,将这个city传给子组件
home-header
,使用属性的形式进行传递,即:<home-header :city="city"></home-header>
, 然后在子组件Header.vue中接收这个数据,并渲染到页面,即使用props来接收这个来自父组件的数据:
Home.vue中:data () { return { city: '' } }
子组件Header.vue中:
export default { name: 'HomeHeader', props: { city: String } }
使用插值表达式的形式将city数据渲染到页面中 “城市”的位置:
<div class="header-right"> {{this.city}} <span class="iconfont arrow-icon"></span> </div>
-
从利用ajax获取到的数据中提取出我们需要的city:
getHomeInfoSucc (res) { res = res.data if (res.ret && res.data) { // 表示如果后端正确返回了结果,并且res里面有data内容项,就是服务器返回的所有数据 const data = res.data this.city = data.city } }
此时,this.city就是从json数据中获取到的具体城市,这个city的值通过属性
<home-header :city="city"></home-header>
传给子组件,子组件Header.vue中props接收这个数据,然后渲染到{{this.city}}
的位置,数据库中city发生变化,子组件中对应位置上就会发生同样的动态变化。 -
swiperList的获取
Home.vue中:data () { return { city: '', swiperList: [] } }
传给组件:
<home-swiper :swiperList="swiperList"></home-swiper>
子组件Swiper.vue中接收数据, 原本的swiperList数据就可以直接删除,取而代之的是使用一个props接收来自父组件的swiperList数据。export default { name: 'HomeSwiper', props: { list: Array }, data () { return { swiperOption: { pagination: '.swiper-pagination', loop: true }, // swiperList: [{ // id: '0001', // imgUrl: 'http://img1.qunarzz.com/sight/source/1505/7d/df6ff468331c43.jpg_r_640x214_1f3783d0.jpg' // },{ // id: '0002', // imgUrl: 'http://img1.qunarzz.com/sight/source/1505/24/f947f286f2cf61.jpg_r_640x214_46fb6378.jpg' // },{ // id: '0003', // imgUrl: 'http://img1.qunarzz.com/sight/source/1505/aa/7baaf8a851d221.jpg_r_640x214_1431200f.jpg' // }] } } }
然后将这个数据渲染到页面中去。
当ajax中数据修改的时候,就要在Home.vue中获取到:getHomeInfoSucc (res) { res = res.data if (res.ret && res.data) { // 表示如果后端正确返回了结果,并且res里面有data内容项,就是服务器返回的所有数据 const data = res.data this.city = data.city this.swiperList = data.swiperList } }
此时会发现, 轮播图的效果呈现出来了,但是在刚开始的时候,它默认是显示的最后一张图片,原因是一开始的
swiperList: []
它是一个空数组, 当数据发生变化的时候它获取到数据才会重新渲染到页面上去,要想默认显示的是第一张图片,可以加一个判断,判断这个数据不是空数组的时候渲染:<swiper :options="swiperOption" v-if="list.length"> <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>
代码美化: (在模板里面尽量避免出现这种逻辑性的代码) — 设置一个计算属性
export default { name: 'HomeSwiper', props: { list: Array }, data () { return { swiperOption: { pagination: '.swiper-pagination', loop: true } } }, computed: { showSwiper () { return this.list.length } } }
然后就可以之前写
v-if="showSwiper"
-
iconList数据的获取
-
recommendList数据获取
-
weekendList数据获取, 这几个数据的传递与接收、渲染的流程是一样的,不再重复。
一个小问题
icons图标区域会自动的来回滚动, 希望去除自动来回切换的效果: 将Swiper.vue中的:options="swiperOption"
粘贴到Icons.vue的swiper标签中,然后在Icons.vue中设置swiperOption:
data () {
return {
swiperOption: {
autoplay: false
}
}
}