Slack使用React重写了Web客户端。在这篇文章中,他们以重写Emoji选择器为例,展示了React在性能和代码可维护性上给他们带来的巨大好处,以及给用户带来的体验升级。查看英文原文: Rebuilding Slack’s Emoji Picker in React。
Slack正在将Web客户端迁移到React。在最开始,我们的前端使用了jQuery和Handlebars。后来,社区开发出更好的方案用于创建可伸缩的、基于数据驱动的用户界面。jQuery的“渲染后修改”模式直截了当,但无法与底层的模型保持同步。不同的是,React的“渲染后再渲染”模式可以保证渲染和模型的一致性。Slack也紧跟业界的步伐,不断改进前端的性能和可靠性。
我们认为,要引入React,最好的办法就是先用React重写现有产品里的某个特性——这样我们就可以比较出新的开发流程和结果与原先的有什么不同。我们需要重写一个组件,这个组件必须具备交互性,能够自包含,并能够体现React在性能方面的优势。我们很快就找到了一个绝佳的组件——被重度使用且极度复杂的Emoji选择器。
Virtual DOM的优势
阅读这篇文章要求对React有一定的了解,如果你不熟悉React,建议先阅读一下React的官方文档。简单地说,React是一个JavaScript代码库,可以用它方便地开发声明式的、基于数据驱动的用户界面。它的API很简单,主要由一个组件类组成,这个类包含了一些生命周期方法。组件本身不会生成HTML,相反,它们会生成类似DOM的树,叫作Virtual DOM。React会比较两个Virtual DOM,并使用最少的操作将其中的一棵树转换成另一棵树。例如,你可以告诉React基于新的模型数据重新渲染整个视图,它就会以最快的速度帮你更新文本节点,就好像它有一个精灵军团在帮你完成DOM的更新操作一样。
React擅长于将组件在单个模板上的各种行为合并在一起。举个例子,假设一个Slack频道变为未读状态时,你通过JavaScript来更新频道的边栏:
找出这个频道的ID
在DOM里查找这个频道对应的节点
将节点状态切换成未读(应用CSS类)
这个过程很简单,不过你还得为其他事件编写不同的处理逻辑,比如“create”、“join”、“leave”和“rename”。相反,React把这5中情况合并在一起统一处理:
使用新的模型数据重新渲染频道边栏。
我们不需要为每一种DOM操作编写代码,而是重新渲染整个组件,React会为我们完成这个过程。React通过让代码变得更通用(一刀切的模板)来简化开发。
Emoji选择器
Emoji是Slack UI的一个组成部分,是最理想的React组件。它动态、离散,只需要少量的输入——一组emoji、默认皮肤和用户的emoji使用历史。刚好现有的Emoji选择器需要进行性能调优,因为现在不管emoji会不会出现在视图里都需要进行渲染。在查找emoji时需要切换每个emoji的可见性,在重度使用时性能很成问题。新的Slack团队准备了1374个默认emoji,这还不包括自定义emoji(在写这篇文章的时候,Slack团队总共有3126个emoji,有些团队甚至更多)。重写Emoji选择器将会对Slack的日常使用产生重大影响。
我们选择在Storybook里开发新的组件,Storybook自称是一个“会让你喜欢上它的UI开发环境”。它不要求你改变开发方式,但会让开发、测试和代码审查变得更有趣。你可以在Storybook里通过指定不同的属性来定义不同版本的组件。我们为Emoji选择器增加了一个新皮肤和几种emoji查找方式。
组件布局
React Emoji选择器的根组件是有状态的,而子组件则是无状态的。我们按照惯例把每个组件导出到单独的文件里。结构如下所示:
Header
分类选项卡:列出了emoji的类别,每个类别都有一个“jump to”链接。
搜索框:通过emoji的名称或别名过滤emoji。
Body
固定的头部:显示当前类别选项卡的名称。
emoji列表:所有类别的emoji虚拟列表。
Footer
emoji预览:当前选择的emoji大图预览。
皮肤选择器:显示当前的皮肤,并可以切换到其他皮肤。