苹果手机中链接怎么点击两次才能跳转?(复盘与反思 - 我们究竟应该如何解决项目中出现的 Bug)

253 阅读7分钟

前言

最近测试报过来一个离谱的 bug ,问题如下:

iOS 系统中,所有链接都需要点击两次才能跳转!

相信所有写过代码的人都知道,这个 bug 有多么严重。于是咱们就基于此 bug ,开启修复之旅。

正文

📱强烈建议公司一定要给开发配专用的测试机,这真的很影响调研的进度!

首次

在遇到这个问题的第一件事,就是定位问题,是所有的链接都有这个问题?还是只有部分按钮有这个问题?

定位问题点

于是我写了以下代码:

<a href="www.baidu.com">百度</a>
<a href="/products">内链</a>
<button on:click={e => location.href = 'www.baidu.com'}>点击链接</button>
<button on:click={e => goto('/products')}>点击链接</button>

上面分别测试:

  • 外链
  • 内链
  • 按钮外链
  • 按钮内链

经过测试有了第一步确认内容:

  • 按钮跳转(goto / location.href)都没有问题
  • 外链跳转没有问题
  • 内链跳转需要点击两次

基于上面的测试,可以确定的是,只有 a 标签上 href 的内链才会出现 iOS 需要点击两次的情况。

定位问题原因

由于公司使用的框架是 svelte ,第一时间想到了 svelte 的预加载出了问题。

于是写了以下代码:

<a href="products" data-sveltekit-preload-data={false}>内链</a>

经过测试,关闭预加载确实能解决点击两次的问题。
但这并不解决问题,因为:

  • 一个是这会大大损失 svelte 为数不多的优点
  • 二是我不能在所有的 a 标签上添加一个属性吧?

寻找解决方案

相信大家在遇到问题的第一时间,百度 + 掘金 等平台就开始全速运行了~
经过几分钟发现不太好找才会上国外找。

svelte 有一个问题,就是国内的参考资料极少。因此第一时间便优先的国外的资源。

  • stackoverflow 搜索:找到相似的问题,但并不完全相似,且没有可行的解决方案
  • reddit 搜索:和 stackvoerflow 是同一个问题
  • google 搜索:也是类似的情况,找不到解决方案

都这一步都没有找到解决方案的问题,一般就很难在搜索引擎上找到了,只能是扒源码或者 github 提问。由于没有太多时间扒源码去看,因此直接在 Discordgithub 上进行了提问。

由于 github 的提问已删除,所以这里就简单给大家看下 Discord 的提问:

image.png

但最终并没有解决问题。

二次

框架版本升级

经过上面的问题,再结合 Discord 的回复,以及我们使用的 svelteKit 框架的版本,当时怀疑是这个版本的 svelteKit 的问题(1.x),于是决定先升级 svelteKit 确定抛开框架问题。

基于官网的升级方案:kit.svelte.dev/docs/migrat…

升级了项目的 svelteKit 的版本,但此时仍没有解决问题。

全局关闭预加载

既然上面都没解决问题,目前的解决方案就是关闭预加载。官网可以看到全局调整的方案,但并没有说明如何关闭:

image.png

经过测试可以确定,可以通过 data-sveltekit-preload-data={false} 关闭预加载。

于是有了以下代码:

<script>
    onMount(() => {
        if (isIOS()) {
            const bodyEl = document.querySelector('body')
            bodyEl.setAttribute('data-sveltekit-preload-data', "false")
        }
    })
</script>

三次

但上面方案还是一种 hard-code 方案,我们并没有找到问题的原因,因此还是需要复现问题,提供给 githubissues ,让维护者确定问题以及解决方案。

于是开始进行最小化可复现单元的实践。

尝试新项目复现

这里就是创建一个新项目,复现问题,上传至 github 上提供给维护者测试。

  1. 创建项目
  2. 复制老项目的 app.html 保持一致
  3. 新建两个页面,并创建内链跳转链接
  4. 测试:问题并未复现
  5. 在需要跳转的 ts 文件中添加接口请求(jsonplaceholder.typicode.com/posts)
  6. 测试:问题并未复现

到这一步就感觉不对了,问题不能复现证明是有其他代码在影响 preload ,可能并不是 svelte 本身的问题。

确定项目问题

经过上面的问题,已经感觉到距离问题本身越来越近了。于是将上面写的两个页面复制到原项目中,发现依然有问题,那么就可以确定问题出现在项目的其他位置,而并不是预加载本身。

定位问题原因

于是进行了以下操作:

  1. 注释掉所有的 app.html / app.css / layout.ts / layout.svelte 等全局内容
  2. 测试是否复现问题:问题未复现
  3. 逐个打开注释内容,直到问题复现

最终定位到同事新写的 AI 嵌入的代码,在 layout.ts 中:

export const load = (async (url) => {
    const icon = 'xxx';
    // 加载ai助手;
    new CozeWebSDK.WebChatClient({
        config: {
            bot_id: 'xxx',
        },
        componentProps: {
            title: 'xxx',
            icon,
        },
        el: document.getElementById('aiDemo'),
    });
                
    // ...
    return { ... };
}) satisfies LayoutLoad;

这段代码其实可以理解为 Vue、React 的路由拦截,但多了一个监听功能,如果不引用 url 则监听一次,如果引用则每次都运行。

因此每次点击时,都新 new 了一个 WebChatClient ...

在配合上预加载,于是出现了最开始我们遇到的问题。

解决方案

找到问题原因,解决就很简单了。将此段代码移动到 layout.svelteonMount 中,只执行一次。

onMount(async (url) => {
    const icon = 'xxx';
    // 加载ai助手;
    new CozeWebSDK.WebChatClient({
        config: {
            bot_id: 'xxx',
        },
        componentProps: {
            title: 'xxx',
            icon,
        },
        el: document.getElementById('aiDemo'),
    });
})

复盘

通过上面的解决过程,会发现,其中做了很多无用功,且耽误了很多时间。因此整理了这次解决途中出现的问题,以及我们应该使用的解决问题流程。

出现的问题

第一时间怀疑框架的问题

这里其实有好几个原因,综合让我第一时间怀疑框架的问题:

  • svelte 过于新(如果使用的是 vuereact ,那么我第一时间就会怀疑是代码本身的问题)
  • svelte 相关资源过少,导致没有人出过类似的问题,怀疑是框架本身
  • svelte 框架本身的不信任(由于部分框架维护者的原因,对 svelte 框架本身并不信任)

实际上一个正在商用的框架,是不会出现这么重大的问题而不被发现的,因此还是要对框架保持基础的信任

过早的考虑 hard-code 解决方案

其实这点本身没错,但问题是项目本身还没有上线,因此不必如此着急的考虑 hard-code 的方案,可以存在脑海,确定解决不了再考虑 hard-code 的问题。

github 提问方式有问题

github 上提问,理论上我们应该提供给维护者一个可复现的最小代码段或者项目,否则维护者没有时间和你交流问题(提问的艺术)。

当前,你在 Discord 中可以这么做,因为 Discord 更倾向于一个社区,而 issues 有一定区别。

解决问题流程

定位问题原因

通过在项目中逐步打断点,确认问题具体出现的位置。并确定是否找到最终原因,如果确定则直接根据原因修改,如果不确定,通过最小可复现项目复现。

通过最小可复现项目复现问题

确定在最小可复现项目中是否可以复现,如果可以则提供给 issues 提问,如果不可以则代表未定位到核心原因,再到项目中查找真实问题原因

根据上面内容查找真实问题原因方法

将项目中所有可能有影响的位置全部关闭,确定到没有问题的时候,逐个开启项目中的各个功能点,直到问题再次出现