学会了这招,排查生产问题再也不头疼了

背景

上周遇到一个生产问题,有一个页面底部有个分享按钮,在生产环境不显示。而同样的代码,在测试环境却是正常的。 如下图所示,左边是生产的,右边是测试环境的。

 

这是为什么呢? 心中带着困惑,开启了破案模式。

初步确定嫌疑犯

先确定一下是不是定位样式引起的问题,之前发生过在IOS12版本手机上,fixed定位显示有问题,导致元素在页面不展示。对比查看了一下测试环境和生产环境渲染之后的页面dom元素,发现生产环境页面底部的分享按钮不显示,是因为dom元素未满足渲染。而非定位样式问题。嫌疑犯已经基本确定。

 

 

通过查看源代码,我们猜测一下嫌疑犯的作案过程。相关的代码片段如下:

html复制代码        <ActivityFooter
          v-if="isShowFooter"
          :staffId="staffId"
          :activityFooterInfo="state.bannerImageInfo"
          @share="shareShow"
        />

isShowFooter控制底部按钮的显示隐藏,跟isShowFooter相关的变量有三个,分别是state.isChat,state.bannerDetail?.shareSidebar, state.bannerImageInfo?.id。这三个变量初始化之后,会发生改变的代码片段如下:

ts复制代码  const state = reactive({
    bannerDetail: {} as IBannerDetail,
    bannerImageInfo: {} as IBannerImageInfo,
    isChat: false, // 侧边栏进入
  });
  const isShowFooter = computed(() => {
    return !(state.isChat && !state.bannerDetail?.shareSidebar) && state.bannerImageInfo?.id;
  });

state.isChat的值改变逻辑是:

ini复制代码wx.invoke('getContext', {}, (res: TRes) => {
  console.log('%c  res:', 'color: #0e93e0;background: #aaefe5;', res);
  if (res.err_msg === 'getContext:ok') {
     const entries = ['single_chat_tools', 'group_chat_tools', 'chat_attachment'];
     state.isChat = entries.includes(res?.entry);
  } else {
    // 错误处理
    console.log('getContext error', res);
   }
});

在控制台打印输出res?.entry,可以看到取值是normal,那么state.isChat的最终值应该是false。

image.png

 

state.bannerDetail的值改变逻辑是:

ts复制代码const detailRes = await bannerApi.getBannerDetails({ longId: bannerId, staffId: staffId });
state.bannerDetail = detailRes.retdata?.detail;

抓包查看接口的响应数据是:

那么state.bannerDetail?.shareSidebar的值应该是true。

state.bannerImageInfo的值改变逻辑是:

ts复制代码const bannerImageInfoRes = await bannerApi.getBannerInfo({ bannerId: bannerId, staffId: staffId });
state.bannerImageInfo = { ...bannerImageInfoRes.retdata };

抓包查看接口的响应数据是:

image.png

 

所以state.bannerImageInfo?.id的值是44。

因为isShowFooter的计算表达式为:

ts复制代码const isShowFooter = computed(() => { 
  return !(state.isChat && !state.bannerDetail?.shareSidebar) && state.bannerImageInfo?.id; 
});

这么计算下来,isShowFooter = !(state.isChat && !state.bannerDetail?.shareSidebar) && state.bannerImageInfo?.id = !(false && false) && 44 = true,应该显示底部分享按钮才对,可是为什么不显示呢。这是一个令人感到困惑的地方,一时间破案陷入僵局。没有了思路。

打破僵局

有一句说的好,只要思想不滑坡,方法总比问题多。在网上不断搜索调试方法,不断尝试,功夫不负有心人,终于找到了救星。这个救星就是Chrome浏览器自带的文件替换功能。下面我们来看看如何使用这个神器,破解僵局。

第一步 查找调试文件

如何找到要调试的在线业务文件呢? 从Chrome开发调试工具的网络面板中可以看到,这个页面大约有20多个脚本文件请求,而大部分根据名称就能排除掉。比如像sdkMsgXXX.js,polyfillXXX.js,vue.XXX.js,vant.XXX,js 等。真正需要查看文件内容的,也就是其中3个以index.XXX.js命名的文件。然后依次点击这三个index.XXX.js文件, 按下CTRL+F搜索快捷键,输入上下文关联变量isChat,定位出要调试的业务文件是index.58a9f303.js。

image.png

 

第二步 启用文件替换功能

在本地创建一个文件夹,名称随意。这里命名为assets。然后切换到Chrome开发调试工具的源代码面板,点击下方的替换==>选择放置替换项的文件夹==>选择刚才创建的assets文件夹,接着浏览器会弹出一行文字,询问是否给予浏览器完整访问本地文件夹的权限,选择允许。

image.png

 

接着点击启用本地文件

image.png

 

切换到浏览器开发调试工具的网络面板,找到刚才定位到的文件,鼠标右键,点击保存并覆盖。之后刷新页面,浏览器每次就会读取本地的文件而不是线上的文件,改本地文件调试代码是令程序员感到很舒服的事情。这个功能极大了方便了调试在线问题。

image.png

 

第三步 修改代码调试

上一步保存到本地的在线文件,是个混淆压缩文件, 可读性比较差。我们可以在VSCode中打开这个文件,对这个代码进行美化,然后添加调试打印语句

image.png

 

刷新页面,可以看到,空值台输出了isShowFooter相关的三个响应式变量的取值。

image.png

 

令人诧异的是,这条日志应该打印两次,而实际上只执行了一次。从打印出来的值来看,就是页面初始化的时候执行了一次,后面接口数据返回后,被没有再次执行,这是为什么呢?

第四步 破案

我思考了良久,综合查看的页面的dom结构,接口数据,打印日志,认为最有可能出错的地方,就是isShowFooter计算表达式那里。

ts复制代码const isShowFooter = computed(() => { 
  return !(state.isChat && !state.bannerDetail?.shareSidebar) && state.bannerImageInfo?.id; 
});

查看了一下压缩文件,结合源代码推算出混淆压缩文件中的st代表vue的computed关键字, 分别查看一下逻辑正确的测试环境和有问题的生产环境,这个st是怎么来的。

测试环境,st是从./vue.183497e1.js中导出来的。

js复制代码import { 
  u as Y, 
  w as U, 
  _ as $, 
  C as M, 
  b as H, 
  o as kt, 
  H as At, 
  y as $t 
} from "./index.be88cf51.js";
import {
  j as T,
  S as lt,
  J as Tt,
  K as s,
  P as m,
  Q as o,
  u as t,
  _ as D,
  L as b,
  M as C,
  t as B,
  a as O,
  b as dt,
  k as f,
  a0 as W,
  a1 as X,
  H as G,
  O as g,
  $ as z,
  r as N,
  Y as xt,
  F as St,
  U as Lt,
  V as st,
} from "./vue.183497e1.js";

生产环境,st是从index.c3315d0c.js导出的。

js复制代码import {
 u as X, 
 w as U,
 _ as $, 
 C as M,
 b as H, 
 o as kt, 
 aR as st,
 H as At, 
 y as $t
} from "./index.c3315d0c.js";
import {
  j as T,
  S as lt,
  J as Tt,
  K as s,
  P as m,
  Q as o,
  u as t,
  Z as D,
  L as b,
  M as C,
  t as B,
  a as O,
  b as dt,
  k as f,
  $ as W,
  a0 as Y,
  H as G,
  O as g,
  _ as z,
  r as N,
  X as xt,
  F as St,
  U as Lt,
} from "./vue.85dd2d76.js";

是不是这个st导出文件不同,引起的生产问题?验证一下。将生产环境中的st导出方式,修改为和测试环境一样,从./vue.85dd2d76.js导出st。

js复制代码import {
    // ...
    V as st
} from "./vue.85dd2d76.js"; 

将从./index.c3315d0c.js导出的st变量删除

js复制代码import {
    // ...
    // 删除这句
    aR as st,  
} from "./index.c3315d0c.js";

保存文件,Ctrl+R刷新页面,可以看到,isShowFooter的运行逻辑正常了,分享按钮也显示出来了。 至此,本案告破。

image.png

 

最后

Chrome开发工具还有这么好用的功能,以前也看到过,泛泛而学。等真的遇到问题需要使用的时候,根本想不起来。所以我感觉,学习知识要带着应用场景去学,你怎么用,就怎么记忆,学习要学到可以应用的程度。否则可能会产生虚假的收获感和充实感,以为自己把时间都用在了该用的地方,当时也感觉学到了不少知识。然而实践是检验真理的唯一标准,等需要用的时候,如果联想不起来,学过就等于白学。实践不会陪你演戏。


原文链接:https://juejin.cn/post/7255939146375299133

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值