小程序开发技巧总结
0.小程序如何请求设置头信息
由于在前一篇文章中写过,这里就不重复了。参考下面这个链接:
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页面:
<view wx: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})
}
具体原因要参考这个链接
参考资料:微信小程序中this指向作用域问题this.setData is not a function报错
5. 小程序点击控件隐藏一个View,显示另一个View
隐藏和显示,一定明白是用样式控制了。而且是在JS控制样式,代码段如下:
wxml页面:
<view wx: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 //需要预览的图片链接列表
})
}
},
这里要说一下,我这边的需求是先把图片上传到开发者服务器,然后再从开发者服务器把图片读取保存到另一个系统所在的服务器上。读取图片的时候不用下载图片,我上传图片的时候会在数据库一个表保存图片的相关信息,列表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。后来看这个参考链接就全部改过来了,然后页面在不同的设备不会溢出啦。参考链接如下:
最后,想说所遇到的问题点几乎都经过查阅资料解决。在知识红利的时代,大家无私分享自己的所学,在此非常感谢分享开发总结的伙伴。诚挚感谢!