- 自定义导航栏高度精确适配方案
之前自己做小程序,凡是自定义导航栏的地方,我都是 状态栏高度用 getSystemInfo 中的 statusBarHeight导航栏高度固定 44 px 不过最近发现在几款安卓较新的机型上,状态栏高度都OK,但导航栏的高度要么大了要么小了。于是我又重新研究了一下,尝试归纳目前(截至发文 2021-06-08)小程序的导航栏高度的规律,总结如下。 小程序自定义导航栏高度精确适配方案 iOS 端 状态栏高度用 getSystemInfo 中的 statusBarHeight导航栏高度固定 44 px导航栏高度 44 px 的表现效果,与默认导航栏("navigationStyle": "default")一致,但这种情况下胶囊不是上下居中的,各位需根据具体场景来自行选择方案。如果想要胶囊精确地上下居中,可使用下方 Android 端的方案Android 端 状态栏高度用 getSystemInfo 中的 statusBarHeight导航栏高度根据胶囊位置与高度计算,计算方式如下let sysInfo = wx.getSystemInfoSync(); let menuInfo = wx.getMenuButtonBoundingClientRect(); let navigationBarHeight = (menuInfo.top - sysInfo.statusBarHeight) * 2 + menuInfo.height; 按照胶囊位置计算出的导航栏高度,与默认导航栏("navigationStyle": "default")一致,且这种情况下胶囊是上下居中的 以上的方案经过多款 iOS 设备与 Android 设备的测试验证,但我们能用于测试的设备有限,得出的结论并不 100% 适用于所有设备。若有补充欢迎在评论区发言。
2021-06-08 - [生存指南]面试题(3): 如何做前端sentry性能监控中的错误和数据采集
[图片] 随着 “大前端” 近两年愈演愈烈(juan),需要我们掌握的技能也越来越多,延伸而来的就是各种前端性能和异常监控框架。 市面上有很多种开源的框架,如 Sentry、Rollbar 等,大厂也会自己建立一套完整的监控体系,有时候还会配合后端的ELK系统一起使用,无论用什么框架,最关键的一步就是:前期的 数据采集和错误捕获, 这里为大家简单分享下一些主流的方式。 1、代码异常捕获 try-catch [代码]try { // Some code that may throw an error } catch (error) { //发送错误信息 console.log(error); //sendError(error) } [代码] 相信大家在一些关键的同步请求的时候,会用上try-catch,避免因为报错,导致整个功能弹出,这也是常规用来捕捉错误日志的方法。 在前端中使用try-catch语句可以帮助开发人员及时发现和解决错误,提高应用程序的健壮性和稳定性。同时,使用第三方错误监控工具可以自动化地收集和汇总错误信息,减少手动调试和排查错误的工作量。 2、页面监控window.onerror [代码] /** * @param {String} errorMessage 错误信息 * @param {String} scriptURI 出错的文件 * @param {Long} lineNumber 出错代码的行号 * @param {Long} columnNumber 出错代码的列号 * @param {Object} errorObj 错误的详细信息,Anything */ window.onerror = function(errorMessage, scriptURI, lineNumber,columnNumber,errorObj) { console.log("错误信息:" , errorMessage); console.log("出错文件:" , scriptURI); console.log("出错行号:" , lineNumber); console.log("出错列号:" , columnNumber); console.log("错误详情:" , errorObj); } [代码] 这种错误监听,可以获取同步和异步的方法,也是比较实用,且常规的一种错误监听的方法,此外,还能提供详尽的数据位置,地址等 3、异常捕获(addEventListener) window.addEventListener(“error”) [代码]// 监听 JavaScript 异常 window.addEventListener('error', function(event) { // 收集错误信息 const error = { message: event.message, filename: event.filename, lineno: event.lineno, colno: event.colno, error: event.error, }; //发送错误信息 }); //图片加载错误监听 image.addEventlistener("error",e => ( e.preventDefault(); //发送错误信息 console.log(e); } // 监听 JavaScript 文件的加载状态 const script = document.createElement('script'); script.addEventListener('load', function(event) { // JavaScript 文件加载完成 console.log('JavaScript file loaded'); }); script.addEventListener('error', function(event) { // JavaScript 文件加载失败 console.error('JavaScript file failed to load'); }); script.src = 'path/to/javascript/file.js'; document.head.appendChild(script); // 监听 CSS 文件的加载状态 const link = document.createElement('link'); link.addEventListener('load', function(event) { // CSS 文件加载完成 console.log('CSS file loaded'); }); link.addEventListener('error', function(event) { // CSS 文件加载失败 console.error('CSS file failed to load'); }); link.rel = 'stylesheet'; link.href = 'path/to/css/file.css'; document.head.appendChild(link); [代码] 也可以用另外一个省代码的写法 [代码]// 监听处理js/css window .addEventListener("error", function (e) { var typeName = e.target.localName; var sourceUrl = ""; if(typeName === "link"){ sourceUrl = e.target.href; }else if(typeName === 'script'){ sourceUrl = e.target.src; } //发送错误信息 }); [代码] 使用 addEventListener 来监听 JavaScript 和 CSS 文件的加载状态可以帮助开发者及时发现和解决加载问题,提高应用程序的稳定性和可靠性,从而提升用户体验和满意度。 window.addEventListener(“unhandledrejection”) [代码]window.addEventListener("unhandledrejection", function(event) { // 阻止默认行为,防止 Promise 异常被浏览器捕获并输出到控制台 event.preventDefault(); // 收集 Promise 异常信息 const error = { message: event.reason.message, stack: event.reason.stack, }; // 将异常信息发送到服务器或其他地方进行分析和报告 sendErrorData(error); }); [代码] 在上面的代码中,我们使用 window.addEventListener(“unhandledrejection”) 方法来监听未处理的 Promise 异常,并收集异常信息。当 Promise 被拒绝时,会触发 unhandledrejection 事件,并将异常信息作为事件参数传递给回调函数。然后我们将异常信息发送到服务器或其他地方进行分析和报告。 需要注意的是,当 Promise 对象被拒绝时,浏览器会默认输出异常信息到控制台。为了防止 Promise 异常被浏览器捕获并输出到控制台,我们需要在回调函数中调用 event.preventDefault() 方法,阻止默认行为的发生。 使用 window.addEventListener(“unhandledrejection”) 方法来监听 JavaScript Promise 的拒绝状态可以帮助开发者及时发现和解决异常问题,提高应用程序的稳定性和可靠性,从而提升用户体验和满意度。 4、性能监听(Performance) 可以使用 Performance API 来做监听 [代码]// 初始化 Performance API const perf = window.performance; // 创建一个新的性能条目 const entry = perf.mark('start'); // 在某个时间点开始执行某段代码 const startTime = performance.now(); // 在某个时间点结束执行某段代码 const endTime = performance.now(); // 收集性能数据 const data = { duration: endTime - startTime, // 其他性能指标,如占用 CPU 时间、内存使用等等 }; // 将性能数据发送到服务器或其他地方进行分析和报告 [代码] Performance API 是浏览器提供的一组 API,用于监测前端应用程序的性能指标,帮助开发者发现性能问题并进行优化。Performance API 提供了多个方法和属性,可以监测页面加载时间、资源加载时间、代码执行时间、内存使用情况、帧率等等性能指标。 以下是 Performance API 中常用的一些方法和属性: performance.mark(name):创建一个新的性能条目,并在当前时间点标记该条目。 performance.measure(name, startMarkName,endMarkName):在两个标记点之间测量性能,并创建一个新的性能条目。 performance.getEntries():返回所有的性能条目。 performance.getEntriesByName(name):返回指定名称的性能条目。 performance.now():返回当前时间点的时间戳,精度为毫秒。 performance.timing:返回页面加载时间的性能指标,包括 DNS 查询时间、TCP 连接时间、请求时间、响应时间等等。 5、框架层面的错误捕获 VUE: Vue.config.errorHandler Vue.config.errorHandler 是 Vue.js 提供的一个全局错误处理器,用于捕获 Vue.js 应用程序中的未处理异常。以下是一个例子,演示如何使用 Vue.config.errorHandler 捕获 Vue.js 应用程序中的未处理异常: [代码]Vue.config.errorHandler = function(err, vm, info) { // 收集异常信息 const errorData = { message: err.message, stack: err.stack, info: info, }; // 将异常信息发送到服务器或其他地方进行分析和报告 sendError(errorData) }; [代码] React: ErrorBoundary React 的 ErrorBoundary 是 React 提供的一个组件,用于捕获 React 应用程序中的未处理异常。以下是一个例子,演示如何使用 ErrorBoundary 捕获 React 应用程序中的未处理异常: [代码]class MyErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { return { hasError: true }; } componentDidCatch(error, info) { // 收集异常信息 const errorData = { message: error.message, stack: error.stack, info: info, }; // 将异常信息发送到服务器或其他地方进行分析和报告 sendError(errorData); } render() { if (this.state.hasError) { return <h1>出错了!</h1>; } return this.props.children; } } [代码] 我们使用 ErrorBoundary 组件来捕获 React 应用程序中的未处理异常,并收集异常信息。当 React 应用程序发生未处理异常时,会触发 componentDidCatch 事件,并将异常信息作为事件参数传递给回调函数。然后我们将异常信息发送到服务器或其他地方进行分析和报告。
2023-07-25 - 如何彻底解决小程序滚动穿透问题
背景 俗话说,产品设计有三宝:弹窗、浮层加引导。可见“弹窗”在产品设计中的重要性。对于前端开发者来说,实现一个基础的模态框(Modal)并不难,可一旦模态框内部出现可滚动内容,各种意想不到的问题就会接踵而至——其中最为典型的,就是“滚动穿透”。 什么是滚动穿透? 滚动穿透指的是:当我们在顶层弹窗中执行滑动操作时,实际滚动的是底层页面的内容。这种体验非常影响交互,也常让用户感到困惑。 解决方案分析 解决滚动穿透的思路通常有两种:改变顶层或改变底层。 1. 改变顶层(不推荐) 一种自然的想法是阻止顶层的事件继续传播。例如,给遮罩层(蒙层)绑定 catchtouchmove 事件。但这种方法在部分场景下并不生效,因此并不是一个可靠的通用方案。 2. 改变底层(推荐) 既然问题是底层内容跟随滚动,那么只要在弹窗打开时禁止底层页面滚动,问题就迎刃而解。 具体实现方案 🟡 不成熟的方案 将底层页面的最外层 view 设置为 position: fixed,但这会导致页面滚动位置丢失,回到顶部。 另一种方式是:在打开弹窗前记录当前滚动位置,关闭弹窗后使用 wx.pageScrollTo 滚动回原位置。这种方法实现较为繁琐,且体验不够流畅。 ✅ 成熟的方案 方案一:使用 page-meta 组件 通过微信小程序提供的 page-meta 组件,我们可以直接控制页面根容器的样式,类似于在 H5 中设置 body { overflow: hidden; }。 我们可以动态设置 overflow 属性为 hidden 或 auto,来控制页面是否可滚动。 方案二:使用 wx.setPageStyle 方法 这是一个更灵活的 API,特别适合在组件内部(如封装好的弹窗组件)使用,无需额外编写 page-meta 结构。 [代码]wx.setPageStyle({ style: { overflow: 'hidden' // 或 'auto' } }) [代码] 代码示例 这里提供一个可供测试的代码片段,帮助你快速上手: 👉 点击查看代码片段 拓展:支付宝小程序的情况 支付宝小程序虽然也支持 page-meta 组件,但由于其 WebView 内核限制(版本 69),设置 overflow: hidden 并不能完全阻止底层滚动。 支付宝团队为此专门提供了新的 API:my.setPageScrollable,用于精确控制页面是否可滚动。 [代码]my.setPageScrollable({ scrollable: false, success: (res) => console.log(res), fail: (err) => console.log(err) }) [代码] 更新(2025.06.18) 该 API 已正式开放。不过目前测试发现:在 安卓端 设置禁止滚动后,弹窗内的可滚动区域也会被一并禁止;iOS 端 则表现正常,仅底层页面被锁定。 原因分析: 安卓与 iOS 在滚动禁止的层级控制上存在系统级差异: 安卓 采用 Webview 级别的滚动限制,生效时整个页面(包括所有弹层)均不可滚动; iOS 采用组件级控制,能智能区分层级,仅锁定底层页面而保持弹窗内部可滚动。
2025-11-13 - getPhoneNumber|agreePrivacyAuthorization无效?
从基础库 2.33.1 版本起,该组件支持与手机号快速验证组件、手机号实时验证组件耦合使用,调用方式为 <button open-type="getPhoneNumber|agreePrivacyAuthorization"> 这个没用啊 单个agreePrivacyAuthorization倒是可以 [图片] [图片]
2023-08-16 - 刚收到通知获取手机号收费开始了?
[图片] https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getRealtimePhoneNumber.html https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/paymentManage.html 未来:旧版本接口依然可以使用,只是需要收费而已,不想做改动的交钱就行了。如果想用新API方法就去改吧,多花一分钱。 可能支持的省钱办法: 授权手机号后,服务端将openId、手机号进行绑定。用户onLaunch打开小程序的时候通过wx.login获取code去解密openId,同时由于服务端已经绑定过手机号,所以可以使用该手机号进行登录,并同步返回token、jwtToken等登录态。这样可以做到用户冷启动小程序时自动登录上,减少使用授权的逻辑。业务按钮点击后 先调用wx.login,如果返回token则进行后续业务,如果没返回则弹出自定义弹窗,弹窗内点击按钮再进行手机号授权。(也可以在部分页面onLoad里wx.login),这个场景因为会延长流程,所以产品说不考虑,先直接打开页面就登录上,你们的各自看各自的业务场景吧。然后有四个疑问: 充值购买次数后会,如果小程序被封禁了,充值的金额是否可退款。购买数量是否支持按量付费?如果次数用完了,未购买新的次数,用户端的表现是什么?如果次数用完了,之前文档说的余量20%、10%、5%时会发模板消息提醒,文档相关现在已经删除了,是否还会发?[图片] ———————————————————————————————————————————————— 今天看了下文档做了改动: 退款规则:若购买有误,且未正式开始使用资源包前,可以在支付成功后的7天内申请退款。款项将在3-5个工作日内从原支付路径返回;若资源包已经开始使用(使用1次及以上),则不能申请退款;若支付成功后超过7天,未发起退款申请,亦不能再申请退款。 那么小程序被封了应该是不退的。不确定,等官方回复次数用完了,用户授权不会弹出授权弹窗,会返回一个errNo:1400001,用户判断等于这个errNo的时候跳转到自己的账密登录页面。不确定,等官方回复———————————————————————————————————————————————— 据了解老版本的快速验证组件(获取手机号),180天才会发送短信验证一次,为啥能每次授权都收费0.03元。 社区搜了一张图,180天没验证的应该会弹这个,不是说是短信运营成本么?为啥不是第180天验证那次费用让我们付,而是每次授权都付? [图片] 手机号授权改造后的效果: 打开职位详情页:优先调用接口判断openId是否绑定过。 如果未绑定:使用button的open-type=“getPhoneNumber”,点击报名弹出手机号授权,授权成功后与openId进行绑定落库。 如果已绑定,页面通过变量判断使用wx.login静默授权,同时服务端拿到绑定的手机号后进行登录操作,同步返回登录态(token/jwtToken)。 退出登录页面增加解绑操作(服务端解除openId与手机号的绑定),此时用户再次点击报名,就会弹出手机号授权,方便用户切换手机号。 [视频]
2023-07-27 - ios下的ad组件穿透地图?
描述: 目前地图已支持view同层渲染, 在同层渲染的view里添加 ad 广告组件, view已经通过 catch:touchmove 阻止地图穿透。 但实际测试过程中,ios 机型, view不会引起地图拖动没错, 但是如果是滑动 ad 组件区域,地图仍旧可拖动。 [图片] 可复现的代码片段: https://developers.weixin.qq.com/s/PBGXmem47FBG
2022-08-03 - 插屏广告导致页面卡死,无法关闭
[视频] 有2个页面,第一个页面每次打开会调用插屏广告,第二个面不会调插屏广告。 复现流程:第一个页面打开,展示了插屏广告(有时候第一页广告不展示也可以复现)。关掉广告后,进入第二个页面。在第二个页面停留一定时间(满足再次打开第一个页面时会展示插屏广告),返回第一个页面,第一个页面弹出插屏广告。此时第一个页面卡死,插屏广告也无法关闭,只能杀掉小程序进程,才可以打开。 不会百分百的复现,安卓和ios都有可能出现。 也可能不是卡死,只是广告关不掉。点击插屏详情按钮,按钮有点击特效,但是页面没有变化。点击投诉是可以跳转的。点击关闭按钮,没有任何反应,点击黑色背景区域也无法关闭。小程序内的swiper是可以正常轮播的。
2021-10-08 - 蓝牙接口调用需要授权问题?
访问蓝牙、需要用户授权小程序处理用户的个人信息,需要获取用户明示同意,平台计划从2022年2月21日24时起对以下接口增加用户授权:访问蓝牙:调用wx.openBluetoothAdapter、wx.createBLEPeripheralServer,需要授权scope.bluetooth开发者可在平台调整前提前增加使用 wx.getSetting 获取用户当前的授权状态的逻辑,若授权状态为false可以调用 wx.openSetting 打开设置界面,引导用户开启授权。 openSetting 打开设置界面是还没有蓝牙选项 ??????????
2022-01-05 - 小程序wx.request存在大量失败请求(失败率为0.6%-2%),其中75%为请求超时?
目前小程序wx.request存在大量失败请求,失败率为0.6%-2% 其中75%为请求超时,但网关接口性能最大响应时间在400ms,官方能不能给分析一下? 急!!!
2019-11-21