mpVue高仿美团小程序教程
本章涉及知识点
1、前言
2、mpVue框架简介
3、项目配置和结构
4、主程序入口配置
5、首页页面
6、城市选择页面
7、搜索页面
8、订单列表页面
9、我的页面
10、商品详情页面
11、评论列表页面
12、提交订单页面
13、订单详情页
14、通用方法封装
15、Vuex的设计
16、后记
一、前言
继写完上一篇原生JS实现轻量Vue+指令解析+变异数组监听的代码和文章后,本来笔者已经写完了原生JS实现轻量React的代码,而正在写其文章的时候,突然被mpVue打断,因为从前自己一直不喜欢小程序开发,依赖微信平台不说,微信还重新封装了一遍其语法糖,但是mpVue的开源让开发者可以用原生Vue来开发小程序,其实其本质任然是将Vue编译成原生小程序的AST,最后再翻译成原生webView代码,于是笔者暂停下了写React的文章转而用mpVue先开发个小程序
既然是美团开源的框架,那我们就高仿一个美团呗!于是就写了这篇文章教程,笔者花了3天时间,用mpVue框架+自定义Vue组件+原生小程序组件+Vuex开发出了一个高仿美团的小程序,相似度达到90%以上,体验程度还是非常流畅的,毕竟翻译到最后是原生App代码
二、mpVue框架简介
我们知道微信小程序有着自己的一套语法糖,包括自己的标签,样式规范和语法,本身也没有组件化机制(表示难以接受),其推出的Wepy也是类Vue的开发规范,用着也是别扭,为此,mpVue是完全基于Vue的开发规范,包括生命周期,且完全支持Vue自定义组件规范,对于Vue开发者来说,可以无缝对接的开发小程序
关于mpVue更多的功能,可以参考其官网:mpVue官网
三、项目配置和结构

可以看到mpVue的项目目录结构非常熟悉,下面我们开始开发各个页面
四、主程序入口配置

我们在项目入口里引入公共css、公共方法、以及store,并将公共方法挂载到Vue的原型上

根据小程序app.json的配置,我们配置好所有页page、window以及tabBar
五、首页页面
首页有轮播图,图标九宫格,猜你喜欢的商品列表,还有当前城市和搜索面板,其分别是选择城市页面和搜索页面页面的入口
首页页面的页面布局代码为



其中我们提取封装出商品卡片组件good

注意我们注入store中的当前城市变量,让其不被跳转影响

最终首页在真机上显示如下

六、城市选择页面
城市选择页面有当前/最近访问城市、热门城市、根据首字母展示所有城市,还有右侧固定住的26个首字母作为点击事件,可以滑动到相应的首字母下的城市列表
我们封装出city组件,其页面布局代码为


我们引入cityData,并在点击任意城市的时候emit给父组件(即城市选择页面)
对于点击首字母滑动页面到相应首字母对应城市的位置的处理,由于小程序没有任何dom,我们不能使用锚点或者window.scrollTo,但是我们可以利用原生小程序提供的scroll-view中scroll-into-view属性来滚动到相应位置,为此我们只需要用Vue来托管scroll-into-view变量currView即可

最终城市选择页面在真机上显示如下

七、搜索页面
搜索页面有搜索输入框,猜你想找和历史搜索的面板,对于动态监听到输入的搜索关键词,以滚动列表的形式展示符合搜索关键词的结果集合,所以猜你想找和历史搜索的面板,与搜索结果列表为互斥关系
搜索页面的页面布局代码为


对于模拟搜索动态列表,我们在data里定义搜索总集合、当前搜索关键词和根据当前搜索关键词匹配到的结果列表

我们需要监听input的输入事件和完成事件,来维护当前搜索关键词和动态过滤符合搜索关键词的结果集

最终搜索页面在真机上显示如下

八、订单列表页面
订单列表页面比较简单了,只有商品列表的滚动展示
这里为了演示滚动效果,我模拟循环了12次常量数据,订单列表页面的页面布局代码为

最终订单列表页面在真机上显示如下

九、我的页面
我的页面有登录、退出、以及4个入口项,在点击登录后动态获取用户信息,渲染登录按钮为用户的头像、昵称、性别和居住地信息,点击退出按钮后清除用户信息并替换为登录按钮
我的页面的页面布局代码为


我们引入store来在用户点击登录或者退出的时候,动态维护用户登录状态和用户信息

最终我的页面在真机上显示如下

十、商品详情页面
商品详情页面有商品主图价格信息、映像标签、商家信息、套餐信息、购买须知、部分用户评论和相关商品推荐板块,页面里有两个其它页的入口,分别是提交订单和用户评论列表入口
商品详情页面的页面布局代码为





这个页面里用到了卡片组件good,打星评分组件star和评论组件comment,good组件首页已经封装好,下面我们封装出star和comment组件
下面是star组件的布局代码

注意:由于小程序没有伪类,为此我们利用absolute来动态渲染星星的颜色
为此需要动态的计算每一个星星染色宽度,100表示完全染色,30表示1/3染色

下面是comment组件的布局代码

最终商品详情页面在真机上显示如下

十一、评论列表页面
这个页面是通过商品详情页为入口进入,其评论分为全部、晒图评论、低分评论和最新评论,各个评论的通过swiper滑动切换来更新相应的评论列表
评论列表页面的页面布局为



为了演示不同类型评论的切换,我们定义好全集评论列表,并过滤计算出不同类型的数组用于渲染不同的列表

最终评论列表页面在真机上显示如下

十二、提交订单页面
这个页面主要有数量的动态增加或者减少,以及价格的动态变化
提交订单页面的页面布局为


由于数量需要动态增减,我们封装出计数组件counter
counter组件的布局为

增减数量后emit给父组件即可

最终订单提交页面在真机上显示如下

十三、订单详情页面
订单详情页面由订单列表页进入,主要就是商家信息和订单信息的展示


最终订单详情页面在真机上显示如下

十四、通用方法封装
开发完所有页面后,我们来封装一些通用函数,方便挂载到Vue原型上在任何页面使用
首先是Ajax封装,由于微信提供的request是类似Jquery的语法里带上各个回调函数,这让我们使用习惯Promise或者es7的async/await的异步编程风格后显得非常不舒服,所以我们将request封装成Promise风格,让我们可以在任何页面通过async/await来接收服务器的返回值
封装Ajax为Promise风格的函数如下

下面我们简单的封装一些常用的方法,如打开新窗口,返回上一级窗口和重定向

最后在主程序入口里挂载即可

十五、Vuex的设计
我们在store里设置三个状态,分别为当前城市、用户是否登录标志和用户信息集合,并在mutations里提供更新维护这三个状态的方法接口即可


最后将store挂载到Vue的原型上即可

十六、后记
这个案例,通过使用mpVue框架模仿一个美团小程序,业务上模拟了从商品列表,到商品详情,到评论列表,到订单提交,到订单列表,到订单详情,以及城市选择、全局搜索过滤,以及我的页面的登录等,可以看到用Vue模板引擎框架和原生JS编程,能够非常轻易的做出一个体验非常流畅的小程序
由于案例只是做纯前端的渲染逻辑展示,所以数据全集都是写死的,并没有Ajax服务器数据,不过开发者应该都知道,前后端分离,只要将所有页面data里的数据通过封装好的Ajax拉取到服务器数据后渲染即可
案例代码见:高仿美团小程序