- 了解小程序的启动流程(上)
[视频] *所有课程源码的链接: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 - 小程序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 - 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 - live-pusher的bindstatechange返回的code?
监听返回4998、4999、1022、-3301、1019 是啥意思哇
2022-03-18 - canvas createImage()方法中的onload在真机上无效?
canvas createImage()方法中的onload在真机上无效 [图片]
2022-01-24 - Source Map 文件为空
开发者工具需开启 es6->es5 / 打开增强编译 / 压缩混淆 中的一个或多个才会有sourcemap
2020-12-03 - 微信开放社区运营规范
微信开放社区运营规范 微信团队一直致力于将微信打造成一个强大的、全方位的服务工具。通过全面开放的能力,连接更多的开发者和用户。微信开放社区是微信为用户提供的内容问答社区。使用微信开放社区服务(以下简称“微信开放社区”或本“功能”),微信开放社区用户(以下简称“你”)必须阅读并遵守《微信公众平台服务协议》、《微信小程序平台服务条款》、《微信小程序平台运营规范》以及腾讯为此制定的专项规则等。 本《微信开放社区运营规范》(以下简称,本“运营规范”)是在上述协议及规则基础上进行解释和说明,相关内容和举例旨在帮助开发者更加清晰地理解和遵守相关协议和规则,以便能够更加顺利地在微信开放社区进行运营,而不是修改或变更上述协议及规则中的任何条款。 一、发布内容规范: 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 - 小程序与小游戏获取用户信息接口调整,请开发者注意升级。
为优化用户体验,使用 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 - 小程序跳转链接获取方式
1、登陆小程序管理后台,从上方“工具”入口进入“生成小程序码”: [图片] 2、添加需要获取页面跳转链接的小程序appid: [图片] 3、添加项目成员微信号: [图片] 4、相应微信帐号即可在小程序上复制到页面跳转链接: [图片]
2021-02-05 - 微信小商店·商家成长学习资料
内含开店指引、店铺运营和平台规则,帮你快速掌握小商店经营秘诀。
2024-09-05 - 微信内容服务商开放入驻公告
自微信小程序上线以来,小程序的运营场景越来越丰富。公众号、直播、短视频都已成为了运营小程序的有效工具。同时商家对于能够提供内容类代运营服务的服务商(MCN)的需求也越来越旺盛。为了支持更多的商家向用户提供高质量的服务,我们筹备上线了内容服务(MCN)专区。 通过MCN专区,我们将支持内容类代运营服务商向商家展示自己的服务能力,满足商家找内容服务的需求。内容服务类服务商可通过专区实现服务案例及数据的积累,并有机会服务更多客户。 内容服务(MCN)专区将在一定量服务商完成入驻后正式上线。 我们欢迎符合以下标准的服务商入驻服务平台—内容服务专区: (1) 需为独立公司法人,有固定办公场地,且为一般纳税人资格; (2) 已注册并经营1年以上; (3) 经营范围应包括:达人、红人、自媒体等相关业务,提供市场策划及宣传业务,提供供应链或电商代运营业务等,且从事相关业务半年以上; (4) 注册资本50万元以上; (5) 具有在微信生态内为商家提供直播、短视频、图文运营的服务经验。满足如下条件之一: ① 至少与3个以上的小程序商家签署真实、有效的直播代运营业务合同,直播合作关系可查,服务期间直播综合数据表现良好; ② 具备与3个以上的微信生态内商家签署的基于短视频、图文的代运营业务合同。 ③ 如①②两点均不具备,则需证明在微信内容服务领域其他可证明服务能力的资料。 入驻流程如你的企业符合以上标准,可通过邮件发送申请材料至servicemarket@tencent.com提交申请。 请将申请资料整理成PPT(PDF)打包文件作为邮件附件发送。 邮件的内容需要包括但不限于: ①公司整体介绍(包含公司历史、发展历程、公司所在地、融资情况等); ②公司核心团队介绍,包括CEO、核心运营负责人背景介绍; ③业务联系人员信息:姓名、职务、微信号、联系电话; ④至少与3个商家签约的包含但不限于直播、短视频、图文代运营的服务协议,并需提供服务案例简介及商家的小程序(公众号)appid; ⑤其他可以证明公司拥有微信小程序(公众号)代运营业务经验的案例。 我们在收到申请并审核材料通过后,会与你取得联系,指引后续的入驻流程。 微信团队 2020年06月09日
2020-06-15 - 小程序内用户帐号登录规范调整和优化建议
为更好地保护用户隐私信息,优化用户体验,平台将会对小程序内的帐号登录功能进行规范。本公告所称“帐号登录功能”是指开发者在小程序内提供帐号登录功能,包括但不限于进行的手机号登录,getuserinfo形式登录、邮箱登录等形式。具体规范要求如下: 1.服务范围开放的小程序 对于用户注册流程是对外开放、无需验证特定范围用户,且注册后即可提供线上服务的小程序,不得在用户清楚知悉、了解小程序的功能之前,要求用户进行帐号登录。 包括但不限于打开小程序后立即跳转提示登录或打开小程序后立即强制弹窗要求登录,都属于违反上述要求的情况; 以下反面示例,在用户打开小程序后立刻弹出授权登录页; [图片] 建议修改为如下正面示例形式:在体验小程序功能后,用户主动点击登录按钮后触发登录流程,且为用户提供暂不登录选项。 [图片] 2.服务范围特定的小程序 对于客观上服务范围特定、未完全开放用户注册,需通过更多方式完成身份验证后才能提供服务的小程序,可以直接引导用户进行帐号登录。例如为学校系统、员工系统、社保卡信息系统等提供服务的小程序; 下图案例为正面示例:校友管理系统,符合规范要求。 [图片] 3.仅提供注册功能小程序 对于线上仅提供注册功能,其他服务均需以其他方式提供的小程序,可在说明要求使用帐号登录功能的原因后,引导用户进行帐号注册或帐号登录。如ETC注册申请、信用卡申请; 如下反面示例,用户在进入时未获取任何信息,首页直接强制弹框要求登录注册ETC,这是不符合规范的。 [图片] 建议修改为如下正面示例所示形式:允许在首页说明注册功能后,提供登录或注册按钮供用户主动选择点击登录。 [图片] 4.提供可取消或拒绝登录选项 任何小程序调用帐号登录功能,应当为用户清晰提供可取消或拒绝的选项按钮,不得以任何方式强制用户进行帐号登录。 如下图所示反面示例,到需要登录环节直接跳转登录页面,用户只能选择点击登录或退出小程序,这不符合登录规范要求。 [图片] 建议修改为下图正面示例形式,在需帐号登录的环节,为用户主动点击登录,并提供可取消按钮,不强制登录。 [图片] 针对以上登录规范要求,平台希望开发者们能相应地调整小程序的帐号登录功能。如未满足登录规范要求,从2019年9月1日开始,平台将会在后续的代码审核环节进行规则提示和修改要求反馈。
2019-07-20