還記得上次你遇到一個讓你抓耳撓腮幾個小時的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 - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

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

SublimeText3 Linux新版
SublimeText3 Linux最新版

禪工作室 13.0.1
強大的PHP整合開發環境