写点什么

颠覆式前端 UI 开发框架:React

  • 2014-12-14
  • 本文字数:3330 字

    阅读完需:约 11 分钟

基于 HTML 的前端界面开发正变得越来越复杂,其本质问题基本都可以归结于如何将来自于服务器端或者用户输入的动态数据高效的反映到复杂的用户界面上。而来自 Facebook 的 React 框架正是完全面向此问题的一个解决方案,按官网描述,其出发点为:用于开发数据不断变化的大型应用程序(Building large applications with data that changes over time)。相比传统型的前端开发,React 开辟了一个相当另类的途径,实现了前端界面的高效率高性能开发。

首先,对于 React,有一些认识误区,这里先总结一下:

  • React 不是一个完整的 MVC 框架,最多可以认为是 MVC 中的 V(View),甚至 React 并不非常认可 MVC 开发模式;
  • React 的服务器端 Render 能力只能算是一个锦上添花的功能,并不是其核心出发点,事实上 React 官方站点几乎没有提及其在服务器端的应用;
  • 有人拿 React 和 Web Component 相提并论,但两者并不是完全的竞争关系,你完全可以用 React 去开发一个真正的 Web Component;
  • React 不是一个新的模板语言,JSX 只是一个表象,没有 JSX 的 React 也能工作。

1. React 的原理

在 Web 开发中,我们总需要将变化的数据实时反应到 UI 上,这时就需要对 DOM 进行操作。而复杂或频繁的 DOM 操作通常是性能瓶颈产生的原因(如何进行高性能的复杂 DOM 操作通常是衡量一个前端开发人员技能的重要指标)。React 为此引入了虚拟 DOM(Virtual DOM)的机制:在浏览器端用 Javascript 实现了一套 DOM API。基于 React 进行开发时所有的 DOM 构造都是通过虚拟 DOM 进行,每当数据变化时,React 都会重新构建整个 DOM 树,然后 React 将当前整个 DOM 树和上一次的 DOM 树进行对比,得到 DOM 结构的区别,然后仅仅将需要变化的部分进行实际的浏览器 DOM 更新。而且 React 能够批处理虚拟 DOM 的刷新,在一个事件循环(Event Loop)内的两次数据变化会被合并,例如你连续的先将节点内容从 A 变成 B,然后又从 B 变成 A,React 会认为 UI 不发生任何变化,而如果通过手动控制,这种逻辑通常是极其复杂的。尽管每一次都需要构造完整的虚拟 DOM 树,但是因为虚拟 DOM 是内存数据,性能是极高的,而对实际 DOM 进行操作的仅仅是 Diff 部分,因而能达到提高性能的目的。这样,在保证性能的同时,开发者将不再需要关注某个数据的变化如何更新到一个或多个具体的 DOM 元素,而只需要关心在任意一个数据状态下,整个界面是如何 Render 的。

如果你像在 90 年代那样写过服务器端 Render 的纯 Web 页面那么应该知道,服务器端所要做的就是根据数据 Render 出 HTML 送到浏览器端。如果这时因为用户的一个点击需要改变某个状态文字,那么也是通过刷新整个页面来完成的。服务器端并不需要知道是哪一小段 HTML 发生了变化,而只需要根据数据刷新整个页面。换句话说,任何 UI 的变化都是通过整体刷新来完成的。而 React 将这种开发模式以高性能的方式带到了前端,每做一点界面的更新,你都可以认为刷新了整个页面。至于如何进行局部更新以保证性能,则是 React 框架要完成的事情。

借用 Facebook 介绍 React 的视频中聊天应用的例子,当一条新的消息过来时,传统开发的思路如上图,你的开发过程需要知道哪条数据过来了,如何将新的 DOM 结点添加到当前 DOM 树上;而基于 React 的开发思路如下图,你永远只需要关心数据整体,两次数据之间的 UI 如何变化,则完全交给框架去做。

可以看到,使用 React 大大降低了逻辑复杂性,意味着开发难度降低,可能产生 Bug 的机会也更少。至于 React 如何做到将原来 O(n^3) 复杂度的 Diff 算法降低到 O(n),大家可以参考这篇文章

2. 组件化的开发思路

虚拟 DOM 不仅带来了简单的 UI 开发逻辑,同时也带来了组件化开发的思想,所谓组件,即封装起来的具有独立功能的 UI 部件。React 推荐以组件的方式去重新思考 UI 构成,将 UI 上每一个功能相对独立的模块定义成组件,然后将小的组件通过组合或者嵌套的方式构成大的组件,最终完成整体 UI 的构建。例如,Facebook 的 instagram.com 整站都采用了 React 来开发,整个页面就是一个大的组件,其中包含了嵌套的大量其它组件,大家有兴趣可以看下它背后的代码。

如果说 MVC 的思想让你做到视图 - 数据 - 控制器的分离,那么组件化的思考方式则是带来了 UI 功能模块之间的分离。我们通过一个典型的 Blog 评论界面来看 MVC 和组件化开发思路的区别。

对于 MVC 开发模式来说,开发者将三者定义成不同的类,实现了表现,数据,控制的分离。开发者更多的是从技术的角度来对 UI 进行拆分,实现松耦合。

对于 React 而言,则完全是一个新的思路,开发者从功能的角度出发,将 UI 分成不同的组件,每个组件都独立封装。

在 React 中,你按照界面模块自然划分的方式来组织和编写你的代码,对于评论界面而言,整个 UI 是一个通过小组件构成的大组件,每个组件只关心自己部分的逻辑,彼此独立。这样最外层的界面的 Render 只需要如下代码:

通过这种方式,每个组件的 UI 和逻辑都定义在组件内部,和外部完全通过 API 来交互,通过组合的方式来实现复杂的功能。React 认为一个组件应该具有如下特征:

(1)可组合(Composeable):一个组件易于和其它组件一起使用,或者嵌套在另一个组件内部。如果一个组件内部创建了另一个组件,那么说父组件拥有(own)它创建的子组件,通过这个特性,一个复杂的 UI 可以拆分成多个简单的 UI 组件;

(2)可重用(Reusable):每个组件都是具有独立功能的,它可以被使用在多个 UI 场景;

(3)可维护(Maintainable):每个小的组件仅仅包含自身的逻辑,更容易被理解和维护;

(4)可测试(Testable):因为每个组件都是独立的,那么对于各个组件分别测试显然要比对于整个 UI 进行测试容易的多。

3. 一个 React 组件开发的例子:Tab 选择器

上面从总体上介绍了 React 带来的全新的前端开发方法,以及其带来的影响,并没有介绍如何使用。为了让大家对其有一个具体的印象,这里实际来开发一个简单的组件:Tab 选择器。网店的产品页面通常需要这样的控件来选择产品属性,例如选择衣服的颜色。这个控件接受一个数据源展示多个 Tab 供点击,点击后就选中了某个颜色,界面通常如下图所示。

按传统方式,我们可以用如下代码来实现一个 jQuery 插件:

用 React 方式,代码如下:

通过比较可以看到,jQuery 插件方式,开发者首先需要考虑控件第一次 Render 出来时的 DOM 构建;其次,需要知道如何切换 UI 上的选中状态。

而 React 的方式,开发者仅仅需要考虑整体界面的 DOM 构建,不再需要关心局部更新,每次在一个 React 的 Component 上调用 setState 方法,都会触发 render 来重建整个界面。从开发思想的角度看,你可以认为每次数据的更新都会做整体的完全刷新。逻辑简单而直接。

如果我们再多考虑一步,控件的值不只在初始化和点击时可以设置,而且还可以通过程序动态的去设置。那么对于 jQuery 的方案而言,我们需要额外的方法和入口去做对应的 UI 更新。而对于 React 方式,则无需做任何改变,外部只需调用 setState 方法改变它的状态即可。这就是简化 UI 逻辑带来的好处。

完整的代码和演示已上传在 Github 上: https://github.com/supnate/react-tab-selector ,大家可以实际试用一下。

4. 结论

如上所述,React 是一个全新思路的前端 UI 框架,它完全接管了 UI 开发中最为复杂的局部更新部分,擅长在在复杂场景下保证高性能;同时,它引入了基于组件的开发思想,从另一个角度来重新审视 UI 的构成。通过这种方法,不仅能够提高开发效率,而且可以让代码更容易理解,维护和测试。Facebook 以这样一种方式将沉淀多年的前端开发经验和技术的积累完全开源出来,值得所有前端开发者去借鉴和学习。并且 React 在发布一年的时间里就获得了极大的关注,Github 上拥有超过 1 万的 Star,相信其对前端开发的方向,甚至 Web Component 的标准,都将产生一定的影响。

作者简介

王沛,邮件: supnate@gmail.com

关注 IT 趋势,承载前沿、深入、有温度的内容。感兴趣的读者可以搜索 ID:laocuixiabian,或者扫描下方二维码加关注。


感谢郭蕾对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。

2014-12-14 16:11147098

评论 2 条评论

发布
用户头像
哈哈哈,我是看老师的极客时间课程来搜的
2021-03-26 13:54
回复
用户头像
2019-03-20 20:33
回复
没有更多了
发现更多内容

Java的异常处理

java易二三

异常处理 java 编程 #编程

保护 TDengine 查询性能——3.0 如何大幅降低乱序数据干扰?

爱倒腾的程序员

数据库

云环境与服务器的四大区别简单聊聊

行云管家

云计算 云服务器 云环境

结合线上线下环境,提供产品的服务...

Bonaparte

产品 产品经理 产品设计 产品思维 产品服务

深入探析数智时代下的分布式系统架构设计

不在线第一只蜗牛

AI 低代码 分布式架构 数智时代

Java框架——SSM框架增删改查

java易二三

SSM框架 java 编程

如何更快地渲染?深入了解3D渲染性能的指南

Finovy Cloud

创业大赛|第二届“金靴奔跑”创新创业大赛!

科兴未来News

深耕零售行业数字化,乐檬软件与华为云携手共进

华为云开发者联盟

云计算 后端 华为云 华为云开发者联盟 企业号 7 月 PK 榜

大数据实时链路备战——数据双流高保真压测 | 京东云技术团队

京东科技开发者

大数据 压测 企业号 7 月 PK 榜 双流 数据双流

中企出海,数智人力构建全球化组织的驱动力!

用友BIP

中企出海 数智人力

几种验证端口开启的方法

林十二XII

点云标注在自动驾驶中的难点

数据堂

提升直播软件源码开发平台性能关键利器功能_山东布谷科技创作

山东布谷科技

源码 软件 软件开发 直播 源码搭建

点云标注在自动驾驶中的优化策略

数据堂

TE智库 |《中国通用大模型内容生成及安全性能力评测》报告发布,深度测评中国大模型玩家

TE智库

粗粮细作,铁合金行业的节能降耗

用友BIP

冶金

Python案例实现|租房网站数据表的处理与分析

TiAmo

Python 数据分析 爬虫

高性能网络 SIG 月度动态:再获认可!3 位成员成为 Linux 内核社区 reviewer

OpenAnolis小助手

Linux 内核 高性能网络 龙蜥sig smc

告别传统人肉运维,实现360°可观测!奇点云数据存算引擎DataKun R2.0发布

极客天地

频繁FullGC的原因竟然是“开源代码”? | 京东云技术团队

京东科技开发者

JVM GC 企业号 7 月 PK 榜 Full GC

【专业 TypeScript 实战】15 个高级技巧,开创卓越开发之路!

汽车之家客户端前端团队

大型企业采购云管平台的需求是什么?选择哪家厂商好?

行云管家

云计算 企业上云 云管平台

拆解雪花算法生成规则 | 京东物流技术团队

京东科技开发者

算法 雪花算法 企业号 7 月 PK 榜

AI驱动税务智能,开启智慧税务新纪元

用友BIP

AI 税务管理

百度商业AI技术创新大赛区域赛落幕,各大高校科技新秀角逐复赛

百度Geek说

人工智能 百度 AIGC 企业号 7 月 PK 榜

antv-x6使用及总结 | 京东物流技术团队

京东科技开发者

数据可视化 可视化开发 企业号 7 月 PK 榜 antv-x6

颠覆式前端UI开发框架:React_Web框架_王沛_InfoQ精选文章