还记得上次你遇到一个让你抓耳挠腮几个小时的UI相关bug吗?也许问题是随机发生的,或者在特定情况下出现(设备、操作系统、浏览器、用户操作),或者只是隐藏在项目中众多前端技术之一中?
我最近再次体会到UI bug的复杂性。我最近修复了一个有趣的bug,它影响了Safari浏览器中的一些SVG,没有任何明显的模式或原因。我搜索过任何类似的问题,希望能找到一些线索,但没有找到有用的结果。尽管有障碍,我还是设法解决了它。
我通过使用一些有用的调试策略来分析问题,这些策略我也将在本文中介绍。提交修复程序后,我想起了Chris之前发布的一条推文。
写下你希望在谷歌搜索时找到的那篇文章。
— Chris Coyier (@chriscoyier) 2017年10月30日
……我们来了。
问题描述
我在一个正在进行的项目中发现了以下bug。这是在一个上线的网站上。
我创建了一个CodePen示例来演示这个问题,以便您可以自己检查它。如果我们在Safari中打开示例,按钮在加载时可能看起来符合预期。但是,如果我们点击前两个较大的按钮,问题就会出现。
每当浏览器绘制事件发生时,较大按钮中的SVG渲染不正确。它只是被截断了。它可能在加载时随机发生。它甚至可能在调整屏幕大小时发生。无论情况如何,它都会发生!
以下是我解决这个问题的方法。
首先,让我们考虑一下环境
回顾项目的细节以了解环境和存在bug的条件总是一个好主意。
- 这个特定的项目使用React(但这篇文章不需要)。
- SVG作为React组件导入,并通过webpack内联到HTML中。
- SVG是从设计工具导出的,没有语法错误。
- SVG应用了一些来自样式表的CSS。
- 受影响的SVG位于一个HTML
<div> 元素内。 <li>问题只出现在Safari中(在13版本上被注意到)。</li> <h3 id="深入问题">深入问题</h3> <p>让我们来看看这个问题,看看我们能否对正在发生的事情做出一些假设。像这样的bug会变得很复杂,我们不会立即知道发生了什么。我们不必在第一次尝试时就100%正确,因为我们将逐步进行,形成我们可以测试的假设,以缩小可能的原因。</p> <h3 id="形成假设">形成假设</h3> <p>起初,这看起来像是一个CSS问题。某些样式可能会在悬停事件中应用,从而破坏SVG图形的布局或溢出属性。它也看起来像是问题随机发生,每当Safari渲染页面时(调整屏幕大小、悬停、点击等时的绘制事件)。</p> <p>让我们从简单而最明显的途径开始,并假设CSS是问题的根源。我们可以考虑Safari浏览器中存在一个bug的可能性,该bug会导致在某些特定样式应用于SVG元素(例如flex布局)时SVG渲染不正确。</p> <p>通过这样做,我们<strong>形成了一个假设</strong>。我们的下一步是设置一个测试,该测试要么证实要么反驳该假设。每个测试结果都会产生关于bug的新事实,并有助于形成进一步的假设。</p> <h3 id="问题简化">问题简化</h3> <p>我们将使用一种称为<strong>问题简化</strong>的调试策略来尝试查明问题。康奈尔大学的CS讲座将此策略描述为“一种逐渐消除与bug无关的代码部分的方法”。</p> <p>通过假设问题在于CSS,我们可以最终查明问题或从等式中消除CSS,从而减少可能原因的数量和问题的复杂性。</p> <p>让我们尝试确认我们的假设。如果我们暂时排除所有非浏览器样式表,则问题不应发生。我在我的源代码中通过注释掉项目中的以下代码行来做到这一点。</p> <pre class="brush:php;toolbar:false"><code>import 'css/app.css';</code></pre> <p>我创建了一个方便的CodePen示例来演示这些不包含CSS的元素。在React中,我们正在将SVG图形作为组件导入,并且它们使用webpack内联到HTML中。</p> <p>如果我们在Safari中打开这个pen并点击按钮,我们仍然会遇到这个问题。它仍然在页面加载时发生,但在CodePen上,我们必须通过点击按钮来强制它。我们可以得出结论,CSS不是罪魁祸首,但我们也可以看到只有五个按钮中的两个在这种情况下面临问题。让我们记住这一点,然后继续下一个假设。</p> <h3 id="隔离问题">隔离问题</h3> <p>我们的下一个假设指出,Safari在HTML <code><div> 元素内渲染SVG时存在bug。由于问题发生在前两个按钮上,我们将<strong>隔离第一个按钮</strong>,看看会发生什么。 <p>Sarah Drasner解释了隔离的重要性,如果您想了解有关调试工具和其他方法的更多信息,我强烈建议您阅读她的文章。</p> <blockquote> <p>隔离可能是所有调试中最强的核心原则。我们的代码库可能庞大,包含不同的库、框架,并且可能包含许多贡献者,甚至包括不再参与该项目的人员。隔离问题有助于我们慢慢剔除问题中不必要的部件,以便我们可以单独关注解决方案。</p> </blockquote> <p>它也经常被称为“简化的测试用例”。</p> <p>我将此按钮移动到一个单独的空测试路由(空白页面)。我创建了以下CodePen来演示该状态。即使我们已经得出结论,CSS不是问题的原因,我们也应该在找出bug的真正原因之前将其排除在外,以尽可能简化问题。</p> <p>如果我们在Safari中打开这个pen,我们可以看到我们<strong>无法再重现这个问题</strong>,并且<strong>SVG图形在点击按钮后按预期显示</strong>。<strong>我们不应该将此更改视为可接受的bug修复</strong>,但它为创建最小的可重现示例提供了一个良好的起点。</p> <h3 id="最小的可重现示例">最小的可重现示例</h3> <p>前两个示例之间的主要区别在于按钮组合。在尝试了所有可能的组合之后,我们可以得出结论,此问题仅在对同一页面上较小的SVG图形旁边的较大SVG图形发生绘制事件时才会发生。</p> <p>我们创建了一个<strong>最小的可重现示例</strong>,允许我们在没有任何不必要元素的情况下重现bug。使用最小的可重现示例,我们可以更详细地研究问题并准确地查明导致问题的代码部分。</p> <p>我创建了以下CodePen来演示最小的可重现示例。</p> <p>如果我们在Safari中打开此演示并点击按钮,我们可以看到问题正在发生,并形成一个假设,即这两个SVG以某种方式相互冲突。如果我们将第二个SVG图形叠加在第一个图形上,我们可以看到第一个SVG图形上被裁剪的圆圈的大小与较小的SVG图形的精确尺寸相匹配。</p> <h3 id="分而治之">分而治之</h3> <p>我们将问题缩小到两个SVG图形的组合。现在我们将把问题缩小到导致问题的特定SVG代码。如果我们只对SVG代码有基本的了解,并且想要查明问题,我们可以使用<strong>二叉树搜索</strong>策略,并采用分而治之的方法。康奈尔大学的CS讲座描述了这种方法:</p> <blockquote> <p>例如,从一大段代码开始,在代码中间放置一个检查点。如果错误没有出现在该点,则表示错误发生在后半部分;否则,它就在前半部分。</p> </blockquote> <p>在SVG中,我们可以尝试从第一个SVG中删除<code><filter></filter>
(以及<defs></defs>
,因为它无论如何都是空的)。让我们首先检查<filter></filter>
的作用。Sara Soueidan的这篇文章最好地解释了它。与SVG中的线性渐变、蒙版、图案和其他图形效果一样,滤镜有一个方便命名的专用元素:
<filter></filter>
元素。<filter></filter>
元素永远不会直接渲染;它唯一的用途是可以使用SVG中的filter
属性或CSS中的url()
函数来引用它。在我们的SVG中,
<filter></filter>
在SVG图形底部应用了一个轻微的内阴影。在我们将其从第一个SVG图形中删除后,我们预计内阴影将消失。如果问题仍然存在,我们可以得出结论,SVG标记的其余部分存在问题。如果我们从<g filter="url(#filter0_ii)"></g>
中删除剩余的id,则阴影将完全移除。到底是怎么回事?让我们再看看前面提到的
<filter></filter>
属性的定义,并注意以下细节:<filter></filter>
元素永远不会直接渲染;它唯一的用途是可以被引用,使用SVG中的filter
属性。(我的强调)
因此,我们可以得出结论,第二个SVG图形的滤镜定义被应用于第一个SVG图形,并导致错误。
修复问题
我们现在知道问题与
<filter></filter>
属性有关。我们也知道这两个SVG都有filter
属性,因为它们使用它来创建圆形上的内阴影。让我们比较这两个SVG之间的代码,看看我们能否解释和修复这个问题。我已经简化了两个SVG图形的代码,以便我们可以清楚地看到发生了什么。以下代码段显示了第一个SVG的代码。
<code><svg height="46" viewbox="0 0 46 46" width="46"><g filter="url(#filter0_ii)"></g><defs><filter height="46" width="46" x="0" y="0"></filter></defs></svg></code>
以下代码段显示了第二个SVG图形的代码。
<code><svg height="28" viewbox="0 0 28 28" width="28"><g filter="url(#filter0_ii)"></g><defs><filter height="28" width="28" x="0" y="0"></filter></defs></svg></code>
我们可以注意到,生成的SVG使用相同的id属性
id=filter0_ii
。Safari应用了它最后读取的滤镜定义(在本例中是第二个SVG标记),并导致第一个SVG被裁剪到第二个滤镜的大小(从46px到28px)。id属性在DOM中应该具有唯一值。通过在一个页面上拥有两个或多个id属性,浏览器无法理解要应用哪个引用,并且滤镜属性在每次绘制事件中重新定义,这取决于导致问题随机出现的竞争条件。让我们尝试为每个SVG图形分配唯一的id属性值,看看是否能解决这个问题。
如果我们在Safari中打开CodePen示例并点击按钮,我们可以看到通过为每个SVG图形文件中
<filter></filter>
属性分配唯一的ID,我们修复了这个问题。如果我们考虑这样一个事实:我们为像id这样的属性具有非唯一值,这意味着此问题应该存在于所有浏览器上。出于某种原因,其他浏览器(包括Chrome和Firefox)似乎在没有任何bug的情况下处理了这个边缘情况,尽管这可能只是一个巧合。总结
这是一次相当长的旅程!我们从几乎对一个看似随机发生的问题一无所知,到完全理解并修复它。如果问题的根本原因不明确或复杂,调试UI和理解视觉bug可能很困难。幸运的是,一些有用的调试策略可以帮助我们。
首先,我们通过形成假设来简化问题,这帮助我们消除了与问题无关的组件(样式、标记、动态事件等)。之后,我们隔离了标记,并找到了最小的可重现示例,这使我们能够专注于单个代码块。最后,我们使用分而治之的策略查明了问题,并修复了它。
感谢您抽出时间阅读这篇文章。在我离开之前,我想给您留下最后一种调试策略,它也在康奈尔大学的CS讲座中有所介绍。
记住在调试尝试之间休息一下,放松并清理你的思绪。
如果在bug上花费了太多时间,程序员就会感到疲倦,调试可能会适得其反。休息一下,清理你的思绪;休息之后,尝试从不同的角度思考这个问题。
参考
- 康奈尔大学CS 312讲座26 – 调试技术
- 调试技巧和窍门
- 简化的测试用例
- SVG滤镜101
以上是这是我如何使用经过久经考验的调试策略来解决一个怪异的错误的详细内容。更多信息请关注PHP中文网其他相关文章!

React生态系统为我们提供了许多库,所有库都集中在拖放的相互作用上。我们有反应,反应,可爱dnd,

我可以说我经常使用背景折叠。 IT Wager IT几乎从未在日常CSS工作中使用。但是在斯特凡·朱迪斯(Stefan Judis)的帖子中,我想起了它,

使用RequestAnimationFrame进行动画化应该很容易,但是如果您还没有彻底阅读React的文档,那么您可能会遇到一些事情

听着,我不是GraphQL专家,但我确实喜欢与之合作。作为前端开发人员,它向我曝光数据的方式非常酷。它就像一个菜单


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

MinGW - 适用于 Windows 的极简 GNU
这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

EditPlus 中文破解版
体积小,语法高亮,不支持代码提示功能

SublimeText3汉化版
中文版,非常好用

SublimeText3 Linux新版
SublimeText3 Linux最新版

禅工作室 13.0.1
功能强大的PHP集成开发环境