小程式開發技巧總結
0.小程式如何請求設定頭資訊
由於在前一篇文章中寫過,這裡就不重複了。參考下面這個連結:
ofollow,noindex" target="_blank">ASP.NET WebAPI 雙向token實現對接小程式登入邏輯
下面這個連結是參考資料,直接參考這一篇就明白怎麼設定請求頭
jquery ajax 設定請求頭header引數1. 微信小程式引用同一js檔案中的方法函式(function)
如上圖紅色框所示,引用同一js檔案中的函式不像普通js呼叫,需要加上下面程式碼段加粗部分。(不知道在程式碼段裡面為什麼不顯示粗體了,就是**這裡)
onLoad: function (options) { **var that=this;** **this.loadList();** }, **loadList**:function(e){ wx.request({ url: app.globalData.apiurl + '/api/NoUploadBackBill', data: { mobile: app.globalData.phone, }, header: { 'content-type': 'application/json', // 預設值 'Authorization': "BasicAuth " + util.getToken() }, success(res) { console.log(res.data.length); } }) },
2. 小程式中迴圈遍歷(即列表渲染)
我的需求是顯示返回的列表資料,頁面需要分條顯示。通常是要用js遍歷,但在小程式裡面,只需要返回資料集,在wxml頁面用wx:for繫結陣列即可顯示。如下圖所示,使用wx:for後沒有提供wx:key會報一個warning,在這小節後面第一個連結是官方的文件,裡面有提到【如不提供 wx:key,會報一個 warning, 如果明確知道該列表是靜態,或者不必關注其順序,可以選擇忽略。】

wxml頁面: <viewwx:for="{{newBillList}}" wx:key="*this" class="list"></view>
該陣列要在js檔案的data內宣告。
js檔案: 宣告: data: { newBillList: [], //要顯示的列表以及搜尋操作後的列表 }, 賦值: loadList: function(e) { //發起請求,根據手機號載入未上傳回單 wx.request({ url: app.globalData.apiurl + '/api/NoUploadBackBill/Loading', data: { mobile: app.globalData.phone, }, header: { 'content-type': 'application/json', // 預設值 }, success: res => { if (res.data.length > 0) { var list = []; for (var i = 0; i < res.data.length; i++) { res.data[i].ImageList = []; list.push(res.data[i]); } this.setData({ billList: list, **newBillList: list** }); } }, fail(res) { console.log("loadList fail:" + res.data); } }) }
3. 小程式js傳遞多個引數
小程式的js裡面和通用的js傳參一樣,通過全域性變數傳遞,或函式引數傳遞。
//呼叫處理列表圖片的方法 this.addImageList(1, 0); //重新處理資料,把圖片列表加入 addImageList: function (type, count) {}
4. this.setData is not a function
Page.prototype.setData(Object data, Function callback)
在微信小程式中,一般通過this.setData來修改值。比如在函式裡面修改資料,如下程式碼段所示:search要在data內初始化
searchinput: function (e) {//查詢輸入框賦值 this.setData({ search: e.detail.value }) }
但是,當通過wx.request請求資料成功後繫結資料卻出現this.setData is not a function的錯誤。

//發起請求,根據手機號載入未上傳回單 wx.request({ url: app.globalData.apiurl + '/api/NoUploadBackBill', data: { mobile: app.globalData.phone, }, header: { 'content-type': 'application/json', // 預設值 }, success(res) { console.log(res.data); if(res.data.length>0){ this.setData({ billList: res.data }); } },fail(res){ console.log("fail"+res); } })
為什麼會出現這個error呢,我當時百思不得其解,也沒有想到作用域這一層,是查詢資料才解決該問題。
原因是因為:因為this作用域指向問題 ,success函式實際是一個閉包 , 無法直接通過this來setData。
第一種方法: var that=this; this.setData({billList:res.Data});
第二種方法: success:res=>{ this.setData({billList:res.data}) }
5. 小程式點選控制元件隱藏一個View,顯示另一個View
隱藏和顯示,一定明白是用樣式控制了。而且是在JS控制樣式,程式碼段如下:
wxml頁面: <viewwx:for="{{billList}}" wx:key="*this" class="list" style="display:{{billdisplay}};" >
/** * 頁面的初始資料 */ data: { **billdisplay**:'block',//回單列表是否顯示 **searchdisplay**:'none'//查詢列表是否顯示 }, searchList:function(e){//搜尋事件 var _this=this; //設定首次載入的回單列表隱藏,顯示查詢的列表 _this.setData({ billdisplay:'none', searchdisplay:'block' }); }
後記:當時因為想法不完整,所以把顯示資料列表用兩個View表示,用樣式控制,但其實這兩個View顯示的內容是一樣的。後來通過初始化兩個陣列,一個顯示查詢後的列表,一個是原始資料的列表,用兩個陣列通過在使用中重新push資料,這樣解決了用兩個view處理的冗餘辦法。這個做法在第12點。
6. 小程式裡push的用法
在小程式裡面,在data裡初始化的變數是無法直接push的。要在使用的時候另外宣告變數,push,最後再賦值。如下程式碼段加粗部分(即有**的部分):
//重新處理資料,把圖片列表加入 addImageList: function (type, count) { var that = this; **var listArr = [];//宣告一個可操作變數陣列** for (let i = 0; i < this.data.newBillList.length; i++) { if (this.data.shipmentid == this.data.newBillList[i].ShipmetId) { //累加上傳的回單 var cnt = this.data.newBillList[i].DocumentsCnt; count=count+cnt; let row = { ShipmetId: this.data.newBillList[i].ShipmetId, EarliestPickTime: this.data.newBillList[i].EarliestPickTime, FromAddress: this.data.newBillList[i].FromAddress, ToAddress: this.data.newBillList[i].ToAddress, DriverName: this.data.newBillList[i].DriverName, LicenseNumber: this.data.newBillList[i].LicenseNumber, DocumentsCnt: type == 1 ? this.data.newBillList[i].DocumentsCnt : count, ImageList:this.data.imageListArr } **listArr.push(row);** } else { let row = this.data.newBillList[i]; **listArr.push(row);** } } //賦值 that.setData({ //顯示列表 **newBillList: listArr** }); },
參考資料
小程式push陣列報錯解決辦法7. 小程式獲取view的ID
wxml頁面: <button catchtap="removeImg" class="button_img" type="default" id="{{image}}"data-shipmentid="{{item.ShipmetId}}">移除</button>
js頁面: //直接獲取ID var viewId = e.target.id; console.log(viewId); //獲取data-shipmentid var shipmentid=e.target.dataset.shipmentid; console.log(shipmentid);
本來想貼一下官方的api有關e.target以及e.current.target的資料,但是忽然搜尋不出來.....
8. 字串拼接值(官方叫字串運算)
這裡比較簡單,又易理解,直接貼官方的程式碼
<view>{{"hello" + name}}</view>
Page({ data:{ name: 'MINA' } })
9. 引入util.js出錯

如上圖所示,我當前開發中的檔案目錄如下:
pages/business/nouploadbackbill/nouploadbackbill,
我在js檔案中引用util.js的時候,我以為可以這樣的:
const util = require('../../utils/util.js')
但是,實際上似乎要根據當前開發檔案的目錄結構引用
//引用 const util = require('../../../utils/util.js')
不知道是不是這樣理解,如有誤導或錯誤請指正批評~
10. 小程式remove元素
沒有remove,沒法remove,只能通過運算元組處理。程式碼段參考第6點以及第12點
11. 小程式獲取上級元素
當時的問題是這樣的,因為有一個view有id,然後view裡有一個button,這個button事件需要取到view的id進行移除操作,需要標識是移除哪一個view。
但是後現發現,view可以不用給id屬性,button可以直接給id屬性,直接用它自己的id值判斷就可以操作。
程式碼參考第7點。
12. 小程式所有頁面路徑都要放在app.json
當時我以前在app.json檔案配置的都是主頁。主頁裡面跳轉的頁面不必配置。直到報錯才知道所有頁面都要放在app.json裡面配置。不然執行的時候會報not in app.json。如下圖所示:

13. 小程式操作動態迴圈的資料
因為顯示列表裡面還有一個圖片列表的陣列,該列表有一個上傳圖片和移除圖片操作,上傳圖片就把圖片push到這個圖片陣列,然後重新push列表顯示,移除也一樣,把要移除的圖片從列表裡排除,重新push列表顯示。
以下這段程式碼非常重要!因為正是看到了以下這段程式碼,我遇到的難題得以解決,感恩遇見~,哈哈~
由於這個連結是在小程式社群看到的,所以兩個連結都貼出來,參考連結如下:
for (var i = 0; i < this.data.newBillList.length; i++) { if (e.target.dataset.id == this.data.newBillList[i].shipmentid) { newBillList[i] = { id: this.data.newBillList[i].id, price: this.data.newBillList[i], one2one: this.data.newBillList[i], } } else { txtArray1[i] = { id: this.data.liuliangItems[i].id, changeColor: false, price: this.data.liuliangItems[i].price, name: this.data.liuliangItems[i].name, one2one: this.data.liuliangItems[i].one2one } } }
我的資料有三次迴圈量,因為在從一個迴圈列表裡面操作兩個迴圈。暫且說它是複合迴圈吧。當時困我好多天的難題用以下這段程式碼解決,快誇我~哈哈
//移除圖片,按id移除 removeImg: function(e) { var that = this; var listArr = []; var imgArr = []; for (var i = 0; i < this.data.newBillList.length; i++) { if (e.target.dataset.shipmentid == this.data.newBillList[i].ShipmetId) { //相等的當前選中的資料,移除列表的圖片的需要重新組合資料 for (var j = 0; j < this.data.newBillList[i].ImageList.length; j++) { //第一種寫法 if (e.target.id != this.data.newBillList[i].ImageList[j]) { //相等的值是要移除的值,不作處理 imgArr.push(this.data.newBillList[i].ImageList[j]); //重新push圖片 } //第二種寫法,es6的寫法 //let id = e.target.id; //this.data.newBillList[i].ImageList[j].splice(id,1);//刪除 //splice(index,count) 第一個引數是索引,第二個引數是刪除的個數 } //把資料重新push var row = { ShipmetId: this.data.newBillList[i].ShipmetId, EarliestPickTime: this.data.newBillList[i].EarliestPickTime, FromAddress: this.data.newBillList[i].FromAddress, ToAddress: this.data.newBillList[i].ToAddress, DriverName: this.data.newBillList[i].DriverName, LicenseNumber: this.data.newBillList[i].LicenseNumber, DocumentsCnt: this.data.newBillList[i].DocumentsCnt, ImageList: imgArr } listArr.push(row); } else { //資料不變的是不用處理的列表塊,直接push var row = this.data.newBillList[i]; listArr.push(row); } }
14. 小程式上傳檔案wx.uploadFile
這一部分被我形容是第二難題。(當然,是針對我在自己當前開發的功能裡面~)
這裡分為幾個部分:
wx.chooseImage({ count: this.data.count[this.data.countIndex], sizeType: ['original', 'compressed'], sourceType: ['album', 'camera'], success: res => { // tempFilePath可以作為img標籤的src屬性顯示圖片 this.setData({ shipmentid: e.target.dataset.shipmentid, imageListArr: res.tempFilePaths }) //呼叫處理列表圖片的方法 this.addImageList(1, 0);//該函式完整程式碼在第6點 }, fail: function(data) { wx.showToast({ title: "選擇圖片出錯", icon: "none", duration: 1000 }); } });
//預覽圖片 previewImage: function(e) { let current = e.target.dataset.src; let imageList = []; for (let i = 0; i < this.data.newBillList.length; i++) { if (e.target.dataset.shipmentid == this.data.newBillList[i].ShipmetId) { imageList = this.data.newBillList[i].ImageList; } wx.previewImage({ current: current, //當前顯示圖片的連結 urls: imageList //需要預覽的圖片連結列表 }) } },
-
將本地資源上傳到伺服器UploadTask wx.uploadFile(Object object)
這裡要說一下,我這邊的需求是先把圖片上傳到開發者伺服器,然後再從開發者伺服器把圖片讀取儲存到另一個系統所在的伺服器上。讀取圖片的時候不用下載圖片,我上傳圖片的時候會在資料庫一個表儲存圖片的相關資訊,列表id,圖片名稱,在開發者伺服器的所在路徑等一些相關用到的欄位。
第一步:先上傳圖片到開發者伺服器,客戶端發起一個 HTTPS POST 請求,其中 content-type 為 multipart/form-data
//上傳圖片,將本地資源上傳到開發者伺服器 uploadImage: function(e) { var that = this; that.setData({ shipmentid: e.target.dataset.shipmentid }); var listArr = []; var uploadImgCount = 0; var uploadArr = []; var imgLength = 0; for (var i = 0; i < this.data.newBillList.length; i++) { if (e.target.dataset.shipmentid == this.data.newBillList[i].ShipmetId) { imgLength = this.data.newBillList[i].ImageList.length; var index = i; console.log("列表的待上傳的回單數:" + imgLength); if (imgLength < 1) { wx.showToast({ title: '沒有可上傳的回單', icon: 'none', mask: true, duration: 1000 }); var row = this.data.newBillList[i]; listArr.push(row); } else { //啟動上傳等待中... wx.showToast({ title: '正在上傳...', icon: 'loading', mask: true, duration: 10000 }); //遍歷圖片列表上傳 for (var j = 0; j < this.data.newBillList[i].ImageList.length; j++) { //記錄當前列表的索引,因為到了success裡就獲取不到啦 var index = i; //上傳圖片到開發者伺服器 wx.uploadFile({ url: app.globalData.apiurl + '/api/NoUploadBackBill/PostImageFile', filePath: this.data.newBillList[i].ImageList[j], //要上傳檔案資源的路徑 name: 'image', // 這裡的具體值,要與後臺保持一致 header: { 'content-type': 'multipart/form-data', 'Authorization': "BasicAuth " + util.getToken() }, formData: { //HTTP 請求中其他額外的 form data 'shipmentid': this.data.shipmentid, 'imgIndex': j //上傳的圖片編號(後臺提供給前端判斷圖片是否全部上傳完) }, success: res => { //console.log("res:"+res.data); uploadImgCount++; uploadArr.push(res.data); //判斷是否上傳完畢 if (uploadImgCount == imgLength) { //顯示資料,呼叫處理列表圖片的方法,只修改所選運單的回單數即可 this.addImageList(2, uploadImgCount); //儲存操作的列表數到TMS資料表 let row = { ShipmetId: this.data.newBillList[index].ShipmetId, EarliestPickTime: this.data.newBillList[index].EarliestPickTime, FromAddress: this.data.newBillList[index].FromAddress, ToAddress: this.data.newBillList[index].ToAddress, DriverName: this.data.newBillList[index].DriverName, LicenseNumber: this.data.newBillList[index].LicenseNumber, DocumentsCnt: uploadImgCount, ImageList: uploadArr } this.setData({ saveImageList: row }); //儲存圖片到TMS this.saveImage(); } }, fail: res => { console.log("uploadImage fail:" + res); wx.hideToast(); wx.showModal({ title: '錯誤提示', content: '上傳圖片失敗', showCancel: false, success: function(res) {} }); } }); } } } } }
這裡大家一定很關心我的後臺程式碼是如何編寫的,我是參考第一個連結做的。
第二步:把圖片從開發者伺服器讀取並儲存到另一個伺服器
這裡當時因為POST資料以及'content-type',還有JSON.stringify也卡住了好久(知識點不紮實)
//儲存圖片 saveImage: function(e) { //console.log("新json字串:" + JSON.stringify(this.data.saveImageList)); wx.request({ url: app.globalData.apiurl + '/api/NoUploadBackBill/SaveImageFile', data: JSON.stringify(this.data.saveImageList), header: { 'content-type': 'application/json', 'Authorization': "BasicAuth " + util.getToken() }, method: "POST", success: res => { console.log(" POST success:" + res.data.Message); if (res.data.Code == "0") { wx.showToast({ title: '上傳成功', icon: 'none', mask: true, duration: 2000 }); } else { wx.showToast({ title: '上傳圖片到伺服器失敗:' + res.data.Message, icon: 'none', mask: true, duration: 2000 }); } }, fail: res => { console.log("saveImage fail:" + res.data); } }); },
這裡大家一定也會關心資料的轉換,這裡貼一下後臺程式碼:
//接收引數為dynamic型別 public IHttpActionResult SaveImageFile(dynamic obj){ var s = JsonConvert.SerializeObject(obj); //例項化一個能夠序列化資料的類 JavaScriptSerializer js = new JavaScriptSerializer(); //將json資料轉化為物件型別 var entity = js.Deserialize<NoUploadBackBillModel>(s); }
還有資料讀取以及儲存,這裡使用檔案流FileStream處理,從圖片轉換為二進位制流讀取,這裡是呼叫web service處理的。具體程式碼不在我這邊,就沒有辦法貼出來啦~
參考連結:
WebAPI 介面引數15. WebAPI 找到了與該請求匹配的多個操作
16. 小程式自適應單位rpx
這個rpx我原來不知道是微信小程式推出了新尺寸單位。直到我做完功能測試發現沒有自適應。因為我在裡面慣性使用了px和em。後來看這個參考連結就全部改過來了,然後頁面在不同的裝置不會溢位啦。參考連結如下:
最後,想說所遇到的問題點幾乎都經過查閱資料解決。在知識紅利的時代,大家無私分享自己的所學,在此非常感謝分享開發總結的夥伴。誠摯感謝!