写点什么

Slack 使用 React 重写 Web 客户端

  • 2017-06-13
  • 本文字数:2996 字

    阅读完需:约 10 分钟

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 大图预览。
  • 皮肤选择器:显示当前的皮肤,并可以切换到其他皮肤。
  • 快捷动作(可选的):emoji 的子集,用于快速回复消息。

React 为编写无状态组件提供了两种方式:PureComponent 类和 function。function 更为简单一些,不过它们在每次合并时都会进行渲染,会影响性能。React 团队计划对 function 进行优化,不过目前最好还是避免使用它们。于是我们选择了 PureComponent,它预定以了 shouldComponentUpdate 方法,这个方法可以防止在遇到相同属性时进行更新操作。

React 是一个视图层,把它与自己开发的应用集成要比把它与标准的框架集成直截了当得多。我们不应该破坏 Emoji 选择器的封装性,这样才能很好地与 Slack 现有的模式集成在一起——我们希望这个组件就像是从一个端到端的 React 应用里拿出来的一样。为了保持选择器的纯净,我们在现有的模块系统里创建了一个轻量级的适配器。适配器挂载选择器组件,抽取模型数据,并监听来自外部的信号。采用这种模式,我们可以在开发新功能的同时逐步地迁移代码库。

新的开发流程

虽然使用 React 进行开发是一件很愉悦的事情,但将它集成到我们已有的开发流程里却不是那么一回事——至少在一开始不是那么令人愉快。在那个时候,Slack 使用的是自己开发的前端构建管道,没有所谓的导入、依赖或者复杂的转换(比如 transpilation)。我们决定采用 JSX 语法和 ES2015+,并使用 Babel 和 webpack 在本地构建 Emoji 选择器的资源。

我们预期签入本地编译的代码会很痛苦,但我们低估了接连发生的合并冲突和依赖管理问题是多么令人抓狂。最后,我们尝试将 webpack 集成到我们的开发和 staging 环境里,目标是无缝地替代已有的工作流。为此,我们做了如下的工作。

  • 基于 webpack-dev-server 开发了一个服务,当相关资源和依赖发生变更时,自动编译本地开发服务器上的资源。
  • 支持将 webpack 资源加载到单元测试里(这样就有可能为 React 组件编写测试用例)。
  • 重构生产环境的构建流程,将 webpack 资源推送到我们的 CDN。

通过重写 Emoji 选择器,迫使我们反思我们的构建管道如何能够以一种更健壮、更具伸缩性的方式打包资源。

性能

我们在少量的团队里部署了新的组件,并观察结果。我们观察了 Emoji 选择器在用户使用不同的 5 种交互方式下的渲染速度,对于大部分的操作,React 表现出了显著的速度提升。以下列出了选择器在正常规模团队里的不同渲染时间。

  • 第一次挂载:-270 毫秒(减少了 85%)
  • 第二次挂载:-158 毫秒(减少了 91.3%)
  • 搜索(多个结果):+27 毫秒(增加了 259%)
  • 搜索(一个结果):-25 毫秒(减少了 53.2%)
  • 重置搜索:-68 毫秒(减少了 70.1%)

最大的改进来自“第一次挂载”,从 318 毫秒到 48 毫秒,减少了 270 毫秒,也就是 85%。这要极力归功于 react-virtualized ——一个虚拟列表代码库——减少重新渲染 emoji 的数量。在默认视图上,React Emoji 选择器比 DOM 少渲染了 85%。

或许最让人感到吃惊的变化来自“搜索(多个结果)”,时间从 17 毫秒增加到了 44 毫秒,增加了 27 毫秒。旧选择器只是把不匹配的 emoji 隐藏起来,也就是说,当匹配到大部分 emoji 时会相对较快。但它的缺点也是显而易见的,“搜索(一个结果)”和“重置搜索”就让它的缺点原形毕露,因为此时它需要隐藏更多的 emoji。

未来

使用 React 重写 Emoji 选择器加快了渲染速度,同时简化了代码,让代码更容易维护。我们正在使用 React 重写剩余的代码。我们还有很多工作要做,这次重写将为用户的日常体验带来积极的影响,为此我们感到非常兴奋。与此同时,我们积累了 React 的实践经验,可以帮助平台更进一步。

2017-06-13 19:002947
用户头像

发布了 322 篇内容, 共 142.7 次阅读, 收获喜欢 147 次。

关注

评论

发布
暂无评论
发现更多内容

这些面试技巧,助你升职加薪、迎娶白富美

伤感汤姆布利柏

2023年,低代码秀起了肌肉

树上有只程序猿

PoseiSwap IDO、IEO 结束,即将登录 BNB Chain

鳄鱼视界

Postman Test 校验入门指南:轻松进行接口测试并验证响应

Liam

Java 程序员 Postman 开发工具 API

探秘华为云盘古大模型:AI for industries的身体力行

华为云开发者联盟

人工智能 华为云 盘古大模型 华为云开发者联盟 企业号 6 月 PK 榜

走进用友BIP数智人力,揭开中国企业智慧管理的神秘面纱

用友BIP

数智人力

[NLP] langchain-ChatGLM 本地知识库

alexgaoyh

知识库 私有化部署 langchain ChatGLM-6B

低代码——前端开发人员的利器

伤感汤姆布利柏

QCN6274 QCN9274 What is the difference?|WIFI7 Solution|Wallys

wallyslilly

qcn9274 qcn6274

是时候了!MySQL 5.7 的下一站,不如试试 TiDB?

编程猫

Flume简述

数新网络官方账号

数据采集 flume

hive数据迁移

数新网络官方账号

大数据 hadoop hadoop spark hive

预约直播 | 展心展力MetaApp:基于DeepRec的稀疏模型训练实践

阿里云大数据AI技术

人工智能 模型训练

HTML5 游戏开发实战 | 贪吃蛇

TiAmo

html html5 6 月 优质更文活动

免费开源项目管理工具有哪些

爱吃小舅的鱼

项目管理 项目管理软件

全球化数字经济时代,国产替代成为重中之重!

用友BIP

国产替代

广州丨阿里云 Serverless 技术实战营邀你来玩!

阿里巴巴云原生

阿里云 Serverless 云原生

拥抱jsx,开启vue3用法的另一种选择

快乐非自愿限量之名

Vue JSX

升级数智底座助力快速构建创新应用

用友BIP

低代码 数智底座 Pass平台

PoseiSwap IDO、IEO 结束,即将登录 BNB Chain

威廉META

面试了一个前阿里P7,Java八股文与架构核心知识简直背得炉火纯青

程序员小毕

程序员 后端 高并发 架构师 java面试

为什么需要数字化?如果实现数字化?

高端章鱼哥

低代码 数字化 企业管理软件 JNPF

Kafka优化

数新网络官方账号

大数据 zookeeper

拉链表的原理及简单实现

数新网络官方账号

大数据 拉链式散列表

9 个值得推荐的 VUE3 UI 框架

互联网工科生

Vue UI VUE 3.0 源码

用这个开源项目,网络小白也能搞定容器网络问题排查

阿里巴巴云原生

阿里云 容器 云原生 KubeSkoop

产品能力|AIRIOT数据采集与控制引擎在物联网项目中的硬核应用

AIRIOT

物联网

构建数字工厂丨数据分析与图表视图模型的配置用法

华为云开发者联盟

后端 物联网 华为云 华为云开发者联盟 企业号 6 月 PK 榜

BI商业智能工具改变企业发展态势

对不起该用户已成仙‖

华为云专家出品《深入理解边缘计算》电子书上线

华为云PaaS服务小智

边缘计算 华为云 华为开发者联盟

Slack使用React重写Web客户端_语言 & 开发_Chris Montrois_InfoQ精选文章