让我掉下眼泪的 不止内存泄漏 让我夜夜不眠的 不止你的需求 明天还要改多久 你攥着我的手 让我感到为难的 是善变的需求 发布总是在半夜 回滚是永远的愁 错误(Bug)随时的暴漏 困扰着我心头
作为程序员,以上这些场景你一定都经历过。今天就来聊聊如何快速定位问题。
先划重点,下文所写都是一家之言,本人工作经验不多,语言表达能力有限,如果写的不好,还望轻喷。另外,本文所讲都是站在 Java 后端开发者的角度。
下文所讲内容,都会围绕以下几个真实案例来做举例分析,先描述一下具体案例:
背景交代完毕,那如果是你,在遇到这几个问题的时候,会怎么处理呢?
当测试大佬反馈问题时,首先要做的就是复现问题。如果问题能复现,好嘛,已经解决一大半了,作为开发,我觉得还是要有这个自信的。能复现的问题,那就一定能修复(修复成本有高低,这个不在本文讨论范围之内哦),实在是找不到 Bug 代码,我可以一行一行的调试嘛!所以,遇到问题不用慌,淡定淡定。
那如果问题不能复现呢?怎么办?
这个时候,我一般的做法是去查日志。如果日志中有错误信息,我们便可以根据错误信息快速定位到 Bug 所在的具体代码。那如果这个时候也没有错误信息呢?嗯...我想想,好像也没有别的办法了。问题不能复现,程序没有报错,那只能麻烦测试大佬再多测试一下,看看能不能复现吧。
经过上一步骤,我们已经可以让 Bug 复现了,那接下来要做的就是快速定位。快速定位?定位什么呢?
一般公司项目开发,都会分后端开发、前端开发、APP 开发,这里说的快速定位,指的就是要快速定位到是三端中的哪一端出的问题。
如果你熟悉这个功能的整体流程,清楚整个功能会经历哪些步骤、哪些模块,这对你去快速定位问题是非常有帮助的。当然,也有一些监控工具可以来帮助开发者做快速定位,帮助开发了解整个流程。例如:sentry、skywalking 等。
案例 1:App 首页白屏。 案例 2:小程序商品会员价显示不正确 这两个问题反馈过来的时候,我打开 app、H5、小程序都看了一下,发现:只有 app 的首页白屏了,H5 和小程序的首页都是好的,考虑到 App、H5、小程序首页都是由同一个后端接口负责提供数据,那这个问题大概率是 app 那边的问题,于是请 app 开发同事帮忙定位一下问题。 而 app、H5、小程序这三端都出现了商品会员价显示不正确这个问题,于是我断定,这大概率是一个后端的逻辑问题。三端都写错代码取错了会员价这个概率应该不大。
案例 4:某用户购买的 xx 评测专栏的评测课无法打开。 这是一个产品反馈的线上问题,由测试大佬提到开发这边的时候,测试大佬当时并不能复现。由于评测课的特殊性,它是需要由用户做题输入到系统,系统解析用户答题情况,然后做系统推荐。 这是一个典型的与用户行为数据相关的问题,可能只有具有某些特性行为、数据的用户才会遇到。遇到这种问题,测试也是很难复现的。可以查一下日志,看看有没有报错信息。 当时遇到这个问题的时候,由于项目接入了 sentry 平台,开发这边也是收到了系统异常报错的邮件提醒,很快速的就找到了原因。
好,经过上面几轮的大致判断,这大概率就是一个后端 Bug 了。现在我们需要做的就是,快速定位到出问题的具体接口。如果移动端,就用 Charles 抓个包,H5 端就直接打开 Chrome 控制台。
so easy~~ 妈妈再也不用担心我找不到接口啦~~ 当然了,在实际操作过程中,可能并没有这么简单。前端渲染页面可能请求了 N 多个接口。
案例 2:小程序商品会员价显示不正确。 因为 app、H5、小程序三端使用的是同一个接口来获取商品相关信息,我会优先在 H5 平台调试,毕竟不用开 Charles,方便嘛~~
chrome控制台遇到问题,快速响应和解决才是重点,特别的线上问题。所以有时候这个功能可能不是你开发的,那么如何在这么多请求中如何快速定位找个具体接口呢?这就要靠你的经验和聪明的大脑了。 这里就分享一个我的经验吧,不一定适合所有场景。就拿这个案例来说:打开商品详情页,打开控制台。基于我对系统的整体了解,我确信一定会有一个接口返回商品的会员价,具体哪个接口我也不知道。 好,这个时候怎么办呢?猜接口!当然了,也不是乱猜。获取商品会员价,那这个接口大概率需要前端传给后端一个商品 id,那商品 id 在哪里呢?商品 id 一般都会出现在当前页面的 URL 里。于是,在控制台的 filter 框中(图中已标红)输入商品 id。这个时候已经可以过滤掉大部分的请求了。 接下来你要做的,还是猜!看看剩下这些请求地址名称,猜一下他的作用;看看接口返回的字段名称,有没有名称像“会员价”字段,有没有返回值和前端显示的会员价一样的字段。最后,经过大胆猜想之后,我们要做的就是小心求证,确认我们定位的接口是否正确。
定位到接口之后,我们就可以准备看代码,修 Bug 啦!
不知道你有没有遇到过这样的情况。打开代码,一眼望去,这个代码这么长,而且之前也不是我写的,我该怎么办呢?下面我们就来讲一下如何来快速定位 Bug 代码。
案例 2:小程序商品会员价显示不正确。
经过我们之前一顿猛如虎的操作,终于定位到了问题。
会员价显示不正确,也就是"vipPrice":0
这个字段有问题。
打开代码,找到该接口对应 Controller,找到该 Controller 返回的 VO,找到 VO 中的vipPrice
字段的 setter 方法,鼠标右键find Usages
。恭喜你,这个时候你已经找到了这个vipPrice
的值是在哪一行被设置的了,将重点聚焦于此即可,Bug
就在这个代码附近了。看一下这个vipPrice
的值是怎么计算出来的,是不是计算逻辑写错了。
如果这个时候,很不幸 Controller 的 VO 是通过BeanUtils
这些工具类将属性映射过去的,那么你运行find Usages
可能就找不到属性是在哪里被设置的了。唉,写代码时用的爽,出问题时泪汪汪。那只能查这个 VO 是在哪里被用到了,然后去代码里查了。
案例 3:案例 3:优惠券领取不了了,弹窗显示“领取失败,该优惠券仅限新人领取”! 如果“领取失败,该优惠券仅限新人领取”这个文案,是你的接口返回给客户端的,那么,这个时候你要做的就是,IDEA 全局查找这个关键词。
代码搜索哈哈哈,恭喜你,快速定位了,在PayUserRuleChecker
的第 51 行,是不是很简单?
既然已经定位到具体的代码了,那么就可以进行问题修复了。这个时候就要看个人经验啦,有经验的程序员可能一眼就能看出来问题。
这里列举一些需要注意的点:
借用测试大佬的一句话:"没 bug 是不可能的,这辈子都不可能没 bug 的"。
而我们要做的,一是要尽可能的减少 Bug,避免问题重复出现;二是要遇到问题,快速修复。千万不要害怕 Bug,更不要担心出 Bug 就不敢写代码。
最后的最后,就来做个简单总结:
注意:配置好后一定要关闭原来的server,重新npm run dev
启动项目。不然无效。
axios的封装,主要是用来帮我们进行请求的拦截和响应的拦截。在请求的拦截中我们可以携带userToken,post请求头、qs对post提交数据的序列化等。在响应的拦截中,我们可以进行根据状态码来进行错误的统一处理等等。axios接口的统一管理,是做项目时必须的流程。这样可以方便我们管理我们的接口,在接口更新时我们不必再返回到我们的业务代码中去修改接口。
为什么要使用按需加载的方式而不是一次性全部引入,原因就不多说了。这里以vant的按需加载为例,演示vue中ui库怎样进行按需加载:
ps:出来vant
库外,像antiUi
、elementUi
等,很多ui库都支持按需加载,可以去看文档,上面都会有提到。基本都是通过安装babel-plugin-import插件来支持按需加载的,使用方式与vant的如出一辙,可以去用一下。
5.如何优雅的只在当前页面中覆盖ui库中组件的样式
我们正常写的所有样式,都会被加上[data-v-23d425f8]这个属性(如1所示),但是第三方组件内部的标签并没有编译为附带[data-v-23d425f8]这个属性。所以,我们想修改组件的样式,就没辙了。怎么办呢,有些小伙伴给第三方组件写个class,然后在一个公共的css文件中或者在当前页面再写一个没有socped属性的style标签,然后直接在里面修改第三方组件的样式。这样不失为一个方法,但是存在全局污染和命名冲突的问题。约定特定的命名方式,可以避免命名冲突。但是还是不够优雅。作为一名优(强)秀(迫)的(症)前(患)端(者),怎么能允许这种情况出现呢?好了,下面说下优雅的解决方式:通过深度选择器解决。例如修改上图中组件里的van-ellipsis类的样式,可以这样做:
这样就不会给van-ellipsis
也添加[data-v-23d425f8]
属性了。至此你可以愉快的修改第三方组件的样式了。当然了这里的深度选择器/deep/
是因为我用的less
语言,如果你没有使用less/sass
等,可以用>>>
符号。更多的关于深度选择器的内容,在文章后面有介绍。
我在a页面写一个定时,让他每秒钟打印一个1,然后跳转到b页面,此时可以看到,定时器依然在执行。这样是非常消耗性能的。如下图所示:
首先我在data函数里面进行定义定时器名称:
方案1有两点不好的地方,引用尤大的话来说就是:
timer
,如果可以的话最好只有生命周期钩子可以访问到它。这并不算严重的问题,但是它可以被视为杂物。
该方法是通过$once这个事件侦听器器在定义完定时器之后的位置来清除定时器。以下是完整代码:
方案2要感谢@zzx18023在评论区提供出的解决方案。类似于其他需要在当前页面使用,离开需要销毁的组件(例如一些第三方库的picker组件等等),都可以使用此方式来解决离开后以后在背后运行的问题。综合来说,我们更推荐使用方案2,使得代码可读性更强,一目了然。如果不清楚once、on、$off的使用,这里送上官网的地址教程,在程序化的事件侦听器那里: 。
7.rem文件的导入问题
我们在做手机端时,适配是必须要处理的一个问题。例如,我们处理适配的方案就是通过写一个rem.js,原理很简单,就是根据网页尺寸计算html的font-size大小,基本上小伙伴们都知道,这里直接附上代码,不多做介绍。
这里说下怎么引入的问题,很简单。在main.js中,直接import './config/rem'
导入即可。import的路径根据你的文件路径去填写。
在我们使用的很多ui库(vant、antiUi、elementUi等)中,都有轮播组件,对于普通的轮播效果足够了。但是,某些时候,我们的轮播效果可能比较炫,这时候ui库中的轮播可能就有些力不从心了。当然,如果技术和时间上都还可以的话,可以自己造个比较炫的轮子.这里我说一下vue-awesome-swiper
这个轮播组件,真的非常强大,基本可以满足我们的轮播需求。swiper相信很多人都用过,很好用,也很方便我们二次开发,定制我们需要的轮播效果。vue-awesome-swiper组件实质上基于swiper的,或者说就是能在vue中跑的swiper
。下面说下怎么使用:
swiper需要配置哪些功能需求,自己根据文档进行增加或者删减。附上文档:npm文档,swiper3.0/4.0文档,更多用法,请参考文档说明。
9.打包后生成很大的.map文件的问题
项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错。
而生成的.map后缀的文件,就可以像未加密的代码一样,准确的输出是哪一行哪一列有错可以通过设置来不生成该类文件。但是我们在生成环境是不需要.map文件的,所以可以在打包时不生成这些文件:在config/index.js
文件中,设置productionSourceMap: false,
就可以不生成.map
文件
开发移动端项目,点击事件会有300ms延迟的问题。至于为什么会有这个问题,请自行百度即可。这里只说下常见的解决思路,不管vue项目还是jq项目,都可以使用fastClick
解决。安装fastClick
:
11.组件中写选项的顺序
为什么选项要有统一的书写顺序呢?很简单,就是要将选择和认知成本最小化。