uni-app小程序(快手)日志打印坑位记录
前情
uni-app是我比较喜欢的跨平台框架,它能开发小程序/H5/APP(安卓/iOS),重要的是对前端开发友好,自带的IDE让开发体验也挺棒的,公司项目就是主推uni-app。
坑位
最近在开发一需求,页面上的内容需要根据当前主查询接口返回的某一个字段A是否为null来做接口轮询,直到它获取到非null的真正内容,在开发者工具一切都正常,但在真机快手小程序上测试时并没有触发页面接口轮询,导致页面数据显示异常。
于是我通过小程序控制台查看,发现确实字段A是非null值,但又不是接口文挡上注明的值,我开始怀疑是服务端数据返回问题,把截图甩给服务端说数据返回不太对,服务端向我要了请求参数后,他通过postman模拟请求,说数据是对的,我在小程序开发者工具控制台上看了接口数据确实也是对的,重新测了真机确实是不行的,因为项目我是中间接手的,我怀疑是不是代码请求的封装方法中有什么骚操作,于是仔细review了一遍通用请求封装的方法,除了找到一个可以优化的点外,并没有发现有什么不妥当的地方。

Why?
我猜应该是快手小程序的vConsole在日志打印不知道做了什么操作修改了对象中字段为null的数据,目前发现的是null会被替换为当前传入的整个对象,无限循环下去,导致页面获取不到接口返回的原始数据,导致功能异常。于是我在快手小程序论坛提了贴子,反映了目前遇到的问题,链接:开发小程序日志输出异常 (kuaishou.com)
几小时后官方就给了我回复说,没有重现出来,我于是新建一个最小项目,重现了下问题,最小示例项目很简单就是一个页面,纯打印我项目中接口返回的测试数据日志:
<template>
<view class="content">
<view class="text-area">
首页
</view>
<button @click="log">输出日志</button>
</view>
</template>
<script setup>
let logObj = {
"status": "success",
"code": 200,
"message": "操作成功",
"data": {
"order": {
"id": 21184,
"platform": 9,
"order_no": "662955300914989888",
"order_mode": 1,
"state": 11,
"cancel_status": 441,
"address_id": 685,
"shop_id": 0,
"user_id": 464,
"payment_funder_mark": "YUE_RONG_ZHONG_KE",
"source_platform": 2,
"source_order_no": "",
"capital_order_id": "3252408100001353",
"product_id": 11211,
"sku_id": 28081,
"id_card_name": "泽城",
"id_phone": "16249235181",
"id_card_number": "451581199711097777",
"security_id": 0,
"exception_type": 0,
"risk_decision": 0,
"total_price": "30.00",
"first_actual_payment_amount": "1.25",
"first_payment_rent_payable": "1.25",
"deposit_total_amount": "10.00",
"deposit_discount_type": 0,
"deposit_free_amount": "0.00",
"deposit_recovery_deduction_amount": "0.00",
"deposit_actual_amount": "0.00",
"total_rent": "30.00",
"buy_out_price": "1.00",
"buy_out": 0,
"repaid_amount": "10.00",
"schemes": 3,
"lease_days": 720,
"repayment_periods": 24,
"courier_number": "",
"esign_sign_flow_id": "",
"esign_state": 2,
"esign_face_flow_id": "",
"esign_face_state": 0,
"age": 26,
"state_exception_note": "",
"note": "",
"creator": "",
"created_at": "2024-08-10 17:55:48",
"updated_at": "2024-08-10 18:14:15",
"lease_start_time": "2024-08-14 00:00:00",
"lease_end_time": "2026-08-04 00:00:00",
"user_bound_at": null,
"first_shared_at": null,
"esign_finish_at": null,
"task_order_at": "2024-08-10 17:55:48",
"deleted_at": null,
"serial_number": "",
"cancellation_reason": null,
"sn": "202408108001",
"contract_file": "/contact/12/18/e38154fbb9ac9308b66e336a01e69a23.pdf",
"overdue_state": 0,
"order_process": 1,
"rent_price_model": 2,
"payment_mode": 4,
"conversion_method": 1,
"deposit_switch": 2,
"deposit_payment_process": 0,
"risk_result": 0,
"sync_supply_chain_time": "2024-08-10 17:58:56",
"complete_sub_status": 0,
"verify_delivery_address": 0,
"cancel_express_status": 2,
"shipping_phone": "13249235081",
"source_platform_text": "快手",
"state_text": "已关闭"
},
"order_apply_refund_amount": "0.00",
"order_refund_examine": {
"id": 2962,
"order_no": "662955300914989888",
"order_state": 4,
"state": 2,
"refund_type": 2,
"refund_reason": 0,
"audit_time": "2024-08-10 18:14:12",
"user_id": 25,
"remark": "订单退货退款",
"reason": "首付太高",
"deduction_amount": "0.00",
"deduction_reason": 0,
"created_at": "2024-08-10 18:11:27",
"updated_at": "2024-08-10 18:14:16",
"refund_receive_state": 2,
"refunded_at": "2024-08-10 18:14:16",
"examine_no": "SHTH2024081062424559",
"process": "PASSED",
"category": 1,
"mode": 2,
"force_source_platform": 0,
"force_out_order_no": "",
"refund_process": "DEFAULT",
"refund_return_process": "ALREADY_IN_STOCK",
"refund_return_courier_number": "SF100862",
"refund_return_consignee_phone": "13249235081",
"refund_return_device_expired_time": "2024-08-16 18:12:10",
"refund_return_device_expired_expend_times": 0,
"refund_return_express_records": null,
"refund_return_audit_state": 1,
"refund_return_audit_time": "2024-08-10 18:12:10",
"refund_return_reship_consignee_name": "",
"refund_return_reship_consignee_phone": "",
"refund_return_reship_consignee_address": "",
"refund_return_reship_courier_number": "",
"refund_return_reship_express_records": null,
"user_remark": "",
"refund_return_tips": {
"return_express_tip_info": "请选用顺丰快递(货到付款)",
"return_address_tip_info": "18098965084 深圳市福田区世界中心2楼201"
}
},
"is_order_refunded": 1,
"is_order_allow_apply_refund": 0
},
"error": {}
};
const log = () => {
console.log('---- log ----:', logObj.data.order_refund_examine.refund_return_express_records, logObj);
}
</script>
<style>
</style
解决方案
- 注释掉日志输出代码(最直接)
- 打印日志的时候打印到特定你想知道的key,或者你想知道的KEY的父级对象,测试中发现层级在一二级不会有问题
- 打印前通过JSON.stringify转化为字符串输出(对象较大的时候很难找到想要看到的key的值)
- 深拷贝后再输出,虽然打印的日志是错的,但是不会影响原始值,至少页面上拿到的是正确的值,在项目主入口main.js中增加如下代码重写console.log方法是可行的,同时还帮处理了生产环境让所有log方法失效
const log = console.log;
/**
* @description 深度克隆
* @param {object} obj 需要深度克隆的对象
* @param cache 缓存
* @returns {*} 克隆后的对象或者原值(不是对象)
*/
function deepClone(obj, cache = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj;
if (cache.has(obj)) return cache.get(obj);
let clone;
if (obj instanceof Date) {
clone = new Date(obj.getTime());
} else if (obj instanceof RegExp) {
clone = new RegExp(obj);
} else if (obj instanceof Map) {
clone = new Map(Array.from(obj, ([key, value]) => [key, deepClone(value, cache)]));
} else if (obj instanceof Set) {
clone = new Set(Array.from(obj, value => deepClone(value, cache)));
} else if (Array.isArray(obj)) {
clone = obj.map(value => deepClone(value, cache));
} else if (Object.prototype.toString.call(obj) === '[object Object]') {
clone = Object.create(Object.getPrototypeOf(obj));
cache.set(obj, clone);
for (const [key, value] of Object.entries(obj)) {
clone[key] = deepClone(value, cache);
}
} else {
clone = Object.assign({}, obj);
}
cache.set(obj, clone);
return clone;
}
// 重写日志输入方法
console.log = (...params) => {
if (process.env.NODE_ENV === "development") {
const paramsTemp = params.map((item) => {
return deepClone(item);
})
log(...paramsTemp);
}
}
- 和服务端沟通,对于值为null的返回为空字符串或者undefined等(不推荐,成本大)
推荐前三种方式,对现有项目没有任何侵入,方式4也是可以的,这种问题是平台级的特有BUG,BUG已提给平台,静待平台尽早修复吧。
思考
小程序开发是依赖第三方小程序平台的,对于一些难定位的问题,自己折腾找不到原因后,可以优化论坛搜一搜,如果没有那就提一个贴子,一般几天内会有回复,最好是能做一个最小复现的demo,以便于官方定位问题,做最小demo的时候也许自己也能发现解决或绕过问题的方法,在开发的时候可以依赖开发者工具完成需求,但遇到开发者工具和小程序上表现不一致的时候应该以真机为准,因为你最后项目是跑在真机上的。当然也希望各小程序平台积极优化,给广大小程序开发者更好的开发体验。
uni-app小程序(快手)日志打印坑位记录的更多相关文章
- 微信小程序 PDF下载打印
在开发微信小程序时,需要打印生成的PDF,实现思路是:后端生成相应的PDF,微信小程序下载并打开. 但是微信小程序并不可以打印,所以需要借助其他APP比如:WPS,但是发现微信小程序down的PDF在 ...
- 小程序onLaunch事件的坑
记一个小程序踩过的坑 小程序项目中app.js里面定义了globalData,即全局变量,里面定义了一个token字段 需求是这样的,每次进入小程序的时候需要检验该token有没有,没有就请求后台获取 ...
- Java生鲜电商平台-APP/小程序接口传输常见的加密算法及详解
Java生鲜电商平台-APP/小程序接口传输常见的加密算法及详解 说明:Java生鲜电商平台-APP/小程序接口传输常见的加密算法及详解,加密算法,是现在每个软件项目里必须用到的内容. 广泛应用在包括 ...
- Java生鲜电商平台-电商中"再来一单"功能架构与详细设计(APP/小程序)
Java生鲜电商平台-电商中"再来一单"功能架构与详细设计(APP/小程序) 说明:在实际的业务场景中(无论是TO B还是TO C)不管是休闲食品.餐饮.水果.日用百货.母婴等高频 ...
- uni与小程序,vue的区别
标签区别 uni使用小程序的标签,vue使用web端的标签 标签名变化的: 标签描述\类别 vue uniapp 文本 span\font text 链接 a navigator/ router-li ...
- 小程序重新封装打印函数console.log
习惯性使用console.log打印获取到的数据,信息等,然后上星期大佬看见了说怎么那么多打印信息出来,线上那个也是吗?问我能不能线上的就不打印出来? 我就说那就封装一个打印函数呗. 重写一个没问题, ...
- 微信小程序开发常见之坑
https://www.cnblogs.com/shunxing/articles/6971648.html input里的value会在浮层上面的,要解决这一问题还是很简单的,在小程序中input有 ...
- 微信小程序—setTimeOut定时器的坑
原文地址: http://fanjiajia.cn/2018/06/27/%E5%BE%AE%E4%BF%A1%E5%B0%8F%E7%A8%8B%E5%BA%8F%E2%80%94setTimeOu ...
- 小迪安全 Web安全 基础入门 - 第三天 - 抓包&封包&协议&APP&小程序&PC应用&WEB应用
一.抓包工具 1.Fiddler.Fiddler是一个用于HTTP调试的代理服务器应用程序,能捕获HTTP和HTTPS流量,并将其记录下来供用户查看.它通过使用自签名证书实现中间人攻击来进行日志记录. ...
- 开发微信小程序 中遇到的坑 及解决方法
1.wx.request 只能访问 https 解决: 新建项目 不填appid 即可访问 localhost 2.页面中多重三元表达式 解析有问题 解决: <!--{{index}} { ...
随机推荐
- shell脚本参数读取列表文件,循环执行时,要注意这个列表文件转换成 unix 格式
当报一些很奇怪的错,比如报错多了一个空格,但 vim 看打开没有,比如: 或者肉眼看上去路径正常,但却报路径不存在: 这种时候要使用 dos2unix 工具,把这个列表文件转换一下:
- Windows Terminal3.1
其实就是为了把之前写的东西集成起来所以搞了一个终端. 下载 集成功能 Wordle ABCG RandTool cmd 便捷功能 FastFile (快速打开目标文件夹) show 文件说明 Term ...
- [TK] 颜色
谴责这道题发明在线莫队的人,简直就是异端,还好我给在线莫队卡了,支持正义制裁 题意简述 给定序列,设 \(f(l,r,x)\) 表示 \(x\) 在 \([l,r]\) 内的出现次数,对给定 \(l, ...
- 基于全息感知的智慧高速IT设施监控运维方案
作为智能交通的重要细分领域,建设智慧高速是实施交通强国战略的重要基础.在信息化时代,交通行业已经依托信息化建设取得了显著的成果,其中以收费网络.办公网络.监控网络和通讯网络为基础的网络架构已经形成,并 ...
- Lazy TLB Mode 的工作原理
Lazy TLB (Translation Lookaside Buffer) mode 是操作系统和处理器在管理虚拟内存时的一种优化技术,旨在提高处理器的性能.要理解 Lazy TLB mode,需 ...
- WiFi基础(六):天线基础知识
liwen01 2024.10.01 前言 麦克斯韦预言了电磁波的存在,赫兹通过实验证实了麦克斯韦的预言,马可尼基于无线电磁波的原理发明了无线电报系统,从此人类进入无线通信系统时代. 天线是通信系统中 ...
- 别人可以访问本项目的ip地址
.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...
- 如何将rust日志输出到android终端
本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0许可协议.转载请注明来自 唯你 背景 在 Rust 中,使用 println!打印日志时,输出实际上是发送到标准输出(stdout),而 ...
- 前台sessionStorage存取对象注意事项
一.概述 使用sessionStorage存取全局数据: 语法: sessionStorage.setItem("key",value); object = sessionStor ...
- DRF请求的生命周期
作为一个工作3年左右的码农,在各种框架的摸爬滚打中,我也接触了不少前端后端的技术栈,其中DRF算是我后端日常工作中的用得最多的框架.今天就简单聊聊DRF请求的生命周期.由于篇幅原因,我在此篇文章中只是 ...