- 了解小程序的启动流程(上)
[视频] *所有课程源码的链接:https://gitee.com/geektime-geekbang_admin/weapp_optimize 你好,我是李艺,是腾讯云TVP 、小程序从0到1的作者,极客时间 微信小程序全栈开发实战课程讲师。 小程序上线已经5年时间了,日活目前已经达到了4.5亿+,已然成为任何一家互联网企业都不能忽视的产品运营阵地。小程序的开发能力经过微信团队数年来的不断地努力,目前已经日臻完善,它早已经不是那个随随便便翻翻文档就可以完成开发的一个简易框架了,尤其是在性能优化方面,会优化与不会优化 ,对于产品的运行效果可以说有霄壤之别。 首先我们看第一部分,需要明确一下有哪些需要优化的现象。不知道你的小程序产品有没有遇到过这样的一些问题,点开小程序一直都是白屏什么也看不到、Loading加载提示转了好几圈页面还不显示,单击页面链接的时候页面跳转迟钝迟迟打不开、有的按钮单击了好几次一点反应也没有,长列表内容在滑动的时候越往下滑 页面越卡顿。你有没有因为这些问题而遭受过用户的抱怨,你会以为这些问题都是因为平台技术不完善而造成的吗? 那么为什么京东 滴滴等大厂的一些小程序,它们的功能那么繁杂。但使用起来却还是那么流畅呢,当我们抱怨框架不够给力的时候,我们对微信小程序的性能优化技巧又真正了解和使用多少?性能优化它是一个现人现地的活,讲究具体问题具体分析,需要有一个字节 一个字节去抠,一个毫秒 一个毫秒去节省的这样的一个细致精神。这个课程我们会演示相关的性能优化技巧。我准备了一个性能比较堪忧的项目,这个课程我们就一起诊断 优化这个项目,让它从体态臃肿的一个状态慢慢变得健步如飞,为了更好地理解和应用小程序优化技巧,在开始实践之前,我们十分有必要看一下小程序整体的运行环境以及启动的流程。小程序的运行环境大体可以分为三类,第一类是iOS端、Mac微信端,第二类是Android端 PC微信端,第三类就是我们开发者经常使用的微信开发者工具模拟器端。另外还有儿童手表上面也有微信,但是那个环境它没有小程序,所以不在我们的讨论范围之内。 三类的运行环境,虽然它们在底层是基于不同的技术实现的,但是它们的启动流程大体上是相似的,小程序的优化主要是指从小程序开始启动到首页完全渲染显示,也就是Page.onReady事件派发,这个过程之间的一个优化。这个过程主要包含了三个流程的节点,这一步包括小程序运行进程及运行环境的准备,这里面具体又包括拉取小程序基本信息,包括代码包版本 地址等信息,另外还有Native小程序进程和微信基本模块的一个初始化。例如在Android环境里面有Activity活动组件的一个初始化,再往下是代码包的下载 校验以及初始化,再往后是系统组件 WebView组件容器和原生组件的一个初始化,最后是JS引擎初始化以及域的创建。 下面我们看第二步关于代码注入,这一步主要包含两大部分:第一部分是框架及第三方基础代码的一个初始化,这里面又分为三个小部分: 第一部分是小程序基础库的注入; 第二小部分是扩展库,例如我们在配置文件里面通过使用useExtendedLib引入的WeUI以及kbone这样的一个类库的初始化; 第三小部分是插件、自定义组件 扩展库代码的一个注入。 第二部分是开发者代码的一个注入,这个里面主要分为两个小部分: 第一个小部分是开发者逻辑层代码的一个注入,这里会派发小程序里面的App.onLaunch还有App.onShow这些事件的一个派发,这些事件都是在这个阶段进行派发的; 第二小部分是开发者视图层代码的一个注入,包括公共代码以及页面代码的一个注入。 下面我们看第三部分关于首屏渲染,这个部分大致可以分为五个小部分: 第一小部分是页面的初始化,这个时间点是initDataSendTime这个时间点的一个触发时机,会有Page.onLoad一个事件的派发; 第二小部分是时间点走到viewLayerReaderStartTime这样的一个时间点的阶段,这个时候会有Page.onShow事件的一个派发; 第三小部分是开发者代码从后端拉取数据,准备data数据,这个时候也是一个阶段,是第三小部分; 第四小部分是页面的一个整体的渲染; 第五部分是当这个时间点走到viewLayerReaderEndTime这个时间点的时候,它会有一个Page.onReady事件的派发,这个时候就标志着我们首屏渲染的一个完成。 小程序采用逻辑层、视图层双线程运行机制,Native的工作准备它是先于这两个线程开始之前开始的,基础的执行环境准备好以后,逻辑层与视图层两个线程才开始工作,并且两个线程几乎是并发执行的。在视图层与逻辑层它代码完全注入以后,这个时间点它会对齐以后才会进入下一个阶段,也就是首屏渲染这个阶段的开始执行。 这些节点它并不是每一次小程序启动时都会经历的,有些会有,有些不会有。微信有运行环境预加载机制,如果小程序在启动时命中了预加载的环境,有关准备运行环境的节点就可以省略掉,这一部分的启动时间也可以节省了。对于开发者紧跟小程序框架的更新,及时使用用户覆盖率最广的基础库版本,让自己的小程序运行环境大众化、普通化,则有助于预加载环境的一个命中,终端类型不同经历的节点也不尽相同。 在Android上小程序启动的时候,微信它开启了新线程,在iOS上则没有 iOS上小程序它在启动的时候,它会复用与微信相同的一个进程,因此Android上有小程序进程与Activity初始化这样的一个节点,在iOS上则没有,再加上iOS的设备它普遍的性能是高于Android设备的,所以这使得iOS的设备的启动的效率普遍就高于Android。 对于相同版本的小程序在同一性能级别的设备上运行,iOS设备平均会比Android大概会少0.5的这样一个启动时间,另外还有启动方式对流程经历的节点也有影响。 下面我们看一下启动方式。小程序按启动方式不同分为冷启动和热启动两种方式。 什么是冷启动?什么是热启动?如果小程序在用户设备上是第一次打开或者是销毁后再次打开,这个时候的启动就是冷启动。热启动是相对冷启动而言的,热启动是小程序启动的一种优化机制,小程序进入后台30分钟以内再次进入前台,可以直接从后台状态然后恢复到前台,在这种热启动方式里面,像前面我们提到的小程序基本信息、拉取代码包的下载还有JS引擎初始化等等这些流程节点,甚至像App.onLaunch、Page.onLoad以及Page.onReady这些一次性的流程事件都不会有了。小程序第一次启动以及冷启动30分钟以后被系统回收重新再启动都是冷启动。 前面我们讲的启动流程的主要节点是冷启动流程的节点。我们说的小程序性能优化主要是指冷启动性能的一个优化以及运行时渲染性能的一个优化,在小程序冷启动流程里边涉及到一些程序以及页面事件。下面我们统一看一下这些事件。 在小程序中App与Page都有它们各自的一个生命周期函数,这些周期函数有一些与启动流程是密切相关的。我们先看一下App周期函数,这里面有三个事件需要我们注意: 第一个是onLaunch,它是监听小程序初始化的一个事件; 第二个是onShow 监听小程序启动或切前台这样一个事件; 第三个是onHide 监听小程序切后台这样的一个事件。 下面我们再看一下Page周期函数。这个里面有五个事件是与优化相关的: 第一个是onLoad它监听页面加载; 第二个是onShow监听页面显示; 第三个是onReady监听页面初次渲染完成; 第四个是onHide监听页面隐藏; 第五是onUnload监听页面卸载。 App.onShow事件和Page.onShow事件是视图界面开始显示时派发的,它们会重复派发与启动流程优化密切相关的一次性事件,主要有App.onLaunch Page.onLoad和Page.onReady这三个事件,在这三个事件节点恰当的安排执行合适的逻辑代码是优化的重要技巧一。 至于像App.onShow App.onHide以及Page.onHide Page.onUnload是与运行时性能优化十分相关的一些事件,下面我们根据小程序的冷启动流程以及与其相关的密切相关的一些八个生命周期函数大致讲一下有哪些节点是可以优化的。 第一条在这个环境准备阶段中,在拉取小程序基本信息阶段,这个阶段是有可能优化的。微信对用户设备上经常使用的小程序它会有轮询机制,在轮询的时候会自动拉取小程序的一个基本信息,正常情况下这个小程序的基本信息的一个拉取它是同步的,它会阻塞我们后续流程节点的一个执行,如果通过轮询节省了这样的一个过程,启动流程跳过这个节点时间是可以节约的,当然这个节点开发者基本上做不了什么事情,开发者并不能左右微信的轮询机制。但是越受用户欢迎的一个小程序因为它属于经常使用的小程序,它会命中轮询机制,启动的一个性能也会更好,而那些不被用户经常访问的小程序反而没有这个福利,这大概就是技术里面的一个马太效应,就是好的会更好然后坏的会越差。 针对环境准备阶段微信提供了环境预加载机制,微信客户端会根据用户设备的使用场景和设备资源的一个消耗情况,依据一定的一个策略,在小程序启动之前对运行环境进行部分的预加载,这个过程开发者基本也无法干涉,开发者能做的仅是紧跟小程序基础库的一个更新,积极使用最新的、最普遍的、最广泛的一个基础库版本以及提高预加载环境的一个命中率。 在代码注入阶段,逻辑层与视图层代码都需要注入,两个线程的代码都注入完成以后首屏渲染流程才能开始,Page.onLoad事件才能触发。我们可以想方设法减少代码的一个注入量和复杂度以期减少启动时间,小程序在这方面有分包、有独立分包、有按需注入、有用时加载和占位组件等等这些特性,这些都是这一阶段的一些优化技巧。这些技巧稍后我们在课程里面都会详细介绍。 在合适的生命周期函数节点执行合适的代码也可以优化启动性,Page.onReady事件派发于首屏渲染完成的时候,如果我们要从后端拉取数据并在首页上进行渲染,在这个事件函数里面执行拉取操作,势必会造成二次渲染的CPU资源浪费,但如果我们在Page.onLaunch这个事件触发的时候就开始数据拉取,又可能会阻塞小程序正常的一个启动流程,在这种情况下我们要怎么去做?我们可以使用异步转同步的编程范式以及使用并发复合命令,在多个文件里边对齐这个代码的执行点,这样的话就显得尤为重要了。具体的优化办法,稍后我们在课程里面会详细讲解。 从Page.onLoad事件派发页面开始渲染到Page.onReady这个事件派发首屏渲染完成,这中间涉及到的动态数据加载,其加载的数据量有多少、网络请求所需的时间有多少还有图片等静态资源它加载所需要的时间有多少,都会影响首屏渲染的一个效率,这个阶段使用骨架屏技巧包括压缩图片、提高服务器接口响应效率和数据传输效率等等,这些都可以优化首屏渲染的一个用户体验。针对小程序里面用到的一些数据,微信还提供了数据预加载周期性更新机制,不需要开发者自己去拉取微信就可以代为拉取,小程序在启动的时候,直接取用这些已经加载好的数据就可以了,这也是优化启动流程的一个技巧之一。 当然了这个技巧是微信团队特意为开发者而设计的,针对低端机首次渲染需要较长的一个时间,微信提供了初始渲染缓存机制,启用初始渲染缓存可以使视图层不需要等待逻辑层代码初始化完毕就可以直接提前将这个页面初始化的数据渲染的结果展示给用户。 以上就是针对启动流程中部分节点的一个性能优化技巧,稍后我们在课程里面都会详细地进行讲解在运行的时候针对小程序的双线程运行机制和视图重渲染机制也有相关的一些性能优化技巧。下面我们就再看一下这方面的一些技巧。
2022-07-29 - 实现一个虚拟滚动的React-Hook
前言 网页的日常开发中,渲染长列表的场景非常常见。比如旅游网站需要完全展示出全国的城市列表,或者购物网站的商品列表。 长列表的数量一般在几百条范围内不会出现意外的效果,浏览器本身足以支撑.可一旦数量级达到上千,页面渲染过程会出现明显的卡顿。数量突破上万甚至十几万时,网页可能直接崩溃了。 为了解决长列表造成的渲染压力,业界出现了相应的应对技术,即长列表的[代码]虚拟滚动[代码]。 [代码]虚拟滚动[代码]的本质,不管页面如何滑动,[代码]HTML 文档[代码]只渲染当前屏幕视口展现出来的少量[代码]Dom[代码]元素。 假设长列表有[代码]10[代码]万条数据,,对用户而言,他永远只会看到屏幕展现出的那十几条数据。因此页面滑动时,通过监听滚动事件快速切换视口的数据,就能高度模拟滚动效果。 参考下图加深理解: [图片] [代码]虚拟滚动[代码]最终只需要渲染少量的[代码]Dom[代码]元素就能模拟出相似的滚动效果,这让前端工程师开发几万甚至十几万条的长列表都成为了可能。 下图是手机上实测滑动一张涵盖全球所有城市的长列表页面. [图片] 虚拟滚动实现步骤 实现「虚拟列表」可以简单理解为就是在列表发生滚动时,改变「可视区域」内的渲染元素。大概的文字逻辑步骤如下: 根据单个元素高度计算出滚动容器的可滚动高度,并撑开滚动容器; 根据可视区域计算总挂载元素数量; 根据可视区域和总挂载元素数量计算头挂载元素(初始为 0)和尾挂载元素; 当发生滚动时,根据滚动差值和滚动方向,重新计算头挂载元素和尾挂载元素。 核心的实现步骤: [图片] 数据项不定高 还可以稍微做点小拓展,将item高度设定为一个配置项,可以设定为一个方法,改方法以每一个数据项的值和索引index作为参数。 按照这种方式,我们的hook能支持的自定义化会更加强大一些,我们在计算展示List片段的时候,在需要用到item高度的时候都需要针对性的做一些改变,高度不一定是个固定数值,可能是一个方法。 虚拟滚动和React Hook [代码]Hook[代码]是 React 16.8 中增加的新功能,可以让我们更好地复用React状态逻辑代码,我们可以使用React提供给我们的hook将虚拟滚动抽取为一个hook,方便我们随时复用这块功能。 Hook整合虚拟滚动 利用 [代码]useEffect[代码]监听容器元素,当容器元素渲染完毕,注册[代码]Scroll[代码]事件监听,当Scroll事件触发时候,根据滚动距离计算需要展示的List片段 按照虚拟滚动原理,截取需要展示的List并重置页面state,触发页面重新渲染 hook将需要展示的List片段作为状态返回,方便Hook与组件进行交互 总结 通过借助Hook和我们对于虚拟滚动功能的抽象,我们后续就可以很方便的给我们组件复用这块相对复杂的功能。 [代码]const [list, scrollTo] = useVirtualList(originalList, { containerTarget: containerRef, wrapperTarget: wrapperRef, itemHeight: 60, overscan: 10, }); [代码] 存在的不足 虚拟功能虽然很强大,但是仍然存在它的不足,列表项的高度必须相对固定,如果每个列表项渲染的高度完全未知,那虚拟滚动功能就无法使用。 链接 源代码地址 demo地址
2022-06-27 - JavaScript对象、数组、日期操作方法封装
1.获取对象属性个数 objLength(obj) { var count = 0; for(var i in obj) { if(obj.hasOwnProperty(i)) { count++; } } return count; }, 2.数组排序 sortArr(arr,s){ // s:true 升序 false 降序 var s = '' || s; arr.sort(function (a, b) { if (s) {//从小到大排序 return a - b; }else{//从大到小排序 return b - a; } }); return arr;//返回已经排序的数组 }, 3.根据数组对象中的某个属性值进行排序的方法 sortBy(arr,attr,rev){ /*** arr 需要排序的数组 * attr 排序的属性 如number属性 * rev true表示升序排列,false降序排序 * */ //第二个参数没有传递 默认升序排列 if(rev == undefined){ rev = 1; }else{ rev = (rev) ? 1 : -1; } return arr.sort(function(a,b){ a = a[attr]; b = b[attr]; if(a < b){ return rev * -1; } if(a > b){ return rev * 1; } return 0; }) }, 4.数组去重 unique(arr) { return Array.from(new Set(arr)) }, 5.根据数组对象的某个属性进行去重 uniqueObj(arr1,from) { // arr1:要去重的数组 from:属性 const res = new Map(); return arr1.filter((a) => !res.has(a[from]) && res.set(a[from], 1)) }, 6.对象数组去重并且保留最后的对象 arrayUnique2(arr, name) { var hash = {}; return arr.reduce(function (acc, cru,index) { if (!hash[cru[name]]) { hash[cru[name]] = {index:index} acc.push(cru) }else{ acc.splice(hash[cru[name]]['index'],1,cru) } return acc; }, []); }, 7.删除数组中小于某个值的元素 handleArr(arr,smail){ // arr:数组 small:需要比这个值小 var newArr = arr.filter(item => item > smail); return newArr; }, 8.将一个数组等分成若干个数组,每个数组里有n条数据 bisectionArr(arr,n){ /**arr:数据 n每个数组里保留几条数据 * 用法 * app.bisectionArr(this.data.Arr,5); **/ var n=Number(n); var newarr = []; var len = arr.length / n; len = Math.ceil(len); for (var j = 1; j <= len; j++) { newarr[j - 1] = []; for (var i = (n * j - n); i < n * j; i++) { if (arr[i] != undefined) { newarr[j - 1].push(arr[i]); } } } return newarr; }, 9.根据身份证号获取出生年月日 getBirthdayFromIdCard(idCard) { var birthday = ""; if (idCard != null && idCard != "" && checkIdcard(idCard)) { if (idCard.length == 15) { birthday = "19" + idCard.substr(6, 6); } else if (idCard.length == 18) { birthday = idCard.substr(6, 8); } birthday = birthday.replace(/(.{4})(.{2})/, "$1-$2-"); } return birthday; }, 10.只能输入金额 onlyMoney(val){ var regStrs = [ ['^0(\\d+)$', '$1'], //禁止录入整数部分两位以上,但首位为0 ['[^\\d\\.]+$', ''], //禁止录入任何非数字和点 ['\\.(\\d?)\\.+', '.$1'], //禁止录入两个以上的点 ['^(\\d+\\.\\d{2}).+', '$1'] //禁止录入小数点后两位以上 ]; for(var i=0; i<regStrs.length; i++){ var reg = new RegExp(regStrs[i][0]); val = val.replace(reg, regStrs[i][1]); } return val; }, 11.根据身份证号返回所在省 getProvinceFromIdCard(idCard) { var aCity={11:"北京",12:"天津",13:"河北",14:"山西",15:"内蒙古",21:"辽宁",22:"吉林",23:"黑龙江 ",31:"上海",32:"江苏",33:"浙江",34:"安徽",35:"福建",36:"江西",37:"山东",41:"河南",42:"湖北 ",43:"湖南",44:"广东",45:"广西",46:"海南",50:"重庆",51:"四川",52:"贵州",53:"云南",54:"西藏 ",61:"陕西",62:"甘肃",63:"青海",64:"宁夏",65:"新疆",71:"台湾",81:"香港",82:"澳门",91:"国外"}; return aCity[idCard.substr(0,2)]; }, 12.获取当前日期 getNowDate(){ var date=new Date(); var year=date.getFullYear(); var month=date.getMonth()+1; var day=date.getDate(); var hh=date.getHours(); var mm=date.getMinutes(); var ss=date.getSeconds(); var arr=['日','一','二','三','四','五','六']; var week=arr[date.getDay()]; function add0(val){ return (val<10? '0'+val : val); } month=add0(month);day=add0(day);hh=add0(hh);mm=add0(mm);ss=add0(ss); return { data:year+'-'+month+'-'+day+' '+hh+':'+mm+':'+ss, year:year, month:month, day:day, hh:hh, mm:mm, ss:ss, week:week, }; }, 13.日期转时间戳 dateToChuo(riqi){ //riqi格式 2020-01-01 00:00:00 riqi=riqi.replace(/-/g,"/"); var date = new Date(riqi);//兼容ios var time = parseInt(date.getTime()/1000);//除以1000为10位时间戳 不除为13位 return time; }, 14.时间戳转日期 chuoToDate(timestamp){ if(!timestamp){ return false; } timestamp=timestamp.toString().length<13? (timestamp * 1000):timestamp; var date = new Date(timestamp);//时间戳为10位需*1000,时间戳为13位的话不需乘1000 var Y = date.getFullYear(); var M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1); var D = (date.getDate()<10? '0'+date.getDate() : date.getDate()); var h = (date.getHours()<10? '0'+date.getHours() : date.getHours()); var m = (date.getMinutes()<10? '0'+date.getMinutes() : date.getMinutes()); var s = (date.getSeconds()<10? '0'+date.getSeconds() : date.getSeconds()); // + h + m + s return { data:Y+'-'+M+'-'+D+' '+h+':'+m+':'+s, year:Y, month:M, day:D, hh:h, mm:m, ss:s } }, 15.获取当前时间的后20分钟、后一小时(add是正数)、前一天(add是负数)等等相对时间 setDefaultTime(add,danwei){ // 调用方法:app.setDefaultTime(20,'天');20天以后的时间 var danwei=danwei || '秒';//默认单位是秒 if(danwei=='秒'){ add=add*1000;// add 单位是秒 }else if(danwei=='分'){ add=add*1000*60;// add 单位是分 }else if(danwei=='时'){ add=add*1000*60*60;// add 单位是时 }else if(danwei=='天'){ add=add*1000*60*60*24;// add 单位是天 } let t = new Date().getTime() + add; let d = new Date(t); let theMonth = d.getMonth() + 1; let theDate = d.getDate(); let theHours = d.getHours(); let theMinutes = d.getMinutes(); let getSeconds=d.getSeconds(); function add0(val){ return (val<10? '0'+val : val); } theMonth=add0(theMonth); theDate=add0(theDate); theHours=add0(theHours); theMinutes=add0(theMinutes); getSeconds=add0(getSeconds); let date = d.getFullYear() + '-' + theMonth + '-' + theDate; let time = theHours + ':' + theMinutes + ':' + getSeconds; let Spare = date + ' ' + time; return { datas:d.getFullYear()+'-'+theMonth+'-'+theDate, data:Spare, year:d.getFullYear(), month:theMonth, day:theDate, hh:theHours, mm:theMinutes, ss:getSeconds } }, 16.已知开始日期和结束日期 计算出中间的所有日期 getAllDate(start, end){ // start:2020-07-14 end:2020-07-20 let dateArr = [] let startArr = start.split('-') let endArr = end.split('-') let db = new Date() db.setUTCFullYear(startArr[0], startArr[1] - 1, startArr[2]) let de = new Date() de.setUTCFullYear(endArr[0], endArr[1] - 1, endArr[2]) let unixDb = db.getTime() let unixDe = de.getTime() let stamp const oneDay = 24 * 60 * 60 * 1000; for (stamp = unixDb; stamp <= unixDe;) { // parseInt(stamp) 13位的时间戳 dateArr.push(this.format(new Date(parseInt(stamp)))) stamp = stamp + oneDay } return dateArr }, format(time){ // time=new Date(13位的时间戳) let ymd = '' let mouth = (time.getMonth() + 1) >= 10 ? (time.getMonth() + 1) : ('0' + (time.getMonth() + 1)) let day = time.getDate() >= 10 ? time.getDate() : ('0' + time.getDate()) ymd += time.getFullYear() + '-' // 获取年份。 ymd += mouth + '-' // 获取月份。 ymd += day // 获取日。 return ymd // 返回日期。2020-07-14 }, 17.获取上个月的年月 getLastMonth(riqi){ riqi=riqi.replace(/-/g,"/"); var date = new Date(riqi); var year = date.getFullYear(); var month = date.getMonth(); if(month == 0){ year = year -1; month = 12; } return { year:year, month:month<10? '0'+month : month }; }, 18.获取指定日期的星期 getWeek(riqi){ riqi=riqi.replace(/-/g,"/"); var date = new Date(riqi);//兼容ios var arr=['日','一','二','三','四','五','六']; var week=arr[date.getDay()]; return week; }, 19.当前日期是今年的第几周 getYearWeek(year,month,date){ /* app.getYearWeek(2019,4,19) dateNow是当前日期 dateFirst是当年第一天 dataNumber是当前日期是今年第多少天 用dataNumber + 当前年的第一天的周差距的和在除以7就是本年第几周 */ let dateNow = new Date(year, parseInt(month) - 1, date); let dateFirst = new Date(year, 0, 1); let dataNumber = Math.round((dateNow.valueOf() - dateFirst.valueOf()) / 86400000); return Math.ceil((dataNumber + ((dateFirst.getDay() + 1) - 1)) / 7); }, 20.当前日期是当月的第几周 getMonthWeek(year,month,date){ /* app.getMonthWeek(2019,4,19) month = 6 - w = 当前周的还有几天过完(不算今天) year + month 的和在除以7 就是当天是当前月份的第几周 */ let dateNow = new Date(year, parseInt(month) - 1, date); let w = dateNow.getDay();//星期数 let d = dateNow.getDate(); return Math.ceil((d + 6 - w) / 7); }, 21.判断某年某月有多少天 getCountDays(ym) { var curDate = new Date(ym.replace(/-/g,"/")); /* 获取当前月份 */ var curMonth = curDate.getMonth(); /* 生成实际的月份: 由于curMonth会比实际月份小1, 故需加1 */ curDate.setMonth(curMonth + 1); /* 将日期设置为0 */ curDate.setDate(0); /* 返回当月的天数 */ return curDate.getDate(); }, 22.获取指定日期的第几个月 getHowMonth(date,num) { // date 格式为yyyy-mm-dd的日期,如:2014-01-25 // num 第几个月 下一个月 1 下两个月 2 上一个月-1 上两个月-2 以此类推 date=date.replace(/-/g,"/"); var dt=new Date(date); return this.chuoToDate(dt.setMonth(dt.getMonth() + Number(num))); }, 23.计算两个日期之间相差的年月日 monthDayDiff(startDate,endDate) { let flag = [1, 3, 5, 7, 8, 10, 12, 4, 6, 9, 11, 2]; let start = new Date(startDate); let end = new Date(endDate); let year = end.getFullYear() - start.getFullYear(); let month = end.getMonth() - start.getMonth(); let day = end.getDate() - start.getDate(); if (month < 0) { year--; month = end.getMonth() + (12 - start.getMonth()); } if (day < 0) { month--; let index = flag.findIndex((temp) => { return temp === start.getMonth() + 1 }); let monthLength; if (index <= 6) { monthLength = 31; } else if (index > 6 && index <= 10) { monthLength = 30; } else { monthLength = 28; } day = end.getDate() + (monthLength - start.getDate()); } return { year,month,day }; }, 24.计算两个时间之间的差 diffTime(startDate,endDate) { //用法 diffTime('2017-03-02 09:10:10','2017-03-17 04:10:12') startDate=startDate.replace(/-/g,'/');//ios兼容 endDate=endDate.replace(/-/g,'/');//ios兼容 startDate= new Date(startDate); endDate = new Date(endDate); var diff=endDate.getTime() - startDate.getTime();//时间差的毫秒数 //计算出相差天数 var days=Math.floor(diff/(24*3600*1000)); //计算出小时数 var leave1=diff%(24*3600*1000);//计算天数后剩余的毫秒数 var hours=Math.floor(leave1/(3600*1000)); //计算相差分钟数 var leave2=leave1%(3600*1000);//计算小时数后剩余的毫秒数 var minutes=Math.floor(leave2/(60*1000)); //计算相差秒数 var leave3=leave2%(60*1000);//计算分钟数后剩余的毫秒数 var seconds=Math.round(leave3/1000); var returnStr = seconds + "秒"; if(minutes>0){ returnStr = minutes + "分" + returnStr;} if(hours>0){returnStr = hours + "小时" + returnStr;} if(days>0){returnStr = days + "天" + returnStr;} return { data:returnStr, day:days,hh:hours,mm:minutes,ss:seconds }; }, 25.倒计时 countDown(jssj,success,times){ /*用法:app.countDown("2020-08-24 07:23:00",function(res){ console.log(res) },1000) jssj:设置结束时间 2020-08-10 12:12:12 times:设置倒计时的时间间隔 */ fun(); var timer=setInterval(function(){ fun(); },times); function fun(){ var lefttime = parseInt((new Date(jssj.replace(/-/g,"/")).getTime() - new Date().getTime())); if(lefttime <= 0) { success({day:"00",hour:"00",min:"00",sec:"00"}); clearInterval(timer); return; } var d = parseInt(lefttime /1000 /3600 /24); //天数 var h = parseInt(lefttime / 1000 / 3600 % 24); //小时 var m = parseInt(lefttime / 1000 / 60 % 60); //分钟 var s = parseInt(lefttime / 1000 % 60); //当前的秒 d < 10 ? d = "0" + d : d; h < 10 ? h = "0" + h : h; m < 10 ? m = "0" + m : m; s < 10 ? s = "0" + s : s; success({ day: d, hour: h, min: m, sec:s }) } }, 26.多长时间之前 timeago(stringTime){ var minute = 1000 * 60; var hour = minute * 60; var day = hour * 24; var week = day * 7; var month = day * 30; var time1 = new Date().getTime();//当前的时间戳 var time2 = Date.parse(new Date(stringTime));//指定时间的时间戳 var time = time1 - time2; var result = "刚刚"; if (time < 0) { console.log("设置的时间不能早于当前时间!"); } else if (time / month >= 1) { // result = "" + parseInt(time / month) + "月前"; result=stringTime.slice(0,10);//大于等于1个月的时候显示具体日期 } else if (time / week >= 1) { result=stringTime.slice(0,10);//大于等于1周的时候显示具体日期 // result = "" + parseInt(time / week) + "周前"; } else if (time / day >= 1) { result = "" + parseInt(time / day) + "天前"; } else if (time / hour >= 1) { result = "" + parseInt(time / hour) + "小时前"; } else if (time / minute >= 1) { result = "" + parseInt(time / minute) + "分钟前"; } else { result = "刚刚"; } return result; }, 27.获取n到m之间的所有数 getNDMnumber(n,m){ // n,m是整数 且n<m; var n=Number(n); var m=Number(m); var arr=[]; var i=n; while(i<=m){ arr.push(i); i++; } return arr; },
2022-02-23 - 【接口相关】聊一聊数据接口的登录态校验以及JWT
最近和群里网友(就是评论区厚颜无耻要冠名的那个子不语啦)聊天,发现他在数据接口中校验登录状态用的还是session,在我及时劝说和科普之后,他最终决定改用JWT。那么接下来我们就聊一聊数据接口应该怎么管理登录状态以及什么是JWT 混合开发的时候是怎么做的 前后端混合开发的时候,用户登录状态的管理一般都是通过session来实现的,原理很简单:用户登录后,服务端将登录用户信息存储到服务器上的特定位置,并生成对应的session id存储到浏览器的cookie中。需要校验的时候先读取cookie中的session id,找到服务器中对应的存储内容,完成校验。 很显然,这个机制是建立在cookie基础上的,cookie又依赖于浏览器,而且有域名限制。是不适合app、小程序、以及前后端时数据接口采用其他域名等情况的。 app、小程序、前后端分离的时候要怎么做 app、小程序、前后端分离时的数据接口一般采用token来做登录信息校验。原理是用户登录后,服务端生成对应用户的一个token(一般都是一段无意义的唯一字符串)后返回,app、小程序、前端(以下统称为前端)拿到token后保存,在需要校验用户登录的接口请求中加入token(可以是get、post参数或者http header的形式),服务端拿到token后校验真实性、有效性等信息后完成登录校验。一般为了防止盗用,还会设置一套签名校验的过程。 其实token和session的原理是差不多的,都是服务端将对应用户的一个key(session的时候是session id,token的时候就是token)交给前端,前端通过token请求服务端,服务端再去反查用户,获取用户登录状态。 现在一般微信、微博等接口都是采用的这种方式。但是这种方式也有弊端,主要是: 服务端必须保存token,以及有效期,校验的时候必须要有数据读取的过程; 校验签名的时候一般需要一个secret做为加密签名的附加字符,前端必须也要同时保存这个secret,这样显然不适合代码会暴露的网页前端。 这时候,就轮到我们这次的主角JWT出场了。 什么是JWT JWT是JSON Web Token的简称,有官网详细介绍,大家可以看一看,这里简单说一下。 JWT其实就是一种特殊的token,原理和使用方法自然和token一样。 JWT是由三部分组成的字符串,结构是:头部+主体内容(官方称之为Payload)+签名,三部分用“.”连接。头部和主体内容都是json格式的字符串再经过base64编码,为了方便放在get请求中,还需要把类似“=”、“/”等特殊字符替换掉。 头部内容是固定的,原始json就是下面这样 [代码]{ "alg": "HS256", "typ": "JWT" } [代码] 主要是说明了最后签名部分的加密算法。 重点是中间的主体内容,原始json一般是类似下面这样的 [代码]{ "user": "John Doe", "exp": "2020-01-01 12:24:30" } [代码] 主体内容一个是当前登录的用户,可以是用户id,也可以是用户名等可以检索定位到用户的信息;还有一个就是过期时间。还可以加入一些其他不私密的信息。 服务端拿到JWT之后可以在不读取数据的情况下,仅通过解码这部分信息就可以完成获取登录用户以及判断是否过期等初期工作。 最后的签名一般是把头部、主体内容再加上secret拼接成字符串再加密,这一步在用户登录生成JWT的时候就完成了。服务端拿到JWT之后只需要把前两部分加上secret再计算一次签名加以比对就可以完成校验签名,前端不需要同时保存secret。 JWT官网提供了各种服务端语言的生成代码,这里我提供一个我自己用PHP写的相对简化的方法,供大家参考 [代码]private function _jwt($payload){ $header['alg']='HS256'; $header['typ']='JWT'; $jwt_header=$this->_base64url($header); $jwt_payload=$this->_base64url($payload); $jwt_sign=hash_hmac('sha256', $jwt_header.'.'.$jwt_payload, $this->secret); $jwt['token']=$jwt_header.'.'.$jwt_payload.'.'.$jwt_sign; $jwt['sign']=$jwt_sign; return $jwt; } private function _base64url($a){ $c=base64_encode(json_encode($a)); $c=str_replace('=', '', $c); $c=str_replace('+', '-', $c); $c=str_replace('/', '_', $c); return $c; } [代码] 我这个方法里需要把主题内容以数组形式的参数传入,最终返回了生成的JWT和签名,方便接收时校验签名。 最后再说一下缺点 JWT在实际使用中也是存在问题的,目前想到以下几点: 安全性:签名包含在token中,一旦token整体被盗用,将没有办法区分,所以有网友称之为“裸奔”; 过期时间放在token中而不是服务端保存处理,一旦token生成并签发出去,将无法灵活的控制有效期; 最后一个是用户体验,其实可以算是token方式的通病。这个问题我也和群友讨论过,大家在访问社区的时候应该会遇到过,还在访问过程中突然变成未登录。我觉得这主要时因为服务端在token过期后就即时判断为用户登录失败,不管你在网页上处于什么状态。这个问题在session方式中是不存在的,前面说过session依赖于cookie,而存储session id的cookie是会保存在整个浏览器进程,就是说只要浏览器不关闭,用户就可以一直保持登录状态。
2019-12-03 - 如何实现一个自定义数据版省市区二级、三级联动
社区可能有其他的方案了,但是再分享下吧,给有需要的童鞋。 效果图: [图片] 额,这个视频转GIF因为社区上传不了大图,所以剪了一部分,具体的效果还是直接工具打开代码片段预览吧~ 第一步:你的页面JSON引入该组件: [代码]{ "usingComponents": { "city-picker": "/components/cityPicker/index" } } [代码] 第二步:你的页面WXML引入该组件 [代码]<city-picker visible="{{visible}}" column="2" bind:close="handleClick" bind:confirm="handleConfirm" /> [代码] 第三步:你的页面JS调用 [代码]// 显示/隐藏picker选择器 handleClick() { this.setData( visible: !this.data.visible }) }, // 用户选择城市后 点击确定的返回值 handleConfirm(e) { const { detail: { provinceName = '', provinceId = '', cityName, cityId='', areaName = '', areaId = '' } = {} } = e this.setData({ cityId, cityName, areaId, areaName, provinceId, provinceName }) } [代码] 组件属性 属性 默认值 描述 visible false 是否显示picker选择器 column 3 显示几列,可选值:1,2,3 values [0, 0, 0] 必填,默认回填的省市区下标,可选择具体省市区后查看AppData的regionValue字段 close function 点击关闭picker弹窗 confirm function 点击选择器的确定返回值 confirm: 属性 默认值 描述 provinceName 北京市 省份名称 provinceId 110000 省份ID cityName 市辖区 城市名称 cityId 110100 城市ID areaName 东城区 区域名称 areaId 110000 区域Id 至于怎么获取你想默认城市的下标,可以滑动操作下选中省市区后,点击确定后查看appData里的regionValue的值。 以上就是一个自定义数据版本的省市区二级、三级联动啦,老规矩,结尾放代码片段。 https://developers.weixin.qq.com/s/F9k9cTmT7LAz
2022-07-20 - 使用 MobX 来管理小程序的跨页面数据
在小程序中,常常有些数据需要在几个页面或组件中共享。对于这样的数据,在 web 开发中,有些朋友使用过 redux 、 vuex 之类的 状态管理 框架。在小程序开发中,也有不少朋友喜欢用 MobX ,说明这类框架在实际开发中非常实用。 小程序团队近期也开源了 MobX 的辅助模块,使用 MobX 也更加方便。那么,在这篇文章中就来介绍一下 MobX 在小程序中的一个简单用例! 在小程序中引入 MobX 在小程序项目中,可以通过 npm 的方式引入 MobX 。如果你还没有在小程序中使用过 npm ,那先在小程序目录中执行命令: [代码]npm init -y [代码] 引入 MobX : [代码]npm install --save mobx-miniprogram mobx-miniprogram-bindings [代码] (这里用到了 mobx-miniprogram-bindings 模块,模块说明在这里: https://developers.weixin.qq.com/miniprogram/dev/extended/functional/mobx.html 。) npm 命令执行完后,记得在开发者工具的项目中点一下菜单栏中的 [代码]工具[代码] - [代码]构建 npm[代码] 。 MobX 有什么用呢? 试想这样一个场景:制作一个天气预报资讯小程序,首页是列表,点击列表中的项目可以进入到详情页。 首页如下: [图片] 详情页如下: [图片] 每次进入首页时,需要使用 [代码]wx.request[代码] 获取天气列表数据,之后将数据使用 setData 应用到界面上。进入详情页之后,再次获取指定日期的天气详情数据,展示在详情页中。 这样做的坏处是,进入了详情页之后需要再次通过网络获取一次数据,等待网络返回后才能将数据展示出来。 事实上,可以在首页获取天气列表数据时,就一并将所有的天气详情数据一同获取回来,存放在一个 数据仓库 中,需要的时候从仓库中取出来就可以了。这样,只需要进入首页时获取一次网络数据就可以了。 MobX 可以帮助我们很方便地建立数据仓库。接下来就讲解一下具体怎么建立和使用 MobX 数据仓库。 建立数据仓库 数据仓库通常专门写在一个独立的 js 文件中。 [代码]import { observable, action } from 'mobx-miniprogram' // 数据仓库 export const store = observable({ list: [], // 天气数据(包含列表和详情) // 设置天气列表,从网络上获取到数据之后调用 setList: action(function (list) { this.list = list }), }) [代码] 在上面数据仓库中,包含有数据 [代码]list[代码] (即天气数据),还包括了一个名为 [代码]setList[代码] 的 action ,用于更改数据仓库中的数据。 在首页中使用数据仓库 如果需要在页面中使用数据仓库里的数据,需要调用 [代码]createStoreBindings[代码] 来将仓库中的数据绑定到页面数据中,然后就可以在页面中直接使用仓库数据了。 [代码]import { createStoreBindings } from 'mobx-miniprogram-bindings' import { store } from './store' Page({ onLoad() { // 绑定 MobX store this.storeBindings = createStoreBindings(this, { store, // 需要绑定的数据仓库 fields: ['list'], // 将 this.data.list 绑定为仓库中的 list ,即天气数据 actions: ['setList'], // 将 this.setList 绑定为仓库中的 setList action }) // 从服务器端读取数据 wx.showLoading() wx.request({ // 请求网络数据 // ... success: (data) => { wx.hideLoading() // 调用 setList action ,将数据写入 store this.setList(data) } }) }, onUnload() { // 解绑 this.storeBindings.destroyStoreBindings() }, }) [代码] 这样,可以在 wxml 中直接使用 list : [代码]<view class="item" wx:for="{{list}}" wx:key="date" data-index="{{index}}"> <!-- 这里可以使用 list 中的数据了! --> <view class="title">{{item.date}} {{item.summary}}</view> <view class="abstract">{{item.temperature}}</view> </view> [代码] 在详情页中使用数据仓库 在详情页中,同样可以使用 [代码]createStoreBindings[代码] 来将仓库中的数据绑定到页面数据中: [代码]import { createStoreBindings } from 'mobx-miniprogram-bindings' import { store } from './store' Page({ onLoad(args) { // 绑定 MobX store this.storeBindings = createStoreBindings(this, { store, // 需要绑定的数据仓库 fields: ['list'], // 将 this.data.list 绑定为仓库中的 list ,即天气数据 }) // 页面参数 `index` 表示要展示哪一条天气详情数据,将它用 setData 设置到界面上 this.setData({ index: args.index }) }, onUnload() { // 解绑 this.storeBindings.destroyStoreBindings() }, }) [代码] 这样,这个页面 wxml 中也可以直接使用 list : [代码]<view class="title">{{list[index].date}}</view> <view class="content">温度 {{list[index].temperature}}</view> <view class="content">天气 {{list[index].weather}}</view> <view class="content">空气质量 {{list[index].airQuality}}</view> <view class="content">{{list[index].details}}</view> [代码] 完整示例 完整例子可以在这个代码片段中体验: https://developers.weixin.qq.com/s/YhfvpxmN7HcV 这个就是 MobX 在小程序中最基础的玩法了。相关的 npm 模块文档可参考 mobx-miniprogram-bindings 和 mobx-miniprogram 。 MobX 在实际使用时还有很多好的实践经验,感兴趣的话,可以阅读一些其他相关的文章。
2019-11-01 - 小程序app.onLaunch与page.onLoad异步问题的最佳实践
场景: 在小程序中大家应该都有这样的场景,在onLaunch里用wx.login静默登录拿到code,再用code去发送请求获取token、用户信息等,整个过程都是异步的,然后我们在业务页面里onLoad去用的时候异步请求还没回来,导致没拿到想要的数据,以往要么监听是否拿到,要么自己封装一套回调,总之都挺麻烦,每个页面都要写一堆无关当前页面的逻辑。 直接上终极解决方案,公司内部已接入两年很稳定: 1.可完美解决异步问题 2.不污染原生生命周期,与onLoad等钩子共存 3.使用方便 4.可灵活定制异步钩子 5.采用监听模式实现,接入无需修改以前相关逻辑 6.支持各种小程序和vue架构 。。。 //为了简洁明了的展示使用场景,以下有部分是伪代码,请勿直接粘贴使用,具体使用代码看Github文档 //app.js //globalData提出来声明 let globalData = { // 是否已拿到token token: '', // 用户信息 userInfo: { userId: '', head: '' } } //注册自定义钩子 import CustomHook from 'spa-custom-hooks'; CustomHook.install({ 'Login':{ name:'Login', watchKey: 'token', onUpdate(token){ //有token则触发此钩子 return !!token; } }, 'User':{ name:'User', watchKey: 'userInfo', onUpdate(user){ //获取到userinfo里的userId则触发此钩子 return !!user.userId; } } }, globalData) // 正常走初始化逻辑 App({ globalData, onLaunch() { //发起异步登录拿token login((token)=>{ this.globalData.token = token //使用token拿用户信息 getUser((user)=>{ this.globalData.user = user }) }) } }) //关键点来了 //Page.js,业务页面使用 Page({ onLoadLogin() { //拿到token啦,可以使用token发起请求了 const token = getApp().globalData.token }, onLoadUser() { //拿到用户信息啦 const userInfo = getApp().globalData.userInfo }, onReadyUser() { //页面初次渲染完毕 && 拿到用户信息,可以把头像渲染在canvas上面啦 const userInfo = getApp().globalData.userInfo // 获取canvas上下文 const ctx = getCanvasContext2d() ctx.drawImage(userInfo.head,0,0,100,100) }, onShowUser() { //页面每次显示 && 拿到用户信息,我要在页面每次显示的时候根据userInfo走不同的逻辑 const userInfo = getApp().globalData.userInfo switch(userInfo.sex){ case 0: // 走女生逻辑 break case 1: // 走男生逻辑 break } } }) 具体文档和Demo见↓ Github:https://github.com/1977474741/spa-custom-hooks 祝大家用的愉快,记得star哦
2023-04-23 - 小程序实现看一看视频滑动切换
最终效果 由于很多人不知道看一看还有个视频功能,所以这里先让大家看下我们最终要完成的效果。 它的入口在 发现 -> 看一看 -> 精选 -> 随便找个视频点进去即可。 [图片] 初步想法 由效果可以看出,其实就是需要监听视频的滚动,当超出可视区范围多少px,就切换到下一个视频。 要实现这个功能,大多数人的想法都是:监听scroll 事件后,在调用目标元素的getBoundingClientRect()方法,得到它对应于视图的坐标,再判断是否在可视区域之内,然后切换视频。 缺点 但是这样做的缺点是:调用目标元素的getBoundingClientRect 是会触发重排的,尤其是元素一多起来,调用所有元素的getBoundingClientRect得到信息在进行判断所以很容易造成性能问题。而且这种切换计算的逻辑会写的非常复杂,可以自行脑补一下。 所以我们要换个思维,不要通过监听scoll事件去计算目标元素距离顶部或者底部距离。而应该是直接监听当前目标元素是否还在可视区域内。当离开可视区域的时候,切换到下一个。 整理完大致思路之后,终于要开搞了。 [图片] 实现 前面已经分析了要通过监听当前目标元素是否还在可视区域内来做切换的动作,那么有什么API是可以用来做这件事的呢? 答案是: IntersectionObserver API 这个API是用来观察目标元素与指定元素交集的变化。当交集 < 0 的时候,说明不在指定元素区域内。当目标元素进入或者退出指定元素的时候,会执行相应的回调函数。所以我们可以通过这个API注册一个回调函数用于切换视频。 在小程序里,同样提供了这个API,是IntersectionObserver。 有了这个API,就可以开始干活了。由于我们这个区域是一个滚动区域,所以我用了scoll-view。 index.wxml 文件 [代码]<scroll-view> <view wx:for="{{ videos }}" wx:for-index="idx" wx:for-item="videoItem"> <!-- <view class="{{ currentPlayVideoIndex === idx ? 'active test' : 'test'}}" data-index="{{ idx }}" id="{{ videoItem.video_id }}"> {{ idx }}dddd</view> --> <span class="{{ currentPlayVideoIndex === idx ? 'active' : ''}}">{{ idx }}ddddddd </span> <video id="{{ videoItem.video_id }}" data-index="{{ idx }}" preload src="http://wxsnsdy.tc.qq.com/105/20210/snsdyvideodownload?filekey=30280201010421301f0201690402534804102ca905ce620b1241b726bc41dcff44e00204012882540400&bizid=1023&hy=SH&fileparam=302c020101042530230204136ffd93020457e3c4ff02024ef202031e8d7f02030f42400204045a320a0201000400" class='video-item' muted controls> </video> </view> </scroll-view> [代码] index.wxss [代码].video-item { height: 450px; } .test { width: 100%; height: 450px; border: 1px solid red; padding: 30px; } .active { color: pink; } [代码] [代码]Page({ /** * 页面的初始数据 */ data: { videos: [{ video_id: 'mpVideo0', url: 'http://wxsnsdy.tc.qq.com/105/20210/snsdyvideodownload?filekey=30280201010421301f0201690402534804102ca905ce620b1241b726bc41dcff44e00204012882540400&bizid=1023&hy=SH&fileparam=302c020101042530230204136ffd93020457e3c4ff02024ef202031e8d7f02030f42400204045a320a0201000400', }, { video_id: 'mpVideo1', url: 'http://mpvideo.qpic.cn/tjg_2394158861_50000_01730a9db3924ffa98201662d51615ed.f10002.mp4?dis_k=23639703f249e3c59cf674369cfcac86&dis_t=1562297977', }, { video_id: 'mpVideo2', url: 'http://mpvideo.qpic.cn/tjg_2394158861_50000_01730a9db3924ffa98201662d51615ed.f10002.mp4?dis_k=23639703f249e3c59cf674369cfcac86&dis_t=1562297977', }, { video_id: 'mpVideo3', url: 'http://mpvideo.qpic.cn/tjg_2394158861_50000_01730a9db3924ffa98201662d51615ed.f10002.mp4?dis_k=23639703f249e3c59cf674369cfcac86&dis_t=1562297977', }, { video_id: 'mpVideo4', url: 'http://mpvideo.qpic.cn/tjg_2394158861_50000_01730a9db3924ffa98201662d51615ed.f10002.mp4?dis_k=23639703f249e3c59cf674369cfcac86&dis_t=1562297977', }, { video_id: 'mpVideo5', url: 'http://mpvideo.qpic.cn/tjg_2394158861_50000_01730a9db3924ffa98201662d51615ed.f10002.mp4?dis_k=23639703f249e3c59cf674369cfcac86&dis_t=1562297977', }, { video_id: 'mpVideo6', url: 'http://mpvideo.qpic.cn/tjg_2394158861_50000_01730a9db3924ffa98201662d51615ed.f10002.mp4?dis_k=23639703f249e3c59cf674369cfcac86&dis_t=1562297977', }, { video_id: 'mpVideo7', url: 'http://mpvideo.qpic.cn/tjg_2394158861_50000_01730a9db3924ffa98201662d51615ed.f10002.mp4?dis_k=23639703f249e3c59cf674369cfcac86&dis_t=1562297977', }], currentPlayVideoIndex: 0, isActive: true }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { // onLoad 的时候立刻调用handleVideoScroll // 对视频进行监听 this.handleVideoScroll(); // cgi请求,用于获取videos 的数据,由于是demo演示,我直接写死了videos... }, /** * 页面上拉触底事件的处理函数 */ onReachBottom: function () { }, controlVideos: function (res) { console.log('调用controvideos', res); }, handleVideoScroll: function () { const currentId = this.data.videos[this.data.currentPlayVideoIndex].video_id; // 关键代码 // relativeToViewport 这里指定对比的就是viewport,viewport的意思就是document中的可视区域 this.observerObj = wx.createIntersectionObserver().relativeToViewport(); console.log('listen ' + currentId); // 监听目标视频跟viewport相交区域的变化 this.observerObj.observe(`#${currentId}`, this.controlVideos); } }) [代码] copy上面的代码进入小程序,你就会看到这样一个界面。 [图片] 其中外面的一圈表示的是viewPort,里面一层就是我们现在正在监听的视频,我用右上角的粉色字体来标记了,它的回调函数是controlVideos。当目标视频进入或者退出viewport的时候,controlVideos就会执行。 onLoad的时候执行了handleVideoScroll,这时候开始对目标视频进行监听,此时目标元素在viewport内,所以会调用controVideos,打印出相关信息。 [图片] 其他的字段先不说,其中的intersectionRatio表示了他们相交的比例。其中1表示完全在viewport内,0表示不在viewport内。 如果我持续去滚动第一个视频,直到它看不到了,就会看到控制台打印出 [图片] 这时候的intersectionRatio = 0,代表已经不在viewport内了,所以我们就可以将currentPlayIndex 切换到下一个了。 切换代码如下: [代码] controlVideos: function (res) { const { currentPlayVideoIndex } = this.data; console.log('当前currentIndex', currentPlayVideoIndex) const currentId = this.data.videos[currentPlayVideoIndex].video_id; if (res && res.intersectionRatio > 0) { // 视频在可视区域内,播放视频 wx.createVideoContext(currentId).play(); console.log("play" + currentPlayVideoIndex) } else { // 需要切换视频的时候,将当前视频暂停播放 // 并且通过handleVideoScroll 来播放下一个视频 wx.createVideoContext(currentId).pause(); // 切换到下一个视频 this.setData({ 'currentPlayVideoIndex': currentPlayVideoIndex + 1 }, () => { // 注意切换完成之后,还需要在调用handleVideoScroll 来对下一个视频进行绑定 this.handleVideoScroll(); }); } }, [代码] 到这一步,应该就可以看到这样的向下切换的效果了。 [图片] 但是,我们现在只是做下向下滚动的切换。那么向上的呢?要做向上滑动的切换,首先要知道视频是在向下还是向上滑动。这里有个字段可以帮助我们识别:boundingClientRect 。 它表示的是目标元素相对与viewport的节点信息。当视频向上滚动的时候,它距离viewport的top值为负,向下滚动的时候,为正值。 [图片] 有了这个字段,我们就可以通过判断向上还是向下的滚动,来切换视频了。 [代码] controlVideos: function (res) { const { currentPlayVideoIndex } = this.data; console.log('当前currentIndex', currentPlayVideoIndex) const currentId = this.data.videos[currentPlayVideoIndex].video_id; if (res && res.intersectionRatio > 0) { // 视频在可视区域内,播放视频 wx.createVideoContext(currentId).play(); console.log("play" + currentPlayVideoIndex) } else { // 需要切换视频的时候,将当前视频暂停播放,并且通过handleVideoScroll 来播放下一个视频 wx.createVideoContext(currentId).pause(); // 当top < 0的时候,说明是在向上滑动,这时候currentPlayVideoIndex 需要加1 if (res.boundingClientRect.top < 0) { if (currentPlayVideoIndex < this.data.videos.length - 1) { this.setData({ 'currentPlayVideoIndex': currentPlayVideoIndex + 1 }, () => { // 同时解绑第一个视频,保证同一个时间只监听一个视频 this.observerObj.disconnect(); this.handleVideoScroll(); }); } } else { // 当top > 0的时候,说明是在向下滑动,这时候currentPlayVideoIndex 需要减1 if (currentPlayVideoIndex - 1 < 0) { return; } this.setData({ 'currentPlayVideoIndex': this.data.currentPlayVideoIndex - 1 }, () => { this.observerObj.disconnect(); this.handleVideoScroll(); }) } }, [代码] 但是我们的产品在体验的过程中,会提出并不是完全看不见了才去切换,可能想要还剩个150px就切换了,所以我这里要对viewport调整一下 [代码] this.observerObj = wx.createIntersectionObserver().relativeToViewport({ top: -300, bottom: -300 }); [代码] 完成之后,你就可以缓缓的滑动你的视频,实现视频切换的效果了。可以看到当视频差不多被遮住不到一半,就开始切换了。 [图片] 总结 整个过程其实就是好好利用了IntersectionObserver这个API而已。当然现在只是一个非常简单的实现,性能问题,以及快读滑动的情况都无法应对,我们下一篇在接着~。
2019-08-15 - wx.getLocation报"errCode":404错误
定位分为网络定位&Gps定位,具体逻辑如下: 1.定位权限&定位开关开启&有sim卡的情况下会使用GPS定位,定位较准 2.不满足上述条件,但连了wifi,会通过网络定位,较不准确 3.网络定位都失败了就会报错
2020-12-04 - 新版自定义交易组件接入指引(更新售后流程相关内容)
自定义交易组件 本文主要介绍新版自定义交易组件的业务流程及各流程的注意事项,文档篇幅较长,如无需查看完整文档可以使用浏览器自带页面搜索功能进行关键字搜索(快捷键Ctrl+F )。 在开始开发前应确认以下必要前置条件是否完成: 1、开通场景经营商户号; 2、部分接口强制校验来源IP白名单,白名单配置方式: 1)服务商配置方式"点我查看" 2)普通小程序:登录小程序管理后台->开发管理->开发设置->“开发者ID”下的“IP白名单” 3、因视频号场景受控订单无微信支付回调,支付结果等事件是通过小程序消息推送进行下发的,需要登录小程序管理后台后,在「开发」-「开发设置」-「消息推送」中启用并设置消息推送配置,开发者接收到的消息需要解密, 消息加解密说明可在开发文档查询。 1、业务流程图 1.1 关键流程逻辑 [图片] 1.2 售后流程 [图片] 1.3 主要业务变动 [图片] 2、接入流程详细说明 整体接入流程 注:本文2.1-2.2为前置必做流程,完成后才可以进行接口调试 [图片] 2.1 开通自定义交易组件 2.1.1 公众平台后台开通 登录公众平台,点击功能下方的“交易组件”,点击“开通”阅读并同意开通协议后即可进行下一步操作。[图片] 开通后页面如图所示[图片] 2.1.2 接口申请开通 调用接口 “shop/register/apply” 申请开通自定义版交易组件,请求成功后将发送“法务协议确认”到管理员微信,管理员需要在“24小时内”点击该模板消息,并确认服务协议。服务协议签署成功后,开发者可以通过“获取接入状态接口(可点击跳转)”或“”事件回调“进行开通状态判断,若状态为“已开通”,开发者才可以继续调用后续接口进行调试接入。[图片][图片][图片] 2.1.3 自定义交易组件“升级版”跟升级前的自定义交易组件有什么区别,哪些接口需要进行升级? 1.新支付接口,必须走新商户号。 2.取消订单, 小程序(小程序内以及发现-小程序我的订单)和视频号双向可取消,之前只可以在小程序上取消,然后同步给视频号状态。 3.申请退款,小程序和视频号双向可申请退款。 4.申请退货退款,小程序和视频号双向可申请退货退款,之前只有小程序上操作。 5.未付款订单,小程序和视频号 可在各自订单中心重新支付,同步状态。 6.确认收货,小程序和视频号双向可确认收货。 7.同步发货状态接口更新。 2.2 开通场景经营所需商户号 视频号场景下,会通过开通商户号、补齐资料,获得一个新的电商收付通的商户号。商家在视频号内的交易资金流,需要使用这个新商户号。 准备事项:需准备营业执照、法人身份证、商户号超级管理员手机号、邮箱信息 注意事项:小程序超管将作为微信支付商户号的超级管理员,如管理员已离职、管理员曾变更过姓名未在小程序后台更新等情况,将影响商户号的签约,请确认无误后再进行商户号申请。 2.2.1 通过公众平台后台申请 在小程序官方后台自定义交易组件页面,点击商户号申请所对应的“去开通”按钮进入商户号申请页面,共计三部分(不想开商户号了,暂时不放图): 2.2.1.1 填写基础信息 此部分主要为上传营业执照、法人身份证、填写商户号超管信息 2.2.1.2验证账户小额打款/法人验证 此部分会有两种情况,如为“小额打款”验证方式,在页面输入收到的打款金额即可;如为法人验证,需要使用实名认证为法人同名微信扫码进行验证。 2.2.1.3确认账户真实性。 点击“去签署”按钮会展示签约二维码,使用超管微信进行扫码,用于确认经营意愿并签署协议,完成商户号注册。 2.2.2 通过API接口申请 调用“提交支付资质”接口,提交场景经营所需商户号申请,提交成功后,商户申请是没有事件回调的,需要服务商主动调用“状态查询接口”查询申请单进度,申请单状态同“电商收付通二级商户进件申请单状态查询”。此处不做过多描述,有问题可以留言,后续视情况补充更新。 2.2.3 开通场景经营商户常见问题 2.2.3.1 Q:新版交易组件需要重新申请商户号吗?是否可以使用原有商户号? A:不可以,新版交易组件必须要申请开通场景专用商户号 2.2.3.2 Q:新版场景专用商户号费率是多少,是否有优惠,结算周期是多久? A:商户号费率为0.6%,无费率优惠,结算周期为7+7日,即用户收货后7天后结算。 2.2.3.3 Q:申请新商户号时,最后一步签约遇到“微信实名信息与管理员信息不一致”是什么原因? A:申请新的场景专用商户号时,“超级管理员”这一项不支持修改,默认为小程序“超级管理员”实名信息,如需修改,需要为该用户前往成员管理为小程序绑定超级管理员。 2.2.3.4 Q:申请新的商户号时,为什么不能修改主体信息? A:“当前主体”这一项不支持修改,因为商户号主体必须和该小程序注册主体保持一致。 2.2.3.5 Q:通过新版自定义交易组件申请的场景专用商户号是否对跨境类小程序(自助报关)有影响? A:会,二级商户当前暂不支持自助清关接口调用,留意后续更新通知 2.2.3.6 Q:新商户号是否支持分账功能? A:即将支持,敬请期待。 2.3 上传商品并通过审核,完成商品接口调用(仅有API接入方式) 基础流程,不做过多说明,只写流程、常见错误和注意事项 2.3.1 添加商品流程: (1)首先去“获取类目详情”,与自己的系统商品类目相匹配 (2)查看对应类目是否需要行业资质,如果需要,请上传相关资质 (3)“上传相关类目资质”获得对应类目的使用权限 (4)如果商品有品牌,“上传品牌信息”获得品牌的使用权限 (5)调用“添加商品”接口将商品进行上传 2.3.2 SPU接口常见错误及注意事项 2.3.2.1 Q:自定义交易组件提交上传类目资质时报错{“errcode”: 1000009, “errmsg”: “调用的类目id不存在”} A: 调用获取类目详情,看对应““qualification_type"与"product_qualification_type"参数是否为"0”,为"0"无需上传类目资质 2.3.2.2 Q:调用自定义交易组件“添加商品”接口shop/spu/add时报错“该账号客服方式必须包含微信客服/小程序客服” {“errcode”:1040042,“errmsg”:"该账号客服方式必须包含微信客服/小程序客服”} A11:需要在MP后台配置微信客服/小程序客服后,然后通过“更新商家信息”接口更新商家信息 [图片] 调用“获取商家信息”接口应返回一下内容才为成功,“service_agent_type”字段需要同时包含0,1,2三个值 [图片] 2.3.2.3 Q:类目审核成功,但是添加商品报错,{“errcode”:1000005,“errmsg”:"该商品使用了未申请通过的类目,请先申请类目且通过后再提审商品 "} A: 检查是否取消开通过,取消开通会清空品牌类目。对于服务商,可以监听账户接入回调事件,目前只有取消开通回调。 2.3.2.4 Q:调用自定义组件上传图片接口报错{“errcode”:1070001,“errmsg”:"文件/图片为空 "} A25:检查请求报文协议,需[代码]Content-Type: multipart/form-data[代码] 2.4 调试支付校验,完成订单接口调用 2.4.1 支付流程: (1)按照"开发指引"修改基础库配置 (2)在小程序中调用"生成订单"接口生成一笔订单 (3)完成订单的支付(视频号场景需要调用生成支付参数后完成收银台的拉起,其他场景按照已有业务逻辑进行支付) (4)调用"同步订单支付结果"接口同步订单的支付结果(二级商户单无需同步支付结果,系统自动流转订单状态) 注意: 这里两个接口都需要调用,以同步订单结果为最后确认步骤,视频号场景则以支付成功作为最终确认步骤。 基础库拉起收银台接口改造后需要发版才可以生效。 完成接口调用后,点击完成,切换状态。 2.4.2 订单接口接口常见错误及注意事项 2.4.2.1 Q:二级商户号订单支付流程与原有订单支付流程有什么区别? A17:主要区别是:二级商户号订单调起支付所需参数是通过“生成支付参数”获取,无需同步支付结果;原流程调起支付是需要通过微信支付统一下单获取,需要同步支付结果。 2.4.2.2 Q:调用自定义交易组件“创建订单”接口shop/order/add时报错“不支持的发货方式” {“errcode”:1010036,“errmsg”:"不支持的发货方式“} A:视频号场景当前只支持“正常快递”方式,其他请留意后续更新。 2.4.2.3 Q:自定义交易组件申请视频号专用商户号后,唤起支付报错: “商户号该产品权限未开通” A:需要先调用“生成订单”接口,生成订单时将fund_type设为1,然后调用“生成支付参数”接口获取调取支付所需参数,不要调用微信支付统一下单接口获取调用支付参数 2.4.2.4 Q:自定义交易组件二级商户单调起支付时报错“JSAPI缺少参数total_fee” A:生成支付参数失败,没返回正确的预支付 ID,重新调用生成支付参数接口获取新的支付参数即可 2.4.2.5 Q:自定义交易组件调用同步订单支付结果时报错 {“errcode”:990022,“errmsg”:"暂不支持操作“} A:二级商户单无需同步支付结果,付款成功后系统自动流转订单状态并下发支付结果回调事件。 2.5 调试发货接口,完成物流相关接口调用 2.5.1 发货流程 (1)先调用获取快递公司列表接口获取快递公司信息 (2)调用"订单发货"接口,完成发货 2.5.2 发货接口常见错误及注意事项 注意事项: 视频号订单状态流转不可逆的,当finish_all_delivery=0时,订单状态流转到21(部分发货), 当finish_all_delivery=1时,订单状态从20(待发货)/ 21(部分发货)流转到30(待收货),部分发货时finish_all_delivery一定要传0,最后一次发货才可以传1,切记!切记!切记!!! 2.5.2.1 Q:调用自定义交易组件“创建订单”接口shop/order/add时报错“不支持的发货方式” {“errcode”:1010036,“errmsg”:"不支持的发货方式“} A:视频号场景当前只支持“正常快递”方式,其他请留意后续更新。 2.5.2.2 Q:当自定义交易组件订单只存在一个SKU,发货时误操作为部分发货,订单状态码流转为21时,在既不能确认收货、也不能退款的情况下应该如何处理? A:按照截图示例传参,即可完成发货,订单状态由21转为30,订单状态变更后就可以正常流转 注:传参时order_id与out_order_id二选一传参 [图片] 2.6 调试售后接口,完成售后接口调用 2.6.1 售后流程、超时时间及售后事件和API、回调的对应关系 看本文1.2 售后流程图 用户及商户处理售后超时时间 类型 超时时间 商家处理退款超时 48小时 商家处理退货超时 48小时 用户超时未申请平台接入 7天 用户上传退货物流超时 7天 商家超时未确认收货 10天 售后事件和API、回调的对应关系 事件 API 触发回调 说明 用户申请退款 用户提交售后申请 用户提交售后申请回调 - 用户申请退货 用户提交售后申请 用户提交售后申请回调 - 用户修改申请 用户更新售后申请 用户更新售后申请回调 - 用户取消申请 用户取消售后申请 用户取消售后申请回调 - 用户申请平台介入 无API,只能由用户在微信侧触发 暂无 - 用户超时未申请平台介入(7天) - 用户申请平台介入超时回调 - 用户上传退货物流 用户上传物流信息 用户上传退货物流回调(待商家确认收货) - 用户超时未上传退货物流(7天) - 用户上传退货物流超时回调 - 用户确认退款凭证 无API,只能由用户在微信侧触发 用户确认退款凭证回调 - 商家同意退款 同意退款 - - 商家处理退款超时(48小时) - 商家处理退款请求超时回调 - 商家同意退货 同意退货 - - 商家处理退货超时(48小时) - 商家处理退货申请超时回调 - 商家拒绝退款 拒绝售后 - - 商家拒绝退货 拒绝售后 - - 商家上传退款凭证 上传退款凭证 - 商家确认收货 同意退款 商家确认收到的货没问题后,调用同意退款API表示确认收货,如果有问题就调用拒绝售后API 商家超时未确认收货(10天) - 商家确认收货超时回调 - 平台退款成功 - 平台退款成功回调 - 平台退款失败 - 平台退款失败回调(待商家线下退款) - 平台判定用户责任 - 纠纷事件更新售后单状态回调 纠纷事件通知请见纠纷回调 平台判定商家退款 - 纠纷事件更新售后单状态回调 纠纷事件通知请见纠纷回调 平台判定商家退货 - 纠纷事件更新售后单状态回调 纠纷事件通知请见纠纷回调 2.6.2 售后接口常见错误及注意事项 注意事项 新旧接口不可混用,新售后接口无法处理旧接口订单,否则会出现很多意想不到的错误 2.6.2.1 枚举值定义 (1)AfterSalesReason 枚举值 描述 1 排错/多拍 2 不想要了 3 无快递信息 4 包裹为空 5 已拒签包裹 6 快递长时间未送达 7 与商品描述不符 8 质量问题 9 卖家发错货 10 三无产品 11 假冒产品 12 其他 (2)AfterSalesState 枚举值 描述 1 用户取消 2 商家受理退款申请中 4 商家拒绝退款 5 商家拒绝退货退款 6 待买家退货 7 售后单关闭 8 待商家收货 11 平台退款中 13 退款成功 21 平台受理退款申请中 22 平台介入处理完成 23 商家受理退货申请中 24 平台受理退货申请中 2.6.2.2 Q:调用自定义交易组件售后相关接口:“创建售后单”、“用户取消售后单”、“用户上传物流信息”、“获取售后单列表”、“获取售后单详情”、“同意退款“、”同意退货“、“拒绝售后”、“上传退款凭证”、“更新售后单”等接口时报错{“errcode”: 48001,“errmsg”: “api unauthorized”} A18:未开通视频号场景经营商户号,需要先开通场景经营商户号才可以调用。 2.6.2.3 Q:调用自定义交易组件创建售后接口ecaftersale/add时报错2747002,参数错误{“errcode”:2747002,“errmsg”:"参数错误 "} A9:1.请检查“orderamt”参数,传参金额应不含邮费。 2.新旧接口不可混合调用,新接口不支持对旧接口生成的订单创建售后。 2.6.2.4 Q:调用自定义交易组件创建售后接口ecaftersale/add时报47001错误{“errcode”:47001,“errmsg”:"data format error "} A:请检查“product_info”字段,注意对应类型为“object”。 2.6.2.5 Q:调用自定义交易组件“同意退款”接口shop/ecaftersale/acceptrefund时报错“同意退款失败” {“errcode”:9700209,“errmsg”:"同意退款失败 退款失败“} A:1.该问题是订单流转状态不对导致,请严格按照文档流程进行操作调用; 2.新旧接口混合调用也会报此错误 3.“orderamt"传参为"0” 2.6.2.6 Q:用自定义交易组件创建售后接口ecaftersale/add时是报错“售后金额不合法,大于最大可退款金额”{“errcode”:2747014,“errmsg”:" 售后金额不合法,大于最大可退款金额"} A:1.请确认请求创建售后时"orderamt"金额是否大于下单时"sku_real_price"金额 2.订单已进行部分退款 码字中 稍后回来
2022-11-16 - 如何实现一个自定义导航栏
自定义导航栏在刚出的时候已经有很多实现方案了,但是还有大哥在问,那这里再贴下代码及原理: 首先在App.js的 onLaunch中获取当前手机机型头部状态栏的高度,单位为px,存在内存中,操作如下: [代码]onLaunch() { wx.getSystemInfo({ success: (res) => { this.globalData.statusBarHeight = res.statusBarHeight this.globalData.titleBarHeight = wx.getMenuButtonBoundingClientRect().bottom + wx.getMenuButtonBoundingClientRect().top - (res.statusBarHeight * 2) }, failure() { this.globalData.statusBarHeight = 0 this.globalData.titleBarHeight = 0 } }) } [代码] 然后需要在目录下新建个components文件夹,里面存放此次需要演示的文件 navigateTitle WXML 文件如下: [代码]<view class="navigate-container"> <view style="height:{{statusBarHeight}}px"></view> <view class="navigate-bar" style="height:{{titleBarHeight}}px"> <view class="navigate-icon"> <navigator class="navigator-back" open-type="navigateBack" wx:if="{{!isShowHome}}" /> <navigator class="navigator-home" open-type="switchTab" url="/pages/index/index" wx:else /> </view> <view class="navigate-title">{{title}}</view> <view class="navigate-icon"></view> </view> </view> <view class="navigate-line" style="height: {{statusBarHeight + titleBarHeight}}px; width: 100%;"></view> [代码] WXSS文件如下: [代码].navigate-container { position: fixed; top: 0; width: 100%; z-index: 9999; background: #FFF; } .navigate-bar { width: 100%; display: flex; justify-content: space-around; } .navigate-icon { width: 100rpx; height: 100rpx; display: flex; justify-content: space-around; } .navigate-title { width: 550rpx; text-align: center; line-height: 100rpx; font-size: 34rpx; color: #3c3c3c; font-weight: bold; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } /*箭头部分*/ .navigator-back { width: 36rpx; height: 36rpx; align-self: center; } .navigator-back:after { content: ''; display: block; width: 22rpx; height: 22rpx; border-right: 4rpx solid #000; border-top: 4rpx solid #000; transform: rotate(225deg); } .navigator-home { width: 56rpx; height: 56rpx; background: url(https://qiniu-image.qtshe.com/20190301home.png) no-repeat center center; background-size: 100% 100%; align-self: center; } [代码] JS如下: [代码]var app = getApp() Component({ data: { statusBarHeight: '', titleBarHeight: '', isShowHome: false }, properties: { //属性值可以在组件使用时指定 title: { type: String, value: '青团公益' } }, pageLifetimes: { // 组件所在页面的生命周期函数 show() { let pageContext = getCurrentPages() if (pageContext.length > 1) { this.setData({ isShowHome: false }) } else { this.setData({ isShowHome: true }) } } }, attached() { this.setData({ statusBarHeight: app.globalData.statusBarHeight, titleBarHeight: app.globalData.titleBarHeight }) }, methods: {} }) [代码] JSON如下: [代码]{ "component": true } [代码] 如何引用? 需要引用的页面JSON里配置: [代码]"navigationStyle": "custom", "usingComponents": { "navigate-title": "/pages/components/navigateTitle/index" } [代码] WXML [代码]<navigate-title title="青团社" /> [代码] 按上面步骤操作即可实现一个自定义的导航栏。 如何实现通栏的效果默认透明以及滚动更换title为白色背景,如下图所示: [图片] [图片] [图片] [图片] 最后代码片段如下: https://developers.weixin.qq.com/s/wi6Pglmv7s8P。 以下为收集到的社区老哥们的分享: @Yunior: 小程序顶部自定义导航组件实现原理及坑分享 @志军: 微信小程序自定义导航栏组件(完美适配所有手机),可自定义实现任何你想要的功能 @✨o0o有脾气的酸奶💤 [有点炫]自定义navigate+分包+自定义tabbar @安晓苏 分享一个自适应的自定义导航栏组件
2020-03-10 - 微信7.0.21加载X5内核失败
[图片][图片]
2020-12-16 - live-pusher的bindstatechange返回的code?
监听返回4998、4999、1022、-3301、1019 是啥意思哇
2022-03-18 - canvas createImage()方法中的onload在真机上无效?
canvas createImage()方法中的onload在真机上无效 [图片]
2022-01-24 - 小程序图片裁剪插件 image-cropper
之前的插件类目没有了导致搜不到了,重新发个文章。 image-cropper 一款高性能的小程序图片裁剪插件,支持旋转。 [图片] 优势 [代码]1.功能强大。[代码] [代码]2.性能超高超流畅,大图毫无卡顿感。[代码] [代码]3.组件化,使用简单。[代码] [代码]4.点击中间窗口实时查看裁剪结果。[代码] ㅤ 初始准备 1.json文件中添加image-cropper [代码] "usingComponents": { "image-cropper": "../image-cropper/image-cropper" }, "navigationBarTitleText": "裁剪图片", "disableScroll": true [代码] 2.wxml文件 [代码]<image-cropper id="image-cropper" limit_move="{{true}}" disable_rotate="{{true}}" width="{{width}}" height="{{height}}" imgSrc="{{src}}" bindload="cropperload" bindimageload="loadimage" bindtapcut="clickcut"></image-cropper> [代码] 3.简单示例 [代码] Page({ data: { src:'', width:250,//宽度 height: 250,//高度 }, onLoad: function (options) { //获取到image-cropper实例 this.cropper = this.selectComponent("#image-cropper"); //开始裁剪 this.setData({ src:"https://raw.githubusercontent.com/1977474741/image-cropper/dev/image/code.jpg", }); wx.showLoading({ title: '加载中' }) }, cropperload(e){ console.log("cropper初始化完成"); }, loadimage(e){ console.log("图片加载完成",e.detail); wx.hideLoading(); //重置图片角度、缩放、位置 this.cropper.imgReset(); }, clickcut(e) { console.log(e.detail); //点击裁剪框阅览图片 wx.previewImage({ current: e.detail.url, // 当前显示图片的http链接 urls: [e.detail.url] // 需要预览的图片http链接列表 }) }, }) [代码] 参数说明 属性 类型 缺省值 取值 描述 必填 imgSrc String 无 无限制 图片地址(如果是网络图片需配置安全域名) 否 disable_rotate Boolean false true/false 禁止用户旋转(为false时建议同时设置limit_move为false) 否 limit_move Boolean false true/false 限制图片移动范围(裁剪框始终在图片内)(为true时建议同时设置disable_rotate为true) 否 width Number 200 超过屏幕宽度自动转为屏幕宽度 裁剪框宽度 否 height Number 200 超过屏幕高度自动转为屏幕高度 裁剪框高度 否 max_width Number 300 裁剪框最大宽度 裁剪框最大宽度 否 max_height Number 300 裁剪框最大高度 裁剪框最大高度 否 min_width Number 100 裁剪框最小宽度 裁剪框最小宽度 否 min_height Number 100 裁剪框最小高度 裁剪框最小高度 否 disable_width Boolean false true/false 锁定裁剪框宽度 否 disable_height Boolean false true/false 锁定裁剪框高度 否 disable_ratio Boolean false true/false 锁定裁剪框比例 否 export_scale Number 3 无限制 输出图片的比例(相对于裁剪框尺寸) 否 quality Number 1 0-1 生成的图片质量 否 cut_top Number 居中 始终在屏幕内 裁剪框上边距 否 cut_left Number 居中 始终在屏幕内 裁剪框左边距 否 [代码]img_width[代码] Number 宽高都不设置,最小边填满裁剪框 支持%(不加单位为px)(只设置宽度,高度自适应) 图片宽度 否 [代码]img_height[代码] Number 宽高都不设置,最小边填满裁剪框 支持%(不加单位为px)(只设置高度,宽度自适应) 图片高度 否 scale Number 1 无限制 图片的缩放比 否 angle Number 0 (limit_move=true时angle=n*90) 图片的旋转角度 否 min_scale Number 0.5 无限制 图片的最小缩放比 否 max_scale Number 2 无限制 图片的最大缩放比 否 bindload Function null 函数名称 cropper初始化完成 否 bindimageload Function null 函数名称 图片加载完成,返回值Object{width,height,path,type等} 否 bindtapcut Function null 函数名称 点击中间裁剪框,返回值Object{src,width,height} 否 函数说明 函数名 参数 返回值 描述 参数必填 upload 无 无 调起wx上传图片接口并开始剪裁 否 pushImg src 无 放入图片开始裁剪 是 getImg Function(回调函数) [代码]Object{url,width,height}[代码] 裁剪并获取图片(图片尺寸 = 图片宽高 * export_scale) 是 setCutXY X、Y 无 设置裁剪框位置 是 setCutSize width、height 无 设置裁剪框大小 是 setCutCenter 无 无 设置裁剪框居中 否 setScale scale 无 设置图片缩放比例(不受min_scale、max_scale影响) 是 setAngle deg 无 设置图片旋转角度(带过渡效果) 是 setTransform {x,y,angle,scale,cutX,cutY} 无 图片在原有基础上的变化(scale受min_scale、max_scale影响) 根据需要传参 imgReset 无 无 重置图片的角度、缩放、位置(可以在onloadImage回调里使用) 否 GitHub https://github.com/wx-plugin/image-cropper/tree/master 如果有什么好的建议欢迎提issues或者提pr
2021-12-15 - 小程序页面(Page)扩展,为所有页面添加公共的生命周期、事件处理等函数
背景 在小程序的原生开发中,页面中经常会用到一些公共方法,例如在页面onLoad中验证权限、所有页面都需要onShareAppMessage设置分享等 假设我们在编码时每个页面都写一遍,显然不是一个高级程序员会干的事情,太Low了。如果我们定义一个公共文件,导出这些公共方法,每个页面都引入,然后再生命周期或者事件处理函数中调用,虽然看起来很方便,但不够优雅,达不到我们最终的目的(偷懒)。 下面给大家介绍一种相对比较优雅的实现方式,扩展Page来实现以上的操作。 Page(页面) 需要传入的是一个 [代码]object[代码] 类型的参数,那么我们重载一个 [代码]Page[代码] 函数,将这个 [代码]object[代码] 参数拦截改掉就可以了,下面直接上代码。 实现 1、在根目录新建一个 [代码]page-extend.js[代码] 文件,公共的逻辑都写在这里面 [代码]/** * * Page扩展函数 * * @param {*} Page 原生Page */ const pageExtend = Page => { return object => { // 导出原生Page传入的object参数中的生命周期函数 // 由于命名冲突,所以将onLoad生命周期函数命名成了onLoaded const { onLoaded } = object // 公共的onLoad生命周期函数 object.onLoad = function (options) { // 在onLoad中执行的代码 ... // 执行onLoaded生命周期函数 if (typeof onLoaded === 'function') { onLoaded.call(this, options) } } // 公共的onShareAppMessage事件处理函数 object.onShareAppMessage = () => { return { title: '分享标题', imageUrl: '分享封面' } } return Page(object) } } // 获取原生Page const originalPage = Page // 定义一个新的Page,将原生Page传入Page扩展函数 Page = pageExtend(originalPage) [代码] 2、在 [代码]app.js[代码] 中引入 [代码]page-extend.js[代码] 文件 [代码]require('./page-extend') App({ // 其他代码 ... }) [代码] 代码片段 https://developers.weixin.qq.com/s/Cyx8iGmV7Ldp 本文内容及评论未经允许,禁止任何形式的转载与复制(代码可在程序中使用)
2019-12-24 - 小程序地图可以增加图层叠加吗?
如题所示,希望小程序地图增加图层叠加功能,比如在图中叠加雷达图,降雨落区图等
2021-06-30 - 订阅消息如果选择“不在询问”后,要是点启了取消 总是保持以上选择 就再也没地方可以开启了吗 ?
订阅消息如果选择“不在询问”后,就再也没地方可以开启了吗 用户点击了取消 然后在设置里面点击了允许接受此消息 但是去推送的时候 还是推送不成功 43101user refuse to accept the msg rid: 609bc1ba-297a4245-3234e5fe 求有遇到解决方案的大佬吗
2021-05-12 - wx.navigateTo跳转传参时,options取参为何会发生数据截断?
参数为图片url,url内有==号,options取参时会把==后面的东西都自动过滤掉了,为何? (1) navigateTo传参如下: [图片] (2)options取值如下: [图片] (3)console打印结果: [图片]
2021-04-07 - wx.getAvailableAudioSources能否获取蓝牙耳机的接入情况?
API组件:wx.getAvailableAudioSources(Object object) 微信版本号:Version 8.0.2(iPhone 8); Version 8.0.1(Samsung S9) 问题描述: wx.getAvailableAudioSources 文档上说从基础库2.1.0开始支持,获取当前支持的音频输入源。我在最新的微信版本上,测试的结果如下: 测试代码: wx.getAvailableAudioSources({ success: (res) => { console.log('success', res); }, fail: (err) => { console.error('fail', err); }, }) 测试结果: [图片] 所以现在wx.getAvailableAudioSources接口的真实返回情况是 iOS:只支持有线耳机,蓝牙耳机不支持?安卓:目前都不支持么? [图片]
2021-03-24 - 吐槽一下weui小程序扩展组件库
最近尝试着用了下weui小程序的组件库,发现几个问题今天发出来跟大家探讨一下。 一:官方文档介绍npm安装方法不够详细。 按照官方文档npm install去下载weui的时候,能下载下来,但是构建npm的时候始终失败,你绞尽脑汁也想不到为什么会失败呢,其实是你的项目里面没有package.json文件。你在install之前需要先初始化一下npm init 然后把相关的信息补全后,会生成这个文件。这个问题可以说是对npm了解不够深刻,但是我觉得官方文档应该在详细一下会更好。 二:构建npm的问题 很想知道微信开发者工具里面的“构建npm”功能和项目设置里面的勾选“使用npn模块“是什么意思。构建npm这次我大概知道了,是从node_modules文件夹里面重新生成出来一套可以供小程序引用的一个文件夹如图: [图片] 看到了吗,构建npm之后会多生成一个npm结尾的文件夹,然后项目里面import文件路径是从这个miniprogram_npm文件里面引用的。 我想说好low啊,为鸡毛已经npm下载好的模块不能直接使用呢,还需要在构建出来一套,为什么不能向react项目那样,npm安装好的包可以直接在项目import,微信小程序这点儿做的感觉多此一举。构建npm这个功能是不是就是从npm下载的包,需要从新生成一套可以项目使用的文件?还有其它的含义吗?目前不清楚。 还有就是项目配置信息里面的 使用npn模块勾选项,请问这个是什么意思,看官方文档没看明白,大致理解的是如果把自己的项目打包上传到npm管理的话,好像是需要勾选这个npm模块选项的。不知道理解的对不对。总体感觉官方文档写的不够详细。很多都是需要自己花费很大时间琢磨的事情。不知道大家有没有同感。 三:weui小程序组件库基础组件太少了 weui小程序扩展组件库,能够满足的场景我感觉是非常少的。你看weui组件库组件就可以看到,他提供的是一套包含布局及页面级别的组件。意思是说,如果你要创建的form表单跟weui提供的form页面组件风格一致的话,你就可以使用。如果不一样的话是没有办法用form组件的,呵呵,试问大家需求千变万化,怎么可能会向weui提供的form组件页面那样高度一致? [图片] [图片] 看图,我理解的是只有你得页面结构及风格跟weui组件一样的时候你才能用weui组件库组件。有没有感觉到实用空间非常小?weui组件库是这样使用的吗?还是我理解的有问题,因为从weui文档上能看到的东西也就这么多。 其实我觉得,weui组件库是在weui样式库上继承过来的,其实它应该更侧重样式这一块儿内容,通过weui样式及微信小程序本身自带的基础组件,我觉得应该会很完美吧。weui扩展的组件库感觉不能够完全撑起复杂多变的业务需求。 不知道以上内容是不是我对weui组件库了解的还不够深,毕竟是初次使用。还是大家跟我的看法一样,还请大家帮忙指点一下weui组件库具体该怎么组合使用。我觉得应该基础组件在丰富些就好了,可以让用户自由组装。类似 蚂蚁金服的ant design 组件库。 欢迎讨论。多多指教
2020-05-11 - 静态网站 H5 跳小程序
https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/staticstorage/jump-miniprogram.html 麻烦问下 这个api是用于第三方打开自己的小程序所提供的服务,还是可以跳转任意已经上线的小程序,里面的参数具体是哪个填我的小程序id和要跳转的小程序id,什么叫资源方appid?
2021-03-15 - wx-open-launch-weapp 按钮显示不出来,怎么回事?
<wx-open-launch-weapp id="launch-btn" username="gh_1ac06b5a8f4e" path="/pages/index/index?cityCode=4401&ysschannel=2019DSGZHCDL_61034_gz"> <template> <style> .btn { padding: 12px } </style> <button class="btn">打开小程序</button> </template> </wx-open-launch-weapp> $().ready(function () { var url = location.href; $.ajax({ url: "../api/UEditor/GetSignature",//后台给你提供的接口 data: { "url": url }, success: function (data) { var datad = JSON.parse(data); //转译为Json字符串 wx.config({ debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来 appId: datad.appid, // 必填,公众号的唯一标识 timestamp: datad.timestamp, // 必填,生成签名的时间戳 nonceStr: datad.noncestr, // 必填,生成签名的随机串 signature: datad.signature,// 必填,签名,见附录1 jsApiList: ["scanQRCode"], // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 openTagList: ["wx-open-launch-weapp"] }); wx.error(function (res) { layer.msg(res); }); }, error: function (error) { layer.msg(error) } }); })
2021-02-03 - 推荐一个自定义导航栏开源库
前言大家都知道官方提供的小程序导航栏相对有限,那么我们如何应对产品大大无限的需求呢? 那么肯定就需要自定义导航栏,而今天我要给大家推荐一款很棒的自定义导航栏开源项目。 少啰嗦,看效果。⬇️ 看效果[图片] [图片][图片] [图片] [图片] [图片] [图片] 看属性[图片] 如何使用?配置 app.json 中的 navigationStyle 和 usingComponents { "window": { "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "自定义导航栏", "navigationBarTextStyle": "black", "navigationStyle": "custom" }, "usingComponents": { "navBar": "/components/navBar/navBar" }, "sitemapLocation": "sitemap.json" } 页面代码: 上地址地址:https://github.com/lingxiaoyi/navigation-bar
2020-08-21 - 业务域名设置--校验文件检查失败自查指引
目前不少开发者在设置业务域名时,发现检查校验文件失败,可先按照如下步骤进行自查: 如果想保存的业务为https://test.com/,下载下来的校验文件为AbC.txt,则需要确保https://test.com/AbC.txt能够访问。 校验文件内容错误。校验文件内容一般是非HTML数据,如果下载下来的校验文件内容为HTML数据,一般为登录态过期。请重新登录小程序下载校验文件。 使用4G网络尝试访问链接,确认自身服务器没有拦截请求(常见于设置了白名单或者防火墙的服务器,需开发者自行确认下) https证书过期。请确保https证书处于有效期内。 使用curl 测试链接,确保curl能够正常访问链接,且curl出来的内容为校验文件内容。 使用time curl https://test.com/abc.txt查看链接时间,建议耗时在1s之内。 请确保url中的文件名与下载下来的文件名大小写一致。如下载的文件是AbC.txt,确保url是https://test.com/AbC.txt,不能是https://test.com/abc.txt 部分用户的服务器配置较陈旧,安全性差(如配置 768位 的 DH),为了保证通信安全,微信后台不支持,请更新服务器配置。 (1)通过https://cloud.tencent.com/product/tools#userDefined12,检测网址是否支持TLS1.2。 (2)可通过工具 https://www.ssllabs.com/ssltest/analyze.html 检查自己的服务器,对该工具标红的各项漏洞逐项修补,建议更新配置直到该工具打分为 C及以上 。 9. 如上述检查都没有问题,请重新下载校验文件重试,确保上传到服务器的文件内容与新下载的文件内容一致。
2018-06-21 - 微信小程序通讯录组件及demo,支持汉字转拼音,排序类型,常规通讯录,全选、多选、单选通讯录。
提供支持通讯录基础组件,来源博主Homilier https://blog.csdn.net/Honiler/article/details/82929111 汉字拼音转换工具 https://github.com/hotoo/pinyin 微信原生代码片段https://developers.weixin.qq.com/s/A3maQemS7qkc 效果图[图片][图片][图片][图片][图片][图片][图片][图片] 使用index.json { "navigationBarTitleText": "教师通讯录", "enablePullDownRefresh": true, "usingComponents": { "alphabet-order-list": "/components/alphabet-order-list/alphabet-order-list" } }
2020-09-09 - 做个优秀的小程序 - 体验评分
随着小程序的开发迭代,慢慢的我们会更加关注小程序的质量,今天来讲讲小程序的隐藏功能 -- 体验评分。 为什么需要体验评分 我们多做一点,就可以给用户更好的体验。(窃喜) 当然,做为开发者的我们,动动鼠标点一点就能帮助我们发现问题,是不是很愉快~~ 接下来我们来看看怎么使用体验评分? 怎么使用体验评分 体验评分的能力目前开放在【微信开发者工具 - 调试器 - Audits】 操作步骤:运行体验评分 - 一顿操作 - 获取体验报告 - 一顿优化。 (优化其实是一个圈,新代码加上之后也要继续关注哦~) [图片] 体验评分实践 我们用《小程序示例》来操作一波看看效果~ 01. 运行体验评分 使用开发者工具打开小程序,调试器区域切换到 Audits 面板,就一个“运行”按钮,点它。 [图片] 02.一顿操作 然后在工具上对小程序进行操作,比如:我点开了 “接口 - 媒体 - 音频 - 播放 ”。 [图片] 03.获取体验报告 操作完之后,点击“停止”,我们就可以获取到体验报告(简单~)。 [图片] 拿到报告之后,我们就可以看到总分 98,最佳实践 80。往下拉会有扣分的实际原因。 看第一条是 “发现正在使用废弃接口”,报告已经很清楚的告诉我们使用了废弃组件 audio,我们根据报告进行优化即可。 [图片] 04.一顿优化 按照报告优化完之后,我们可以继续进行体验评分功能确认优化是否完善。这是一个有用的圈圈⚪⚪⚪ 我们来讲几个优化过程中遇到的问题,咳咳咳 存在图片没有按原图宽高比显示 [图片] 在测试预览图片的时候,发现图片被挤了,体验评分告诉我们宽高比有问题,发现是 <image> 使用了默认的 mode (scaleToFill:缩放模式,不保持纵横比缩放图片,使图片的宽高完全拉伸至填满 image 元素)。所以通过添加 mode="aspectFill" (缩放模式,保持纵横比缩放图片,只保证图片的短边能完全显示出来。)来解决宽高比的问题。 [图片] [图片] 发现固定底部的可点击组件可能不在iPhone X安全区域内 [图片] 这个问题我们用手机测试是正常的,但是体验评分给了提示,所以就来看看实现方式是不是有问题: 原有方式:通过接口监听systemInfo.model.indexOf('iPhone X') 给 view 添加专属 class 官方推荐:官方推荐的方式是用 wxss 来兼容,不一定只有 iPhone X 下面会有安全区域 [图片] 发现正在使用废弃接口 [图片] 这个问题对一些老旧代码来说很有用,比如示例很久之前写的 auto-focus,由于基本没有改动,所以代码就一直保持不变。使用体验评分的时候检测到了这个属性是废弃属性,所以我们更换了可用属性 focus 来解决问题。 [图片] 体验评分总结 使用体验评分进行小程序示例的优化,有以下优点: 可以发现代码中使用的废弃api,避免后续踩坑根据实际操作发现相关耗时久的情况,预先发现体验问题合理的视觉/交互检测,提前做好兼容资源使用检测,用合适的资源做好小程序当然,体验过程中也有不足: 开发者工具不支持预览的 组件 / API 暂不支持体验评分(听说官方已经在努力推进啦)一起体验评分 如果你也在做小程序优化,欢迎使用体验评分来优化哦~ 预祝大家都拿 100婚 !!! [图片] 体验评分文档传送门 如果你有疑问,请在下方评论区留言给binnie,㊗️大家都没有bug,✌️✌️✌️
2020-12-04 - 第三方代注册小程序支持快速设置登录邮箱和密码
近期平台对第三方服务商代注册小程序的流程进行了优化,用户通过第三方服务商成功创建小程序后,将收到设置小程序登录邮箱和密码的微信消息提醒。管理员可通过点击提醒内的“前往设置邮箱密码”或直接搜索「小程序助手」小程序进行邮箱密码的设置。 温馨提示:所设置的邮箱需要是未绑定任何个人微信、公众号、小程序、微信开放平台的邮箱 详细操作流程如下: 1.打开“小程序助手”小程序,选择对应的小程序 [图片] 2.点击“登录邮箱”设置项 [图片] 3.填写邮箱以及设置小程序密码 [图片] 4.系统会发送验证邮件到邮箱 [图片][图片] 5.打开邮件中的链接,确认验证 [图片] 6.邮箱设置成功,可以在官网:mp.weixin.qq.com用所设置的邮箱和密码登录小程序后台 [图片] 如有疑惑,可微信搜索小程序服务商助手-我的-咨询反馈。
2020-07-03 - wx-open-launch-weapp 宽度高度都是0?
[图片] h5拉起小程序页,标签宽高都是0,是由什么原因导致的
2020-11-06 - 微信开放社区运营规范
微信开放社区运营规范 微信团队一直致力于将微信打造成一个强大的、全方位的服务工具。通过全面开放的能力,连接更多的开发者和用户。微信开放社区是微信为用户提供的内容问答社区。使用微信开放社区服务(以下简称“微信开放社区”或本“功能”),微信开放社区用户(以下简称“你”)必须阅读并遵守《微信公众平台服务协议》、《微信小程序平台服务条款》、《微信小程序平台运营规范》以及腾讯为此制定的专项规则等。 本《微信开放社区运营规范》(以下简称,本“运营规范”)是在上述协议及规则基础上进行解释和说明,相关内容和举例旨在帮助开发者更加清晰地理解和遵守相关协议和规则,以便能够更加顺利地在微信开放社区进行运营,而不是修改或变更上述协议及规则中的任何条款。 一、发布内容规范: 1. 你不得在微信开放社区发布、分享传播国家法律法规禁止的以下内容: 1.1 反对宪法所确定基本原则,危害国家安全、泄露国家秘密、颠覆国家政权、破坏国家统一、损害国家荣誉和利益。 1.2 反政府、反社会,或存在煽动性的涉政言论、散布谣言,扰乱社会秩序,破坏社会稳定。 1.3 煽动民族仇恨、民族歧视、破坏民族团结、破坏国家宗教政策、宣扬邪教和封建迷信。 1.4 展示人或动物被杀戮、致残、枪击、针刺或其他伤害的真实图片,描述暴力或虐待儿童的,或包含宣扬暴力血腥。 1.5 淫秽、色情或低俗信息,包括但不限于: 1.5.1 应用中含有淫秽、色情内容,如招嫖、寻找一夜情、性伴侣等内容; 1.5.2 传播以色情为目的的情色文字、情色视频、情色漫画等形式的内容; 1.5.3 传播非法色情交易的信息; 1.5.4 直接或隐晦表现性行为、具有挑逗性或者侮辱性内容,或以带有性暗示、性挑逗的语言描述性行为、性过程、性方式的; 1.5.5 应用中传播非法性药品、性保健品、性用品和性病治疗营销信息等相关内容的; 1.5.6 应用中传播相关部门禁止传播的色情和有伤社会风化的文字、音视频内容的。 1.6 赌博、竞猜和抽奖类信息,包括但不限于: 1.6.1 应用中传播以虚拟货币或真实货币直接进行押输赢、竞猜、参与赌博等内容的; 1.6.2 应用运营过程中可将游戏分数或金币等兑换成真实货币或实物奖励的; 1.6.3 应用运营过程中可根据玩家输赢结果进行抽水、分成等后果的; 1.6.4 其他被认定为宣扬赌博色彩的行为。 1.7 含有虚假、欺诈或冒充类内容,包括但不限于虚假红包、虚假活动、虚假宣传,仿冒腾讯官方或他人业务,可能造成微信用户混淆等内容的。 1.8 任何召集、鼓动犯罪或有明显违背社会善良风俗行为的内容。 1.9 任何违反《计算机信息网络国际联网安全保护管理办法》、《互联网信息服务管理办法》、《互联网电子公告服务管理规定》、《维护互联网安全的决定》或其他国家法律法规规定的内容。 2. 你不得在微信开放社区发布、分享和传播侵犯他人合法权利的信息: 2.1 包含公然侮辱或者诽谤他人,损害他人名誉或商誉内容的。 2.2 包含使用或揭露他人身份信息、照片、隐私,侵害他人肖像权、隐私权合法权益的。 2.3 未经授权,擅自使用他人商标、著作权以及其他侵犯他人知识产权内容的。 2.4 未经授权,擅自使用他人拥有商标权的标识、图像等内容。 2.5 未经授权,使用或传播他人的原创图文消息或其他有合法著作权权利的内容,侵犯他人知识产权。 2.6 依靠抄袭、模仿等手段使用他人拥有商标权或著作权权益的内容,侵犯他人权益的。 3. 你不得在微信开放社区发布、分享和传播违反平台相关规则的信息: 3.1 内容主要为营销、互推或广告用途,包括但不限于空白广告位、招商广告位或作为第三方平台通过应用中的营销、广告模块盈利等。 3.2 内容包含多级分销信息,发布分销信息诱导用户进行分享、传播或直接参与。 3.3 对用户产生误导、严重破坏用户体验,损害用户利益的谣言类内容。 3.4 传播骚扰信息、恶意营销和垃圾信息等内容。 3.5 其他涉及违法违规或违反平台相关协议、规则的内容。 二、使用行为规范 你不得在微信开放社区从事以下行为: 1. 恶意破坏微信开放社区正常秩序,包括但不限于恶意灌水、踩贴、刷流量、刷评论、利用自定义栏目或其他形式传播病毒、垃圾广告、非法信息等。 2. 未经腾讯许可或授权,擅自转载、或爬取微信开放社区文章和内容。 3. 重复发布干扰正常用户体验的内容。包括但不限于: 3.1 重复发表同一文章的; 3.2 重复的回答内容多次发布在不同问题下的; 3.3 频繁发布难以辨识涵义影响阅读体验的字符、数字等无意义乱码的; 3.4 骚扰他人,以评论、@他人、私信等方式对他人反复发送重复或者相似的诉求。 4. 发表不符合版面主题,或者无内容的灌水内容、或者发表色情,猥亵,谩骂、包含人身攻击,诽谤等的内容。 5. 使用不雅或不恰当ID和昵称,头像,个性签名等。 6. 从事违反与腾讯签订的、任何形式的服务协议、平台协议、功能协议的行为。 7. 从事违反腾讯为相关软件、服务、功能等而制定的管理、运营规范、规则的行为。 8. 从事非法商业活动或任何违反国家法律法规的行为。 三、服务商规范 1. 服务平台入驻规则 1.1 服务平台服务商,指为微信小程序提供模版开发、定制化开发、插件、借口能力等服务的服务提供方,包括但不限于具有第三方平台的平台型服务商和定制型服务商。 1.2 任何有意愿为小程序提供开发服务的开发者/企业都可以申请成为小程序服务商。目前阶段,满足以下条件的服务商,经平台审核后可接入微信开放社区-服务平台,小程序用户即可在服务平台中获取关服务商信息: (1)符合上述1.1条定义; (2)在微信开放社区中具有关联第三方平台的企业主页; (3)两个自然周内,单个服务商累计7天符合要求,可在下两个自然周内被搜出。 上述条件中,“第三方平台”[1]为全网发布并经审核通过,并应满足以下条件: (1)服务商业务活跃性:短期内授权一定数量的小程序,并发布上线; (2)小程序活跃度:已上线的小程序保证持续用户活跃; (3)服务商小程序预审能力:无不良审核记录。 1.3 平台有权根据产品策略和业务发展需求,对上述服务商接入的要求和规范进行不时地调整。对于不满足条件的服务商,微信有权进行动态调整。 2. 服务商行为规范 2.1 服务商入驻微信开放社区服务平台,不视为微信向你提供任何授权或进行任何合作。你不得以微信、微信代理商或微信官方合作伙伴的名义从事任何经营活动。你保证,不得以微信或微信代理商的名义拓展合作伙伴或代理商,未经微信同意,不得对外使用“微信”、“微信公众平台”、“微信开放平台”和“微信开放社区”的品牌和LOGO,或进行任何造成或可能造成微信用户混淆的对外宣传行为。 2.2 服务商不得从事或间接协助从事以下任一活动,否则平台可能会对你作出处理,终止合作,追究服务商的法律责任: 2.2.1 在企业主页等位置存在夸大、虚假、承诺性、绝对化等违反广告法等法律法规的描述的; 2.2.2 以微信、微信代理商或微信官方合作伙伴的名义从事经营活动或对外宣传的; 2.2.3 将你在平台的技术资源等转包、分包、转让、有偿或无偿提供给其他主体,或者以代理、加盟等形式拓展合作伙伴并收取费用的; 2.2.4 把服务商资源用于《微信开放平台公众帐号第三方平台开发者服务协议》和本规则约定范围以外的用途的; 2.2.5 服务态度恶劣,陈述有误引起小程序商户误解,或未向商户提供经承诺的服务的; 2.2.6 虚构事实,隐瞒真相,出现违规行为时不配合或阻碍平台正常调查的; 2.3 服务商不得在主页等位置,发布任何干扰微信公众平台正常运营,或侵犯其他用户或第三方合法权益的内容,否则平台可能会对你作出处理。 2.4 服务商使用服务平台向小程序用户提供服务,须严格遵守适用的法律法规。 2.5 服务商入驻服务平台,不视为服务商获得平台的任何授权,也不代表平台对服务商的能力、商誉进行任何形式、任何种类的明示或暗示的推荐或担保,包括但不限于商业适售性、特定用途适用性等。服务商对于其在服务平台向小程序用户提供服务的行为必须自行承担相应法律责任和风险。 四、违规处理 1. 如你违反上述使用规范,微信开放社区有权视开发者的违规程度给予警告、删帖、暂停账号使用、注销帐号等处罚措施,并依法追究你的法律责任。 2. 如服务商违反上述服务商规范,在平台收到对你的违规投诉或平台知悉你存在违规情形后,平台将向你发送电子邮件或站内信或通过其他有效通知方式,服务商应在平台发出通知之日起3个工作日内提供书面回复,如服务商认为违规行为不成立或已整改,应提供相应证明;如服务商未能在限定期间内书面回复,或虽提出异议但未能提供充分证据,视为违规行为成立。平台有权依据相关事实及服务商书面回复内容(如有)进行独立判断,并以电子邮件形式将认定结果进行告知。[T1] 五、免责声明 1. 微信开放社区不对你发表的内容、回答或评论的正确性进行保证。你在微信开放社区发表的内容仅表明其个人的立场和观点,并不代表微信的立场或观点。作为内容的发表者,你需自行对所发表内容负责,因所发表内容引发的一切纠纷,由你承担全部法律及连带责任。微信不承担任何法律及连带责任。 2. 微信开放社区知识库内容(即带有微信官方标识的内容)为微信开放社区官方发布内容。你清楚了解并同意,该等内容为微信开放社区根据平台运营情况或相关行业经验,在特定时间针对某具体问题提供的参考性内容,该等内容非实时更新,亦可能过期失效,仅供用户参考。未经腾讯许可或授权,用户不得擅自转载或爬取微信开放社区知识库内容。 3. 服务商入驻服务平台,不视为服务商获得平台的任何授权,也不代表平台对服务商的能力、商誉进行任何形式、任何种类的明示或暗示的推荐或担保,包括但不限于商业适售性、特定用途适用性等。服务商必须自行对其通过服务平台提供的服务承担相应法律责任和风险。 4. 你理解并同意,在使用微信开放社区时,需自行承担如下腾讯不可掌控的风险,包括但不限于: 4.1 由于受到计算机病毒、木马或其他恶意程序、黑客攻击的破坏等不可抗拒因素可能引 起的信息丢失、泄漏等损失和风险; 4.2 用户或腾讯的电脑软件、系统、硬件和通信线路出现故障导致的服务终端、数据丢失以及其他的损失和风险; 4.3 用户操作不当或通过非腾讯授权的方式使用本服务带来的损失和风险; 4.4 用户发布的内容被他人转发、分享,因此等传播可能带来的风险和责任; 4.5 由于网络信号不稳定、网络服务中断或其他原因所引起的无法使用微信开放社区、页面打开速度慢等风险; 4.6 其他腾讯无法控制或合理预见的情形。 六、遵守当地法律监管 1. 你在使用微信开放社区服务的过程中应当遵守当地相关的法律法规,并尊重当地的道德和风俗习惯。如果你的行为违反了当地法律法规或道德风俗,你应当为此独立承担责任。 2. 你应避免因使用本服务而使腾讯卷入政治和公共事件,否则腾讯有权暂停或终止对你的服务。 七、动态文档 这是一份动态更新的文档,我们会根据新出现的问题、相关法律法规更新或产品运营的需要来对其内容进行修改并更新,制定新的规则,保证微信用户的体验。你应能反复查看以便获得最新信息,请定期了解更新情况。 微信团队
2019-12-27 - 小程序插件快速更新功能说明
功能介绍: 为帮助小程序快速迭代更新,新增插件快速更新功能。小程序开发者可在移动端便捷体验、快速更新小程序内插件的版本,无需修改代码或重新提交审核。 适用范围: 适用于不需要小程序改动原有代码逻辑或针对插件做适配,只更新插件内服务内容的插件版本更新。 使用流程: 1、插件开发者发布新版本时,选择快速更新。并选择要给正在使用哪些版本的小程序发送通知。 [图片] [图片] 2、选择快速更新后,使用低版本插件的小程序开发者管理员会收到通知 [图片] 3、在移动端选择体验最新版插件。系统将会以小程序线上版本+插件最新版本,生成体验版小程序。小程序开发者可在移动端体验该版本。 [图片] 4、体验确认服务预期后,小程序开发者可在移动端操作发布。操作后无需提交审核、直接发布现网,更新小程序版本。 [图片]
2020-03-18 - 小程序内怎么调试web-view?
开发工具上在web-view页面内点击鼠标右键有个调试的选项 需要在真机上调试需要自行引入vconsole:https://github.com/Tencent/vConsole/blob/dev/README_CN.md
2019-10-09 - 打开小程序设置页(wx.openSetting)接口调整
开发者可以通过 [代码]wx.openSetting[代码] 接口来打开小程序设置界面并返回用户的设置结果。在原来的 [代码]wx.openSetting[代码] 接口中,我们允许开发者直接调用此接口,但目前我们发现有不少开发者滥用此接口,使用户在无任何操作时,不断地强行跳转至设置页,导致用户无法正常使用甚至无法退出小程序。 为保证用户获得更顺畅的小程序使用体验,避免此类滥用情况,我们对该接口进行了调整。 调整后“打开小程序设置页”将支持以下两种实现方式: 方法1:使用 [代码]button[代码] 组件来使用此功能,示例代码如下: <button open-type="openSetting" bindopensetting="callback">打开设置页</button>方法2:由点击行为触发[代码]wx.openSetting[代码]接口的调用,示例代码如下: <button bindtap="openSetting">打开设置页</button> openSetting() { wx.openSetting()}方法2已在最新版开发者工具中支持(基础库切到2.2.4及以上),开发者可以尽早适配。 此次调整会对直接调用wx.openSetting接口造成影响 原无需用户点击即可直接调用wx.openSetting接口的实现方式将不再支持,即将废弃的错误使用方式示例如下: onShow() { wx.openSetting()} 10月10日起新提交发布的版本将会受到此调整的影响。 需要各位开发者注意,10月10日起新提交发布的小程序版本将不再支持无需用户点击即可直接调用的“打开小程序设置页”接口,请开发者尽早适配。 调整策略在基础库 2.3.0 及以上版本生效,该基础库版本对应微信客户端6.7.2版本。另外,考虑到兼容性等问题,在基础库版本 2.3.0 以下的环境中不受此策略影响。
2018-09-12 - (4)获取用户信息
背景 我们发现大部分小程序都会使用 [代码]wx.getUserInfo[代码] 接口,来获取用户信息。原本设计这个接口时,我们希望开发者在真正需要用户信息的情况下才去调取这个接口,但很多开发者会直接调用这个接口,导致用户在使用小程序的时候产生困扰,归结起来有几点: 开发者在小程序首页直接调用 [代码]wx.getUserInfo[代码] 进行授权,弹框获取用户信息,会使得一部分用户点击“拒绝”按钮。 在开发者没有处理用户拒绝弹框的情况下,用户必须授权头像昵称等信息才能继续使用小程序,会导致某些用户放弃使用该小程序。 用户没有很好的方式重新授权,尽管我们增加了[代码]设置[代码]页面,可以让用户选择重新授权,但很多用户并不知道可以这么操作。 此外,我们发现开发者默认将 [代码]wx.login[代码] 和 [代码]wx.getUserInfo[代码] 绑定使用,这个是由于我们一开始的设计缺陷和实例代码导致的([代码]wx.getUserInfo[代码] 必须通过 [代码]wx.login[代码] 在后台生成 [代码]session_key[代码]后才能调用)。同时,我们收到开发者的反馈,希望用户进入小程序首页便能获取到用户的 [代码]unionId[代码],以便识别到用户是否以前关注了同主体公众号或使用过同主体的App 。 为了解决以上问题,针对获取用户信息我们更新了三个能力: 1.使用组件来获取用户信息 2.若用户满足一定条件,则可以用[代码]wx.login[代码] 获取到的[代码]code[代码]直接换到[代码]unionId[代码] 3.[代码]wx.getUserInfo[代码] 不需要依赖 [代码]wx.login[代码] 就能调用得到数据 获取用户信息组件介绍 [代码][代码] 组件变化: [代码]open-type [代码]属性增加 [代码]getUserInfo[代码] :用户点击时候会触发 [代码]bindgetuserinfo[代码] 事件。 新增事件 [代码]bindgetuserinfo[代码] :当 [代码]open-type[代码]为 [代码]getUserInfo[代码] 时,用户点击会触发。可以从事件返回参数的 [代码]detail[代码] 字段中获取到和 [代码]wx.getUserInfo[代码] 返回参数相同的数据。 示例: [代码]<button open-type="getUserInfo" bindgetuserinfo="userInfoHandler"> Click me button>[代码]和 [代码]wx.getUserInfo[代码] 不同之处在于: 1.API [代码]wx.getUserInfo[代码] 只会弹一次框,用户拒绝授权之后,再次调用将不会弹框; 2.组件 [代码][代码][代码][代码] 由于是用户主动触发,不受弹框次数限制,只要用户没有授权,都会再次弹框。 通过获取用户信息的组件,就可以解决用户再次授权的问题。 直接获取unionId开发者申请 [代码]userinfo[代码] 授权主要为了获取 [代码]unionid[代码],我们鼓励开发者在不骚扰用户的情况下合理获得[代码]unionid[代码],而仅在必要时才向用户弹窗申请使用昵称头像。为此,凡使用“获取用户信息组件”获取用户昵称头像的小程序,在满足以下全部条件时,将可以静默获得 [代码]unionid[代码]: 1.在微信开放平台下存在同主体的App、公众号、小程序。 2.用户关注了某个相同主体公众号,或曾经在某个相同主体App、公众号上进行过微信登录授权。 这样可让其他同主体的App、公众号、小程序的开发者快速获得已有用户的数据。 不依赖登录的用户信息获取某些工具类的轻量小程序不需要登录行为,但是也想获取用户信息,那么就可以在 [代码]wx.getUserInfo[代码] 的时候加一个参数 [代码]withCredentials: false[代码] 直接获取到用户信息,可以少一次网络请求。 这样可以在不给用户弹窗授权的情况下直接展示用户的信息。 最佳实践 1.调用 [代码]wx.login[代码] 获取 [代码]code[代码],然后从微信后端换取到 [代码]session_key[代码],用于解密 [代码]getUserInfo[代码]返回的敏感数据。 2.使用 [代码]wx.getSetting[代码] 获取用户的授权情况 1) 如果用户已经授权,直接调用 API [代码]wx.getUserInfo[代码] 获取用户最新的信息; 2) 用户未授权,在界面中显示一个按钮提示用户登入,当用户点击并授权后就获取到用户的最新信息。 3.获取到用户数据后可以进行展示或者发送给自己的后端。 One More Thing 除了获取用户方案介绍之外,再聊一聊很多初次接触微信小程序的开发者所不容易理解的一些概念: 1.关于OpenId和UnionId [代码]OpenId[代码] 是一个用户对于一个小程序/公众号的标识,开发者可以通过这个标识识别出用户。 [代码]UnionId[代码] 是一个用户对于同主体微信小程序/公众号/APP的标识,开发者需要在微信开放平台下绑定相同账号的主体。开发者可通过[代码]UnionId[代码],实现多个小程序、公众号、甚至APP 之间的数据互通了。 同一个用户的这两个 ID 对于同一个小程序来说是永久不变的,就算用户删了小程序,下次用户进入小程序,开发者依旧可以通过后台的记录标识出来。 2.关于 getUserInfo 和 login 很多开发者会把 [代码]login[代码] 和 [代码]getUserInfo[代码] 捆绑调用当成登录使用,其实 [代码]login[代码] 已经可以完成登录,[代码]getUserInfo[代码] 只是获取额外的用户信息。 在 [代码]login[代码] 获取到 [代码]code[代码] 后,会发送到开发者后端,开发者后端通过接口去微信后端换取到 [代码]openid[代码] 和[代码]sessionKey[代码](现在会将 [代码]unionid[代码] 也一并返回)后,把自定义登录态 [代码]3rd_session[代码]返回给前端,就已经完成登录行为了。而 [代码]login[代码] 行为是静默,不必授权的,用户不会察觉。 [代码]getUserInfo[代码] 只是为了提供更优质的服务而存在,比如展示头像昵称,判断性别,开发者可通过 [代码]unionId[代码] 和其他公众号上已有的用户画像结合来提供历史数据。因此开发者不必在用户刚刚进入小程序的时候就强制要求授权。 可以在官方的文档中看到 [代码]login[代码] 的最佳实践: [图片] Q & A Q1: 为什么 login 的时候不直接返回 openid,而是要用这么复杂的方式来经过后台好几层处理之后才能拿到? A: 为了防止坏人在网络链路上做手脚,所以小程序端请求开发者服务器的的请求都需要二次验证才是可信的。因为我们采取了小程序端只给 [代码]code[代码] ,由服务器端拿着 [代码]code[代码] 和 [代码]AppSecrect[代码] 去微信服务器请求的方式,才会给到开发者对应的[代码]openId[代码] 和用于加解密的 [代码]session_key。[代码] Q2: 既然用户的[代码]openId[代码] 是永远不变的,那么开发者可以使用[代码]openId[代码] 作为用户的登录态么? A: 不行,这是非常危险的行为。因为 [代码]openId[代码] 是不变的,如果有坏人拿着别人的 [代码]openId[代码] 来进行请求,那么就会出现冒充的情况。所以我们建议开发者可以自己在后台生成一个拥有有效期的 [代码]第三方session[代码] 来做登录态,用户每隔一段时间都需要进行更新以保障数据的安全性。 Q3: 是不是用户每次打开小程序都需要重新[代码]login[代码]? A: 不必,可以将登录态存入[代码]storage[代码]中,用户再次登录就可以拿[代码]storage[代码] 里的登录态做正常的业务请求,只有当登录态过期了之后才需要重新[代码]login[代码] 。这样子做一则可以减少用户等待时间,二则可以减少网络带宽。 目前微信的[代码]session_key[代码] 有效期是三天,所以建议开发者设置的登录态有效期要小于这个值。
2018-08-17 - 手机号授权,有时需要验证码
正常逻辑,一段时间内未验证的手机号,就需要重新验证。
2020-10-16 - 微信开发者工具下载的 sourcemaps 怎么用。
什么是 Sourcemaps uglifyjs、bable 等工具会对 源代码 进行编译处理生成编译后的代码(下称目标代码),而 sourcemaps 就是保留了目标代码在源代码中的 位置信息 --------- 大神分割线 --------- 如何解读 Sourcemaps Sourcemaps 是一个 json [代码]{ "version": 3, "sources": ["a.js", "b.js"], // 源文件列表,这个表示是由 a.js 和 b.js 合并生成 "names": ["myFn", "test"], // 如果开启了变量名混淆,这里会保留变量名在源文件中名字信息 "sourcesContent: [], // 可选项,保存源码信息,顺序与 sources 字段对应,chrome 的 sources 面板中源码使用了这个字段的内容进行展示 "sourceRoot": "", // 源文件所在的目录信息 "file": "dist.js", // 可选,编译后的文件名 "mappings": "" // 这个是重点,是目标代码和源文件的位置的映射关系 } [代码] mappings 目标文件"行"的信息 mappings 是使用 ; 分隔的,每个部分对应目标代码的行 如: “;AAAA;AAAA,BBBB;;” 本例子目标文件有 4 行 第 0 行和第 3 行没有源文件对应信息,所以这两行是编译过程中加入的代码 目标文件的"列"信息 如: “AAAA,CAEA,CAEA;” ‘,’ 表示行内的位置信息分隔符 本例表示目标文件的这一行有三个有效的位置信息。 位置信息的第一位表示目标文件的列的 偏移 信息 本例中,表示列的信息是 ‘A’、‘C’、‘C’,对应的数字为 0、+1、+1,(vlq 编码,在线编解码工具) 注意,这个是偏移信息; 列数从 0 开始,依次累加偏移值可以算出当前的位置信息对应的真正的列 所以本例中表示的是目标文件的第 n 行中的第 0 列,第 1 列,第 2 列(没错是第 2 列) 源文件的信息 如:‘AAAA;ACAA;ADAA;’ 位置信息的第二位表示源文件的信息,本例子中是 ‘A’、‘C’、‘D’,对应数字是 0、+1、-1 如果 sourcemaps 中的 sources 字段只有一个文件的话,那么位置信息中第二位一直是 A(不需要偏移) 假设 sourcemaps 中 sources: [‘a.js’, ‘b.js’] 本例的意思是 AAAA: 目标文件第 0 行第 0 列 对应 第 0 个文件 a.js ACAA; 目标文件第 1 行第 0 列 对应 第 1 个文件 b.js ADAA; 目标文件第 2 行第 0 列 对了 第 0 个文件 a.js (偏移是 -1 又回到了 a.js) 源文件的行信息 位置信息的第三位表示源文件中的行的信息, 理解了位置偏移的概念,我们很容易理解 如:‘AACA,CACA;AACA;‘ 那么 AACA: 目标文件的第 0 行第 0 列 对应 第 0 个文件的第 1 行 CACA: 目标文件的第 0 行第 0+1 列 对应 第 0 个文件的第 1+1 行 AACA:目标文件的第 1 行第 0 列 对应 第 0 个文件的第 1 行 (注意:’;’ 后的行列偏移信息归 0) 源文件中的列信息 位置信息的第四位表示源文件中的列的信息 如:'AAAA,CAAC;' 那么 AAAA: 目标文件的第 0 行第 0 列 对应 第 0 个文件的第 0 行第 0 列 CAAC: 目标文件的第 0 行第 0+1 列 对应 第 0 个文件的第 0 行第 0+1 列 位置信息的第五位 第五位表示变量的偏移,对应 sourcemaps 中的 names 字段,表示目标文件中的变量名对应域源文件中的变量 如:’AAAA,CAACC;AAAAD;' sourcemaps 中 names 字段是 [‘a’, ‘b’] 那么 AAAA: 目标文件的第 0 行第 0 列 对应 第 0 个文件的第 0 行第 0 列,没有变量的信息 CAACC: 目标文件的第 0 行第 0+1 列 对应 第 0 个文件的第 0 行第 0+1 列,有变量信息,变量在源文件中是 ‘b’ (0+1=1) AAAAD: 目标文件的第 1 行第 0 列 对应 第 0 个文件的第 0 行第 0 列,有变量信息,变量在源文件中是 ‘a’ (1-1=0) --------- 大神分割线 --------- 怎么使用 Sourcemaps Q: 线上小程序报错,我怎么通过 sourcemaps 还原到源代码中? A: 如报错 appservice.js 1:15000, 表示目标文件第一行 第 15000 列位置报错。根据上文介绍的,通过 mappings 字段算。 Q: 不会。 A: 如果你会写代码的话,参考下边 [代码]import fs = require('fs') import {SourceMapConsumer} from 'source-map' async function originalPositionFor(line, column) { const sourceMapFilePath = '如果你不真的替换的成 sourcemaps 在硬盘中的位置,那你还是放弃自己写代码吧。 ' const sourceMapConsumer = await new SourceMapConsumer(JSON.parse(fs.readFileSync(sourceMapFilePath, 'utf8'))) return sourceMapConsumer.originalPositionFor({ line, column, }) } originalPositionFor(出错的行,出错的列) [代码] Q: 不会写代码 A: 下载最新版的开发者工具,菜单-设置-拓展设置-调试器插件 [图片] [图片] Q: 为啥都是 null? A: 每个小程序版本都应该对应一个sourcemap文件。 运营中心那里下载的 sourcemap 是对应线上最新的小程序版本。但运营中心的报错集合了多个小程序版本。拿旧小程序版本的报错信息,和最新版本的 sourcemap,是匹配不出的。开发者工具和ci 上传的时候,会提示下载对应版本的 sourcemap 信息,可以自助保存。 [图片] Q: 怎么确定有没有版本对应上 A: 下载的 sourcemap 中有个 wx 字段,标明了该 sourcemap 文件对应小程序版本号。 [图片] [图片] 前提 1.确保发生错误的小程序版本和下载回来的 sourcemap 版本是一致的。 a. 下载 sourceMap 文件,可在 mp 后台或开发者工具上传成功弹窗下载 2.确保 map 文件和发生错误的 js 文件是对应的。sourcemap 的目录和文件说明 a. APP 是主包,FULL 是整包(仅在不支持分包的低版本微信中使用),其他目录是分包 b. 每个分包下都有对应的 app-service.js.map 文件。 c. 如果是使用了按需注入特性(app.json中配置了lazyCodeLoading),那么每个分包下还会有 appservice.app.js.map(对应分包下非页面的js),和所有页面的 xxx.js.map 以上事情都确保正确之后,还是出现行列号匹配不出来的情况。那就需要进一步排查。 线上运行的小程序 sourcemap 文件是怎么生成的? 处理流程:源码 [ a.js a.js.map b.js b.js.map ] -> 开发者工具(JS转 ES5,压缩)-> 微信后台(合并 js 文件)[ appservice.app.js appservice.app.js.map]。 注意:如果源码在交给工具之前是经过了 webpack 等打包工具的处理,那源码这里需要有 map 文件。否则不需要存在 map 文件。 可以看出,map 文件经过三个步骤的处理,每个步骤都有可能导致出错,因此开发者需要先排查,是否是前两个步骤出错导致的 map 文件失效的。 如何排查前两个步骤产生的 map 文件是否有问题。 1.排查 a.js.map 文件是否有问题。 a. 可以在 a.js 的代码中写一下 throw new Error(‘test sourcemap’)。 b. 使用了 webpack 的情况下,要构建为生产环境的版本。 c. 在开发者工具模拟器中运行对应的页面,看看控制台中的报错,错误行列号是否能正常映射到源文件。 2.排查 开发者工具(JS转 ES5,压缩)步骤是否有问题。 在排查完第一步的基础上,点击预览,用微信上扫码预览,并打开调试 vConsole 功能,检查 vConsole 中是否有报错信息,检查报错信息中的行列号是否能正常映射到源文件。 如何排查 微信后台(合并 js 文件)是否有问题。 a. 一定要先排查完前两个步骤再来排查这一步,一般情况下,这一步是不会出错的。 b. 如果有问题,也只会导致 map 文件中的行号信息出现偏移。比如 Error 信息中显示报错地址是 100: 200,行号是 100。那么你可能直接用 100: 200 在 map 文件中搜索不出信息,但是如果 用 150: 200 就可以搜索出来,说明行号偏移了 50。那其他报错也可以偏移 50 后再进行搜索就找到结果。 c. 怎么排查偏移了多少?可以结合 error.message 的内容,初步判断大概错误的内容是什么。把对应的 map 文件放到这个网站上 source-map-visualization 进行搜索,找出哪些相同列号的地方。再结合 error.message 的内容进行判断。 d. 如果排查到是这一步导致的问题,请在社区上联系我们,我们会在后续版本进行修复。 依旧排查不出原因? 先整理一下按照上述步骤排查的结论,再在社区上联系我们协助
2023-02-10 - 关于虚拟支付的汇总整理
本文场景 在iso端做支付,虚拟支付问题是绕不过去,没有处理好,轻则封禁搜索,重则永久封号,所以对待这个问题不可谓不慎重 本文内容本文主要汇总社区相关的几个经典帖子,特别是带有官方回复的帖子,进行截图,最后做一个总结 截图一 [图片] 截图二 [图片] 截图三 [图片] 截图四 [图片] 参考文章 为什么“小鹅通”的小程序可以做虚拟支付?? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/84ef0895eb9e4261b114a88d73dc7621 所谓的小程序IOS不允许虚拟支付到底限制的是谁?? - 微信开放社区 https://developers.weixin.qq.com/community/develop/article/doc/000246265d01201064ea17bc65b813 实名举报手机充值小程序虚拟支付可以正常使用?? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/00066aedd70a907b38ea5043856400 跑腿小程序,支付跑腿费,属于虚拟支付吗?可以在ios里进行支付吗?? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/0002e8ec24cbd0f29cba2e28156400 虚拟业务指南请收好。? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/000cc6c0b383a047c7798e0045b409 本文总结关于虚拟支付的整理 关于一个支付业务到底是不是虚拟支付,在有参考的情况,可以根据官方的 上面社区的锅巴同学整理的非常详细,给出了官方明确定义为非虚拟支付的6种情况 以下内容摘录自锅巴同学的社区回帖内容 小程序在线直播课程,充值加油卡,手机流量等这6种,不算小程序虚拟支付,请参照以下6种情况即可,因为你不符合这6种情况,所以被判定为违规 ① 小程序在线课程直播。用户先买课程,后续在线上安排老师在小程序直播。补充选择教育-在线视频课程类目 ② 线上报名活动,线下培训的类型 ③ 充值加油卡加油,涉及预付卡销售服务,补充商家自营-预付卡销售类目 ④ 充值手机流量,补充IT科技-电信运营商类目 ⑤ 悬赏问答功能,需选择社交红包-社交红包类目,并完成新商户号申请后,再提交代码审核。 ⑥ 微信支付充值积分,签到积分等,积分兑换实物商品,兑换成功后,会直接给用户寄过去。有实际服务存在 最后总结下 你所认为的虚拟支付可能并不是虚拟支付,你所强调的非虚拟支付也有可能被定义为虚拟支付,所以一切以官方口径为依据。
2020-09-07 - 小程序与小游戏获取用户信息接口调整,请开发者注意升级。
为优化用户体验,使用 wx.getUserInfo 接口直接弹出授权框的开发方式将逐步不再支持。从2018年4月30日开始,小程序与小游戏的体验版、开发版调用 wx.getUserInfo 接口,将无法弹出授权询问框,默认调用失败。正式版暂不受影响。开发者可使用以下方式获取或展示用户信息: 一、小程序: 1、使用 button 组件,并将 open-type 指定为 getUserInfo 类型,获取用户基本信息。 详情参考文档: https://developers.weixin.qq.com/miniprogram/dev/component/button.html 2、使用 open-data 展示用户基本信息。 详情参考文档: https://developers.weixin.qq.com/miniprogram/dev/component/open-data.html 二、小游戏: 1、使用用户信息按钮 UserInfoButton。 详情参考文档: https://developers.weixin.qq.com/minigame/dev/document/open-api/user-info/wx.createUserInfoButton.html 2、开放数据域下的展示用户信息。 详细参考文档: https://developers.weixin.qq.com/minigame/dev/document/open-api/data/wx.getUserInfo.html 请各位开发者注意及时调整接口。
2018-04-16 - 长按海报长图无法识别小程序码的解决方案
今天刚好看到 @沐绒。 提的一个问题《show-menu-by-longpress 在图片过长时,ios会识别不了小程序二维码》,于是就一本正经的慢悠悠的打开集齐BUG于一身让人又爱又恨的小程序开发工具,心里在想“呵,识别不了??我就不信了” 基础库:2.11.0 经过了漫长的长达300,000毫秒时间,新建了一个代码片段试了一番,各种预览,真机预览,真机调试,确实存在长按识别不出来的情况 是图片太长了?还是功能让人感觉有忽悠了? 最后发现,问题所在:只有在页面可视区域完全显示小程序码时,长按才能识别 所有在这个长按识码的功能变得更强大之前,解决方案如下:(不想多写代码,画海报的时候就把小程序码画在左上或者右上吧) 1、给图片绑定touchstart事件 2、使用wx.createSelectorQuery获取图片高宽位置信息 3、使用wx.pageScrollTo将页面滚动到图片底部对应页面的高度位置,注意 duration 默认为300ms,这里要设置duration为0,不然... js: touchstart: function (e) { wx.createSelectorQuery().select('.qrcodeImg').boundingClientRect(function (res) { wx.pageScrollTo({ scrollTop: res.top + res.height, duration:0, // 设置页面滚动所需的时间,这里设置为0 }) }).exec() } wxml: <image src="图片地址" bindtouchstart="touchstart" class="qrcodeImg" show-menu-by-longpress="{{true}}" mode="widthFix"></image> 最后猜测:长按识码,貌似是长按时,截取当前手机屏幕的图片进行识别的? 以下图片来自 问题《show-menu-by-longpress 在图片过长时,ios会识别不了小程序二维码》 第1张图,完整显示小程序码,可以识别 第2张图,小程序码显示不完整,无法识别 第3张图,移动vConsole到小程序码上,也无法识别 [图片][图片][图片] 代码片段:https://developers.weixin.qq.com/s/EGde0fm970h2
2020-05-10 - 微信小程序三种授权登录的方式
经过一段时间对微信小程序的研发后 总结出以下三种授权登录的方式,我给他们命名为‘一次性授权’‘永久授权’‘不授权’ 1.一次性授权 常规写法,需要获取用户公开信息(头像,昵称等)时,判断调取授权登录接口,但是此方法如果不经处理的话 用户如果拒绝授权或者删除该微信小程序后 需要重新调取并获取用户公开信息(头像,昵称等),此方法用户体验较差,不建议使用; 2.永久授权 在不必要使用用户公开信息(头像,昵称等)时,不调取授权登录接口,只有在必要的时候再去判断调取授权登录接口并把获取到的用户公开信息存入数据库,这样在每次登录时直接先运行指定函数从数据库索取需要的用户公开信息(头像,昵称等)即可,此方法在删除小程序后不用再次去授权登录(因为在用户第一次授权登录时已经把用户的公开信息存入数据库了以后直接向数据库索取即可),建议使用; 3.不授权 不需要授权登录获取用户公开信息(头像,昵称等),使用wx.login获取用户code并传入后台,后台可以通过用户的code值向微信要一个值(具体需要问后台,我只是个小前端,后台的东西不是很懂,只是知道一些逻辑而且也已经成功实现)然后通过这个用code换取的值就可以识别到指定用户,如果需要的话,前端要显示的头像、昵称等这些信息可以使用自定义可编辑的功能,当然,也可以通过<open-data type=“userAvatarUrl”></open-data><open-data type=“userNickName”></open-data>小程序提供的这个组件显示用户的头像及昵称(不过这个组件只有显示功能),用户如果想直接使用自己的头像昵称,也可以自行授权(比如添加个引导按钮什么之类的),建议使用; [图片][图片] 文中使用的微信自带接口、组件及函数: <open-data type=“userAvatarUrl”></open-data> <open-data type=“userNickName”></open-data> wx.login({ success(res){ console.log(res.code) } }) 微信授权登录 以上三种方式可以灵活运用,也可以把需要的结合到一起,并不冲突; 当然,大佬很多,我也只是个小前端而已,第一次发表技术方面的帖子,希望互相学习,互相指导,如有说的不对的地方还望大佬们及时指出!!! 谢谢
2019-04-18 - 小程序读取excel表格数据,并存储到云数据库
最近一直比较忙,答应大家的小程序解析excel一直没有写出来,今天终于忙里偷闲,有机会把这篇文章写出来给大家了。 老规矩先看效果图 [图片] 效果其实很简单,就是把excel里的数据解析出来,然后存到云数据库里。说起来很简单。但是真的做起来的时候,发现其中要用到的东西还是很多的。不信。。。。 那来看下流程图 流程图 [图片] 通过流程图,我看看到我们这里使用了云函数,云存储,云数据库。 流程图主要实现下面几个步骤 1,使用wx.chooseMessageFile选择要解析的excel表格 2,通过wx.cloud.uploadFile上传excel文件到云存储 3,云存储返回一个fileid 给我们 4,定义一个excel云函数 5,把第3步返回的fileid传递给excel云函数 6,在excel云函数里解析excel,并把数据添加到云数据库。 可以看到最神秘,最重要的就是我们的excel云函数。 所以我们先把前5步实现了,后面重点讲解下我们的excel云函数。 一,选择并上传excel表格文件到云存储 这里我们使用到了云开发,使用云开发必须要先注册一个小程序,并给自己的小程序开通云开发功能。这个知识点我讲过很多遍了,还不知道怎么开通并使用云开发的同学,去翻下我前面的文章,或者看下我录的讲解视频《5小时入门小程序云开发》 1,先定义我们的页面 页面很简单,就是一个按钮如下图,点击按钮时调用chooseExcel方法,选择excel [图片] 对应的wxml代码如下 [图片] 2,编写文件选择和文件上传方法 [图片] 上图的chooseExcel就是我们的excel文件选择方法。 uploadExcel就是我们的文件上传方法,上传成功以后会返回一个fildID。我们把fildID传递给我们的jiexi方法,jiexi方法如下 3 把fildID传递给云函数 [图片] 二,解下来就是定义我们的云函数了。 1,首先我们要新建云函数 [图片] 如果你还不知道如何新建云函数,可以翻看下我之前写的文章,也可以看我录的视频《5小时入门小程序云开发》 如下图所示的excel就是我们创建的云函数 [图片] 2,安装node-xlsx依赖库 [图片] 如上图所示,右键excel,然后点击在终端中打开。 打开终端后, 输入 npm install node-xlsx 安装依赖。可以看到下图安装中的进度条 [图片] 这一步需要你电脑上安装过node.js并配置npm命令。 3,安装node-xlsx依赖库完成 [图片] 三,编写云函数 我把完整的代码贴出来给大家 [代码]const cloud = require('wx-server-sdk') cloud.init() var xlsx = require('node-xlsx'); const db = cloud.database() exports.main = async(event, context) => { let { fileID } = event //1,通过fileID下载云存储里的excel文件 const res = await cloud.downloadFile({ fileID: fileID, }) const buffer = res.fileContent const tasks = [] //用来存储所有的添加数据操作 //2,解析excel文件里的数据 var sheets = xlsx.parse(buffer); //获取到所有sheets sheets.forEach(function(sheet) { console.log(sheet['name']); for (var rowId in sheet['data']) { console.log(rowId); var row = sheet['data'][rowId]; //第几行数据 if (rowId > 0 && row) { //第一行是表格标题,所有我们要从第2行开始读 //3,把解析到的数据存到excelList数据表里 const promise = db.collection('users') .add({ data: { name: row[0], //姓名 age: row[1], //年龄 address: row[2], //地址 wechat: row[3] //wechat } }) tasks.push(promise) } } }); // 等待所有数据添加完成 let result = await Promise.all(tasks).then(res => { return res }).catch(function(err) { return err }) return result } [代码] 上面代码里注释的很清楚了,我这里就不在啰嗦了。 有几点注意的给大家说下 1,要先创建数据表 [图片] 2,有时候如果老是解析失败,可能是有的电脑需要在云函数里也要初始化云开发环境 [图片] 四,解析并上传成功 如我的表格里有下面三条数据 [图片] 点击上传按钮,并选择我们的表格文件 [图片] 上传成功的返回如下,可以看出我们添加了3条数据到数据库 [图片] 添加成功效果图如下 [图片] 到这里我们就完整的实现了小程序上传excel数据到数据库的功能了。 再来带大家看下流程图 [图片] 如果你有遇到问题,可以在底部留言,我看到后会及时解答。后面我会写更多小程序云开发实战的文章出来。也会录制本节的视频出来,敬请关注。
2019-11-12 - 升级开发者工具到最新,种种原因需要恢复旧版,旧版安装包找不到应急处理方法
刚需 新的开发者工具出来发现跟现有项目有兼容性问题。 而项目一天不能停,要急着修改上架,所以要恢复最新的开发者工具到旧版本 旧版本安装包没备份找不到了怎么办,2个方案: 解决方案 1、使用自带的回退功能 开发者工具菜单,回退功能,如果这里回退的版本不行或者不能正常回退,那么使用方案2 2、稳定版更新日志页面选择回退 如果是稳定版,可以在稳定版的更新日志页面找到对应旧版本的历史下载链接: 稳定版 Stable Build 更新日志 | 微信开放文档 https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html 3、根据规律找出历史版本下载链接 分析规律 找出指定旧版本的下载链接,直接打开链接下载旧版本。 应急处理的下载链接格式: [代码]https://servicewechat.com/wxa-dev-logic/download_redirect?type=x64&from=mpwiki&download_version=1021910120&version_type=1 [代码] 这个链接:后面版本号(1910120)改成你想要的版本就为所欲为的下载旧版本了。 版本号1910120的分拆:前6位代表发布的年月日(YYMMDD)格式后面的一位代表当天的第几个版本,从0开始 另外说说url里面的type是代表平台(x64/ia32/darwin) version_type代表版本类型:正式版/预览版/开发版(推测) 下载链接demo 经过上面分析后,可以得出1910120的X64版本下载链接为: https://servicewechat.com/wxa-dev-logic/download_redirect?type=x64&from=mpwiki&download_version=1021910120&version_type=1 MacOs版本下载链接为: https://servicewechat.com/wxa-dev-logic/download_redirect?type=darwin&from=mpwiki&download_version=1021910120&version_type=1
2020-09-24 - 微信小商店·商家成长学习资料
内含开店指引、店铺运营和平台规则,帮你快速掌握小商店经营秘诀。
09-05 - 论函数复用的几大姿势
开发过小程序的朋友们应该都遇到这样的情况,可能很多个页面有相同的函数,例如[代码]onShareAppMessage[代码],有什么最佳实践吗,应该如何处理呢? 本次开发技巧,我从以下几种解决办法剖析: 将它复制粘贴到每个地方(最烂的做法) 抽象成一个公共函数,每个[代码]Page[代码]都手动引用 提取一个behavior,每个页面手动注入 通过[代码]Page[代码]封装一个新的[代码]newPage[代码],以后每个页面都通过[代码]newPage[代码]注册 劫持Page函数,注入预设方法,页面仍可使用[代码]Page[代码]注册 复制粘贴大法 这是最直观,也是初学者最常用到的办法。也是作为工程师最不应该采取的办法。这有一个致命的问题,如果某一天,需要改动这个函数,岂不是要将所有的地方都翻出来改,所以这个办法直接否决。 抽象公共函数 这种方式,解决了复制粘贴大法的致命问题,不需要改动很多地方,只需要改动这个抽象出来的函数即可。但是其实,这个方式不便捷,每次新增页面都需要手动引入这个函数。 以下都通过[代码]onShareAppMessage[代码]方法举例。 假设在[代码]app.js[代码]通过[代码]global[代码]注册了[代码]onShareAppMessage[代码]方法: [代码]// app.js global.onShareAppMessage = function() { return { title: '我在这里发现了很多好看的壁纸', path: 'pages/index/index', imageUrl: '' } } [代码] 那么此时每次新增的Page都需要这样引入: [代码]// page.js Page({ ...global.onShareAppMessage, data: {} }) [代码] 这样的缺点也是非常明显的: 创建新页面时,容易遗忘 如果多个相同的函数,则需要每个独立引入,不方便 提取Behavior 将多个函数集成到一个对象中,每个页面只需要引入这个对象即可注入多个相同的函数。这种方式可以解决 抽象公共函数 提到的 缺点2。 大致的实现方式如下: 同样在[代码]app.js[代码]通过[代码]global[代码]注册一个[代码]behavior[代码]对象: [代码]// app.js global.commonPage = { onShareAppMessage: function() { return { title: '我在这里发现了很多好看的壁纸', path: 'pages/index/index', imageUrl: '' } }, onHide: function() { // do something } } [代码] 在新增的页面注入: [代码]// page.js Page({ data: {}, ...global.commonPage, }}) [代码] 缺点仍然是,新增页面时容易遗忘 封装新Page 封装新的[代码]Page[代码],然后每个页面都通过这个新的[代码]Page[代码]注册,而不是采用原有的[代码]Page[代码]。 同理,在[代码]app.js[代码]先封装一个新的[代码]Page[代码]到全局变量[代码]global[代码]: [代码]// app.js global.newPage = function(obj) { let defaultSet = { onShareAppMessage: function() { return { title: '我在这里发现了很多好看的壁纸', path: 'pages/index/index', imageUrl: '' } }, onShow() { // do something } } return Page({...defaultSet, ...obj}) } [代码] 往后在每个页面都使用新的[代码]newPage[代码]注册: [代码]// page.js global.newPage({ data: {} }) [代码] 好处即是全新封装了[代码]Page[代码],后续只需关注是否使用了新的[代码]Page[代码]即可;此外大家也很清晰知道这个是采用了新的封装,避免了覆盖原有的[代码]Page[代码]方法。 我倒是觉得没什么明显缺点,要是非要鸡蛋里挑骨头的话,就是要显式调用新的函数注册页面。 劫持Page 劫持函数其实是挺危险的做法,因为开发人员可能会在定位问题时,忽略了这个被劫持的地方。 劫持[代码]Page[代码]的做法,简单的说就是,覆盖[代码]Page[代码]这个函数,重新实现[代码]Page[代码],但这个新的[代码]Page[代码]内部仍会调用原有的[代码]Page[代码]。说起来可能有点拗口,通过代码看就一目了然: [代码]// app.js let originalPage = Page Page = function(obj) { let defaultSet = { onShareAppMessage: function() { return { title: '我在这里发现了很多好看的壁纸', path: 'pages/index/index', imageUrl: '' } }, onShow() { // do something } } return originalPage({ ...defaultSet, ...obj}) } [代码] 通过这种方式,不改变页面的注册方式,但可能会让不了解底层封装的开发者感到困惑:明明没注册的方法,怎么就自动注入了呢? 这种方式的缺点已经说了,优点也很明显,不改变任何原有的页面注册方式。 其实这个是一个挺好的思路,在一些特定的场景下,会有事半功倍的效果。
2020-03-23 - 小程序打开后白屏
[图片]
2020-08-27 - 个人在社区看到的常见问题整理备份
前言:把自己在社区看到的常见问题整理一下,以作备份。 1,如何查询账号主体注册的小程序列表个人建议: 1,登录小程序管理后台(入口页:https://mp.weixin.qq.com/);2,找到设置—基本设置—主体信息,进入信息栏右侧详情页,查询主体绑定帐号信息建议依据: 如何查询帐号主体注册的小程序信息?? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/000264368108209d9dc9da2f651409 2,企业主体和个人主体分别能注册多少个小程序个人建议: 1,企业、政府、媒体、其他组织主体可以注册50个小程序;2,个体户和个人类型主体可注册5个小程序主体注册次数不占公众号次数限制建议依据: 参见:https://kf.qq.com/faq/170109F3MRFj170109eYJ7fi.html3,同一主体下的两个小程序如何相互更换名称个人建议: 已发布的小程序修改名称后,原名称会有2*24小时(即2天)的保护期;在保护期内,只有同一主体下的其他账号可以使用该名称;建议依据: 同一主体下的两个小程序如何相互更换名称?? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/000aea4dc14cd0290ea970c1a5b4094,公众号迁移成功后邮箱一直没释放个人建议: 公众号迁移成功后并不会释放邮箱;原帐号运营者身份证、手机号,管理员微信号会被释放建议依据: 详见:https://kf.qq.com/faq/170221aUnmmU170221eUZJNf.html 5,小程序长时间不使用无法登录提示被冻结个人建议: 1,小程序账号长时间不使用,官方会先发消息提醒,让在规定时间前登录使用;2,在官方提醒时间内仍未登录的,官方按规定冻结账号;3,被冻结的小程序账号会释放账号昵称资源;找回账号网址:http://mp.weixin.qq.com/acct/findacct?action=scan建议依据: 详见:https://kf.qq.com/faq/190605A3QrUR190605maANZj.html 6,如何注销微信开放平台账号个人建议: 按照官方规定:open(即开放平台)帐号一旦注册,将不支持注销,如若需要注册其他平台,请更换其他邮箱。建议依据: 详见:https://kf.qq.com/faq/170824uaMfqA170824Mj6FRR.html 7,注册提示邮箱被占用,找回密码又提示账号不存在个人建议: 同一个邮箱只能绑定微信产品的一种账号。你的邮箱可能已注册某一个下列平台:1,微信开放平台2,微信公众号(服务号/订阅号)3,微信小程序4,微信企业号(企业微信)5,个人微信(邮箱注册方式)建议新注册一个邮箱,用来微信产品注册。个人推荐网易126邮箱。建议依据: 公众号注册提示“邮箱已被占用”该怎么办?? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/0006ea7b9f0eb0c4c0792e5325b009 8,个人微信号可以绑定成为几个小程序开发者个人建议: 每个微信号可以成为50个小程序的项目成员。每个微信号可以成为50个小程序的体验成员。每个微信号可以成为5个小程序的管理员。建议依据: 详见:https://kf.qq.com/faq/170302zeQryI170302beuEVn.html 9,怎么解绑公众号管理员个人建议: 1,个人主体公众号可申请账号注销,会释放管理员信息;2,已冻结的个人主体公众号,必须先找回账号再走注销流程释放管理员;3,企业主体公众号,可微信关注公众号“公众平台安全助手”,在“绑定查询-微信号绑定账号”页面就有解除绑定的入口。(仅适用于非封禁状态的公众号)建议依据: 公众号已冻结,管理员如何解绑?? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/0008e8d491cfa80a125909d4a51009 10,为什么我的公众号没有留言功能个人建议: 转官方通告:根据国家监管部门要求,自2018年2月12号起,新注册的微信公众帐号暂无留言功能,之前已开通留言功能的帐号可以继续使用,未开通留言功能的帐号暂不支持开通。建议依据: 为什么公众号没有留言功能?? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/0002ee5d300860ee377aa145356409 11,网站被拦截了怎么办个人建议: 个人经验,目前已知的拦截分两种,一种是微信生态内(比如微信浏览器)被拦截,另一种是腾讯系产品整体拦截(比如微信、QQ都不能访问网址)。在确认已清除违规内容后,可自行申诉申请解封。1,微信内被拦截。微信内访问你的域名,再点击停止访问页面中间的“申请恢复访问”按钮进行自行申诉。2,腾讯系产品整体拦截。打开腾讯安全-网址安全中心(https://urlsec.qq.com/complain.html),按提示进行拦截申诉。3,直接在微信开放社区发帖,写上域名请求社区客服帮忙确认解封。判断腾讯系产品整体拦截方法:在腾讯安全-网址安全中心,直接查询你的域名安全状态,https://urlsec.qq.com/check.html建议依据: 相关经验来自社区客服的帖子回复。 12,域名已备案,但微信生态内提示未备案个人建议: 1,如果是刚备案,就是数据同步问题,等一等就可以了(1~3天);2,如果备案有段时间了,建议直接发帖说域名,让社区客服帮忙查询同步一下。建议依据: 相关经验来自社区客服的帖子回复。 13,当前绑定的银行卡暂不支持实名验证个人建议: 1,确认自己微信号是否已绑定银行卡;2,重新绑定微信关联银行卡试试。微信中,我-支付-钱包-银行卡,点击已绑定的银行卡,打开页面右上角点开“解除绑定”,然后重新绑一遍再试试实名验证;3,确认绑定的银行卡是I类银行卡。具体判断方法可咨询对应银行客服。建议依据: 公众号扫码提示“未能获得实名信息”怎么办?? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/00080809a103c0e4c179fe15e51c09 14,微信扫码身份确认一直失败个人建议: 请使用手机摄像头扫码,直接识别二维码是不行的。 特别是见到下列扫码失败提示的:“请使用微信扫一扫完成验证,其他方式无效。”建议依据: 相关经验来自社区的帖子回复。 15,注册支付验证的小额打款会退回来吗个人建议: 1,一般会在验证成功后3个工作日内退至你的汇款账号。2,验证失败,也会把小额打款返还至你的汇款账号。3,发生小额打款超时未退款问题,可在微信里打开以下网站,准备好相关资料后提交官方核实处理。https://kf.qq.com/touch/bill/171220selfqab3f11c83.html建议依据: 小程序注册成功,小额打款未退回? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/0004ae8e3e8b182db3393f95051809 16,改名时提交次数过于频繁,请稍后再试个人建议: 触发频控限制后,需要等24小时后再操作。中间不能去尝试!尝试后会从最后一次尝试时间计算24小时。建议依据: 相关经验来自社区客服的帖子回复。 17,绑了同一个开放平台,获得的unionid不一致个人建议: 根据社区诸多帖子经验表明,最可能的原因是:1,你认为是绑了同一个开放平台,但实际不是!请仔细查询核实每个公众号或小程序的关联设置。(小程序:设置-》关联设置-》绑定的微信开放平台账号,公众号:开发-》基本配置-》已绑定的微信开放平台账号)2,可能是搞混了openid的对应,openid必须对应所属的APPID;建议依据: 绑定在一个开发者帐号下,同个用户获取到的unionID不一样? - 微信开放社区 https://developers.weixin.qq.com/community/develop/doc/000e0652ed430840e219af88c57809 18,官方安全内容审核接口不起作用个人建议: 可能部分敏感信息或图片官方接口没能查出来,个人建议:1,自己加敏感词库,对疑似的消息直接转入人工审核;2,数量级不大的,所有消息都开启人工审核;建议依据: 相关经验来自社区的帖子回复。 19,查询自己微信号是什么时候注册的个人建议: 1,注册第一天就发朋友圈的打开微信-》我-》相册,一直往下拉,拉到你的第一条朋友圈信息,就能判断注册时间。2,使用QQ注册的去翻QQ邮箱,搜“微信”,看看能不能找到注册微信时的欢迎邮件,由此判断注册时间。3,没删过登录设备信息的打开微信-》我-》设置-》账号与安全-》登录设备管理,查看最后一条设备登录时间,依此判断。4,根据微信支付的账单模糊查询打开微信-》我-》支付-》钱包-》账单,然后选年份,往上翻,能一直翻到你注册微信的年月。建议依据: 相关经验来自社区的帖子回复和百度搜索。 20,你提供的应用下载链接开放商与账号主体不一致,不能通过审核个人建议: 开放平台以当时认证时的主体为准。查询方法:打开微信开放平台=》账号中心=》开发者资质认证=》查看订单,点击“电子发票”,“点击下载电子发票”。然后查看发票抬头的公司名,即是你们当时开放平台的认证主体公司名称。 确实就是不同主体,还可提交相应授权书及软著证书(若有)等资料证明你有权合理使用该应用信息。建议依据: 相关经验来自社区的帖子回复和百度搜索。 21,微信认证申请公函找不到下载入口了?个人建议: 1,首先登录你要认证的平台(公众平台或开放平台)登录后,把浏览器地址栏的token值拷贝出来。2,替换公函下载页面的token值https://mp.weixin.qq.com/acct/wxverifyorder?action=apply_letter_page&token=123456&lang=zh_CN把上面URL地址中的token值(红色部分),用你第一步的token值替换;然后把地址粘贴到浏览器地址栏打开,就是可以看到公函下载页面了。希望这个回答能对你有用。建议依据: 个人总结。 *不断更新中。
2020-09-17 - RestTemplate 调用 小程序的 security.imgSecCheck
微信小程序开发中,使用 RestTemplate 访问 security.imgSecCheck来判断图片是否违规,网上试了好多办法,终于解决了!果然世上没有过不去的坎! 首先定义原始url: [图片] 因为参数有一个是 FormData 类型,所以需要给访问增加header参数,具体方法如下: [图片] 注意,远程调用的restTemplate也要进行特殊配置: [图片] 上面是整个的检测调用过程,亲测可用,调用方法如下: [图片] 注意上面的 accessToken 写上自己的就行。来看一下一个违规图片的调用结果: [图片] 注意上面标注的需要注意的地方!日志中也返回了正确的结果: [图片]
2020-06-01 - 关于【个体工商户】微信认证相关问题
微信小程序、公众号等认证分为多个主体类型,其中一个即为【个体工商户】,而个体工商户在认证过程中出现问题的概率也较大。 1.公司注册时间(以营业执照上信息为准)未达15天 在此期间内,公司认证是不会成功的,请耐心等待15天。 2.公司注册时间(以营业执照上信息为准)已超过15天 仍提示【企业信息或法定代表人信息不一致】或【企业信息或法定代表人信息未更新】,此情况下,可能是微信系统的bug,建议社区发帖询问,等待官方回复,发帖内容请写明APPID、公司名等相关信息 另,可以将认证失败的相关帖子或认证失败的内容在此文章下回复,以便官方浏览处理(前提是官方会看)
2020-08-03 - 【12.21更新】微信小商店常见问题汇总
一、小商店是什么 1.小商店是什么? 微信小商店是小程序团队提供的一项新能力,无需开发、免费开店,帮助商家快速生成卖货小程序。小商店将降低商家在微信经营和卖货的门槛,让更多中小微企业和个体创业者可以低门槛的进入小程序生态尝试经营,开展电商业务。 2.小商店有几种类型? 小商店有四种类型: (1)企业/个体店 企业营业执照类型为:有限责任公司、股份有限公司、合伙企业、个人独资企业等。 个体营业执照类型为:个体工商户 (2)个人店 按电商法规定无须进行市场主体登记的个人主体。 (3)事业单位 是国家为了社会公益目的,利用国有资产设立的,从事教育、科技、文化、卫生等活动的社会服务组织,如旅游、报社、杂志社、学校等机构。 (4)其他组织 不属于个体户、企业、政府/事业单位的组织机构,如民办非企业、社会团体、基金会、合作社等组织。 3.“企业/个体店/事业单位/其他组织”与“个人店”有什么区别? (1)功能差异 目前企业店功能更加完善,支持直播、优惠券、数据中心等功能,个人店的功能也在同步完善中,敬请期待。 (2)收款限额 个人店正常日收款上限额度为10万,若交易良好则自动提升至20-30万/日,若交易异常则自动下降至5万以下/日。信用卡单日收款上限不超过1千,信用卡单月收款不超过1万。 二、如何开通一个小商店 1.开通流程包括哪些?需要准备什么材料? (1)搜索“小商店助手”小程序,点击“免费开店”,选择开店类型 (2)企业/个体/事业单位/其他组织店 pc登录网址: https://shop.weixin.qq.com/ https://mp.weixin.qq.com/ 扫码登录小商店,完成开店任务(共四步),即可卖货经营 -经营信息填写:营业执照(营业执照照片并填写相关信息)/事业单位法人证书(证件照片并填写相关信息)/相关登记证书(证件照片并填写相关信息)、经营者/法人信息(身份证正反面照片)、超级管理员信息(姓名、身份证号、手机、邮箱) -验证账户:法人扫描二维码验证/小额打款验证,两种方式任选其一即可 -基础信息填写:小商店头像、名称、店铺简介;品牌与类目信息(如有品牌,提供商标注册证相关信息) -签约开张:用超级管理员的微信扫描二维码即可完成签约 (2)个人店 -完善经营者信息:身份证正反面照片 -签约开张/协议签署 2.没有营业执照可以开通小商店吗? 如果没有营业执照,可以开通个人小商店,请搜索“小商店助手”小程序,点击“我要开店”,选择开店类型“个人”。 3.开通小商店有数量限制吗? (1)企业/个体/事业单位/其他组织店:一个微信号能申请3个“企业”,“个体”,”事业单位“和”其他组织“主体的小商店。 (2)个人店:一个微信号仅能申请1个“个人”主体的微信小商店。 4.开店后还可以变更店铺类型吗?从企业店改为个人店? 目前暂不支持变更店铺类型,如果需要开设其他类型的店铺,需重新申请。 三、如何登录小商店 1.小商店怎么登录? (1)企业/个体/事业单位/其他组织店: pc端登录网址: https://shop.weixin.qq.com/ https://mp.weixin.qq.com/ 用微信扫描二维码,在移动端选择对应小商店,即可跳转小商店后台。 (2)个人店:搜索“小商店助手”小程序,点击“进入我的店” 2.可以支持手机登录小商店管理后台吗? 已实现双端互通,个人店和非个人店均支持网页端、移动端登陆及管理。 3.谁可以扫码登录小商店? 注册小商店的微信号、超级管理员、添加的其他管理人员。 四、开店任务常见问题 (一)主体信息 1.营业执照/事业单位法人证书/相关登记证书信息审核不通过怎么办? 请根据营业执照/事业单位法人证书/相关登记证书驳回原因,仔细核对填写的相关信息是否准确。保证:营业执照/事业单位法人证书/相关登记证书证书未过期、清晰可见、如有二维码,尽量保证二维码可扫描。如果核对之后未发现错误,请至小商店后台,点击联系客服,我们将尽快跟进排查。 2.普通营业执照/事业单位法人证书/相关登记证书为什么要提交组织机构代码证? 个体工商户的工商营业执照和2015年10月1日前企业申请的工商营业执照及事业单位法人证书/其他组织相关登记证书,不包含组织机构代码信息,为非三证合一类营业执照/证书,因此商家提交该类证件时需要补充组织机构代码证。参考图例: https://developers.weixin.qq.com/miniprogram/product/mini-store/yingyezhizhao/putong.html 3.商户简称怎么填? 商户简称会在支付完成页向买家展示,填写商户名称即可。个体工商户,若营业执照上商户名称为空或为“无”,填写“个体工商户+经营者姓名”即可。 (二)超级管理员信息 1.超级管理员要填谁? 超级管理员信息需要填写注册微信号时的身份信息。 2.超级管理员可以更换吗? 小商店后台暂时不支持更换超级管理员。 3.填写超级管理员的时候要注意什么? 填写超级管理员信息时,务必注意填写真实姓名、身份证号,否则无法完成校验。 4.小商店可以多人管理吗?怎么进行多人管理? 企业/个体/事业单位/其他组织店支持多人管理(个人店暂不支持)。在“店铺-成员管理”中,添加成员即可,可以添加不同岗位,设置岗位权限。 (三)验证账户 1.怎么验证账户? 验证账户有两种方法,任选其一即可。 (1)法人验证:使用法人微信号扫描二维码即可验证成功。 (2)汇款验证:根据页面提示小额打款至指定账户,验证成功后,打款金额会原路退回。 2.验证账户有有效期吗? 若30天内一直未完成验证,将自动驳回申请单。若未超过30天,申请单被审核驳回,可再次提交申请单,完成账户验证流程。 (四)基础信息填写 1.店铺名称可不可以重名? 小商店不会出现店铺同名的情况,以下几点请各位商家注意: (1)小商店是小程序的一种类型,所以小商店不能和现有小程序同名,即使是同一主体也暂不支持。 (2)如果小商店和现有公众号同名,需保证二者是同一主体。 2.店铺名称、头像、简称、介绍可以修改吗? 小商店的名称和头像都可以修改。 个人店请至移动端店铺后台,点击“我-店铺信息” 企业店请至pc端后台,点击“店铺管理-基础信息” 修改次数限制如下: (1)店铺名称:企业/个体每年5次,个人每年3次 (2)店铺头像:企业/个体/个人每年5次 (3)店铺简称:企业/个体每年2次,个人每年3次 (4)店铺介绍:企业/个体/个人每月5次 3.店铺名称补充材料是什么? 当店铺名称命中一些品牌、旗舰店保护词之后,需要提交相关资质材料,请根据页面提示上传相应材料。 (五)品牌申请 1.如何申请品牌? 开店任务中可以申请品牌,需要依照页面提示上传相应资质。 开店任务完成后,在“店铺-品牌与类目”模块中,可以申请新品牌。 2.品牌有哪几种类型?申请不同类型的品牌需要什么资质? 品牌分为三种类型:自有品牌、代理品牌、无品牌。不同类型品牌需要不同的资质,详细规则可以点击链接查看。 自有品牌:https://developers.weixin.qq.com/miniprogram/product/mini-store/pinpai/ziyou.html 代理品牌:https://developers.weixin.qq.com/miniprogram/product/mini-store/pinpai/daili.html 3.如果是自有品牌,但商标主体和小商店主体不一致怎么办? 如果属于以下情况,我们支持提交相关证明材料。详情请点击链接至(开通品牌常见问题)查看。 https://developers.weixin.qq.com/miniprogram/product/mini-store/#三、微信小商店品牌开通规则 五、在小商店可以卖什么 (一)开放类目范围 小商店共开放超过2000个类目,一级类目包括:宠物生活、厨具、家用电器、手机通讯、数码、电脑&办公、服饰内衣、鞋靴、个人护理、母婴、美妆护肤、家纺、家居日用、家具、家庭清洁&纸品、家装建材、工业品、汽车用品、玩具乐器、运动户外、箱包皮具、酒类、食品饮料、钟表、农资园艺、生鲜、二手、生活服务、图书、艺术品、教育培训 详细类目开放范围请点击链接查看: 非个人主体店:https://developers.weixin.qq.com/doc/ministore/minishopspecification/leimuzizhi/qiye.html 个人店:https://developers.weixin.qq.com/doc/ministore/minishopspecification/leimuzizhi/geren.html 为什么我的类目列表里没有xx类目? 小商店部分类目需要资质,不会直接显示在“新增商品”的类目列表里,需要到“店铺-品牌与类目”模块申请所需类目,并上传相应资质。 (二)类目资质要求 哪些类目需要资质?这些资质有示例吗? 小商店部分类目需要资质,当你在“新增商品”过程中,发现没有某个类目时,可以到“品牌与类目”模块申请类目,如果选择了需要资质的类目,页面会提示上传相应资质。 整体类目及商品资质如下,请点击链接查看,具体资质证照可以点击跳转查看示例 非个人主体店类目资质: https://developers.weixin.qq.com/doc/ministore/minishopspecification/leimuzizhi/qiyeleimu.html 非个人主体店商品资质: https://developers.weixin.qq.com/doc/ministore/minishopspecification/leimuzizhi/qiyeshangpin.html 个人店商品资质: https://developers.weixin.qq.com/doc/ministore/minishopspecification/leimuzizhi/gerenshangpin.html (三)类目/商品审核 1.我的商品还没通过审核怎么办? 2.我提交了资质,对审核结果有异议怎么办? 3.希望开设更多类目,对现有类目的资质要求有异议怎么办? 如果你出现以上情况,可以通过后台的联系客服途径,反馈问题,客服人员会尽快跟进处理。 六、有什么营销功能 (一)怎样让人看到我的小商店? 小商店曝光渠道:任务栏、发现入口、客服消息、小程序码、搜索、对话分享、群分享、APP分享、模板消息、公众号Profile页/文章插入、LBS广告、微信广告 商品曝光渠道:对话分享、群分享、APP分享、小程序互跳、公众号自定义菜单/文章插入、二维码、模板消息 [图片] (二)优惠券 1.优惠券有哪几种类型? 目前小商店支持的优惠券功能,包括店铺优惠券和指定商品优惠券,每一种类型的优惠券都会分为直减券、满减券和折扣券。所有券支持两种发放渠道,小商店内推广发放和自定义渠道推广发放。 2.店铺内推广发放和自定义渠道发放的区别? (1) 小商店内推广发放: 商家在管理后台制作优惠券并生效后,优惠券会自动出现在小商店的各个位置,如小商店首页、商品详情页出现优惠的特殊样式,下单页也会出现简易的凑单提示。 商家可设置“仅该商品可以使用的优惠券”,在视觉样式上提升该商品的下单转化率,或设置一些合理的梯度满减优惠券,在给用户更多优惠的同时提高单用户单次访问的购买订单消费总额。 (2)渠道优惠券: 当商家开通小商店并将商品分发给其他人进行售卖时,可配置专属优惠券,当其他人员售卖商品时,商家在后台的优惠券详情明窗看到专属优惠券具体产生了多少次购买,了解该人员带来的订单量。 3.优惠券创建创建之后可以取消吗? 商家发布优惠券时可设置有效期,生效后的优惠券,用户已领取,则暂时不支持作废用户已领的优惠券。 (三)公众号 1.小商店怎么关联公众号? 登陆公众号后台-小程序管理-关联小程序搜索-输入小商店名称 2.小商店的带货商品链接怎么插入公众号推广? 带货商品链接支持插入公众号文章和自定义菜单进行推广,具体步骤请查看下方链接: https://developers.weixin.qq.com/community/business/doc/0000e265b90e00615a2b7c6c75b00d (四)带货 1.如何查看佣金的打款时间? 带货佣金需要等待对应平台打款后,才能打款给商家。 可点击「带货收入」查看佣金,进入「待打款佣金」页面,显示「预估X月20日可打款」栏有金额显示,如果填写的银行卡信息正确,到日期后就会进行打款。一般下单到最终收到佣金需要45-60天; 显示「平台未打款」栏,即对应第三方平台并未打款至小商店,可查看到预计的佣金款项。 更多小商店带货能力结算规则,请查看: https://developers.weixin.qq.com/community/business/doc/00064631af05c0783d8b3e9955b00d 2.商品接入范围? 鉴于微信小商店对带货平台、带货商品的资质有严格的审核要求,目前链接搜索仅支持京东、拼多多、有赞的符合小商店售卖规定的商品,其它平台暂未支持;其他平台(包括唯品会和当当)上仅支持部分已经进入商品库的商品。 3.带货成功后需要我联系供应商发货吗? 不需要。只需负责带货,生成订单后,供应商主动发货,售后问题由该带货商品的平台、供应商负责。 4.带货商品也可以在视频号售卖吗? 是的,带货商品上架小商店之后也可以在视频号直播的时候进行选择添加并售卖。 5.怎么带货? 小商店带货指引详情请见 https://developers.weixin.qq.com/community/business/doc/0000a210138b70c91c2bc32f35180d 更多关于小商店带货能力推广问题,请查看: https://developers.weixin.qq.com/community/business/doc/00084c90ecc3b0983a8b23c8156c0d 6.企业店支持带货吗? 现阶段暂时只支持个人小商店带货,企业店带货能力敬请期待。 7.为什么带货订单里看到的佣金,和我分享的时候显示的不一样? 在带货的时候看到的佣金为预估佣金,由于买家下单时可能会使用京豆、京东余额等虚拟货币,导致记佣金额减少,进而导致订单收益变少。 8.订单取消、订单已完成、平台未打款,分别是什么意思?哪些情况下我可以收到打款? 订单取消,是指因为用户主动取消订单、或第三方平台审核失败等原因,导致订单失效无法记佣。这种情况下无法收到打款。 订单已完成是指买家已经收到货并确认,但第三方平台还没有和腾讯结算,还不能收到打款。 平台未打款,是指京东、拼多多等第三方平台还没有和腾讯结算,还不能收到打款。 9.买家收货后,我什么时候可以收到打款? 买家收货并且没有发生退款及其他售后纠纷等行为的情况下,第三方电商平台会将该笔订单标记为审核通过的订单。腾讯公司会在每个月20号左右和第三方平台对当月审核通过的订单进行结算,并在次月20号左右完成打款。通常这一周期为30~60天。 更多关于小商店带货能力收入管理问题,请查看: https://developers.weixin.qq.com/community/business/doc/0002aea8e94a58f83b8be95ea5600d (五)导购分销 导购分销已开放给所有企业/个体店(个人店不支持) 1.导购被添加后,无法登录手机端后台管理?商家如何正确添加导购? 如果「店铺管理」中没有添加导购管理员,只在「导购分销」中添加导购,则导购是无法成功登陆后台导购管理体系的。 (1)方法一:商家需先在「店铺管理」-「成员管理」中添加导购管理员,商家或已被成功添加的导购管理员进入「营销中心」-「导购分销」添加导购分销员。 注:添加导购管理员和导购分销员,需在小商店后台由管理员主动添加,暂不支持增加自主申请成为导购。 (2)方法二:需要该导购删除「小商店助手」小程序后,重启该小程序。 2.导购如何推广带货商品? (1)导购分销员可通过生成「海报」和「复制链接」的推广方式,引导客户进入小商店购买,链接可挂在公众号或其他小程序中使用(链接分享至视频号不支持结算分成)。 (2)现已支持通过小程序转发记佣。导购通过在推广商品中点击进入程序,直接分享该小程序商品页面,也算该导购的。 3.导购员分享商品且交易完成,但后台没有给导购员提成? (1)情况一:该导购进行了无效推广 若客户在导购A的有效客户关系中,通过导购B推广的链接购买商品,该客户直接从B的链接购买,则计算给B。 (2)情况二:该商品被设置的结算规则有误 需确认该商品被设置的结算规则,有以下两种结算方式。 交易完成后结算——客户确认收货后,即订单完成后,该订单的相应提出才能结算给导购。 无法发起退款后结算——用户无法发起退货退款后才能结算。 (3)情况三:被同店其他导购抢客 顾客A通过导购A分享的商品海报或链接进店后,无立即购买到商品,又通过同一家店的导购B海报或链接扫码进店,购买商品,则会形成被同店导购B抢客的情况,如不在有效客户关系内购买,导购员无法获得提成。 4.商家如何管理业绩? (1)导购等级实时升降,不再与提成结算时间一致,而是在订单交易后即提升相应的业绩,相应的订单发生退款时,也会减少业绩。 (2)「营销中心」-「导购分销」中分销的佣金结算能力已支持自动结算给导购分销员。 (3)如有线下结算需要,后台支持导购业绩批量导出,方便对账。 更多导购分销疑问,请点击链接查看 https://developers.weixin.qq.com/community/business/doc/000cc6f0030558c19f2be29365100d (六)店铺直播 1.怎么开通直播功能? 成功开通的小商店(企业/个体店)自带直播能力,商家只需同时满足已开张、且商品库中有成功发布的商品,后台的直播页面将会自动开通直播功能,商家点击管理直播跳转到直播管理后台,可创建直播间、指定主播、设定直播开播时间。 Tips:建议商家在直播间选货之前,先在小商店上传好商品,直播间选品时会自动同步小商店已经上传并且发布成功的商品,提高直播的效率。 2.怎么让用户看到商家直播? 商家可直接以直播间小程序码的形式进行分享,也可以把商家的小商店主动分享给用户,凡是设定了直播的商家,小商店主页都会看到直播入口,用户点击可进入直播间观看或订阅直播。 在直播将开始时,直播间会自动给预约的用户发消息提醒,为商户提供定时召回用户的功能。直播间还会完整展示主播推荐的商品、讲解和抽奖能力,并设有直播浮窗的功能,保证用户边看边买的流畅体验。 3.店铺直播与视频号直播的区别? (1)店铺直播是小商店自带的微信小程序直播,帮助商家在自有小程序(小商店)中实现直播互动与商品销售闭环,提高转化率。店铺直播优势为低门槛、快运营,一键分享、一键订阅。 更多小程序直播信息可查看以下链接:https://developers.weixin.qq.com/community/business/doc/000e28dbfd8a8082282a60d6f5b80d (2)视频号直播作为视频号的重要能力之一,现已与微信小商店打通,商家或导购可以在直播时添加在架商品,进行引流、带货。视频号优势在于可以快速突破熟人圈,拓展更广阔的私域流量。 (七)视频号 1.视频号怎么关联小商店? 视频号关联/取消关联小商店的具体步骤请查看下方链接: https://developers.weixin.qq.com/community/business/doc/000ce0f5014a186f871bf21b459c0d?page=2#comment-list 2.视频号直播怎么带货? 视频号直播可支持带货小商店在架商品,视频号关联小商店后,即可在视频号添加带货商品,具体步骤请查看以下链接: https://developers.weixin.qq.com/community/business/doc/000ce8f5688b4873a72b2d6c15ac0d (八)后续营销能力规划 小商店是一种卖货小程序,未来,用户可以从下方图片中展示的场景,比如任务栏、会话框、扫码、搜索、分享等进入小商店。小商店将持续与微信生态的场景实现更紧密地结合,为商家提供更多连接用户的方式。 七、服务提升 1.运费险(卖家版)保障什么?保障周期是多久? 运费险是帮助买家适当减少退换货运费损失的一种服务,卖家为买家购买运费险,当买家退换货的时候,保险公司会向用户赔偿部分运费。 运费险从卖家发货开始生效,到买家确认收货/到期系统自动确认收货为止,总时长不超过90天。 2.如果同一笔订单,发生多次退换货行为,商家需要多次投保吗? 一笔订单对应一笔运费险,因此商家只需购买一次运费险。 3.运费险理赔金额标准怎么定? 理赔金额是根据用户的收货地址和商家在系统里预留的退货地址的距离进行首公斤价格赔付的,具体以实际赔付金额为准。 更多运费险疑问,请点击查看 https://developers.weixin.qq.com/community/business/doc/0006a651408b80b12dda778cd5180d 八、订单物流 1.订单是否支持改价? 商家可修改「待付款订单」的价格(目前仅支持调低价格)、减免运费。 用户拍下商品但未付款前,商家可以在订单管理中找到对应订单并修改订单价格(可直接输入修改后的价格或折扣,系统会自动计算)及选择是否减免运费,设置完成后点击确定; 用户微信上会收到订单价格已修改的服务通知,点击后可按修改后的价格进行付款; 2.如何确定收货? 目前统一的确认收货规则为:用户可手动对订单确认收货,若不手动确认收货,在商家发货15天后系统将自动确认收货。若订单中有商品发生了退款/售后,订单自动确认收货的倒计时将停止。 3.小商店支持的物流方式有哪些? 目前仅支持快递物流配送,上门自提、同城配送、无需物流模板正在开发中,敬请期待。 4.商家快递单号填写错误后,是否影响正常收货流程? 只要快递顺利到达买家手中,就不会影响正常收货。修改物流单号的功能正在开发中,敬请期待。 九、怎么结算 1.小商店结算账户是什么? 开通小商店,将自动开通一个全新的微信支付商户号。该商户号暂时不支持更换。商家可以直接在小商店后台进行经营对账,客户的每一笔货款都会直接到达商家自己的微信支付商户号账号中。 2.什么时候可以提现? 到账的款项无特殊情况均为待结算状态,商家将在客户确认收货后,可对该笔款项进行提现至银行账户操作。 3.收款提现多长时间到账? 收款提现后,无特殊情况下,24h内到账。 4.收款可以提现到私人账户吗? 如果是企业,会自动提现至对公账户,不支持提现到私人账户; 如果是个体工商户,可以选择对公或者对私账户; 个人主体的直接结算到绑定的对私银行账户。 5.小商店的结算费率0.6%指的是? 微信支付收取每笔订单实际销售金额的千分之六; 小商店不收取手续费,免费开店、免费提现至银行账户。 十、客服 1.商家客服添加多名客服后,是否只有开通小商店的微信收到顾客咨询信息? 添加多名客服后,只要绑定客服帐号的微信都可以收到顾客的资讯信息。 2.商家客服可以手机接收到消息吗? 企业/个体店,在小商店后台-客服与售后模块中,点击添加,可以绑定客服帐号,绑定后,扫描下方二维码,即可在移动端小程序客服进行客服沟通。 客服消息指南如下:https://developers.weixin.qq.com/miniprogram/introduction/custom.html#网页版客服工具 [图片] 十一、服务商 1.小商店支持服务商吗? 毋庸置疑,我们需要服务商一起参与,小商店将开放店铺、商品、订单、物流、客服等一系列API接口供第三方介入,完善小商店能力。未来,小商店会开放接入ERP/CRM、智能客服、店铺装修、营销等经营工具,和第三方合作伙伴共同帮助商家经营成长。 2.小商店开放接口在哪里? 微信官方文档-小商店板块中,已上线小商店开放组件介绍,请点击查看:https://developers.weixin.qq.com/doc/ministore/minishopopencomponent/Introduction.html
2021-02-05 - [开盖即食]小程序Canvas官方新版API实战
[图片] [图片] 最近本人在开发一个新项目的时候,注意到官方在2.9.0开始支持了一个canvas 2D的新API,同时对webGL上支持也有了很大的改进,相信很多人用canvas的组件做一些分享海报,战绩和新闻帖功能。 [图片] 这里是新的引入方式。 官方文档地址: https://developers.weixin.qq.com/miniprogram/dev/component/canvas.html 那么新的canvas2D API有啥好处呢? 原本的API微信有做一定的修改,现在全面支持源生H5 JS的写法,迁移H5的老代码变成更加容易,学习成本更低 修复了一些诡异的BUG,例如原本在IOS早期版本写法顺序会导致clip()图片裁切失效等~ 性能上的优化和提升,复杂动画上帧数明显 举例写法上的一些改变: 1、设置font的写法: [代码]//原本(传值的写法) ctx.setFontSize(20); ctx.fillText('MINA', 100, 100) ctx.draw() //现在(和源生H5写法一致,赋值) ctx.font = "16px"; ctx.fillStyle = 'blue'; //可以直接写颜色,原本的不支持 //不需要 ctx.draw() [代码] 2、获取并添加图片写法: [代码]//原本 //使用的是 wx.getImageInfo的方法 wx.getImageInfo({ src: mainImg,//服务器返回的图片地址 success: function (res) { console.log(res); ctx.drawImage(res.path, 0, 0); ctx.draw(true); }, fail: function (res) { //失败回调 } }); //现在 //可以直接img.onload调用 const headerImg = canvas.createImage(); headerImg.src = headImage;//微信请求返回头像 headerImg.onload = () => { ctx.save(); ctx.beginPath()//开始创建一个路径 ctx.arc(38, 288, 18, 0, 2 * Math.PI, false)//画一个圆形裁剪区域 ctx.clip()//裁剪 ctx.drawImage(headerImg,0,0); ctx.closePath(); ctx.restore(); } [代码] 3、将canvas生成虚拟地址便于下载(重点): [图片] 由于官方文档没有写清楚,误导了挺多人的。这里canvas对象必须通过选择器获取,并获得对应的node节点。 [代码]async saveImg() { let self = this; //这里是重点 新版本的type 2d 获取方法 const query = wx.createSelectorQuery(); const canvasObj = await new Promise((resolve, reject) => { query.select('#posterCanvas') .fields({ node: true, size: true }) .exec(async (res) => { resolve(res[0].node); }) }); console.log(canvasObj); wx.canvasToTempFilePath({ //fileType: 'jpg', //canvasId: 'posterCanvas', //之前的写法 canvas: canvasObj, //现在的写法 success: (res) => { console.log(res); self.setData({ canClose: true }); //保存图片 wx.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success: function (data) { wx.showToast({ title: '已保存到相册', icon: 'success', duration: 2000 }) // setTimeout(() => { // self.setData({show: false}) // }, 6000); }, fail: function (err) { console.log(err); if (err.errMsg === "saveImageToPhotosAlbum:fail auth deny") { console.log("当初用户拒绝,再次发起授权") } else { util.showToast("请截屏保存分享"); } }, complete(res) { wx.hideLoading(); console.log(res); } }) }, fail(res) { console.log(res); } }, this) }, [代码] 分享个canvas海报的代码片段: [图片] 片段名: PoCf4emw7TgE 片段link: https://developers.weixin.qq.com/s/PoCf4emw7TgE [图片] [图片] 总结,相对之前还要看官方文档的canvas自定义API,现在写起来更加的方便,老代码迁移起来得心应手,只要你之前会canvas,那么各种效果和动画,拿来就怼,没什么大问题~ 一些奇怪的问题(注意!!!) canvas 2d 目前(2020年4月3日)还不支持真机调试,会报错!!! IDE工具 1.02.2003190 直接保存新版本canvas的API图片是打不开的,但是直接用手机保存在相册是没问题的,请更新到1.02.2003250 最新版即可解决~ 一些老款手机用新的API保存图片会有报错问题,如华为NOTE10,请更新系统到能支持的最新,且微信也是,即可解决~ 部分Android设备诡异的闪退和报错 [图片] 这种有可能是代码写法的问题,比如: [代码]//缺省写法 会导致部分Android机器 闪退 ctx.font = "bold 16px"; ctx.fillStyle = "#000" //在canvas 2D的写法中,所以写法必须规范且完整 ctx.font = "normal bold 12px sans-serif"; ctx.fillStyle = '#707070'; [代码] 所以在canvas 2D 的环境,所以写法必须原始且规范,不能用缺省写法,不然就会有诡异的闪退/报错。 后续:官方在7.0.13的Android版本已修复。 https://developers.weixin.qq.com/community/develop/doc/00088c13e1437890692afd8d85ec00 看完觉得有帮助记得点个赞哦~ 你的赞是我继续分享的最大动力!^-^
2020-05-09 - 微信小程序UI组件库合集
UI组件库合集,大家有遇到好的组件库,欢迎留言评论然后加入到文档里。 第一款: 官方WeUI组件库,地址 https://developers.weixin.qq.com/miniprogram/dev/extended/weui/ 预览码: [图片] 第二款: ColorUI:地址 https://github.com/weilanwl/ColorUI 预览码: [图片] 第三款: vantUI(又名:ZanUI):地址 https://youzan.github.io/vant-weapp/#/intro 预览码: [图片] 第四款: MinUI: 地址 https://meili.github.io/min/docs/minui/index.html 预览码: [图片] 第五款: iview-weapp:地址 https://weapp.iviewui.com/docs/guide/start 预览码: [图片] 第六款: WXRUI:暂无地址 预览码: [图片] 第七款: WuxUI:地址https://www.wuxui.com/#/introduce 预览码: [图片] 第八款: WussUI:地址 https://phonycode.github.io/wuss-weapp/quickstart.html 预览码: [图片] 第九款: TouchUI:地址 https://github.com/uileader/touchwx 预览码: [图片] 第十款: Hello UniApp: 地址 https://m3w.cn/uniapp 预览码: [图片] 第十一款: TaroUI:地址 https://taro-ui.jd.com/#/docs/introduction 预览码: [图片] 第十二款: Thor UI: 地址 https://thorui.cn/doc/ 预览码: [图片] 第十三款: GUI:https://github.com/Gensp/GUI 预览码: [图片] 第十四款: QyUI:暂无地址 预览码: [图片] 第十五款: WxaUI:暂无地址 预览码: [图片] 第十六款: kaiUI: github地址 https://github.com/Chaunjie/kai-ui 组件库文档:https://chaunjie.github.io/kui/dist/#/start 预览码: [图片] 第十七款: YsUI:暂无地址 预览码: [图片] 第十八款: BeeUI:git地址 http://ued.local.17173.com/gitlab/wxc/beeui.git 预览码: [图片] 第十九款: AntUI: 暂无地址 预览码: [图片] 第二十款: BleuUI:暂无地址 预览码: [图片] 第二十一款: uniydUI:暂无地址 预览码: [图片] 第二十二款: RovingUI:暂无地址 预览码: [图片] 第二十三款: DojayUI:暂无地址 预览码: [图片] 第二十四款: SkyUI:暂无地址 预览码: [图片] 第二十五款: YuUI:暂无地址 预览码: [图片] 第二十六款: wePyUI:暂无地址 预览码: [图片] 第二十七款: WXDUI:暂无地址 预览码: [图片] 第二十八款: XviewUI:暂无地址 预览码: [图片] 第二十九款: MinaUI:暂无地址 预览码: [图片] 第三十款: InyUI:暂无地址 预览码: [图片] 第三十一款: easyUI:地址 https://github.com/qq865738120/easyUI 预览码: [图片] 第三十二款 Kbone-UI: 地址 https://wechat-miniprogram.github.io/kboneui/ui/#/ 暂无预览码 第三十三款 VtuUi: 地址 https://github.com/jisida/VtuWeapp 预览码: [图片] 第三十四款 Lin-UI 地址:http://doc.mini.talelin.com/ 预览码: [图片] 第三十五款 GraceUI 地址: http://grace.hcoder.net/ 这个是收费的哦~ 预览码: [图片] 第三十六款 anna-remax-ui npm:https://www.npmjs.com/package/anna-remax-ui/v/1.0.12 anna-remax-ui 地址: https://annasearl.github.io/anna-remax-ui/components/general/button 预览码 [图片] 第三十七款 Olympus UI 地址:暂无 网易严选出品。 预览码 [图片] 第三十八款 AiYunXiaoUI 地址暂无 预览码 [图片] 第三十九款 visionUI npm:https://www.npmjs.com/package/vision-ui 预览码: [图片] 第四十款 AnimaUI(灵动UI) 地址:https://github.com/AnimaUI/wechat-miniprogram 预览码: [图片] 第四十一款 uView 地址:http://uviewui.com/components/quickstart.html 预览码: [图片] 第四十二款 firstUI 地址:https://www.firstui.cn/ 预览码: [图片]
2023-01-10 - 小程序怎样监听滚动到页面展示dom?
请问怎样实现这种效果阿?这个组件是循环出来的,怎样监听滑动到对应位置的时候,这个组件才显示,否则的话就隐藏,各位大佬指导一下思路呢[图片]
2020-03-17 - 如何使用scroll-view制作左右滚动导航条效果
最新:2020/06/13。修改为scroll-view与swiper联动效果,新增下拉刷新以及上拉加载效果。。具体效果查看代码片段,以下文章内容和就不改了 刚刚在社区里看到 有老哥在问如何做滚动的导航栏。这里简单给他写了个代码片段,需要的大哥拿去随便改改,先看效果图: [图片] 代码如下: wxml [代码]<scroll-view class="scroll-wrapper" scroll-x scroll-with-animation="true" scroll-into-view="item{{currentTab < 4 ? 0 : currentTab - 3}}" > <view class="navigate-item" id="item{{index}}" wx:for="{{taskList}}" wx:key="{{index}}" data-index="{{index}}" bindtap="handleClick"> <view class="names {{currentTab === index ? 'active' : ''}}">{{item.name}}</view> <view class="currtline {{currentTab === index ? 'active' : ''}}"></view> </view> </scroll-view> [代码] wxss [代码].scroll-wrapper { white-space: nowrap; -webkit-overflow-scrolling: touch; background: #FFF; height: 90rpx; padding: 0 32rpx; box-sizing: border-box; } ::-webkit-scrollbar { width: 0; height: 0; color: transparent; } .navigate-item { display: inline-block; text-align: center; height: 90rpx; line-height: 90rpx; margin: 0 16rpx; } .names { font-size: 28rpx; color: #3c3c3c; } .names.active { color: #00cc88; font-weight: bold; font-size: 34rpx; } .currtline { margin: -8rpx auto 0 auto; width: 100rpx; height: 8rpx; border-radius: 4rpx; } .currtline.active { background: #47CD88; transition: all .3s; } [代码] JS [代码]const app = getApp() Page({ data: { currentTab: 0, taskList: [{ name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, { name: '有趣好玩' }, ] }, onLoad() { }, handleClick(e) { let currentTab = e.currentTarget.dataset.index this.setData({ currentTab }) }, }) [代码] 最后奉上代码片段: https://developers.weixin.qq.com/s/nkyp64mN7fim
2020-06-13 - 使用wx.downloadFile在真机上只有开启调试才能下载成功
[图片]
2020-08-05 - h5跳转小程序wx-open-launch-weapp跳转页面显示405?
h5使用开放标签跳转小程序点击按钮跳转页面405 [图片] 代码如下: