JS生态系统加速探索Draft-js emoji插件功能及使用探索

 更新时间:2024年01月21日 11:24:39   作者:大家的林语冰 人猫神话  
这篇文章主要介绍了JS生态系统加速探索Draft-js emoji插件功能使用探究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

长话短说:从 42kB 的重字符串中,从零开始构建了 7138 次正则表达式。缓存该计算大大加快了 Draft-js 表情符号插件的初始化阶段。

本期《前端翻译计划》共享的是“加速 JS 生态系统系列博客”,包括但不限于:

  • PostCSS,SVGO 等等
  • 模块解析
  • 使用 eslint
  • npm 脚本
  • draft-js emoji 插件
  • polyfill 暴走
  • 桶装文件崩溃
  • Tailwind CSS

本期共享的是第 5 篇博客 —— Draft-js emoji 插件。

Draft-js emoji 插件

我通过 Josh Goldberg 的电邮收到了一个非常有趣的问题,涉及一个网站卡死了大约 2-3 秒。该网站使用 Draft-js 富文本编辑器进行一些输入,它能够将范围缩小到 Draft-js 表情符号插件中出现的问题。

通过 Chrome 的分析器捕获的快速记录证实了最初的怀疑。表情符号插件出 bug 了。我们可以看到,底部有一大坨频繁的函数调用,占用了大部分时间。不幸的是,Chrome 的分析器没有像 speedscope 那样的某种“left-heavy”可视化功能。这使得确定哪个函数值得研究变得有点困难。“left-heavy”对此更好,因为它将类似的调用堆栈合并为一个。

每个单独的调用看起来都棒棒哒,并且总是以调用正则表达式引擎结束。但一大坨调用已经足以引发忧虑。Chrome 分析器一个很酷的事情是,它可以使用采样的跟踪来注释源码行。这将为您提供每行执行所需的大概时间。由于前端项目涉及大量转译,它并不是 100% 准确,但它足以得出一些早期结论。

消耗大部分时间的两种方法都处理正则表达式。大家可能很容易得出结论,正则表达式本身是罪魁祸首,但我有一种感觉,这只是深层问题的表症。我们首先检查的是此函数被调用的频率。这可以通过增加一个简单的计数器,或直接使用 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 方法就会被调用 7318 次。接下来,我们检查了 escapeRegExp 被调用的参数类型。理论上,也许它会被相同的参数反复调用。

一分钟后,这个假设被实锤了,因为它反复转义同一个字符串。我们知道此方法是从 replaceAll 调用的,所以让我们检查一下是否总是在那里传递相同的参数。果然,第一个 string 参数收到两个不同的值,但第二个 find 参数始终相同。这就是后来传递给 escapeRegExp 的那个。

图穷匕见的问题是:“谁在调用 replaceAll,为什么它们总是传递相同的参数?”

再次放大配置文件,我们观察到对 replaceAll 的所有调用都将 toShort 作为共同祖先。

这里发生了一些非常有趣的事情,传递给 replaceAll 的第二个参数,根本不依赖传递给 toShort 的参数。沿着 unicodeCharRegex 的线索,我们可以更清楚地了解此处代码的用途。与 Slack 等流行聊天 App 一样,draft-js 插件允许您输入文本 :smile:,然后该文本会自动转换为正确的表情符号“😀”。但反过来也是必要的,这就是我们在这里看到的。

寻找解决方案

进一步了解此代码的用途后,我们注意到,正则表达式是通过迭代 unicode 字符及其元数据的大数据结构构建的。然后将此正则表达式应用于传入的文本,匹配表情符号并将其替换为短代码。每次调用 toShort 时,都会从零开始构建正则表达式。这成为了一个性能问题,因为 unicode 标准有无数的表情符号,并且随着肤色和其他东西变化。是的没错,难怪生成的正则表达式很大。

Chrome 的控制台显示,构建正则表达式的生成字符串比单独的 42kB 更大。

与大多数昂贵计算的情况一样,我们可以通过缓存先前的结果来避免大量工作。这样我们就不需要反复重新计算同样的事情。这是侵入性最小的修复,不需要对插件的架构大更改。我们做了一个 PR,它总共将阻塞时间从 2-3s 减少到 <200ms。

完结撒花

如果我们退后一步,每当该模块首先加载时,总是构建该正则表达式似乎是浪费。结果总是相同的,因此优化是存储转换后的结果,并在插件中使用它。每当发布新版本的插件时都可以构建它。

另一个潜在的想法是,使用手工函数来匹配表情符号。它可以让您非常快速地缩小潜在匹配项的搜索空间,但仍然需要验证此方案在该情况下是否比正则表达式更快。我认为值得一试。

一个更好的想法是,我们可以利用最近的 unicode 增强功能,而不是构建 >40kB 的正则表达式。这包括特殊的 unicode 属性转义,比如 Emoji_Presentation,它允许您直接匹配所有表情符号(比如:/\p{Emoji_Presentation}/gu)。这样我们就可以完全摆脱所有正则表达式生成代码。

总的来说,这个特定问题很好地提醒我们时不时地分析我们的代码。这提醒我们,即使是看似无害的函数,也会对性能产生巨大影响。

免责声明

本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考,英文原味版请传送 Speeding up the JavaScript ecosystem - draft-js emoji plugin

以上就是JS生态系统加速探索Draft-js emoji插件功能及使用探索的详细内容,更多关于JS Draft-js emoji插件的资料请关注脚本之家其它相关文章!

相关文章

最新评论