BT

你的观点很重要! 快来参与InfoQ调研吧!

More than React(一)为什么ReactJS不适合复杂交互的前端项目?

| 作者 杨博 关注 1 他的粉丝 发布于 2016年8月9日. 估计阅读时间: 14 分钟 | ArchSummit社交架构图谱:Facebook、Snapchat、Tumblr等背后的核心技术

《More than React》系列的文章会一共分为五篇和一则附录。本文是第一篇,介绍用 ReactJS开发时遇到的种种问题。后面四篇文章的每一篇将会分别详细讨论其中一个问题,以及Binding.scala如何解决这个问题。附录是一则指南,指引你从头一步步创建Binding.scala项目。

背景介绍

去年 4 月,我第一次在某个客户的项目中接触到ReactJS 。

我发现ReactJS要比我以前用过的AngularJS简单很多,它提供了响应式的数据绑定功能,把数据映射到网页上,使我可以轻松实现交互简单的网站。

然而,随着我越来越深入的使用ReactJS,我发现用ReactJS编写交互复杂的网页很困难。我希望有一种方式,能够像ReactJS一样简单解决简单问题。此外,还要能简单解决复杂问题。

于是我把ReactJS用Scala重新写了一个。代码量从近三万行降到了一千多行。

用这个框架实现的 TodoMVC 应用,只用了154行代码。而用ReactJS实现相同功能的TodoMVC,需要488行代码

下图是用Binding.scala实现的TodoMVC应用。

这个框架就是Binding.scala

问题一:ReactJS组件难以在复杂交互页面中复用

ReactJS中的最小复用单位是组件。ReactJS的组件比AngularJS的Controller和View 要轻量些。每个组件只需要前端开发者提供一个 render 函数,把 props 和 state 映射成网页元素。

这样的轻量级组件在渲染简单静态页面时很好用,但是如果页面有交互,就必须在组件间传递回调函数来处理事件。尤其是复杂的网页结构,往往需要多个组件层层嵌套,导致回调函数也必须在父子组件间层层传递,代码变成一团乱麻,维护就很难了。

我将在《More than React(二)组件对复用性有害?》中用原生DHTML API、ReactJS和Binding.scala实现同一个需要复用的页面,介绍Binding.scala如何简单实现、简单复用复杂的交互逻辑。

问题二:ReactJS的虚拟DOM 算法又慢又不准

ReactJS的页面渲染算法是虚拟DOM差量算法。

开发者需要提供 render 函数,根据 props 和 state 生成虚拟 DOM。然后 ReactJS 框架根据 render 返回的虚拟 DOM 创建相同结构的真实 DOM.

每当 state 更改时,ReacJS 框架重新调用 render 函数,获取新的虚拟 DOM 。然后,框架会比较上次生成的虚拟 DOM 和新的虚拟 DOM 有哪些差异,然后把差异应用到真实DOM上。

这样做有两大缺点:

  1. 每次 state 更改,render 函数都要生成完整的虚拟 DOM. 哪怕 state 改动很小,render函数也会完整计算一遍。如果 render 函数很复杂,这个过程就白白浪费了很多计算资源。
  2. ReactJS框架比较虚拟DOM差异的过程,既慢又容易出错。比如,假如你想要在某个 <ul> 列表的顶部插入一项<li> ,那么ReactJS框架会误以为你修改了 <ul> 的每一项 <li>,然后在尾部插入了一个 <li>。

这是因为 ReactJS收到的新旧两个虚拟DOM之间相互独立,ReactJS并不知道数据源发生了什么操作,只能根据新旧两个虚拟DOM来猜测需要执行的操作。自动的猜测算法既不准又慢,必须要前端开发者手动提供 key 属性、shouldComponentUpdate 方法、componentDidUpdate 方法或者 componentWillUpdate 等方法才能帮助 ReactJS 框架猜对。

我将在《More than React(三)虚拟DOM已死?》中比较ReactJS、AngularJS和Binding.scala渲染机制,介绍简单性能高的Binding.scala精确数据绑定机制。

问题三:ReactJS的HTML模板功能既不完备、也不健壮

ReactJS支持用JSX编写HTML模板。

理论上,前端工程师只要把静态HTML原型复制到JSX源文件中,增加一些变量替换代码,就能改造成动态页面。理论上这种做法要比Cycle.js、Widok、ScalaTags等框架更适合复用设计师提供的HTML原型。

不幸的是,ReactJS对HTML的支持残缺不全。开发者必须手动把class和for属性替换成className和htmlFor,还要把内联的style样式从CSS语法改成JSON语法,代码才能运行。这种开发方式下,前端工程师虽然可以把HTML原型复制粘贴到代码中,但还需要大量改造才能实际运行。比Cycle.js、Widok、或者、ScalaTags省不了太多事。

除此之外,ReactJS还提供了propTypes机制校验虚拟DOM的合法性。然而,这一机制也漏洞百出。即使指定了propTypes,ReactJS也不能在编译前提前发现错误。只有测试覆盖率很高的项目时才能在每个组件使用其他组件时进行校验。即使测试覆盖率很高,propTypes仍旧不能检测出拼错的属性名,如果你把onClick写成了onclick,ReactJS就不会报错,往往导致开发者额外花费大量时间排查一个很简单的bug。

我将在《More than React(四)HTML也可以静态编译?》中比较ReactJS和Binding.scala的HTML模板,介绍Binding.scala如何在完整支持XHTML语法的同时静态检查语法错误和语义错误。

问题四:ReactJS与服务器通信时需要复杂的异步编程

ReactJS从服务器加载数据时的架构可以看成MVVM(Model–View–ViewModel)模式。前端工程师需要编写一个服务访问层作为Model,把ReactJS的state当做ViewModel,而render当做View。Model负责访问后端API并把数据设置到state(即View Model)上,可以用Promise和fetch API实现。然后,render,即View,负责把View Model渲染到页面上。

在这整套流程中,前端程序员需要编写大量闭包组成的异步流程,设置、访问状态的代码五零四散,一不小心就会bug丛生,就算小心翼翼的处理各种异步事件,也会导致程序变得复杂,既难调试,又难维护。

我将在《More than React(五)为什么别用异步编程?》中比较ReactJS和Binding.scala的数据同步模型,介绍Binding.scala如何自动同步服务器数据,避免手动异步编程。

结论

尽管Binding.scala初看上去很像ReactJS,但隐藏在Binding.scala背后的机制更简单、更通用,与ReactJS和Widok截然不同。

所以,通过简化概念,Binding.scala灵活性更强,能用通用的方式解决ReactJS解决不了的复杂问题。

比如,除了上述四个方面以外,ReactJS的状态管理也是老大难问题,如果引入Redux或者react-router这样的第三方库来处理状态,会导致架构变复杂,分层变多,代码绕来绕去。而Binding.scala可以用和页面渲染一样的数据绑定机制描述复杂的状态,不需要任何第三方库,就能提供服务器通信、状态管理和网址分发的功能。

以下表格中列出了上述Binding.scala和ReactJS的功能差异:

    Binding.scala ReactJS
复用性 最小复用单位 方法 组件
复用难度 不论交互内容还是静态内容都容易复用 容易复用静态内容组件,但难以复用交互组件
页面渲染算法 算法 精确的数据绑定 虚拟 DOM
性能
正确性 自动保证正确性 需要开发者手动设置 key 属性,不然复杂的页面会错乱。
HTML 模板 语法 Scala XML 字面量 JSX
是否支持 HTML 或 XHTML 语法 完整支持 XHTML 残缺支持。正常的 XHTML 无法编译。开发者必须手动把 class 和 for 属性替换成 className 和 htmlFor,还要把内联的 style 样式从 CSS 语法改成 JSON 语法。
如何校验模板语法 自动编译时校验 运行时通过 `propTypes` 校验但无法检测简单的拼写错误。
服务器通讯 机制 自动远程数据绑定 MVVM + 异步编程
实现难度 简单 复杂
其他 如何分派网址或者锚点链接 支持把网址当成普通的绑定变量来用,无需第三方库。 不支持,需要第三方库 react-router
功能完备性 完整的前端开发解决方案 本身只包含视图部分功能。需要额外掌握 react-router 、 Redux 等第三方库才能实现完整的前端项目。
学习曲线 API 简单,对没用过 Scala 的人来说也很好懂 上手快。但功能太弱导致后期学习第三方库时曲线陡峭。
    Binding.scala ReactJS

两个多月前,我在Scala.js的论坛发布Binding.scala时,当时Scala.js社区最流行的响应式前端编程框架是Widok。Tim Nieradzik是Widok的作者。他在看到我发布的框架后,称赞这个框架是Scala.js社区最有前途的 HTML 5渲染框架。

他是对的,两个月后,现在Binding.scala已经成为Scala.js社区最流行的响应式前端编程框架。

Awesome Scala网站对比了Scala的响应式前端编程框架,Binding.scala的活跃程度和流行度都比Udash、Widok等其他框架要高。

我在最近的几个项目中,也逐渐放弃JavaScript和ReactJS,改用Scala.js和Binding.scala搭建新时代的前端技术栈。

相关链接

鸣谢

感谢张凯峰和郑培真帮我检阅了这一系列文章,你看到的文章的结构和内容许多地方都来自他们提出的修改意见。

More than React 系列文章

《More than React(一)为什么ReactJS不适合复杂交互的前端项目?》

《More than React(二)组件对复用性有害?》

《More than React(三)虚拟DOM已死?》

《More than React(四)HTML也可以静态编译?》

《More than React(五)异步编程真的好吗?》

作者简介

杨博是 Haxe 和 Scala 社区的活跃贡献者,发起和维护的开源项目包括 protoc-gen-as3Stateless Futurehaxe-continuationFastringEachMicrobuilderBinding.scala 。杨博曾在网易任主程序和项目经理,开发过多款游戏。现在ThoughtWorks任Lead Consultant,为客户提供移动、互联网、大数据、人工智能和深度学习领域的解决方案。


感谢张凯峰对本文的策划,韩婷对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注我们。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

期待 by 杨 景

很期待接下来的文章,也准备在项目中尝试。

Reactjs只是视图层工具 by Nightingale Yanni

它不能算是一个框架,诸如异步数据之类的逻辑不应该放到 component 中,而需要配合 flux 之类的控制流架构。

Re: Reactjs只是视图层工具 by Yang Bo

对的。本系列第五篇文章就会探讨异步编程的问题。异步编程并不只是ReactJS的问题。因为其他框架大都也缺乏服务端数据自动同步的功能,所以只能强迫程序员手写异步编程代码。

Re: Reactjs只是视图层工具 by Yang Bo

如果一个框架要解决这个问题,可能只需要像Binding.scala一样提供一套自动同步服务端数据的功能就行了。

广告贴 by Dou Sean

广告贴

Re: Reactjs只是视图层工具 by Li Zhiheng

什么时候发表第五篇呢? 很期待的呢

React Flux模式 by Huang Jam

在“问题四:ReactJS与服务器通信时需要复杂的异步编程”中,只字未提React的Flux模式,是不合适的

Re: React Flux模式 by Yang Bo

我觉得没必要提,因为Flux并不解决异步编程的问题。您可以等第五篇发表以后看看是不是需要提Flux。

过于贬低React了... by 张 亦驰

远程数据同步得跟relay进行对比... 至于模板的绑定得跟vue相提并论。 模糊了前后端的边界倒挺像rails和php。。。

个人观点 by 翁 润雨

作者的不少观点我都不赞同,列举以下两点:
1.作者认为“每次 state 更改,render 函数都要生成完整的虚拟 DOM,这个过程就白白浪费了很多计算资源。”事实其实并非这样,你可以通过‎shouldComponentUpdate()方法来判断哪些state的修改是需要重新render的。
2.作者认为“ReactJS框架比较虚拟DOM差异的过程,既慢又容易出错。”react的key就是用来解决这类问题的,key是唯一的当你删除某一元素时它会判断出你是删除一个dom元素还是重新修改了所有。
作者可以仔细阅读以下react的官方文档。

个人看法 by 杨 少华

当我看到贬低reactjs的时候 我就知道作者走到了极端。

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

11 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT