今天遇到一個問題,在使用codemirror對兩條文字內容進行對比時,有同事反饋在它的電腦上會顯示成:前面一半是正常顯示差異內容,而後面就變成了全部是新增的。
像這樣:
預期的對比結果是這樣:
我們觀察用於對比的兩個文字,實際上上面的文字都是去掉後面括號中的內容,對比結果不應該表現成全部刪除全部新增。
於是我開始在本地嘗試復現,很不幸,有時候可以,有時候不行
接著我開始查詢codemirror使用的對比庫,diff-match-patch
,這個庫的對比方法的建構函式如下:
/**
* Class containing the diff, match and patch methods.
* @constructor
*/
var diff_match_patch = function() {
// Defaults.
// Redefine these in your program to override the defaults.
// Number of seconds to map a diff before giving up (0 for infinity).
this.Diff_Timeout = 0.5;
// Cost of an empty edit operation in terms of edit characters.
this.Diff_EditCost = 4;
// At what point is no match declared (0.0 = perfection, 1.0 = very loose).
this.Match_Threshold = 0.5;
// How far to search for a match (0 = exact location, 1000+ = broad match).
// A match this many characters away from the expected location will add
// 1.0 to the score (0.0 is a perfect match).
this.Match_Distance = 1000;
// When deleting a large block of text (over ~64 characters), how close do
// the contents have to be to match the expected contents. (0.0 = perfection,
// 1.0 = very loose). Note that Match_Threshold controls how closely the
// end points of a delete need to match.
this.Patch_DeleteThreshold = 0.5;
// Chunk size for context length.
this.Patch_Margin = 4;
// The number of bits in an int.
this.Match_MaxBits = 32;
};
看到這個bug,我首先懷疑是由於閾值,所以嘗試修改 Diff_Threshold
和 Match_Distance
嘗試將它們調小,看是否能夠復現。很不幸,也不行。
接著我又去查了 diff-match-patch
的文件
注意到了 Diff_Timeout
這個引數,文件中解釋了為什麼會有這個引數,主要是為了避免對比所耗費的時間,預設值是1s,超過1s為完成對比,剩餘部分就會以新增/刪除來返回。
引用一段文件中的解釋:
儘管此函式中使用了大量最佳化,但diff的計算可能需要一段時間。Diff_Timeout屬性可用於設定任何Diff的探索階段可能需要多少秒。預設值為1.0。值為0會禁用超時,並讓diff執行直到完成。如果diff超時,返回值仍然是一個有效的差值,儘管可能不是最佳值。
所以這個bug不一定所有人的裝置都能復現,即使是同一個的裝置,在不同的裝置狀況(cpu使用率、記憶體佔用)等情況下,也會有區別。在知道原因後,透過手動修改 Diff_Timeout
的值來嘗試復現bug就能成功了。
那麼如何解決呢,根據文件,我們可以設定一個較大的超時時間,來確保diff可以完成。或者設定為 0
這樣就會讓diff執行直到結束。實際程式碼可以透過對DiffMatchPatch
建構函式做一層包裹來實現:
window.diff_match_patch = function () {
const dmp = new DiffMatchPatch()
// https://github.com/google/diff-match-patch/wiki/API#diff_maintext1-text2--diffs
// 設定超時時間為0,禁用超時設定,直至diff執行結束
dmp.Diff_Timeout = 0
return dmp
};