抖音小程序与微信小程序差异
- 获取用户信息的方式
- 通过button按钮,设置属性 open-type="getUserInfo",获取获得用户信息的授权
- 兼容性,通过wx.canIUse('button.open-type.getUserInfo')的值,来判断按钮是否可用
- 通过拥有上述属性的按钮事件bindgetuserinfo -
https://developers.weixin.qq.com/miniprogram/dev/component/button.html
来获取,事件处理函数中ev.detail.userInfo对应的用户信息 - wx.getUserInfo在用户未授权的情况下,将不再出现授权弹窗,所以才使用引导用户点击按钮的授权方式
- 通过onload生命周期,wx.getSetting来获取用户权限,并对用户信息状态赋值
请升级微信版本
...
//查看授权按钮是否可以使用
if(wx.canIUse('button.open-type.getUserInfo')){
_that.setData({
'showLoginBtn': true
});
};
...
//获取用户信息与请求服务器获取userid
loginFn(ev){
var _that = this;
//获取用户的当前授权设置
wx.getSetting({
success(res){
//查看【获取用户信息】授权
if(res.authSetting['scope.userInfo']){
wx.getUserInfo({
success(res){
var _userInfo = res.userInfo;
//获得code,再进行注册
_that.registerFn(function(_code){
//请求后台接口,获取userid
});
},
fail(err){
console.log(err);
}
});
}else{
app.showPopFn({
'title':'授权失败',
'icon': 'none',
'duration': 1000
});
}
}
})
}
1-2. 如果wx:if挂载的标签上条件,与通过此条件来增加标签上的class类名时候,不会产生动画效果
解决: 需要增加额外条件,来改变class类名,可能的话,还要增加延时定时器来进行这个类名条件的设置
- 最新官方关于 用户信息相关接口调整 -
https://developers.weixin.qq.com/community/develop/doc/000cacfa20ce88df04cb468bc52801
【背景】:
很多开发者在打开小程序时就通过组件方式唤起getUserInfo弹窗,如果用户点击拒绝,无法使用小程序,这种做法打断了用户正常使用小程序的流程,同时也不利于小程序获取新用户。
【注意】:
- 建议多用
getUserProfile
,即便被拒绝,再次调用getUserProfile,仍可以拉起授权。 - 授权被拒后,通过wx.openSetting打开设置菜单,是无法找到被拒的授权信息的。
官方变化:
- 新的授权书写方式
由于getUserProfile接口从2.10.4版本基础库开始支持(覆盖微信7.0.9以上版本),考虑到开发者在低版本中有获取用户头像昵称的诉求,对于未支持getUserProfile的情况下,开发者可继续使用getUserInfo能力
//--wxml
{{userInfo.nickName}}
//--js
Page({
data: {
userInfo: {},
hasUserInfo: false,
canIUseGetUserProfile: false,
},
onLoad() {
if (wx.getUserProfile) {
this.setData({
canIUseGetUserProfile: true
})
}
},
getUserProfile(e) {
// 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
// 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
wx.getUserProfile({
desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
},
getUserInfo(e) {
// 不推荐使用getUserInfo获取用户信息,预计自2021年4月13日起,getUserInfo将不再弹出弹窗,并直接返回匿名的用户个人信息
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
})
},
})
2-2. 区别与获取用户昵称与信息的授权,有些授权拒绝后,再次调用API,不会再次弹出授权界面
如果用户已拒绝授权,则不会出现弹窗,而是直接进入接口 fail 回调。需要发者兼容用户拒绝授权的场景
以获取用户地理信息的授权为例
2-3. 关于授权的过期时间
- 在开发者工具中,点击 [ 清缓存-全部清楚 ] ,即可一次性清楚所有数据缓存与授权记录信息
- 借用微信文档说明:在真机上,一旦用户明确同意或拒绝过授权,其授权关系会记录在后台,直到用户主动删除小程序。
直接在微信-小程序内,找到对应的小程序将其删除,可能的话,再加上(我-设置-通用-存储空间-缓存-清理),更加干净的清理对应小程序的各种数据缓存与授权信息
rpx(responsive pixel): 可以根据屏幕宽度进行自适应。
规定屏幕宽为750rpx。
如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。获取用户坐标
在app.json中,添加获取目标位置的权限:
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
}
获取用户坐标 wx.getLocation
getUserLocalFn(){
var that = this;
//获取用户坐标
wx.getLocation({
type: 'wgs84',
success(res){
var _userLocal = that.data.userLocal;
_userLocal.latitude = res.latitude;
_userLocal.longitude = res.longitude;
that.setData({
"userLocal": _userLocal
});
},
fail(err){
console.log(err);
}
});
}
- 图片预加载
app.js
//图片预加载
loadImg(_link,sucFn,errFn,cmpFn){
wx.getImageInfo({
src: _link,
success(res){
sucFn && sucFn(res);
},
fail(err){
errFn && errFn(err);
},
complete(res){
cmpFn && cmpFn(res);
}
});
},
//加载所有图片
loadAllImgs(imgListArr,compFn){
//图片预加载 相关
var _that = this;
var num = 0;
for(var i=0; i
使用,pages下某个页面:
//加载所有图片
app.loadAllImgs([
'https://www.xxx.net/minisoft/zhenze/everyDayPic_1.jpg',
'https://www.xxx.net/minisoft/zhenze/everyDayPic_2.jpg',
'https://www.xxx.net/minisoft/zhenze/everyDayPic_3.jpg'
],function(){
_that.setData({
allimgLoaded: true
});
});
通过allimgLoaded这个数据变量来切换loading条和图片
wxml结构:
css3 loading图标,激活状态运动,否则不运动
wxss
/*loadingGear - 加载条2*/
.loadingGear { position:absolute; left:50%; top:50%; width:4rpx; height:4rpx; margin:-2rpx 0 0 -2rpx;
z-index:-1;
}
.loadingGear .inset { position:relative; z-index:2; width:100%; height:100%; visibility:hidden; }
/*激活*/
.loadingGearAc { z-index:9999;}
.loadingGearAc .inset { visibility:visible;
-webkit-animation:gearAni linear 2s infinite;
-moz-animation:gearAni linear 2s infinite;
-ms-animation:gearAni linear 2s infinite;
-o-animation:gearAni linear 2s infinite;
animation:gearAni linear 2s infinite;
}
.loadingGear .inset .blocks { position:absolute; left:0; bottom:0; z-index:2; width:100%; height:300%; overflow:hidden; background:#006d78; font-size:0; line-height:0;
-webkit-transform-origin:center bottom; -moz-transform-origin:center bottom; transform-origin:center bottom;
}
.loadingGear .inset .blocks:nth-child(1){ -webkit-transform:rotate(0deg) translateY(-10rpx); -moz-transform:rotate(0deg) translateY(-10rpx); transform:rotate(0deg) translateY(-10rpx); }
.loadingGear .inset .blocks:nth-child(2){ -webkit-transform:rotate(45deg) translateY(-10rpx); -moz-transform:rotate(45deg) translateY(-10rpx); transform:rotate(45deg) translateY(-10rpx); }
.loadingGear .inset .blocks:nth-child(3){ -webkit-transform:rotate(90deg) translateY(-10rpx); -moz-transform:rotate(90deg) translateY(-10rpx); transform:rotate(90deg) translateY(-10rpx); }
.loadingGear .inset .blocks:nth-child(4){ -webkit-transform:rotate(135deg) translateY(-10rpx); -moz-transform:rotate(135deg) translateY(-10rpx); transform:rotate(135deg) translateY(-10rpx); }
.loadingGear .inset .blocks:nth-child(5){ -webkit-transform:rotate(180deg) translateY(-10rpx); -moz-transform:rotate(180deg) translateY(-10rpx); transform:rotate(180deg) translateY(-10rpx); }
.loadingGear .inset .blocks:nth-child(6){ -webkit-transform:rotate(225deg) translateY(-10rpx); -moz-transform:rotate(225deg) translateY(-10rpx); transform:rotate(225deg) translateY(-10rpx); }
.loadingGear .inset .blocks:nth-child(7){ -webkit-transform:rotate(270deg) translateY(-10rpx); -moz-transform:rotate(270deg) translateY(-10rpx); transform:rotate(270deg) translateY(-10rpx); }
.loadingGear .inset .blocks:nth-child(8){ -webkit-transform:rotate(315deg) translateY(-10rpx); -moz-transform:rotate(315deg) translateY(-10rpx); transform:rotate(315deg) translateY(-10rpx); }
/*gearAni*/
@-webkit-keyframes gearAni {
0% { -webkit-transform:rotate(0deg); -moz-transform:rotate(0deg); transform:rotate(0deg);}
100% { -webkit-transform:rotate(360deg); -moz-transform:rotate(360deg); transform:rotate(360deg);}
}
@keyframes gearAni {
0% { -webkit-transform:rotate(0deg); -moz-transform:rotate(0deg); transform:rotate(0deg);}
100% { -webkit-transform:rotate(360deg); -moz-transform:rotate(360deg); transform:rotate(360deg);}
}
- 使用微信小程序API时候,需要先要测试下兼容
compareVersion(v1, v2){
v1 = v1.split('.')
v2 = v2.split('.')
const len = Math.max(v1.length, v2.length)
while(v1.length < len){
v1.push('0')
}
while(v2.length < len){
v2.push('0')
}
for(let i = 0; i < len; i++){
const num1 = parseInt(v1[i])
const num2 = parseInt(v2[i])
if (num1 > num2){
return true;
}else if(num1 < num2){
return false;
}
}
}
放在app.js内,pages使用js调用
如:音频API的兼容
var _btn = app.compareVersion(version,'1.6.0');
if(_btn){
//基础库 1.6.0 开始支持,低版本需做兼容处理。
InnerAudioContext = wx.createInnerAudioContext();
}else{
wx.showModal({
title: '提示',
content: '当前微信版本过低,无法使用音频功能,请升级到最新微信版本后重试。'
});
};
- 跳转到第三方小程序,需要获知对方的小程序APPID
也可以放在app.js内方便,统一调用
jumpOtherMinisoft(_appid,_path,sucFn,errFn){
var _that = this;
//兼容
var version = wx.getSystemInfoSync().SDKVersion;
var _btn = _that.compareVersion(version,'1.3.0');
if(_btn){
//基础库 1.3.0 开始支持
wx.navigateToMiniProgram({
appId: _appid,
path: _path,
success(res){
sucFn && sucFn(res);
},
fail(err){
errFn && errFn(err);
}
});
}else{
wx.showModal({
title: '提示',
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
});
};
}
pages页面内js直接使用
app.jumpOtherMinisoft('wx2c348cf579062e56','page/index/index?id=123',null,function(err){
console.log(err);
app.showPopFn({
'title':'跳转小程序失败'
});
});
7-2. 通过标签navigater跳转
查看详情
7-3. 获取三方小程序的APPID以及详细页的path
- 如何查看小程序的appid,
打开微信 - 搜索小程序 - 进入 - 点击右上角三个点 - 点击 弹出层小程序的名称 - 更多资料 - 即可查看 APPID - 如何获取详细页的链接
登录后台 - 工具 - 生成小程序码 - 输入小程序APPID - 获取更多页面路径 - 添加自己微信号 - 开启 ( 注:复制功能仅保留10分钟,10分钟后功能消失 ) - 打开小程序详细页 - 右上三个点 - 选择复制链接 (如果不存在,则说明上面一步时间过期了,或没有开发者权限)
针对京东来说,需要将sku字段保留,其余删除,包括".html" - 将复制的链接黏贴至上一步左侧输入框,生成小程序码
- 注:如果复制链接,提示 【网络错误稍后重试】, 排除断网的情况,可能是微信版本过低,重新安装最新版本微信即可
7-4. 配置跳转三方小程序的白名单,
在app.json内,配置navigateToMiniProgramAppIdList
补充说明:
- 每个小程序可跳转的其他小程序数量限制为不超过 10 个 从 2.4.0 版本以及指定日期(具体待定)开始,开发者提交新版小程序代码时,如使用了跳转其他小程序功能,则需要在代码配置中声明将要跳转的小程序名单,限定不超过 10 个,否则将无法通过审核。
- 该名单可在发布新版时更新,不支持动态修改。
- 转发以及分享到朋友圈
转发的触发方式为,wxml内放入一个button,open-type,此外在页面的js内要定义onShareAppMessage与onShareTimeline,分别用来定义转发和分享到朋友圈
//用户点击右上角转发 图片长宽比是 5:4
//基础库 1.2.0 开始支持,低版本需做兼容处理
onShareAppMessage(res){
//兼容
var version = wx.getSystemInfoSync().SDKVersion;
var _btn = app.compareVersion(version,'1.2.0');
if(_btn){
// 来自页面内转发按钮
if(res.from === 'button'){};
return {
'title': res.target.dataset.tit || app.globalData.shareObj.sharemes,
'path': res.target.dataset.path || app.globalData.shareObj.sharePath,
'imageUrl': app.globalData.shareObj.shareSendPic
}
}else{
wx.showModal({
title: '提示',
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
});
};
},
//用户点击右上角转发到朋友圈 query-当前页面路径携带的参数 图片长宽比是 1:1
//从基础库 2.11.3 开始支持
onShareTimeline(){
//兼容
var version = wx.getSystemInfoSync().SDKVersion;
var _btn = app.compareVersion(version,'2.11.3');
if(_btn){
return {
'title': app.globalData.shareObj.sharemes,
'query': null,
'imageUrl': app.globalData.shareObj.shareFriendPic
}
}else{
wx.showModal({
title: '提示',
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
});
};
},
注: 分享的图片比例分别为 5:4、1:1 (可以是本地文件路径、代码包文件路径或者网络图片路径。支持PNG及JPG)
在wxml内调用,添加自定义属性,可能会方便些
- 通过坐标,打开微信小程序内置地图,进行导航
//使用微信内置地图查看位置
openLocationFn(_lat,_lon,sucFn,errFn){
if(_lat=='' || _lon==''){
this.showPopFn({
'title': '坐标错误'
});
return false;
}else{
wx.openLocation({
'latitude': _lat,
'longitude': _lon,
'scale': 17,
success(res){
sucFn && sucFn(res);
},
fail(err){
errFn && errFn(err);
}
});
};
}
调用
goOpenLocationFn(){
app.openLocationFn(30.91185865822398,120.50376582713723,null,function(err){
console.log(err);
app.showPopFn({
'title': '调用失败',
'icon': 'success',
'duration': 500
})
})
}
- 小程序音频使用
声明音频对象
var version = wx.getSystemInfoSync().SDKVersion;
var _btn = app.compareVersion(version,'1.6.0');
if(_btn){
InnerAudioContext = wx.createInnerAudioContext();
}else{
wx.showModal({
title: '提示',
content: '当前微信版本过低,无法使用音频功能,请升级到最新微信版本后重试。'
});
};
配置音频属性,以及调用方法
//监听播放结束后,关闭对播放结束的监听
InnerAudioContext.src = _nowAudioSrc;
InnerAudioContext.play()
InnerAudioContext.onEnded(function(){
InnerAudioContext.offEnded();
InnerAudioContext.stop();
...业务样式等操作...
console.log('播放完毕');
});
注: 上述监听结束的方法,最好结束后调用stop,重置音频
10-1. 播放进度与获取音频时长
//2. 监听播放、并获取音频的时长、播放位置与 进度百分比
nowAudioObj.offPlay();
nowAudioObj.onPlay(() => {
//获取总时长与当前播放位置
nowAudioObj.offTimeUpdate();
nowAudioObj.onTimeUpdate(()=>{
//初始化 -- 获取音频总时长
_listsData[_index].audio.duration = nowAudioObj.duration;
//实时更新 -- 获取当前播放的位置
_listsData[_index].audio.startTime = nowAudioObj.currentTime;
//实时更新 -- 进度条百分比
_listsData[_index].audio.nowTimePercent = Math.floor(nowAudioObj.currentTime/nowAudioObj.duration*100);
_that.setData({
'listsData': _listsData
});
});
});
//3. 播放结束监听
nowAudioObj.offEnded();
nowAudioObj.onEnded(() => {
//重置播放位置、重置样式
_listsData[_index].audio.startTime = 0;
_listsData[_index].audio.nowTimePercent = Math.floor(0/nowAudioObj.duration*100);
_listsData[_index].audio.playStatue = false;
app.showPopFn({
'title': '播放完毕'
});
//停止监听
nowAudioObj.stop();
nowAudioObj.offPlay();
nowAudioObj.offEnded();
nowAudioObj.offTimeUpdate();
_that.setData({
'listsData': _listsData
});
});
//4. 捕获错误
nowAudioObj.offError();
nowAudioObj.onError(function(errMsg,errCode){
console.log(errMsg);
var _errMes = '';
switch(errCode){
case 10001:
_errMes = '系统错误';
break;
case 10002:
_errMes = '网络错误';
break;
case 10003:
_errMes = '文件错误';
break;
case 10004:
_errMes = '格式错误';
break;
case -1:
_errMes = '未知错误';
break;
};
app.showPopFn({
title:_errMes,
duration:600
});
});
注:
- 音频不能使用 中文名字 ,否则不能被捕获报错信息
- 音频在页面之前切换不可以停止,所以在切换页面之前,需要关闭音频,
onHide -- wx.navigateTo 或 底部tab切换到其他页面触发
onUnload -- wx.redirectTo 或 wx.navigateBack时候触发
- 输入框表单,通过键盘进行搜索,而非一个自定义按钮
通过bindconfirm事件,可以在键盘敲击回车、在手机键盘输入法触摸完成后,即可进行搜索
通过事件对象 ev.detail.value 可以完成内容获取
inputConfirmFn(ev){
var _val = ev.detail.value;
_val = _val.replace(/\s+/g,"");
if(_val != ''){
console.log('提交的内容为',_val);
}else{
app.showPopFn({
'title': '内容不能为空',
'icon': 'none',
'duration': 600
});
};
}
- 小程序自带的顶部工具栏,可以通过在app.json的window内,通过"navigationStyle":"custom"关闭默认导航
只保留右上角三个点
"navigationStyle":"custom"
定义一个自定义状态栏组件,可以使用的属性与插槽,官方手册 - https://developers.weixin.qq.com/miniprogram/dev/extended/weui/navigation.html
问题:
不同设备的状态栏的高度不一样,因此自定义导航图标,可能会因设备不一样而跑偏,
所以,为了统一这个比例,可以通过wx.getSystemInfo获取系统信息,通过系统信息返回statusBarHeight获得状态栏高度( 单位px )
再通过 wx.getMenuButtonBoundingClientRect()方式是获得胶囊按钮的空间信息,以及系统状态栏的高度,之后进行对应的计算
【注】:getMenuButtonBoundingClientRect 部分手机有获取设备 信息延迟 - https://developers.weixin.qq.com/community/develop/doc/0006eeb2db0ae022a098c58f85d800
,所以官方建议加100MS延迟,等待信息!
具体的方法是:
app.js onLaunch声明周期中
//获取状态栏高度
wx.getSystemInfo({
success(res){
_that.globalData.sysBarHeight = res.statusBarHeight;
console.log(res.statusBarHeight);
},
fail(err){
console.log('系统信息错误',err);
}
});
//获取胶囊按钮的空间信息
//基础库 2.1.0 开始支持
if(_that.compareVersion(version,'2.1.0')){
var ButtonBoundingRes = wx.getMenuButtonBoundingClientRect();
if(ButtonBoundingRes){
_that.globalData.ButtonBoundingHright = (ButtonBoundingRes.top - _statusBarHeight)*2 + ButtonBoundingRes.height
};
}else{
wx.showModal({
title: '提示',
content: '当前微信版本过低,无法获取胶囊按钮尺寸功能,请升级到最新微信版本后重试。'
});
};
通过上面方式计算结果 自定义导航栏的高度应该为
statusBarHeight | 下面的胶囊实际占位 | 最终自定义导航高度 |
---|---|---|
20 | (24-20)*2+32 | 60 |
20 + (24-20)*2+32 = 60
上面计算的单位为px,如果在页面内使用,需要使用rpx,因为px是固定值,不同手机设备高度不一,可能引起适配问题:
在app.js的onLaunch生命周期内,获取当前设备的px与rpx比例,将px转化为rpx,在页面内使用更精准些
var _windowWidth = wx.getSystemInfoSync().windowWidth
var _rate = 750 / _windowWidth
_that.globalData.rateRpx = _rate;
- 小程序组件lifetimes 可以补充onload、onshow的生命周期
js
lifetimes: {
attached: function() {
// 在组件实例进入页面节点树时执行
...
},
detached: function() {
// 在组件实例被从页面节点树移除时执行
...
},
},
- 通过分享进来,没有返回主页入口的困局
问题:
如果分享页面是详细页面,通过分享点击进来,无法通过常规后退按钮返回,因此需要一个返回首页的按钮
但是需要一个判断的条件,即 如何判断此时页面是否为分享点击进来的。
方案:
在app.js内,可以在App的onLaunch和onShow,或wx.getLaunchOptionsSync中获取上述场景值,
来获取 scene (小程序的场景值)
官方的场景值对应表 -https://developers.weixin.qq.com/miniprogram/dev/reference/scene-list.html
通过将获取的scene来判断是否显示一个返回主页的入口,与表格对比可知,
1007 -- 是分享给朋友,朋友通过链接点击过来的
1008 -- 群聊会话中的小程序消息卡片
app.js
App({
onLaunch(option){
//可以查看scene值
if(option.scene==1007 || option.scene==1008){
_that.globalData.formShare = true;
};
...
pages/xx/xx.wxml内
pages/xx/xx.js内
onLoad:function(option){
//是否显示返回主页按钮
if(app.globalData.formShare){
_that.setData({
'homeBackBtn': true
})
};
},
backHomeFn(){
//重置状态
app.globalData.formShare = false;
this.setData({
'homeBackBtn': false
});
//完成跳转
app.returnHomeFn();
}
- wx.navigateTo 与 wx.redirectTo 的使用差异
不同点:
wx.navigateTo 用于页面流程为串行的操作,最多纪录10层历史路径,可以通过wx.navigateBack进行后退,
如果超过10层,会进行报错。
navigateTo webview cont limit exceed
wx.redirectTo 可以用来代替上页面跳转的问题,但是无法使用历史路径。
相同点:
都不能跳到 tabbar 页面
- 复杂数据的修改,通过字符串拼接好数据,包裹上[ ]来进行解析
推荐的方式:
var _nowIndex = this.data.nowIndex;
this.setData({
['listsData['+_nowIndex+'].sonIndex']: 新的数据
});
或
var _dataStr = "mapDatas.tabLists["+_mapDatas.tabIndex+"].sonLists";
_that.setData({
[_dataStr]: _tempArr
});
引起问题的方式:
var _sonIndex = ev.detail.current;
var _listsData = this.data.listsData;
var _nowIndex = this.data.nowIndex;
_listsData[_nowIndex].sonIndex = _sonIndex;
this.setData({
'listsData': _listsData
});
出现的具体问题是:
- listsData下某个无需修改数据,重新覆盖,会引起undefined的渲染错误,比如地图组件的经纬度;
- 此外对局部的数据修改,是被建议的
- 渲染大小的建议
由于小程序运行逻辑线程与渲染线程之上,setData的调用会把数据从逻辑层传到渲染层,数据太大会增加通信时间。
**得分条件:`setData`的数据在`JSON.stringify`后不超过 256KB**
scroll-view组件内,存在子元素,包裹一个swiper组件和同级增加一个浮层,滚动scroll-view时候,浮层会消失
解决方法:
将浮层从swiper同级提取到更高一层,可以解决问题。wx.navigateTo跳转,会出现重复跳转
原因:如 wx.navigateTo、wx.redirectTo跳转官方没有做节流
具体操作:
//修复BUG、节流timer
var navigaterTimer = null;
var redirectTimer = null;
App({
onLaunch(option){
var _that = this;
...
//跳转 wx.navigateTo 小程序中页面栈最多十层
jumpLink(_link,sucFn,errFn){
clearTimeout(navigaterTimer);
navigaterTimer = setTimeout(function(){
wx.navigateTo({
url: _link,
success(res){
sucFn && sucFn(res);
},
fail(err){
errFn && errFn(err);
}
})
},600);
},
//跳转
jumpLink2(_link,sucFn,errFn){
clearTimeout(redirectTimer);
redirectTimer = setTimeout(function(){
wx.redirectTo({
url: _link,
success(res){
sucFn && sucFn(res);
},
fail(err){
errFn && errFn(err);
}
});
},500);
},
注:延迟时间越久,修复效果越好,如果延迟时间设置300ms,在模拟器上仍可能出现重复跳转的情况,理解可能是设备性能来不及反应?
text标签内如果要添加长文本,为了方便用户浏览时copy可以,为text添加user-select属性,
此外,首行缩进(text-indent:2em;)不能添加到父级标签,否则会溢出屏幕针对富文本,处理带有HTML标签的文案
- 获取rpx与px的转化比例值
已知:小程序规定屏幕宽为750rpx,这个无关机型(rpx(responsive pixel): 可以根据屏幕宽度进行自适应)
未知:当前设备的宽度
let windowWidth = wx.getSystemInfoSync().windowWidth
let rate = 750 / windowWidth
-
小程序加载分页的格式
22-2. 分页代码格式
js:
'commentLists':{
'list':[],
'loaded': false,
'isLoading': false,
'noMore': false,
'totalcount':0,
'pageindex': 0
}
wxml:
{{itemEle.mes}}
暂无更多评论
暂无任何数据
加载中...
- 小程序跳转第三方小程序中某个页面
具体流程:
- 配置app.json ---(已经废弃)
小程序限定不超过10个,否则将无法通过审核。
此名单可在发布新版时更新,不支持动态修改。
"navigateToMiniProgramAppIdList": [
"wx91d27dbf599dff74",
"wx0e6ed4f51db9d078"
]
备注:
从2020年4月24日起,使用跳转其他小程序功能将无需在全局配置中声明跳转名单
,跳转其他小程序将不再受数量限制
- 获取跳转小程序的appid
微信 - 搜索小程序 京东 - 打开小程序 - 右上角三个点 - 点击浮层内小程序名称 - 更多资料 - 点击即可查看AppID - 查看该商品是否可以获取详情地址,
打开要跳转的小程序目标页面,右上角三个点,发送给朋友,
如果点击这个链接可以达到目前链接,则继续后续步骤。
如果仅能跳转至目标小程序的首页,那么说明无法获取商品详情地址,则可以停止后续流程了 -
打开微信公众平台,登录账号后,顶部菜单 工具-生成小程序码,将上面获取的appid输入,搜索小程序;
然后将自己的小程序账号放进去,点击"开启"即可显示复制链接按钮;
【注】: 这个"开启"按钮有一个有效时间,过了时间就不显示复制链接按钮了,需要再次这一步开启
上面开启有时间限制,如果过了一段时间,跳转的小程序内,点击右上角三个点,弹出层内可能会不显示"复制链接"
- 编辑链接地址
点击这个分享的图文框,再点击右上角的三个点,浮层内,点击复制链接;
将复制的链接,在记事本内编辑,删除.html与id参数之后的内容;
例如:
pages/gs/sight/newDetail.html?sightId=25603&mktshare=eyJhbGxpYW2IjNyojIklWZj5ODQsInNpZCI6NzExNDY1LCJvdWlkIjoiIiwic291cmNlaWQiOjU1NTUyNjg5LCJmcm9tYWxsaWFuY2VpZCI6MCwiZnJvbXNpZCI6MCwiZnJvbW91aWQiOiIiLCJmcm9tc291cmNlaWQiOjAsImZyb21vcGVuaWQiOiIyMTVhZGVhZi1lMmRiLTQ3YWUtODRiZC1hMWU2ZWViMWNlMTAifQ()()KY&allianceid=262684&sid=711465&ouid=&sourceid=55552689
处理链接为:
pages/gs/sight/newDetail?sightId=25603
wxml标签内使用,方式为:
查看详情
- pinker针对数组包裹对象的形式,需要处理一份用于展示的pinker数据
- 原始数据:
deptData:{
indexnum:0,
list: [
{ id:1, name:'服装艺术与工程学院' },
{ id:2, name:'材料设计与工程学院' }
]
}
- 处理后数据:
deptData:{
indexnum:0,
list: [
{ id:1, name:'服装艺术与工程学院' },
{ id:2, name:'材料设计与工程学院' }
],
pinkerlist: [
'服装艺术与工程学院','材料设计与工程学院'
]
}
结构:
--wxml--
选择学院:
{{deptData.pinkerlist[deptData.indexnum]}}
--js--
changeDeptFn(ev){
var _that = this;
var _indexnum = ev.detail.value;
_that.setData({
['deptData.indexnum']: _indexnum
});
//根据学院,加载专业
_that.getSpecDataFn();
}
- 组件的使用
组件目录位置:同pages相同目录的components文件夹
- 设置组件
配置组件目录下的 .json文件
{
"usingComponents": {},
"component": true
}
- 使用组件
配置组件目录下的 .json文件
{
"usingComponents": {
"workDetail": "/components/workdetail/index"
}
}
引入组件
25-1. 组件内通信 官方链接 - https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/events.html
- 父组件 -> 子组件
通过挂载属性的方式实现,数据挂载
//--wxml
//--js
addCollectFn(e){
console.log(e.detail);
}
- 子组件 -> 父组件
//--js
this.triggerEvent('addCollectFn', _addObj);
25-2. 父组件使用子组件内的方法
//--wxml
//--js
onReady(){
this.selectComponent('#workDetailCmp').fn1();
}
- 通过option设置数值,并将数据挂载至组件上,子组件内通过properties获取数据,
在子组件的生命周期 lifetimes 的attached内调用,存在延迟问题
- 问题产生的原因:
lifetimes 生命周期 -https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/lifetimes.html
内,attached调用,父组件挂载的数据还没过来,等父组件的数据更新后,attached内方法不会再执行
lifetimes: {
attached: function() {
// 在组件实例进入页面节点树时执行
//初始化菜单索引位置
this.getProductDetFn();
},
detached: function(){
// 在组件实例被从页面节点树移除时执行
}
}
- 解决:
挂载的方法,可以对数据设置数据开关,等数据加载完毕后,打开开关进行组件数据挂载
如:
'userCollect': {
'data': [],
'loaded': false
}
- 设置数据 setData 中的问题
- 设置数据时候,如果下面书写会报错
data:{
'userCollect': {
'data': [],
'loaded': false
}
},
fn1(e){
//错误
//var _userCollect = _that.data.userCollect;
//_userCollect.data.push(_tempAddObj);
//正确
var _userCollect = _that.data.userCollect.data;
_userCollect.push(_tempAddObj);
_that.setData({
['userCollect.data']: _userCollect
});
}
- webview相关
- 创建小程序,使用企业身份申请,完成转账等身份验证
- 配置开发管理-开发设置-业务域名,下载验证文件,上传至业务域名服务器的根下
- wxml添加
- 查看 官方文档 -
https://developers.weixin.qq.com/miniprogram/dev/component/web-view.html
, 下载 JSSDK 1.3.2 -https://res.wx.qq.com/open/js/jweixin-1.3.2.js
,以便在H5页面内使用小程序的API(如返回路由)
- 点击事件,阻止冒泡之 catchtap
- bindtap是冒泡的
- catchtap是非冒泡的
- 监听用户上拉触底事件
- 可以在app.json的window选项中或页面配置中设置触发距离onReachBottomDistance。
- 在触发距离内滑动期间,本事件只会被触发一次。
- onReachBottom生命周期内处理事件
- 地图API、组件
- 地图组件
- 地图属性
latitude -- 地图中心纬度 (-90 ~ 90) - 【特别注意都是数字类型】
longitude -- 地图中心经度 (-180 ~ 180) - 【特别注意都是数字类型】
- 地图遮盖物点击事件:bindmarkertap
- 地图获取上下文方法: wx.createMapContext(string mapId, Object this) -- 版本要求:1.9.6
- 地图移动地图中心点的方法
_that.mapCtx.moveToLocation({
longitude: Number(新的中心点经度),
latitude: Number(新的中心点纬度),
success(){},
fail(err){
console.log(err);
}
});
- 地理位置授权 app.json
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
},
- 监听组件上的参数变化
//--wxml
//--js
lifetimes:{
...
},
observers:{
//基础库版本 2.6.1 开始支持
'worksid': function(newV) {
var version = wx.getSystemInfoSync().SDKVersion;
var _btn = app.compareVersion(version,'2.6.1');
if(_btn){
// console.log(newV);
//加载作品详情
this.getProductDetFn();
}else{
wx.showModal({
title: '提示',
content: '当前微信版本过低,无法使用该功能,请升级到最新微信版本后重试。'
});
};
}
}
- 页面渲染截断的情况
- 产生原因:
setData接口的调用涉及逻辑层与渲染层间的线程通信,通信过于频繁可能导致处理队列阻塞,界面渲染不及时而导致卡顿,应避免无用的频繁调用。 - 解决方案:
每秒调用setData的次数不超过 20 次
-
小程序页面性能监测 官方地址 -
https://developers.weixin.qq.com/miniprogram/dev/framework/audits/best-practice.html
小程序开发工具 在调试器区域切换到 Audits 面板,
点击”开始“按钮,然后自行操作小程序界面,运行过的页面就会被“体验评分”检测到。
小程序获取用户信息 新要求
推荐使用 wx.getUserProfile ,不推荐使用getUserInfo
具体范例:
- 不推荐使用getUserInfo获取用户信息,预计自2021年4月13日起,getUserInfo将不再弹出弹窗,并直接返回匿名的用户个人信息
如下:
- 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
//--wxml
//--js
onLoad() {
if (wx.getUserProfile) {
this.setData({
canIUseGetUserProfile: true
})
}
},
getUserProfile(e) {
// 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
// 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
wx.getUserProfile({
desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: (res) => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
},
- 一个两列的瀑布流如何实现,(支持分页)
- 原理:
第一步,将一个数据列表,拆为两个数据列表,并定义两个列表的初始高度
第二步,每次请求后台获取过来的数据,对比一下两列累积高度,哪一侧小,就优先将数据插入至哪一侧,并将对应侧的高度累加
第三步,将更新后的两列数据以及累加后的两个高度值,返回
第四步,与后台接口开发人员,提前告知返回图片宽、高 字段(如例子中的ImgWidth,ImgHeight),用于计算列表高度使用
注:
第一,由于列表高度考量因素是图片高度,如果列表单元,是一个图文配,那么需要将文字部分设置统一高度,文字溢出省略号,才可以(单行省略号或多行省略号)
目的还是将判断哪一列插入数据的考量,缩小为图片高度一个判断依据
第二,瀑布流中两列的宽度必须一致(如列中:375屏宽下,单列162rpx宽),否则两列宽度不统一,通过图片(widthFix模式)获取的等比高度,将不能用于判断
- 数据结构:
'mainNews':{
'loaded': false,
'isLoading': false,
'noMore': false,
'totalcount':0,
'pageindex': 0,
/*变量名代表:左侧数据列表,右侧数据列表,左侧列表累积高度,右侧列表累积高度*/
'listL': [],
'listR': [],
'leftNowH':0,
'rightNowH':0
},
- 全局app.js内,工具函数
/*瀑布流排序 - 计算高度,重新排序
* 如果存在分页
* 传入值: datalist- 待处理数据 | oldLeftH- 旧的左侧高度 | oldRightH- 旧的右侧高度
* 输出值: leftArr- 左侧数据 | rightArr- 右侧数据 | leftNowH - 左侧累积值 | rightNowH - 右侧累计值
*/
sortListFn(datalist,oldLeftH,oldRightH){
var _leftH = oldLeftH || 0;
var _rightH = oldRightH || 0;
var _leftArr = [];
var _rightArr = [];
datalist.forEach(function(item,index){
//162(rpx)为一个页面布局宽度,计算值为布局宽度内的图片高度,仅比大小,不用那么精细
var _layoutH = Math.ceil((item.ImgHeight*162)/item.ImgWidth);
if(_leftH>_rightH){
_rightArr.push(datalist[index]);
_rightH+=_layoutH;
}else{
_leftArr.push(datalist[index]);
_leftH+=_layoutH;
};
});
//每次更新后的,两侧累积高度:
//console.log(_leftH,_rightH);
return {
leftArr: _leftArr,
rightArr: _rightArr,
leftNowH: _leftH,
rightNowH: _rightH
};
}
- 如何使用工具函数
... 某事件回调内
var _that = this;
_that.getDataFn(function(res){
var _mainNews = _that.data.mainNews;
var _sortRes = app.sortListFn(res.data.list,_mainNews.leftNowH,_mainNews.rightNowH);
var _listL = _mainNews.listL.concat(_sortRes.leftArr);
var _listR = _mainNews.listR.concat(_sortRes.rightArr);
//有无更多数据
var _totalcount = res.data.totalcount;
//设置数据
_that.setData({
["mainNews.listL"]: _listL,
["mainNews.listR"]: _listR,
["mainNews.leftNowH"]: _sortRes.leftNowH,
["mainNews.rightNowH"]: _sortRes.rightNowH,
["mainNews.loaded"]: true,
["mainNews.totalcount"]: _totalcount
});
//有无更多数据
if((_listL.length+_listR.length) == _totalcount){
_that.setData({
["mainNews.noMore"]: true
});
}else{
_that.setData({
["mainNews.noMore"]: false
});
};
},function(err){
console.log(err);
});
...
占坑....