JS生態(tài)系統(tǒng)加速探索Draft-js?emoji插件功能及使用探索
引言
長話短說:從 42kB 的重字符串中,從零開始構(gòu)建了 7138 次正則表達式。緩存該計算大大加快了 Draft-js 表情符號插件的初始化階段。
本期《前端翻譯計劃》共享的是“加速 JS 生態(tài)系統(tǒng)系列博客”,包括但不限于:
- PostCSS,SVGO 等等
- 模塊解析
- 使用 eslint
- npm 腳本
- draft-js emoji 插件
- polyfill 暴走
- 桶裝文件崩潰
- Tailwind CSS
本期共享的是第 5 篇博客 —— Draft-js emoji 插件。
Draft-js emoji 插件
我通過 Josh Goldberg 的電郵收到了一個非常有趣的問題,涉及一個網(wǎng)站卡死了大約 2-3 秒。該網(wǎng)站使用 Draft-js 富文本編輯器進行一些輸入,它能夠?qū)⒎秶s小到 Draft-js 表情符號插件中出現(xiàn)的問題。
通過 Chrome 的分析器捕獲的快速記錄證實了最初的懷疑。表情符號插件出 bug 了。我們可以看到,底部有一大坨頻繁的函數(shù)調(diào)用,占用了大部分時間。不幸的是,Chrome 的分析器沒有像 speedscope 那樣的某種“left-heavy”可視化功能。這使得確定哪個函數(shù)值得研究變得有點困難。“left-heavy”對此更好,因為它將類似的調(diào)用堆棧合并為一個。
每個單獨的調(diào)用看起來都棒棒噠,并且總是以調(diào)用正則表達式引擎結(jié)束。但一大坨調(diào)用已經(jīng)足以引發(fā)憂慮。Chrome 分析器一個很酷的事情是,它可以使用采樣的跟蹤來注釋源碼行。這將為您提供每行執(zhí)行所需的大概時間。由于前端項目涉及大量轉(zhuǎn)譯,它并不是 100% 準確,但它足以得出一些早期結(jié)論。
消耗大部分時間的兩種方法都處理正則表達式。大家可能很容易得出結(jié)論,正則表達式本身是罪魁禍首,但我有一種感覺,這只是深層問題的表癥。我們首先檢查的是此函數(shù)被調(diào)用的頻率。這可以通過增加一個簡單的計數(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"); // ... };
事實證明,每當頁面加載時,replaceAll
方法就會被調(diào)用 7318 次。接下來,我們檢查了 escapeRegExp
被調(diào)用的參數(shù)類型。理論上,也許它會被相同的參數(shù)反復(fù)調(diào)用。
一分鐘后,這個假設(shè)被實錘了,因為它反復(fù)轉(zhuǎn)義同一個字符串。我們知道此方法是從 replaceAll
調(diào)用的,所以讓我們檢查一下是否總是在那里傳遞相同的參數(shù)。果然,第一個 string
參數(shù)收到兩個不同的值,但第二個 find
參數(shù)始終相同。這就是后來傳遞給 escapeRegExp
的那個。
圖窮匕見的問題是:“誰在調(diào)用 replaceAll
,為什么它們總是傳遞相同的參數(shù)?”
再次放大配置文件,我們觀察到對 replaceAll
的所有調(diào)用都將 toShort
作為共同祖先。
這里發(fā)生了一些非常有趣的事情,傳遞給 replaceAll
的第二個參數(shù),根本不依賴傳遞給 toShort
的參數(shù)。沿著 unicodeCharRegex
的線索,我們可以更清楚地了解此處代碼的用途。與 Slack 等流行聊天 App 一樣,draft-js 插件允許您輸入文本 :smile:
,然后該文本會自動轉(zhuǎn)換為正確的表情符號“??”。但反過來也是必要的,這就是我們在這里看到的。
尋找解決方案
進一步了解此代碼的用途后,我們注意到,正則表達式是通過迭代 unicode
字符及其元數(shù)據(jù)的大數(shù)據(jù)結(jié)構(gòu)構(gòu)建的。然后將此正則表達式應(yīng)用于傳入的文本,匹配表情符號并將其替換為短代碼。每次調(diào)用 toShort
時,都會從零開始構(gòu)建正則表達式。這成為了一個性能問題,因為 unicode
標準有無數(shù)的表情符號,并且隨著膚色和其他東西變化。是的沒錯,難怪生成的正則表達式很大。
Chrome 的控制臺顯示,構(gòu)建正則表達式的生成字符串比單獨的 42kB
更大。
與大多數(shù)昂貴計算的情況一樣,我們可以通過緩存先前的結(jié)果來避免大量工作。這樣我們就不需要反復(fù)重新計算同樣的事情。這是侵入性最小的修復(fù),不需要對插件的架構(gòu)大更改。我們做了一個 PR,它總共將阻塞時間從 2-3s 減少到 <200ms。
完結(jié)撒花
如果我們退后一步,每當該模塊首先加載時,總是構(gòu)建該正則表達式似乎是浪費。結(jié)果總是相同的,因此優(yōu)化是存儲轉(zhuǎn)換后的結(jié)果,并在插件中使用它。每當發(fā)布新版本的插件時都可以構(gòu)建它。
另一個潛在的想法是,使用手工函數(shù)來匹配表情符號。它可以讓您非??焖俚乜s小潛在匹配項的搜索空間,但仍然需要驗證此方案在該情況下是否比正則表達式更快。我認為值得一試。
一個更好的想法是,我們可以利用最近的 unicode
增強功能,而不是構(gòu)建 >40kB 的正則表達式。這包括特殊的 unicode
屬性轉(zhuǎn)義,比如 Emoji_Presentation
,它允許您直接匹配所有表情符號(比如:/\p{Emoji_Presentation}/gu
)。這樣我們就可以完全擺脫所有正則表達式生成代碼。
總的來說,這個特定問題很好地提醒我們時不時地分析我們的代碼。這提醒我們,即使是看似無害的函數(shù),也會對性能產(chǎn)生巨大影響。
免責聲明
本文屬于是語冰的直男翻譯了屬于是,略有刪改,僅供粉絲參考,英文原味版請傳送 Speeding up the JavaScript ecosystem - draft-js emoji plugin
以上就是JS生態(tài)系統(tǒng)加速探索Draft-js emoji插件功能及使用探索的詳細內(nèi)容,更多關(guān)于JS Draft-js emoji插件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
javascript學習隨筆(編寫瀏覽器腳本 Navigator Scripting )
javascript學習隨筆(編寫瀏覽器腳本 Navigator Scripting )...2007-03-03JavaScript 中 JSON.parse 函數(shù) 和 JSON.stringify 函數(shù)
這篇文章主要介紹了JavaScript -- JSON.parse 函數(shù) 和 JSON.stringify 函數(shù),本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2018-12-12前端發(fā)布緩存導(dǎo)致白屏幾種解決方案總結(jié)
這篇文章主要介紹了前端發(fā)布緩存導(dǎo)致白屏的幾種解決方案,文章還介紹了Jenkins發(fā)布流程優(yōu)化和使用版本號方案,以減少發(fā)布緩存問題,每種方案都有其優(yōu)缺點,需要根據(jù)實際情況進行選擇和調(diào)整,需要的朋友可以參考下2025-04-04微信小程序?qū)崿F(xiàn)點擊按鈕修改字體顏色功能【附demo源碼下載】
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)點擊按鈕修改字體顏色功能,涉及微信小程序wx:for循環(huán)讀取data數(shù)值及事件綁定修改元素屬性相關(guān)操作技巧,需要的朋友可以參考下2017-12-12基于VSCode調(diào)試網(wǎng)頁JavaScript代碼過程詳解
這篇文章主要介紹了基于VSCode調(diào)試網(wǎng)頁JavaScript代碼過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-07-07