一個開源組件 bug 引發的分析

来源:https://www.cnblogs.com/sujinqu/archive/2019/11/06/11809353.html
-Advertisement-
Play Games

這是一個悲傷的故事。某日清晨,距離版本轉測還剩一天,切圖仔的我正按照計劃有條不紊的畫頁面。當我點擊一個下拉彈框組件中分頁組件頁數過多而出現的向後 5 頁省略號時,悲劇開始了,彈框被收回了。情景再現 問題 問題的表象很簡單,使用的是組件庫的下拉彈窗組件,在組件中使用到了分頁組件,當點擊分頁組件的向後 ...


這是一個悲傷的故事。某日清晨,距離版本轉測還剩一天,切圖仔的我正按照計劃有條不紊的畫頁面。當我點擊一個下拉彈框組件中分頁組件頁數過多而出現的向後 5 頁省略號時,悲劇開始了,彈框被收回了。情景再現
file

問題

問題的表象很簡單,使用的是組件庫的下拉彈窗組件,在組件中使用到了分頁組件,當點擊分頁組件的向後 5 頁快速跳轉時,彈窗被收回了。我們的預期是能夠繼續操作的,只有點擊彈框外部時,彈窗才會被收回。

分析

發現這個問題我做瞭如下分析:

  1. 確定這是一個問題
    再次重覆操作問題,確定問題出現的條件,能夠在特定條件下復現的問題才是問題。我穩定的復現了問題條件是:分頁組件出現向前跳轉 5 頁或向後跳轉 5 頁,點擊到不會再出現向前跳轉 5 頁或向後跳轉 5 頁這樣的快速跳轉後,彈框會被收回。

  2. 確定是自己組件配置使用問題還是組件本身 bug
    我在組件的文檔中並沒有看到有關這個問題的特別說明。接著我又在官方提供的代碼運行環境中,寫了一個使用的 demo(實際項目中的代碼複雜度較高,可能存在被其他樣式等因素影響而產生問題,使用功能單一的 demo 有助於我們快速確定問題範圍,且官方運行環境一般使用的是其最新的組件版本),發現與我在項目中使用存在同樣問題。到這裡可以排除是組件配置使用,組件庫版本不是最新,及項目環境影響導致的問題。

  3. 在組件庫的開源項目中查詢 issue
    在 issue 中搜索關鍵字查詢是否有相關 issue,如果運氣好找到相關問題,一般會有相關的討論,就算沒有具體的解決措施也能讓你明白問題大概在哪裡。我運氣比較差,沒有找到相關問題的 issue,所以只能自己提一個 issue 了。有時間有耐心等待維護人解決你提的 issue,問題分析到這裡就可以結束了。

  4. 查看組件源碼
    顯然我的時間很緊急,問題必須儘快解決,且我也想確定一下問題具體在哪裡,看自己能否解決。很快我找到的相關組件的源碼,經過定位發現,彈框下拉組件中使用了 v-click-outside 指令,用來觸發點擊指令之外的元素收起彈框。
    進一步深入源碼,發現 v-click-outside 組件的原理,是在 document 組件上註冊了一個 click 監聽事件,當有點擊事件發生,監聽函數會判斷,點擊事件發生的元素,是否包含在使用指令的元素中。如果是監聽函數返回,否則觸髮指令綁定的事件(在彈框組件中就是隱藏彈框)。代碼片段如下
    // el 表示指令綁定的元素,event為點擊事件
    const isClickOutside = event.target !== el && !el.contains(event.target)

    if (!isClickOutside) {
            return
    }

    if (middleware(event, el)) {
            handler(event, el)
    }
  1. 斷點調試
    經過上面的步驟過後,我還沒有發現問題的所在,所以只能打斷點進行代碼調試了。經過調試發現問題出在 el.contains(event.target) 這一段,分頁組件中的向前 5 頁元素,點擊觸發後就被 v-if 指令從頁面中移除了。所以運行到這一句時,el 中是不包含向前 5 頁元素的。也就是說這裡向前 5 頁元素被錯誤的判斷為不在 el 元素中,指令綁定的彈框收起函數被觸發,彈框被收回了。

解決問題

問題已經找出,現在問題是怎麼解決,因為問題是出在組件中,但是我們又不能直接修改組件庫代碼,直接修改項目依賴代碼,不利於代碼維護,也治標不治本。最好是能在項目代碼中添加代碼解決這個問題。

開始時走了一些彎路,想著用偽元素去覆蓋,被點擊的元素,試圖去混淆 e.target,讓 el.contains(event.target) 為true。這樣確實取得了一些成效,但是在頁數更多了以後依然有問題,而且我也不是很清楚這樣用偽元素遮擋可以讓事件觸發元素不是本來元素的原理是什麼。更重要的一點是,寫這篇文章的時候我完全回憶不出當時我為什麼會想到這個方案,可能是靈感吧,解決問題真的很需要靈感。

在使用了彎路解決辦法暫時解決問題後,我開始思考更徹底的解決方案。歸根結底問題是出在了 v-click-outside 指令上,如果我夠找到一種正確的判斷方式,讓被刪除的元素也可以被判斷為在指令註冊元素中,那麼問題將得到徹底解決。經過不斷的嘗試與思考,我發現將點擊事件註冊在捕獲階段觸發時,得到的指令綁定元素中依然有應該被刪除的點擊元素。到這裡問題基本就明朗了,徹底的解決方案也就出現了。

由於組件引入的 v-click-outside 指令是局部註冊在下拉彈框組件上的,所以我使用了 vue 的extends 繼承了組件的彈框下拉組件。在繼承的組件中重新註冊了指令 v-click-outside,該指令註冊在 document 上的點擊事件是捕獲階段觸發的。

由於是繼承覆蓋組件庫中的組件,所以組件庫升級不會帶來太大的影響,同時也不用重新寫一個組件,減少了工作量。到這裡問題就得到了徹底的解決。

輸出 v-click-out 包

解決完問題之後,我在 npm 上搜索了一些 click-outside 相關的包,發現這些包中註冊在 document 上的點擊事件,普遍是在冒泡階段觸發的,也就是說都存在文中我所遇到的問題。於是經過幾天業餘時間的努力,我開源了一個基於 vue 的 click-outside 指令開源項目catch-click-outside(不要臉求star)

一點思考

在之前的工作中,我也解決了不少問題,這次之所以會記錄解決過程,一個是因為這個問題我產生了一些輸出,可能對別人會有些微的幫助。另一個比較重要的原因是,在解決問題過程中走了一些彎路,也遇到了一些坎,但是這些問題都在不停的思考與事件中逐漸清晰,然後被解決,我覺得這個過程很值得記錄。文字功底有限,這個解決過程記錄的平淡無奇,謹以此作為備忘。

轉載請註明出處!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • MySQL DDL操作執行的三種方式 1,INPLACE,在進行DDL操作時,不影響表的讀&寫,可以正常執行表上的DML操作,避免與COPY方法相關的磁碟I/O和CPU周期,從而最小化資料庫的總體負載。 最小化負載有助於在DDL操作期間保持良好的性能和高吞吐量。 2,COPY,不允許併發執行過多個D ...
  • 完成共用參數的讀寫public class SharedPreference { private Context context; public SharedPreference(Context context) { // TODO Auto-generated constructor stub t... ...
  • 蘋果認可的標識符 Apple提供了各種API,以方便用戶識別各種用途: 通用標識符(UDID) 在iOS的早期,蘋果公司提供了一個uniqueIdentifier財產上UIDevice-親切地稱為udid (不要與UUID混淆)。雖然這樣的功能在今天看來是不可想象的,但該屬性一直存在到IOS 5,直 ...
  • https://developer.umeng.com/docs/66632/detail/66748#createappid ...
  • 版權聲明:本文為xing_star原創文章,轉載請註明出處! 本文同步自http://javaexception.com/archives/225 最近線上報錯,有個用戶連續crash了10次左右,查看了下堆棧信息,發現是提示com.android.camera.action.CROP這個Inten ...
  • 版權聲明:本文為xing_star原創文章,轉載請註明出處! 本文同步自http://javaexception.com/archives/224 禁用EditText 這個其實很簡單,最簡單的一種方式,代碼如下 那麼還有其他的方式麽,我想應該是有的,以下的幾個api我並沒有做驗證 參考資料: ht ...
  • 在一個Vue的PC項目中,要求給錯誤圖片不要讓它顯示醜陋的圖片,就要給圖片寫一個失敗後的預設圖片, 在這裡寫了兩種方法, 第一種方法,也就是百度到的最多的代碼,就是給一張圖片一個預設值。 第二種問題,如果頁面中有很多地方都存在這個東西,那麼在不同的頁面中寫很對是很麻煩的。 就使用到了VUe的指令。 ...
  • 初級菜鳥的作品,各位大佬見笑了 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...