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

咱們以前的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背景色。
子菜單還不支持。

 

新開了個小程序技術交流羣,有什麼疑問能夠加到羣裏提問:

相關文章
相關標籤/搜索