JS生態(tài)系統(tǒng)加速探索Draft-js?emoji插件功能及使用探索
引言
長(zhǎng)話短說(shuō):從 42kB 的重字符串中,從零開(kāi)始構(gòu)建了 7138 次正則表達(dá)式。緩存該計(jì)算大大加快了 Draft-js 表情符號(hào)插件的初始化階段。
本期《前端翻譯計(jì)劃》共享的是“加速 JS 生態(tài)系統(tǒng)系列博客”,包括但不限于:
- PostCSS,SVGO 等等
- 模塊解析
- 使用 eslint
- npm 腳本
- draft-js emoji 插件
- polyfill 暴走
- 桶裝文件崩潰
- Tailwind CSS
本期共享的是第 5 篇博客 —— Draft-js emoji 插件。
Draft-js emoji 插件
我通過(guò) Josh Goldberg 的電郵收到了一個(gè)非常有趣的問(wèn)題,涉及一個(gè)網(wǎng)站卡死了大約 2-3 秒。該網(wǎng)站使用 Draft-js 富文本編輯器進(jìn)行一些輸入,它能夠?qū)⒎秶s小到 Draft-js 表情符號(hào)插件中出現(xiàn)的問(wèn)題。
通過(guò) Chrome 的分析器捕獲的快速記錄證實(shí)了最初的懷疑。表情符號(hào)插件出 bug 了。我們可以看到,底部有一大坨頻繁的函數(shù)調(diào)用,占用了大部分時(shí)間。不幸的是,Chrome 的分析器沒(méi)有像 speedscope 那樣的某種“left-heavy”可視化功能。這使得確定哪個(gè)函數(shù)值得研究變得有點(diǎn)困難。“left-heavy”對(duì)此更好,因?yàn)樗鼘㈩愃频恼{(diào)用堆棧合并為一個(gè)。

每個(gè)單獨(dú)的調(diào)用看起來(lái)都棒棒噠,并且總是以調(diào)用正則表達(dá)式引擎結(jié)束。但一大坨調(diào)用已經(jīng)足以引發(fā)憂慮。Chrome 分析器一個(gè)很酷的事情是,它可以使用采樣的跟蹤來(lái)注釋源碼行。這將為您提供每行執(zhí)行所需的大概時(shí)間。由于前端項(xiàng)目涉及大量轉(zhuǎn)譯,它并不是 100% 準(zhǔn)確,但它足以得出一些早期結(jié)論。

消耗大部分時(shí)間的兩種方法都處理正則表達(dá)式。大家可能很容易得出結(jié)論,正則表達(dá)式本身是罪魁禍?zhǔn)?,但我有一種感覺(jué),這只是深層問(wèn)題的表癥。我們首先檢查的是此函數(shù)被調(diào)用的頻率。這可以通過(guò)增加一個(gè)簡(jiǎn)單的計(jì)數(shù)器,或直接使用 console.count()。
ns.escapeRegExp = function(string) {
+ console.count("escapeRegExp");
return string.replace(/[-[\]{}()*+?.,;:&\\^$#\s]/g, "\\$&");
};
ns.replaceAll = function(string, find) {
+ console.count("replaceAll");
var escapedFind = ns.escapeRegExp(find);
var search = new RegExp("<object[^>]*>.*?<\/object>|<span[^>]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|("+escapedFind+")", "gi");
// ...
};
事實(shí)證明,每當(dāng)頁(yè)面加載時(shí),replaceAll 方法就會(huì)被調(diào)用 7318 次。接下來(lái),我們檢查了 escapeRegExp 被調(diào)用的參數(shù)類型。理論上,也許它會(huì)被相同的參數(shù)反復(fù)調(diào)用。
一分鐘后,這個(gè)假設(shè)被實(shí)錘了,因?yàn)樗磸?fù)轉(zhuǎn)義同一個(gè)字符串。我們知道此方法是從 replaceAll 調(diào)用的,所以讓我們檢查一下是否總是在那里傳遞相同的參數(shù)。果然,第一個(gè) string 參數(shù)收到兩個(gè)不同的值,但第二個(gè) find 參數(shù)始終相同。這就是后來(lái)傳遞給 escapeRegExp 的那個(gè)。
圖窮匕見(jiàn)的問(wèn)題是:“誰(shuí)在調(diào)用 replaceAll,為什么它們總是傳遞相同的參數(shù)?”

再次放大配置文件,我們觀察到對(duì) replaceAll 的所有調(diào)用都將 toShort 作為共同祖先。

這里發(fā)生了一些非常有趣的事情,傳遞給 replaceAll 的第二個(gè)參數(shù),根本不依賴傳遞給 toShort 的參數(shù)。沿著 unicodeCharRegex 的線索,我們可以更清楚地了解此處代碼的用途。與 Slack 等流行聊天 App 一樣,draft-js 插件允許您輸入文本 :smile:,然后該文本會(huì)自動(dòng)轉(zhuǎn)換為正確的表情符號(hào)“??”。但反過(guò)來(lái)也是必要的,這就是我們?cè)谶@里看到的。
尋找解決方案
進(jìn)一步了解此代碼的用途后,我們注意到,正則表達(dá)式是通過(guò)迭代 unicode 字符及其元數(shù)據(jù)的大數(shù)據(jù)結(jié)構(gòu)構(gòu)建的。然后將此正則表達(dá)式應(yīng)用于傳入的文本,匹配表情符號(hào)并將其替換為短代碼。每次調(diào)用 toShort 時(shí),都會(huì)從零開(kāi)始構(gòu)建正則表達(dá)式。這成為了一個(gè)性能問(wèn)題,因?yàn)?nbsp;unicode 標(biāo)準(zhǔn)有無(wú)數(shù)的表情符號(hào),并且隨著膚色和其他東西變化。是的沒(méi)錯(cuò),難怪生成的正則表達(dá)式很大。

Chrome 的控制臺(tái)顯示,構(gòu)建正則表達(dá)式的生成字符串比單獨(dú)的 42kB 更大。
與大多數(shù)昂貴計(jì)算的情況一樣,我們可以通過(guò)緩存先前的結(jié)果來(lái)避免大量工作。這樣我們就不需要反復(fù)重新計(jì)算同樣的事情。這是侵入性最小的修復(fù),不需要對(duì)插件的架構(gòu)大更改。我們做了一個(gè) PR,它總共將阻塞時(shí)間從 2-3s 減少到 <200ms。
完結(jié)撒花
如果我們退后一步,每當(dāng)該模塊首先加載時(shí),總是構(gòu)建該正則表達(dá)式似乎是浪費(fèi)。結(jié)果總是相同的,因此優(yōu)化是存儲(chǔ)轉(zhuǎn)換后的結(jié)果,并在插件中使用它。每當(dāng)發(fā)布新版本的插件時(shí)都可以構(gòu)建它。
另一個(gè)潛在的想法是,使用手工函數(shù)來(lái)匹配表情符號(hào)。它可以讓您非常快速地縮小潛在匹配項(xiàng)的搜索空間,但仍然需要驗(yàn)證此方案在該情況下是否比正則表達(dá)式更快。我認(rèn)為值得一試。
一個(gè)更好的想法是,我們可以利用最近的 unicode 增強(qiáng)功能,而不是構(gòu)建 >40kB 的正則表達(dá)式。這包括特殊的 unicode 屬性轉(zhuǎn)義,比如 Emoji_Presentation,它允許您直接匹配所有表情符號(hào)(比如:/\p{Emoji_Presentation}/gu)。這樣我們就可以完全擺脫所有正則表達(dá)式生成代碼。
總的來(lái)說(shuō),這個(gè)特定問(wèn)題很好地提醒我們時(shí)不時(shí)地分析我們的代碼。這提醒我們,即使是看似無(wú)害的函數(shù),也會(huì)對(duì)性能產(chǎn)生巨大影響。
免責(zé)聲明
本文屬于是語(yǔ)冰的直男翻譯了屬于是,略有刪改,僅供粉絲參考,英文原味版請(qǐng)傳送 Speeding up the JavaScript ecosystem - draft-js emoji plugin
以上就是JS生態(tài)系統(tǒng)加速探索Draft-js emoji插件功能及使用探索的詳細(xì)內(nèi)容,更多關(guān)于JS Draft-js emoji插件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
阻止事件(取消瀏覽器對(duì)事件的默認(rèn)行為并阻止其傳播)
取消瀏覽器對(duì)事件的默認(rèn)行為(響應(yīng))(比如a標(biāo)簽的跳轉(zhuǎn)等)并停止事件的繼續(xù)傳播,下面有一個(gè)不錯(cuò)的示例大家可以感受下2013-11-11
JavaScript避免代碼的重復(fù)執(zhí)行經(jīng)驗(yàn)技巧分享
經(jīng)常會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題,那就是重復(fù)的代碼執(zhí)行,下面就是一些在查看它們的源代碼時(shí)發(fā)現(xiàn)一些問(wèn)題,把這些分享給大家,希望能讓你們更加簡(jiǎn)潔高效的寫出JavaScript代碼2014-04-04
XMLHttpRequest對(duì)象_Ajax異步請(qǐng)求重點(diǎn)(推薦)
下面小編就為大家?guī)?lái)一篇XMLHttpRequest對(duì)象_Ajax異步請(qǐng)求重點(diǎn)(推薦)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09
JavaScript中while循環(huán)的基礎(chǔ)使用教程
這篇文章主要給大家介紹了關(guān)于JavaScript中while循環(huán)的基礎(chǔ)使用教程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用JavaScript具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
原生ajax處理json格式數(shù)據(jù)的實(shí)例代碼
這篇文章主要介紹了原生ajax處理json格式數(shù)據(jù)的實(shí)例代碼,需要的朋友可以參考下2016-12-12

