服務項目
品牌網站建設

數字營銷

係統平台開發

數字產品

安全運維

Menu
官网开云
官网开云
微信小程序開發係列分析《二》數據層
時間:2016-10-18 15:55:00
本文分三塊來講,網絡數據請求,文件操作,本地數據操作。涉及到的示例代碼地址:https://github.com/jsongo/weapp-tutorial-3

1、網絡請求

(1)需要一個數據接口來測試
上篇文章中的數據,我們是寫死在page的data裏的。實際應用中肯定不會這麽幹,隻能是從後台請求,或從本地存儲中獲取。
微信提供了一個接口用來發起請求:wx.request。而且它發起的請求,官方文檔中說,隻能是https請求,且一個微信小程序,同時隻能有5個網絡請求連接。

我們來修改一下上篇文章中用到的例子,我們把數據改成從36Kr那裏獲取。稍微研究下36Kr網站的頁麵,就會發現一個小接口,用來拉取列表數據:
API:Get https://36kr.com/api/info-flow/main_site/posts?b_id=5053833&per_page=20&_=1475166251729
url參數:bid表示上次列表拉到第幾條,那條的id;per_page表示要拉取多少條;最後的下劃線表示當前的時間戳,這個可以省略。
返回:json格式的數據,結構如下

36Kr返回列表數據
其中,當code為0時,表示沒有error,這時拿到的response.data.items就是我們要的數據列表,我們隻取每一項裏的幾個數據就可以了。請求的地址不是https,不過可以發現,我們把地址改成https,也可以請求到數據,說明36Kr後台做了https的接入。

(2)發起請求
wx.request的用法其實跟ajax的調用很像。

wx.request({
  url: url,
  data: {},
  header: {
      'Content-Type': 'application/json'
  },
  success: function(res) {
    … // use res.data
  }
})
它有幾個主要的參數:url指定請求地址;data帶上請求的數據,可能是json數據也可以是字符串;header設置請求的頭部信息,如上麵所示;method指定請求的方式,默認是GET,其它值有:OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT;另外,success用來指定請求成功的回調,fail是失敗的回調,還有一個complete,成功或失敗都會回調。

以上代碼中,在success回調裏拿到res是一個json數據,格式如:{data: xxx} ,res.data就可以拿到服務器返回的數據。完整代碼可以參考github上的pages/index/index.js文件。

開發工具之前的破解版本,其實是發不了網絡請求的。因為微信在工具裏做了一些安全限製,所以這裏為了開發,我們得再去破解開發工具。研究了下工具源碼,找到一個地方會對網絡請求的地址做驗證的,把它改掉就Ok。最新工具破解方法參考https://github.com/jsongo/weApp-ide ,有興趣的讀者可以到app.nw/app/dist/weapp/appservice/asdebug.js 文件中,找到下麵這行代碼,把if條件改掉就可以用了,比較簡單。

破解網絡請求限製

(3)demo功能分析
簡單幾句話分析一下我們的36Kr新聞列表的demo。功能也挺簡單,打開時從網絡上請求36Kr的數據顯示成一個列表,點擊列表的每一項都可以進入查看這篇文章的正文內容(隻是個demo,所以就沒有去取內容,直接拿每個列表項的summary字段去當正文)。列表頁在頂部下拉可以刷新列表,在最底部往上滑可以加載更多。

破解網絡請求限製

(4)demo數據分析
列表的生成邏輯跟上篇文章的一樣,隻是數據的來源不同而已。主要代碼的結構如下:

主代碼
data裏定義了兩個屬性,一個是news數組,用來綁定列表數據;refresh,用來決定上方的loading區域是否顯示,隻有用戶在頂部下拉的時候,才會顯示最頂部的loading...區域,view中通過<view wx:if="{{refresh}}”>來綁定是否顯示,在<scroll-view>的bindscrolltoupper函數中用this.setData({refresh: true}) 把它顯示出來。然後在數據請求回來的complete方法中,再把它隱藏掉。這樣就簡單的實現了,下拉刷新的功能。當然體驗不是很好,讀者可以自己想慢慢地優化軟件的體驗問題。

繼續分析下我們的demo代碼。
大家可能注意到了裏定義了一個loading的屬性,它是用來記錄當前是否是在請求中,如果是就放棄請求。這主要用來防止同一時間發出多個請求。用戶幾個很快的動作可能會觸發了幾次數據加載,比如用戶在頂部下拉的時候,網絡不是很好,數據還沒返回,用戶可能會連續快速下拉好多次,這時用loading這個標識就可以過濾掉這些多餘的操作。它在請求發出的之前設置為true,請求回來的時候,在complete回調中設置為false。

onLoad是生命周期函數,隻做了一件事件,就是調用reqData方法發起第一屏列表數據的請求。
bindItemTap綁定了每個新聞列表項的點擊事件,點擊後,把當前的數據傳入到App的globalData裏,再切換到內容詳情頁麵,然後從globalData取出數據來顯示。在官方的文檔中沒有找到頁麵間傳遞數據的其它辦法,筆者隻想到兩種方式,一是通過剛討論的globalData,另一個是通過本地存儲來傳遞,這個在下麵會再討論。

reqData方法封裝了數據請求的過程,第三個參數用來指定是append(底部加載更多)還是refresh(頂部下拉刷新),因為這兩個接口的請求地址等其它處理過程有些不太一樣。
formatData函數,封裝了把請求返回的數據轉換成我們列表中用得到的數據,修改一些字段,拚裝出每一個列表項數據。
renderData函數是我們重點要討論的,先看看實現,30幾行,也不多:

renderData函數代碼
上方最開始的兩個if,用來驗證請求返回的數據。然後用formatData把它們轉化成我們可以用的數據列表。接著對appendOrRefresh進行判斷,如果是1的話,就是refresh;2的話,即為append,即在底部上滑時加載更多。都不是的話,就執行默認的加載第一屏數據,這個最簡單,隻要setData把數據設置到綁定列表的news這個數據段就可以了。而且對於加載更多,和刷新,這兩個動作,則稍微複雜些。

先來看下appendOrRefresh,就是refresh,頂部下拉刷新。讀者思考一下,其實就是把新數據,追加到原this.data.news列表裏,由於index發生了變化,所以每一項的數據也會跟著變化,微信的文檔中沒有給出相應的解決方案,隻能是整體更新整個列表,這會造成整個列表界麵的整體刷新,效率會很低,希望後續MINA框架會有優化。當然,聰明的讀者可能會想到優化的方案,其實可以在這個時候,把新數據列表的前10項設置給this.data.news,反正用戶是在列表的頂部,第一屏也看不完這麽多條。其它的數據則先存儲到本地,等用戶往下滑動的時候,再去檢查本地存儲是否有需要的數據。這要用戶去做一些標記的邏輯,下麵會講到相關邏輯的實現。

再來看看底部上滑時加載更多,用過react的讀者應該都知道facebook提供了Immutable庫來實現局部刷新,但這個在微信小程序的開發中還不知道怎麽整合。不過沒關係,我們有辦法。官方文檔中有一句話提示了一種解決方法,它上麵的例子是:

    this.setData({
      'array[0].text':'changed data'
    })
看到這個,有沒有突然靈光一閃的感覺?沒錯,setData可以對數據進行局部的更新。所以我們的問題就得到了解決,其實也就是往this.data.news數組追加新數據而且不引起係統覺得整個數據都變了。如果隻是簡單的setData({news: newArr}) 這樣用newArr來賦值,肯定不行,因為在內存的地址都變了。這裏要實現的核心目標是,保持news在內存中的地址不變,MINA才不會去刷新整個列表。這個道理可能用過react的讀者會比較好理解一些。。這裏筆者就假裝所有的讀者都已經理解了(不理解的話就跳過吧,以後再回來看吧)。我們可以做一個for循環,用this.setData方法傳入'array[xxx]': newItemXXX就行了。不過js裏對json數據賦值時,key是不能用'array['+index+']'這樣的方式來做的,會報錯。所以可以像上麵的代碼一樣用es6的語法,用[ ]中括號把key包起來。也或者也可以在上麵定義一個變量newData,用newData['array[' + index + ']'] = newItemXXX這樣的方式來賦值也行,再this.setData(newData)就可以了。
這個知識點講的比較細些,有點囉嗦了。這一係列的文章是針對各種基礎的開發者,所以講得詳細些。

(5)接入列表刷新的接口
現在頂部下拉實現更新,就差一個接口了。上麵第一屏的數據和底部上滑加載更多的數據,我們有同一個接口可以獲取,那刷新呢?

於是我們再去36Kr網站上找找,可惜的是沒找到相應的刷新接口,不過找到了另一個替代品:
API: Get https://36kr.com/api/newsflash?b_id=26330&per_page=20&_=1475202501776
參數跟返回,跟本文介紹的第一個接口一樣,不再贅述。這個接口用來取新聞快訊,沒有圖片,比較麻煩,本文隻做demo演示,所以這些都不是重點,我們就暫且拿接口裏的user的頭像來當新聞圖片吧。36Kr網如若看到本文,希望不要計較這些,這些接口誰都可以從網頁請求中很容易得得到,本文隻用它們來做一些demo演示,不做任何商業用途。當然如果讀者本人有後台開發的能力,也可以自己寫個接口來做demo的數據源。

到這裏,我們已經實現了大部分功能,通過綁定事件來實現滑到詢問時,加載各種數據,整個流程,從打開->加載數據->顯示列表->用戶操作時刷新或加載更多數據->點擊查看正文,小麻雀就已經長了整個雛形了。

2、websocket和文件上傳下載

本文重點在數據層,websocket的知識點,在後麵有一篇文章會專門討論,這裏先簡單一句話帶過。
而文件也是數據,在本文的討論範圍之內,下麵分析兩個基本操作。

(1)文件上傳
其實也是一個post請求,微信封裝了兩個接口,wx.chooseImage選擇文件,和wx.uploadFile指定上傳的參數,這兩個接口一起完成了文件上傳的動作。貼一個官方給的demo讀者自己看就行了,很簡單,也沒什麽好分析的。要測試的小夥伴需要自己搭個後台並像網頁正常的form文件上傳一樣,接收相應的參數和file data再寫入文件就ok。


(2)文件下載
調用wx.downloadFile接口,指定路徑和文件的類型,就可以拿到文件,這時微信會幫你把文件保存在一個臨時的目錄,小程序退出時它就會被清掉。如果這是一個比較重要的文件,以後還會用的話,請開發者一定要調用wx.saveFile方法把文件保存到比較持久目錄,微信不會隨意把它清理掉。保存時也不需要指定要保存到哪裏,微信會在保存成功後,在回調裏傳回給你它保存的路徑。

downloadFile的文件隻能是image/audio/video這三種類型,即圖片、音頻和視頻文件。
在開者應用的時候,你可能會想保存登錄用戶的頭像等文件信息,這就可以用到上麵的saveFile的方法,不過也要先調用wx.downloadFile先存到臨時目錄,因為saveFile隻支持從臨時目錄保存到永久目錄。

不過在測試downloadFile的時候,發現success一直沒有被回調,跟蹤了下代碼,發現到底層源碼在WeixinJSBridge.invoke(‘downloadFile’…)的時候就沒反應了,可能要到真實的手機環境中才能使用,也或許目前的這個開發用的IDE有點問題。

3、本地存儲

也是數據緩存。微信給每個小程序分配了5M的存儲空間,對這個緩存的操作,其實跟H5的localStorage操作是一樣的。

微信提供了三個主要的接口, wx.setStorage(wx.setStorageSync)、wx.getStorage(wx.getStorageSync)、wx.clearStorage(wx.clearStorageSync),括號裏麵的是方法是同步的方法,括號外麵的方法是異步讀寫的方法。這兩類方法調用時傳入的參數大體差不多,隻是異步的是通過傳入回調函數的方式來取到數據,而同步的方法是直接返回數據。

(1)存入
wx.setStorage,參數是個json,字段有key指定存儲的鍵值,data指定存儲的值,如

wx.setStorage({key: 'name', data: 'jason'});
同時 還可以傳入success、fail和complete來取得數據或錯誤信息。
而wx.setStorageSync比較簡單,參數是直接傳的,如:

wx.setStorageSync('name', 'jason');
存入的data數據,可以是字符串,也可以是json格式的對象Object。如果存的是json,取的時候,也會是json格式的。這個用起來會非常方便。

存入的數據,可以在調試工具的Storage裏看到,如上圖。

(2)取出
一樣,wx.getStorage也是傳入json,字段隻要帶一個key字符串就行,通過字段success回調來取得參數。
而wx.getStorageSync就非常簡單,直接就可以取到返回值。

var value = wx.getStorageSync('key');
(3)刪除
wx.clearStorage()或wx.clearStorageSync(),直接調用就成。不過調用這個方法要注意,它會清掉所有的數據。
那要怎麽刪除單個key的數據呢?很簡單,就是通過setStorage或setStorageSync,把data設置成null就可以把數據清掉了,讓它不占空間就可以了。

為什麽要分同步和異步呢?
1是在存儲的數據比較大的時候,同步方法會引起界麵的卡頓,所以用異步好些,不影響界麵。
2是異步方法的錯誤處理,是fail回調,出錯一般是通過fail傳出來的。而同步方法要加上try catch才能避免程序異常中斷。
不過同步比異步在使用的時候簡單很多。如果用戶在存儲的數據很小的時候,基本確定不會有什麽邏輯錯誤,可以直接用同步方法。
但如果覺得你的應用有可能會達到5M的空間的時候,再存就會拋錯,這時同步方法就要注意try catch。
Kaiyun体育官方全站入口服務SERVICE
谘詢
微信掃碼谘詢
電話谘詢
400-888-9358