1. 首页
  2. 小程序

基于mpvue的仿滴滴出行小程序

初衷

本人最近一直在研究小程序,一个偶然的机会发现了mpvue框架,刚好本人对vue十分喜爱,就决定入坑了。曾经在掘金看过一个仿旧版滴滴的小程序,感觉挺不错的,但它是基于原生小程序的,所以就决定花了一段时间用mpvue进行重写。下面就开始正餐了。

效果图

基于mpvue的仿滴滴出行小程序

更多效果请查看 github.com/QinZhen001/…

目录结构


┣━ api # 存放网络请求相关 ┣━ common ● ┣━ constant //常量 ┣━ css //weui.css ┣━ less //通用less样式与变量 ┗━ lib //第三方库 qqmap-wx-jssdk.js ┣━ components ● 抽取出来的组件 ┣━ addressList.vue ┣━ common-footer.vue ┣━ driver-header.vue ┣━ loading-sprinner.vue ┣━ search-bar.vue ┗━ star.vue ┣━ pages ● 页面 ┣━ cars //选择车 ┣━ cityChoose //选择城市 ┣━ destination //选择目的地 ┣━ evaluation //评价 ┣━ index //主页面 ┣━ login //登录 ┣━ orderCancel //订单取消 ┣━ orderClose //订单关闭 ┣━ orderService //订单服务 ┣━ orderWhy //询问原因 ┣━ starting //选择出发地点 ┗━ wait //等待 ┣━ store ● 存放vuex相关 ┣━ index.js ┣━ mutation-types.js ┣━ mutations.js ┗━ state.js ┣━ utils 工具类 ┣━ App.vue ┣━ main.js ┗━ static # 静态资源,存放图片``` ## vuex数据 ```javascript const state = { curNavIndex: 0, //当前头部导航索引 phone: '', //登录号码 curCity: '', //当前所在的城市 startPlace: '出发地', //出发地 startFormattedPlace: '', //更具人性化的描述的出发地 startPosition: [], //包含startLatitude和startLongitude destination: '你要去哪儿', //目的地 endPosition: [], //包含endLatitude和endLongitude driver: {}, //司机信息 包含Cartnumber,cart,id,name,stars cost: 0 //花费 }

功能详情

头部导航自动滑动

基于mpvue的仿滴滴出行小程序

为了让头部导航点击时能自动滑出,滑动swiper的同时头部导航跟着滑动,在cars页面选中车时回退到index页面时头部导航自动滑动,我在vuex中维护了一个索引值curNavIndex。根据不同的curNavIndex对scroll-view设置不同的scroll-left值。

那么如何设置准确的scroll-left值呢?

微信小程序无法进行Dom操作,所以无法动态拿到元素宽度。所以我根据头部导航每项的宽度维护了一个数组navOffsetArr


//两个字宽度+2*margin 也就是 32+10*2 = 52 const NAV_SMALL_WIDTH = 52; //三个字宽度+2*margin 也就是 48+10*2 = 68 const NAV_BIG_WIDTH = 68; this.navOffsetArr = [ 0, 0, NAV_SMALL_WIDTH, NAV_SMALL_WIDTH * 2, NAV_SMALL_WIDTH * 2 + NAV_BIG_WIDTH, NAV_SMALL_WIDTH * 2 + NAV_BIG_WIDTH * 2, NAV_SMALL_WIDTH * 3 + NAV_BIG_WIDTH * 2, NAV_SMALL_WIDTH * 4 + NAV_BIG_WIDTH * 2 ]

获取索引值


computed: { ...mapState([ 'curNavIndex' ]) }

watch里监听索引值,当curNavIndex改变时,拿到不同的navScrollLeft值


watch: { curNavIndex(newIndex){ this.navScrollLeft = this.navOffsetArr[newIndex] } }

最后将scroll-left与navScrollLeft绑定,从而实现自动滑动


<scroll-view class="nav" scroll-x="true" scroll-with-animation="true" :scroll-left="navScrollLeft"> ...... ...... </scroll-view>

首页自动保存位置信息

在进入index首页的时候,就会自动将当前城市,当前经纬度,当前地址存入state中作为出发点信息。这里接入了腾讯地图api,还是比较方便的。


wx.getLocation({ type: 'gcj02', success: (res) => { reverseGeocoder(qqmapsdk, res).then(res => { this.saveStartPlace(res.result.address) this.saveFormattedStartPlace(res.result.formatted_addresses.recommend) this.saveCurCity(res.result.address_component.city) }) this.saveStartPosition([res.latitude, res.longitude]) } })

mapMutations


methods: { ...mapMutations({ saveCurNavIndex: 'SET_CUR_NAV_INDEX', saveStartPlace: 'SET_START_PLACE', saveFormattedStartPlace: 'SET_FORMATTED_START_PLACE', saveCurCity: 'SET_CUR_CITY', saveStartPosition: 'SET_START_POSITION', saveCost: 'SET_COST' }) }

其中reverseGeocoder()就是一个位置转换为地址的函数,是对qqmapsdk.reverseGeocoder()进行了一次封装


function reverseGeocoder(qqmapsdk, {latitude, longitude}) { return new Promise((resolve, reject) => { qqmapsdk.reverseGeocoder({ location: { latitude: latitude, longitude: longitude, }, success: (res) => resolve(res), fail: (res) => reject(res) }) }) }

这样当我们进入index首页的时,就可以在Console中就看到数据成功保存到vuex里

基于mpvue的仿滴滴出行小程序

选择出发点

基于mpvue的仿滴滴出行小程序

在mpvue中使用map组件时会有一些坑,这里先缓一缓,坑稍后再说。

地图map


<map class="map-didi" id="map-didi" :latitude="latitude" :longitude="longitude" :markers="markers" @regionchange="regionChange" @begin="begin" @end="end" show-location > ... </map>

初始化地图时将地图中心移动至startPosition,如果startPosition不存在,就将地图中心移动至wx.getLocation()获取的当前位置坐标


initLocation(){ if (this.startPosition.length) { this.latitude = this.startPosition[0] this.longitude = this.startPosition[1] } else { wx.getLocation({ type: "gcj02", success: (res) => { this.longitude = res.longitude this.latitude = res.latitude } }) } }

采用随机数据模拟附近的车,然后添加到this.markers中,车的图标根据curNavIndex动态设置,这样就可以在选择不同的服务时展示不同的车图标

   this.markers = []
        const carNum = getRandomNum(3, 8)
        for (let i = 1; i <= carNum; i++) {
          // 定义一个车对象
          let car = {
            id: 0,
            iconPath: "/static/img/car/cart1.png",
            latitude: 0,
            longitude: 0,
            width: 35,
            height: 15
          }

          //随机值
          const lon_dis = (Math.ceil(Math.random() * 99)) * 0.00012;
          const lat_dis = (Math.ceil(Math.random() * 99)) * 0.00012;

          car.id = 2 + i
          car.latitude = this.latitude + lat_dis
          car.longitude = this.longitude + lon_dis
          car.iconPath = `/static/img/car/cart${this.curNavIndex + 1}.png`
          this.markers.push(car)
        }

地图中心的红色定位图标以及接驾时间的文字是用cover-view包裹cover-image实现


<cover-view class="center-marker"> <cover-view class="text-center">最快{{minutes}}分钟接驾</cover-view> <cover-image class="inverted-triangle" src="/static/img/triangle-down.png"></cover-image> <cover-image class="img-center" src="/static/img/marker2.png"></cover-image> </cover-view>

其中inverted-triangle是一个倒三角形图片,因为cover-view无法实现复杂css样式,所以底部的倒三角形效果只能用图片实现。

map这里不推荐使用controls,官方也说明 controls即将废弃,请使用 cover-view

选择目的地

基于mpvue的仿滴滴出行小程序

这里首先获取到state中的curCity,利用qqmapsdk.getSuggestion(),并将其参数region设置为curCity, 就可以进行地址模糊检索。选中地址时,利用qqmapsdk.geocoder()进行地址解析,得到目的地的相关数据,再将数据通过mapMutations存入state中


computed: { ...mapState([ 'curCity' ]) }

模糊检索


qqmapsdk.getSuggestion({ keyword: value, region: this.curCity, success: (res) => { this.addresses = res.data } })

点击地址时,解析地址保存数据


choosePlace(item){ //item.address详细地址 //item.title简短语义化地址 console.log(item) qqmapsdk.geocoder({ address: item.address, success: (res) => { this.saveEndPosition([res.result.location.lat, res.result.location.lng]) this.saveDestination(item.title) this.goBack() }, fail: (err) => { console.log(err) } }) }

mapMutations


methods: { ...mapMutations({ saveDestination: 'SET_DESTINATION', saveEndPosition: 'SET_END_POSITION' }) }

选择城市

基于mpvue的仿滴滴出行小程序

这里的样式是按照现在的滴滴小程序实现,只要将选中的城市保存在state中的curCity就好了,搜索功能暂未开发。获取城市列表数据用到了腾讯地图的api中的qqmapsdk.getCityList()。这里其实就是数据的过滤与处理,先初始化了一个空对象temp_citys,然后根据城市的拼音的首字母的大写建立key,对应value为一个数组,数组里面包含所有以这个拼音字母开头的城市,最后将temp_citys赋值给this.cityList


qqmapsdk.getCityList({ success: (res) => { const result = res.result[1] let temp_citys = {} //使用temp_citys 避免频繁改动data里面的数据 for (let i = 0; i < result.length; i++) { let key = result[i].pinyin[0].charAt(0).toLocaleUpperCase() if (!temp_citys[key]) { temp_citys[key] = [] } temp_citys[key].push(result[i].fullname) } this.cityList = temp_citys } })

其他的一些页面就不提了,感兴趣的小伙伴可以去看下源码

使用mpvue的一些好处

可以使用vuex

使用vuex进行状态管理,可以更方便地构建复杂应用。这里讲一个调试小技巧,使用createLogger(),使用之后就可以在Console中清楚地看到state的变化

在store下的index.js


import Vue from 'vue' import Vuex from 'vuex' import state from './state' import mutations from './mutations' import createLogger from 'vuex/dist/logger' Vue.use(Vuex) const debug = process.env.NODE_ENV !== 'production' export default new Vuex.Store({ state, mutations, strict: debug, plugins: debug ? [createLogger()] : [] })

使用vuex时要记得在对应页面的main.js引入store,并将store赋给Vue.prototype.$store

例如:


import Vue from 'vue' import App from './wait.vue' import store from '../../store/index' Vue.prototype.$store = store const app = new Vue(App) app.$mount()

组件化开发

使用mpvue组件化开发更加方便,也方便将组件移植到其他项目中,完整的Vue开发体验,提高了代码复用性。

例如 这里的search-bar:


<template> <div class="search-bar"> <div class="text-location" @click.stop="chooseCity">{{curCity}}</div> <input type="text" v-model="search" class="input-location" placeholder="你在哪儿上车" placeholder-style="color:#cccccc"> <div class="cancel-location" @click.stop="cancel">取消</div> </div> </template> <script type="text/ecmascript-6"> import {debounce} from '../utils/index' export default{ props: { curCity: { type: String, default: '暂无' } }, data(){ return { search: '' } }, methods: { cancel(){ this.$emit('cancel') }, clear(){ this.search = '' }, chooseCity(){ this.$emit('chooseCity') } }, watch: { search(newVal){ debounce(() => { this.$emit('search', newVal) }, 500)() } } } </script>

这里为了节流处理,引入了debounce()函数

可以使用Async/await

原生小程序已经支持Promise,但对于async/await还不支持,利用mpvue框架我们可以封装一些异步函数,避免回调地狱。

例如:网络请求

JS中文网 – 前端进阶资源教程 www.javascriptC.com
一个致力于帮助开发者用代码改变世界为使命的平台,每天都可以在这里找到技术世界的头条内容


export function request(url, method = 'GET', data, header = {}) { return new Promise((resolve, reject) => { wx.showLoading({title: '玩命加载中...'}) wx.request({ url: baseUrl + url, method, data, header: {'Content-Type': 'json'}, success: function (res) { if (res.statusCode === 200) { resolve(res.data) } else { showToast('发生未知错误!') reject(res.data) } }, fail: function () { showToast('获取数据失败!') }, complete:function () { wx.hideLoading() } }) }) }

async getInitData(){ const res = await request('/comments') ... }

使用mpvue的一些坑

年轻人比较冲动,愣头青,说多了都是眼泪,官方文档一定要好好看,首先提一下常规的一些坑。

嵌套列表渲染

只是需要注意一点,嵌套列表渲染,必须指定不同的索引!

示例:


<!-- 在这种嵌套循环的时候, index 和 itemIndex 这种索引是必须指定,且别名不能相同,正确的写法如下 --> <template> <ul v-for="(card, index) in list"> <li v-for="(item, itemIndex) in card"> {{item.value}} </li>,JS中文网 - 前端进阶资源教程 www.javascriptc.com </ul> </template>

regionchange

bindregionchange 事件直接在 dom 上将bind改为@regionchange,同时这个事件也非常特殊,它的 event type 有 begin 和 end 两个,导致我们无法在handleProxy 中区分到底是什么事件,所以你在监听此类事件的时候要同时监听事件名和事件类型


<map @regionchange="functionName" @end="functionName" @begin="functionName"> <map>

事件触发问题

举个简单的例子,slider组件有一个bindchange属性,它是完成一次拖动后触发的事件,那么如果我们想取对应的值该怎么操作。

在小程序中我们使用: event.detail

但在 mpvue中要这样写: event.mp.detail

map闪动

动态更新markers时,地图会闪动,导致无法移动地图,这个可是一个大坑

地图组件bindregionchange的bug:

https://github.com/Meituan-Dianping/mpvue/issues/401

JS中文网 – 前端进阶资源教程 www.javascriptC.com
一个致力于帮助开发者用代码改变世界为使命的平台,每天都可以在这里找到技术世界的头条内容

原因:mpvue在更新某个属性时都会更新整个data, 在数据量比较大的情况下效率低下,而且频繁改动data里面的数据也会导致卡顿问题

解决方案:每次更新数据时使用脏检查优化

github.com/Meituan-Dia…

但是个人觉得这种直接改源码的方式还是比较妖怪的,于是找到了另一种办法


<map class="map-didi" id="map-didi" @regionchange="regionChange" @begin="begin" @end="end" > </map>

let touchTimeStamp = 0 regionChange(){ }, begin({timeStamp}){ touchTimeStamp = timeStamp }, end({timeStamp}){ // 加入时间判断,,JS中文网 - 前端进阶资源教程 www.javascriptc.com if (timeStamp - touchTimeStamp > 50) { this.mapCtx.getCenterLocation({ success: (res) => { reverseGeocoder(qqmapsdk, res).then(res => { this.saveStartPlace(res.result.address) this.saveFormattedStartPlace(res.result.formatted_addresses.recommend) }) const lon_distance = res.longitude - this.longitude const lat_distance = res.latitude - this.latitude // 更新当前位置坐标 this.longitude = res.longitude this.latitude = res.latitude //判断屏幕移动的距离,如果超过阀值 if (Math.abs(lon_distance) >= 0.0022 || Math.abs(lat_distance) >= 0.0022) { //刷新附近的车 this.updateCars() //刷新等待时间 this.minutes = getRandomNum(3, 12) } } }) } }

为了防止map不断地触发begin,end事件导致data频繁更新,这里做了双重判断,当end事件的触发时间减去start事件的触发时间超过一个设定的时间,当中心点移动的距离超过一个阀值,我们才去更新data数据,这里其实相当于做了节流处理。

基于mpvue的仿滴滴出行小程序

小程序的一些坑

常规的坑就不提了,这里说一下奇葩的坑。

cover-view的坑

cover-view覆盖在原生组件之上的文本视图,可覆盖的原生组件包括map、video、canvas、camera、live-player、live-pusher,只支持嵌套cover-view、cover-image。

只支持基本的定位、布局、文本样式。不支持设置单边的border、background-image、shadow、overflow: visible等

那如果我们想在cover-view里实现单边的border应该怎么做?

基于mpvue的仿滴滴出行小程序

可以在cover-view里再增加一个宽度1px的cover-view来模拟单边border


<cover-view class="footer-bar"> <cover-view class="text" @click.stop="cancel">取消订单 </cover-view>,JS中文网 - 前端进阶资源教程 www.javascriptc.com <cover-view class="right-border"></cover-view> <cover-view class="text" @click.stop="endTrip">结束行程 </cover-view> <cover-view class="right-border"></cover-view> <cover-view class="text">下载滴滴APP</cover-view> </cover-view>

JS中文网 – 前端进阶资源教程 www.javascriptC.com
一个致力于帮助开发者用代码改变世界为使命的平台,每天都可以在这里找到技术世界的头条内容


.footer-bar { padding: 0 12px; display: flex; align-items: center; height: 44px; color: rgba(0, 0, 0, .7); background: #fff; font-size: 0; .text { flex: 1 1 auto; display: inline-block; height: 22px; line-height: 22px; text-align: center; font-size: 18px; } .right-border { flex: 0 0 1px; height: 22px; width: 1px; background-color: #d9d9d9; } }

map组件的层级最高,如何在map组件上做出阴影效果呢?

实现方式其实也是类似,利用cover-image添加一张能覆盖在map组件之上的图片来模拟阴影

具体实现请看这篇文章: juejin.im/post/5b1a10…

项目地址

github.com/QinZhen001/…

欢迎小伙伴来一起交流学习,喜欢项目的话请给颗小星星

友情链接

滴滴一夏, 小程序专车来了  https://juejin.im/post/5b15ce94f265da6e29010554

网络请求request的坑  www.cnblogs.com/huangenai/p…

mpvue + vuex 开发微信小程序 mapState、mapGetters不可用问题

blog.csdn.net/wp_boom/art…

作者:渣臻不想说话
链接:https://juejin.im/post/5b3a1354e51d45559b6b658c

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「IT平头哥联盟」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:基于mpvue的仿滴滴出行小程序

链接:https://www.javascriptc.com/2510.html

« 听说你要学小程序?这份书单送你了
两周撸一个掘金微信小程序»
Flutter 中文教程资源

相关推荐

QR code