Fork me on GitHub
余鸢

饿了么45个页面重构(三)轮播图及msite页面开发

miste页面

点击城市地址转跳msite页面。miste页面分为三个部分,分别是头部地址展示、食品分类列表展示和商铺列表展示。食品分类列表使用Swiper插件做轮播图展示,接下来就来了解如何使用Swiper

Swiper的应用

Swiper(Swiper master)是目前应用较广泛的移动端网页触摸内容滑动js插件。

package.json中配置swiper

1
2
3
4
5
6
7
8
9
"dependencies": {
"vue": "^2.5.2",
"vue-router": "^3.0.1",
"vuex": "^3.0.1",
"axios": "^0.17.1",
"good-storage": "^1.0.1",
"babel-runtime": "^6.26.0",
"swiper": "^4.1.0"
},

安装依赖

1
npm install

在需要的组件中引入Swiper就可以使用了!

1
import Swiper from 'swiper'

头部地址展示

引入e-header组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<e-header signin-up='msite'>
<router-link to="/search/geohash" class="link_search" slot="search">
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" version="1.1">
<circle cx="9" cy="9" r="8" stroke="rgb(255,255,255)" stroke-width="2" fill="none"/>
<line x1="15" y1="15" x2="20" y2="20" style="stroke:rgb(255,255,255);stroke-width:2"/>
</svg>
</router-link>
<router-link to="/home" slot="msite-title" class="msite_title">
<span class="title_text">{{msiteTitle}}</span>
</router-link>
</e-header>
<script type="text/ecmascript-6">
import EHeader from 'components/e-header/e-header'
</script>

在data中定义msiteTitle表示城市地址,以及其他相关属性

1
2
3
4
5
6
7
8
data() {
return {
msiteTitle: '获取地址中...',
geohash: '',
latitude: '',
longitude: '',
}
}

通过引入msiteAdress()方法获取当前地址的相关数据

1
2
3
4
5
6
7
8
9
getMsiteAdress () {
var that = this
msiteAdress().then((res) => {
that.msiteTitle = res.name
that.latitude = res.latitude
that.longitude = res.longitude
that.geohash = res.geohash
})
},

页面地址展示:

e8

食品分类列表展示

上面已经说过,使用Swiper做展示。接着看以下代码渲染以及使用swiper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<nav class="msite_nav">
<div class="swiper-container" v-if="foodTypes.length">
<div class="swiper_wrapper">
<div class="swiper-slide food_types_container" v-for="(item, index) in foodTypes" :key="index">
<router-link :to="{path: '/food', query: {geohash, title: foodItem.name, restaurant_category_id: getCategoryId(foodItem.link)}}"
v-for="foodItem in item" :key="foodItem.id"
class="link_to_food">
<figure>
<img :src="imgBaseUrl + foodItem.image_hash + '.jpeg'"/>
<figcaption>{{foodItem.name}}</figcaption>
</figure>
</router-link>
</div>
</div>
<div class="swiper-pagination"></div>
</div>
</nav>

data中定义foodTypes表示为食品分类列表,在页面中使用v-for对foodTypes进行遍历,得到每个item,然后对item再进行遍历,得到每个foodItem以此使用foodItem里的属性进行数据填充渲染。

初始化swiper

1
2
3
4
5
6
7
8
9
initSwiper () {
this.swiper = new Swiper('.swiper-container', {
pagination: {
el: '.swiper-pagination',
type: 'bullets'
},
loop: true
})
},

抓取数据

对饿了么官网进行数据抓取,在webpack.dev.conf.js添加以下代码:

webpack.dev.conf.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
app.get('/api/msiteFoodTypes', (req, res) => {
const url = 'https://h5.ele.me/restapi/shopping/openapi/entries'
axios.get(url, {
headers: {
Host: 'h5.ele.me',
Referer: 'https://h5.ele.me/'
},
params: req.query
}).then((response) => {
res.json(response.data)
}).catch((e) => {
console.log(e)
})
}),

设置访问参数

msite.js

1
2
3
4
5
6
7
8
9
10
11
12
13
export function msiteFoodTypes (latitude, longitude) {
const url = '/api/msiteFoodTypes'
const data = Object.assign({}, {
latitude: latitude,
longitude: longitude,
'templates[]': 'main_template'
})
return axios.get(url, {
params: data
}).then((res) => {
return Promise.resolve(res.data)
})
}

在msite组件中引入msiteFoodTypes方法,得到数据进行渲染。

msite.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { msiteAdress, msiteFoodTypes } from 'api/msite'
getMsiteFoodTypes () {
msiteFoodTypes(this.latitude, this.longitude).then((res) => {
let resLength = res[0].entries.length
let resArr = res[0].entries.concat([])
let foodArr = []
for (let i = 0, j = 0; i < resLength; i += 8, j++) {
foodArr[j] = resArr.splice(0, 8)
}
this.foodTypes = foodArr
}).then(() => {
this.initSwiper()
})
},

页面展示:

e9

商铺列表展示

由于商铺列表在其他组件中也可能用到,所以单独作为基础组件。

基础组件shop-list.vue

基础代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<ul>
<router-link :to="{path: 'food', query:{}}" v-for="item in shopListArr" tag='li' :key="item.id" class="shop_li">
<section>
<img :src="imgBaseUrl + subImgUrl(item.image_path)" class="shop_img">
</section>
<hgroup class="shop_right">
<header class="shop_detail_header">
<h4 :class="item.is_premium? 'premium': ''" class="" class="shop_title ellipsis">{{item.name}}</h4>
<ul class="shop_detail_ul">
<li class="supports" v-for="child in item.supports" :key="child.id">{{child.icon_name}}</li>
</ul>
</header>
<h5>
...
</h5>
<h5>
...
</h5>
</hgroup>
</router-link>
</ul>
</template>

props

一个良好定义的接口中尽可能将父子组件解耦是很重要的。这保证了每个组件可以在相对隔离的环境中书写和理解,也大幅提高了组件的可维护性和可重用性。在vue里,父组件通过 props 向下传递数据给子组件。组件实例的作用域是孤立的。这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据。要让子组件使用父组件的数据,需要通过子组件的 props 选项。

1
2
3
4
5
6
7
8
9
10
11
12
export default{
props: {
geohash: {
type: String,
default: ''
},
shopListArr: {
type: Array,
default: []
}
}
}

shopListArr表示店铺列表数据。

在msite组件中引入shop-list组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
...
<shop-list></shop-list>
</div>
</template>
import ShopList from 'base/shop-list/shop-list'
export default{
components: {
EHeader,
ShopList
}
}

动态绑定数据

在模板中,要动态地绑定父组件的数据到子模板的 props,与绑定到任何普通的HTML特性相类似,就是用 v-bind。每当父组件的数据变化时,该变化也会传导给子组件。这里使用:geohash:shopListArr动态的绑定数据,同时在data中定义geohash和shopListArr属性

1
2
3
4
5
6
7
8
<shop-list :geohash="geohash" :shopListArr="shopListArr"></shop-list>
data(){
return {
geohash: '',
shopListArr: []
}
}

抓取数据

在webpack.dev.conf.js添加以下代码:

webpack.dev.conf.js

1
2
3
4
5
6
7
8
9
10
11
12
13
app.get('/api/shopList', (req, res) => {
const url = 'https://h5.ele.me/restapi/shopping/v3/restaurants'
axios.get(url, {
headers: {
Referer: 'https://h5.ele.me/'
},
params: req.query
}).then((response) => {
res.json(response.data)
}).catch((e) => {
console.log(e)
})
}),

设置访问参数

msite.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export function shopList (latitude, longitude, extras) {
const url = '/api/shopList'
const data = {
latitude: latitude,
longitude: longitude,
offset: 0,
limit: 8,
extras,
extra_filters: 'home',
rank_id: '',
terminal: 'h5'
}
return axios.get(url, {
params: data
}).then((res) => {
return Promise.resolve(res.data)
})
}

在msite组件中引入shopList方法,获得数据。

msite.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
import { msiteAdress, msiteFoodTypes, shopList } from 'api/msite'
mounted () {
this.extras = ['activities', 'tags']
msiteAdress().then(res => {
this.msiteTitle = res.name
this.geohash = res.geohash
shopList(res.latitude, res.longitude, res.extras).then(resq => {
this.shopListArr = Array.from(Object.keys(resq.items).map(key => resq.items[key].restaurant))
})
})
this.getMsiteFoodTypes()
},

页面展示

e10

具体代码见:https://github.com/kakajing/vue-elmm