【微信小程序开发•系列文章五】主界面

咱们以前的demo中,页面都是很简单的一个主页面的结构。没有底部菜单,没在乎页面的结构。而咱们平时接触的app都有各类各样的页面交织组合在一块儿,因此这一篇文章中,咱们开始来关注咱们的页面结构。javascript

先来看看咱们本文相关的demo,地址:https://github.com/jsongo/weApp-frame,三个页面:css

为了展现页面的主框架,这里作了三种风格的页面。第一个是个图片列表,从花瓣上抓的。第二个是个外卖的页面。第三个是我的中心页面。html

1、页面功能简述前端

一、花瓣图片列表
用两列的瀑布流来展现,在顶部向下拉能够加载更多,滑动到底部能够加载更多图片。在网络请求数据的时候,会出现一个浮层,展现加载中的loading图标,顶部的title旁边也会出来加载中图标,等数据加载完,这两个图标都会消失。
这个页面还有个子页面,图片查看页,日后能够滑动到下一张,往前能够滑动到前一张。java

列表数据来源:分析了下花瓣的网站,随便拿了个数据列表接口,经过传入不一样参数来取得分页数据。
本页主要介绍:列表的排布、加载中图标的实现方式、下拉刷新的实现方式、滑动到底部加载更多的实现、navigator及参数传递的实现、引用传递的实现。git

二、外卖订餐页面
页面顶部是个轮播图,几个图片轮流播放。下半部分是两个滑动的列表,菜品分类列表,和菜品详细列表,它们能够分开滑动。
本页主要介绍:轮播图组件、页面分区及各自列表滑动。es6

三、我的中心页面
上半部分是我的信息,下方是菜单列表。退出按钮点击后,能够从下方弹出菜单。
本页主要介绍:模态窗口、下文弹出菜单列表、toastgithub

另外,还有下方tab菜单的实现方式。算法

2、图片列表的实现json

一、底部加载更多

用户滑动到底部的时候,列表会自动加载下一页。
实现也比较简单,在scroll-view标签中的bindscrolltolower绑定个事件响应函数,当滑动到底部时,这个事件就会被触发。
不过这里有个问题要注意下,scroll-view要设置一个高度才行,要否则常常会滑到底部没反应,没触发。获取窗口的高度,把它设置给scroll-view就能够了:

wx.getSystemInfo({
    success: ( res ) => { // 用这种方法调用,this指向Page
        this.setData({
            winH: res.windowHeight
        });
    }
});

在界面中,把winH设置给scroll-view的height

<scroll-view …(省略其它属性)... style="height: {{winH}}px;">

这样scroll-view才能正常在滑动到底部的时候触发bindscrolltolower事件。加载完数据后,能够用以前咱们在《【微信小程序开发•系列文章三】数据层》里讲过方法来追加数据。下面咱们再介绍一种方法来追加数据。
追加的时候,咱们的主要目的是保证data里的items数组的地址不变。因此其实能够直接拿到这个items数组的引用,而后给它push数据,方法以下:

var arr = this.data.items;
arr.push(…newList); // ...三个点是用了es6的解包的语法
this.setData({items: arr}); // 必定要记得setData

这个方法比较hack,先用arr变量来保存this.data.items的引用,push操做不会改变原数组的地址,push完以后,还没更新界面,这时必定要记得再调用一下setData方法,用它来触发界面更新。因此这里,咱们实际上是把setData固然是UI update的方法来用。亲测了一下,整个列表没看到有全局刷新的问题。

二、下拉刷新

微信小程序提供了下拉刷新的样式。这个咱们能修改的很少,只能配置内容和简单的修改几个地方的显示。
(1)配置
咱们的程序中有一个app.json,在“window” 这一项里添加 enablePullDownRefresh: true ,小程序的页面在顶部下拉的时候,就会出现下拉刷新的样式。

(2)事件
当下拉被触发的时候,MINA框架会去找当前页面的page里是否有onPullDownRefresh这个事件响应函数,若是有就会调用。
因此这里在实现下拉刷新的时候,就要定义这个函数,而后在里面去作刷新。

(3)刷新的思路
当前列表数据可能不是最新的,若是这时从新去拉一次第一页的数据,可能会出现前面几项不在当前列表里,这些就是要刷新进来的数据。思路就是去比对id,先取原列表中第一个id,若是在列表里找到这个id的项,则中止,把这个项以前的数据都追回到列表里。可是若是在新加载的第一页中没找到这个id,说明此次加载的数量还不够,还要再加载一次。为了不出bug时,或数据出问题的时候,无限加载的问题,咱们设置一个MAX_PREPEND来限制最多加载的页数。

咱们用数字来表明列表里的一项,简化下模型,让读者更容易理解。
好比咱们第一次加载的列表是[0,1,2,3,4,5,6,7,8,9] 这10项(一次加载10项),第一个id是0。过了5个小时,列表数据有更新了,这时用户下拉刷新,咱们拉到了[-28,-27,-26,-25,-24,-23,-22,-21,-20,-19] 这10项,这时咱们发现,咱们此次加载的新数据没有第一个id,即0,因此中间确定还有一些项咱们要再加载的。因此咱们发了第二个请求,此次拉到了[-18,-17,-16,-15,-14,-13,-12,-11,-10,-9],这里又没有出现0,因此发起第三个请求去拉数据,这时咱们拿到了[-8,-7,-6,-5,-4,-3,-2,-1,0,1],此次0这个id出现了。因此咱们把0前面的8项也全都加入到新列表中。为了安全,咱们设置了MAX_PREPEND,好比为3,表示最多发三个请求,若是这中个请求尚未拉到0这个id,那咱们也再也不发请求了。怕出现无限循环加载的状况,出现bug的时候可能会出现,或者后台把0这条数据删除了,后面这几回请求返回的列表里都不可能出现0,这时也会出现无限加载的状况。

固然上面这个逻辑仍是有bug,假如真出现了后台把0这个项删除掉的时候,那咱们加载的数据就可能会出现数据重复的现象。因此真正产品化的时候,若是有可能删除数据的状况,最好仍是不要用拿第一个id的去对比的方法,由于可能会出现重复。这时要使用另外一种算法,即,能够把全部的id都存到一个数组里,新数据拉回来的时候,一个个拿去数组里对比,没在数组中的就加载到界面中,有的话就忽略。而后出现所有项都在这个数组中的时候,就中止加载。有点复杂。

其实若是把这个逻辑放到后台去作,会简单不少,咱们让id有序,有大小能够比较,或者用时间戳的方式来判断,前端作有点复杂。咱们这个demo中能拿到的数据id也无序,时间也无序,状况远比上面的数字复杂不少,因此没办法只能暂时这么作。读者若是是本身的团队作后台的话,必定要把返回的数据按必定的规则有序化。

如今不少产品化的广泛作法是另外一种思路,就是在刷新的时候,都只拉一页,若是还没所有拉取完,则在新页跟原列表数据中间,显示一个相似“查看更多”等的提示,点击的时候,再去拉取第二页、第三页等等。它的实现相对上面的思路会简单一些。

接下去讨论数据若是插入到界面中列表的前端去。
前面讨论追加列表数据的时候,说到了一种方法,取数组的引用,在保证数组地址不变的状况下,去往里面添加数据。push是一个不会改变地址的操做,不过是往数组后面添加的,那么要往前端插入,就要用unshift,它也会在保证数组地址不变的状况下,在前面添加新数据。

(4)中止刷新的样式
当咱们加载完数据,刷新完的时候,要把微信帮咱们作的加载图标隐藏掉,它本身不会正好在咱们加载完数据的时候隐藏,虽然它有一个默认的隐藏时间。
框架给咱们提供了一个wx.stopPullDownRefresh方法,咱们在加载完数据,render完时,调用一下它就能够了。

(5)数据请求
关于数据请求,这里补充一点。
花瓣对请求作了点限制,直接请求地址会返回一个页面,须要加两个头部才行:

X-Request: JSON
X-Requested-With: XMLHttpRequest

而第二个头部,X-Requested-With,框架默认已经帮咱们加了。咱们只要在请求的时候,加上一个第一个header就好了。
另外,小程序开发工具一开始的时候,有一个头部重复的问题,不过从v0.10.101000开始,就修复了。

三、图片列表
图片列表其实就是传统网页的布局,没有用到flex,只是让元素浮动float,而后限制一下宽度和高度,它天然就排成两行了。有兴趣的读者能够尝试用flex或table去实现一下。

四、图片播放的实现

官方其实提供了一个图片预览的api,能够用来实现图片的播放,实现也很简单:

wx.previewImage({
    current: '', // 当前显示图片的http连接
    urls: [ ] // 须要预览的图片http连接列表
});

在urls里传入图片列表就能够了,不过这个功能比较简单,无法定制,好比想在图片播放的时候,能显示些图片说明文字,这个功能就无法作到了。因此下面咱们来讨论下如何实现本身定义图片播放的功能。

(1)点击跳转
列表里的每一项都加了navigator组件包裹着,它就至关于<a>标签,用来作跳转。
不过要注意的一点是,navigator的宽高都被框架设置成auto,因此在wxss里设置宽高没有用,但在元素里内嵌样式是能够的,这里咱们换种作法。在navigator里放个view把它撑开,给它固定个宽高就能够了,这时对navigator里面元素的点击,事件都会冒泡到navigator元素上,就会触发它的页面跳转。

(2)播放哪张图?
很简单,在跳转的时候,给跳转的地址加个参数就能够了,参数带上点击的这张图在数组中的index。那么问题来了,在跳过去的页面怎么取得这个参数?
微信小程序官方文档里没有特别提到onLoad里,其实还能够传入一个参数options,它用来接收跳转时地址后面带的参数。这个就很是方便咱们使用了。好比地址是 ../pic/pic?index=1,那么这里,咱们用options.index就能够拿到1这个数据了。
onLoad拿到index数据后,setData来修改并指定当前图片列表播放这个index的图片就好了。

(3)播放的图片数据来源
播放图片,咱们不是一张一张来显示的,由于若是只播放一张的话,用户在向左或向右滑动的时候,咱们得去找这张图片的上一张图或下一张图来显示,这样作就比较复杂了。
因此咱们在界面上放一个swiper组件,把当前列表里的全部图片所有设置到里面去。上面咱们拿到的index,在这里就可使用,swiper组件有个current属性用来指定当前显示哪一张图。
这个列表数据,咱们不须要从index页面从新拷贝一份过来。数组,其实存一个引用就好了,这样的两个好处是:1节约内存,2更新的时候更新一个地方的数据就行。实现方式是,在index的onLoad方法里,把this.data.items的引用存到app的globalData里,再在图片播放页的onLoad方法里从app取出这个引用,把它设置给这个页面的data里就行。当前为了界面及时更新,咱们把这个操做放到播放页面的onShow里好些,这样,天天切换到播放页来查看图片的时候,都会去更新图片列表,保证数据是最新的。

(4)播放页界面的实现
先加一个view组件,设置成整个页面大小,背景设置成顶部bar的颜色,这样看起来比较像一个总体。
上面提到,咱们的图片是显示在swiper组件里的,由于这是一个可左右滑动的组件,不用咱们本身去作滑动的事件处理。不过这个组件有个问题,它的高度也是在框架中固定死的,固定成150px,咱们在wxss里改变它的值也没有做用。还好,在标签里内嵌样式能够把框架中定义的样式给覆盖掉。那么为了让图片垂直居中,咱们第一想到的多是,取到图片高度,把它设置给swiper的内嵌样式的height里,再用margin-top等方式让它居中。这里介绍另外一种方式让图片居中。

当父元素被设置成 display:flex 时,加上几个center就可让其内部元素天然水平和垂直居中:

display:flex;
justify-content:center;
align-items: center;
text-align: center;

因此咱们也把swiper组件的高度设置成整个窗口的高度,再用上面的方法把里的image元素天然居中就能够了。image元素的mode设置成aspectFit,它会保持原有的宽高比例,image只有这个mode值是保持图片完整的。更多mode属性能够看官方文档
另外,图片播放页面是第二层弹出页面,没有底部菜单,这样就能够全屏。

五、loading样式的实现
微信小程序提供了两种loading的样式,一种是在顶部bar的标题旁边显示转动的图标,另外一种是用浮层的形式在页面中间显示一个加载图标。
第一种的实现也比较简单
显示:

wx.showNavigationBarLoading

隐藏:

wx.hideNavigationBarLoading

第二种也只须要调用一个函数就能够了
代码:

wx.showToast({
    title: '加载中...',
    icon: 'loading',
    duration: 10000
});
3、外卖页面的实现

一、页面布局
外卖页面基本能够分为三部分,顶部轮播图组件、下半部分是两个横排的滑动列表,他们能够各自滑动。
轮播组件可查看官方文档介绍。几个关键的参数,autoplay设置是否自动播放,current设置当前播放第几张,indicator-dots设置是否显示面板上的指示点。在上面的图片播放页中,咱们把autoplay和indicator-dots都设置成false。注意这些true/false都要写成 false 的形式。
在这个页面中,咱们模仿美团外卖中的首页,自动轮播顶部的广告图片。除了autoplay和indicator-dots设置成true外,还能够修改interval参数来设置等多久再播放下一个,修改duration来设置滑动的时长。

二、页面分区
主要是用wxss来控制,这里依然用flex简单的布局方式,按1:5的比例来放置两边的滚动区域。
左边的列表,给每一个项都添加一个事件,每次点击某个项的时候,它的底色要变。这里咱们经过控制class来改变样式,在class里添加:

{{item.id==chosenType?'selected':''}}

当选中的项的id为choseType时,class就添加selected,这样,咱们就能够经过choseType这个变量来改变列表选中的项的样式。
那怎么实现两个列表数据的联动呢?
咱们在每一个项中也埋下了data-id,点击的时候就知道当前项的id,再拿它去请求相关的列表数据,从而能够拿到右侧的餐品列表数据,再更新到右侧列表上去就能够了。
每一个列表都是独立的,能够各自设置scroll-y让他们各自滚动,互不影响。

4、我的中心页面的实现

总体布局其实就是wxss完成的,也没什么可讲。这一部分主要讨论:模态窗口、下文弹出菜单列表、toast的实现。

一、模态窗口
其实也就是咱们网页中的alert、confirm这些弹出框。当他们弹出来的时候,后面的窗口是不能够操做的,这类窗口称为模态窗口。
小程序框架提供了一个wx.showModal,咱们能够用它来实现alert / confirm / prompt。

wx.showModal({
    title: '关于',
    content: '这是一个演示程序,不要在乎这些细节'
});

先看几个参数:
title设置上面的标题
content就是对话框中部灰色的内容

(1)alert框
添加一个字段:showCancel,设置成false就能够了

(2)confirm框
默认的就是confirm的样式,能够用cancelText字段设置取消文本,cancelColor设置它的字体颜色,confirmText设置确认文本,confirmColor设置它的字体颜色。

(3)prompt弹出输入框
modal框中间的内容是容许插入wxml标签的,因此咱们若是插入一个input组件,它天然就成了prompt弹出框,若是须要用户输入的时候,能够用这个办法弹出。 
用success获得用户点击了哪一个按钮,若是是确认按钮的时候,success回调里经过参数拿到:

success: function(res) {
    if (res.confirm === 1) {
        console.log('用户点击了确认按钮');
    }
    else {
        console.log('用户点击了取消按钮');
    }
}

(4)modal的显示与隐藏
modal弹出框默认的就能够经过“取消”按钮来隐藏,暂尚未提供其它方式。

二、toast

默认样式,上面有个打钩图标,目前微信没有提供定制的属性。若是要显示打叉x的图标,恐怕还无法用。期待后面会更新。

wx.showToast({
    title: '注销成功!',
    icon: 'success',
    duration: 3000
});

咱们demo中用toast来提示注销成功。

三、action-sheet
action-sheet就是下文往上弹出来的小菜单,实现方式也比较简单:

wx.showActionSheet({
    itemList: ['肯定注销'],
    success: (res)=> {
        if (!res.cancel) {
            if (res.tapIndex == 0) { // 肯定注销
                this.doLogout();
            }
        }
    }
});

itemList用来设置上面的菜单列表,从左往右,从0开始编号,从上往下显示,最下页的是取消按钮,它不用在itemList里面写,默认会有。也能够经过cancel这个字段来去掉空上取消按钮。
取消按钮默认会有点击事件,点击会把整个actionSheet隐藏,点击半透明灰黑的地方也能够把它隐藏,这些都是这个组件默认帮咱们作好的。
组件里success用来作点击的事件响应。若是用户点击取消按钮的话,res.cancel就为true,点击其它按钮则会经过res.tapIndex通知咱们 用户点击的是哪一个编号的按钮,从而咱们能够根据它来作出相应的响应。

5、其它

一、各页面标题
每一个页面有不一样的标题。在用v0.10.101400版本开发的时候,测试wx.setNavigationBarTitle方法设置的标题没有做用,会被改回去。不过v0.10.102800版本修复了这个问题。
若是wx.setNavigationBarTitle方法失效的话,还有一个办法能够改标题,在xxx.json里设置新的title就能够把原来的覆盖。
demo代码中,除index页面外,其它页面都有一个xxx.json文件,内容大体以下:

{
    "navigationBarTitleText": "图片详情",
    "enablePullDownRefresh": false
}

第一个设置标题,第二个enablePullDownRefresh的做用是,在不须要下拉刷新的页面中,覆盖window中的这个参数,让它下拉不了。
这个方法还能够用来覆盖其它参数,如页面背景,字体颜色等等。

二、demo中的下文菜单
这个是在app.json里设置的,跟window并列,添加一个tabBar:

"tabBar": {
      "list": [{
        "pagePath": "pages/index/index",
        "iconPath": "res/img/tab/home-off.png",
        "text": "首页",
        "selectedIconPath": "res/img/tab/home-on.png"
      }, {
        "pagePath": "pages/waimai/waimai",
        "iconPath": "res/img/tab/wm-off.png",
        "text": "外卖",
        "selectedIconPath": "res/img/tab/wm-on.png"
      }, {
        "pagePath": "pages/my/my",
        "iconPath": "res/img/tab/my-off.png",
        "text": "个人",
        "selectedIconPath": "res/img/tab/my-on.png"
      }],
      "color":"#174578",
      "selectdColor":"#3cc51f",
      "borderStyle":"white",
      "backgroundColor":"#116fb6"
  },

list定义tab信息,只能配置最少2个、最多5个。每一个tab能够用pagePath指定页面,用iconPath指定选中前的图标,selectedIconPath指定选中后的图标,text指定tab名称。
样式的设置,跟list并列,用color设置文字颜色,用selectdColor设置选中时的文字颜色,borderStyle设置顶部边框颜色,并且只支持black/white,backgroundColor设置tab背景色。
子菜单还不支持。

 

新开了个小程序技术交流群,有什么疑问能够加到群里提问:

相关文章
相关标签/搜索