免费下载案例集|20+数字化领先企业人才培养实践经验 了解详情
写点什么

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

  • 2016-08-08
  • 本文字数:6047 字

    阅读完需:约 20 分钟

《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-as3 Stateless Future haxe-continuation Fastring Each Microbuilder Binding.scala  。杨博曾在网易任主程序和项目经理,开发过多款游戏。现在 ThoughtWorks 任 Lead Consultant,为客户提供移动、互联网、大数据、人工智能和深度学习领域的解决方案。


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

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

2016-08-08 17:3815595

评论

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

上线GitHub七天后就标星87,并发知识体系大全

Java 程序员 后端

两年Java开发经验,混迹“中浩科技,java全套百度云视频

Java 程序员 后端

明道云在保险经纪公司中的应用场景例举

明道云

三年Java开发经验,四面阿里成功斩获offer,分享面经,java面试问项目部署

Java 程序员 后端

三面阿里被灵魂追问,出门就被Java面试官逼哭!,java虚拟机的运行原理

Java 程序员 后端

不是吧工作3年你都不知道这份超详细JVM内存结构,怎么涨薪?(1)

Java 程序员 后端

两道面试题,带你解析Java类加载机制,零基础也能看得懂

Java 程序员 后端

为什么大公司要使用微服务?(建议收藏),java基础案例教程第二版答案

Java 程序员 后端

三面美团Java岗,HR现场直接发offer,他是横着走出来的

Java 程序员 后端

世界顶级Linux技术大神,耗时一年力作1300页Linux开发实战

Java 程序员 后端

为什么分布式一定要有Redis?,java面试逻辑题三个开关三盏灯

Java 程序员 后端

不是吧工作3年你都不知道这份超详细JVM内存结构,怎么涨薪?

Java 程序员 后端

不能错过的分布式ID生成器(Leaf-),积累总结

Java 程序员 后端

两年半,50W的offer,三本,springboot运行原理生命周期

Java 程序员 后端

一辈子也不容错过学习的微服务网关与用户身份识别,Zuul过滤器

Java 程序员 后端

为什么建议大家使用 Linux 开发?爽(外加七个感叹号,java高级编程实训总结

Java 程序员 后端

不是吧阿sir,你这业务太熟了吧,震惊面试官第八年,献给真心想学Java的打工人

Java 程序员 后端

三年Java开发每天增删改查,终于靠着这份面试题,成功上岸京东

Java 程序员 后端

不是吧阿sir!System,java面试问项目中遇到的技术难点

Java 程序员 后端

业务中台的困境、及可能的解,java基础笔试题判断题

Java 程序员 后端

15个问题自查真的了解java编译优化吗

华为云开发者联盟

Java 编译 对象 语义 符号表

丹丹学妹哭着对我说:学长,JVM如何实现动态类型语言支持的呢

Java 程序员 后端

为了你们不错过金三银四,我真是操碎了心!RabbitMQ面试真题来了

Java 程序员 后端

为了进大厂,我深扒了阿里字节等大厂面经,总结了50道必考题

Java 程序员 后端

一见面就脱裤子?这份Spring Cloud微服务笔记应对面试怎么这么骚?

Java 程序员 后端

为什么要谨慎使用Arrays-asList、ArrayList的subList?

Java 程序员 后端

三、Redis在SpringBoot中使用案例,java程序员面试笔试真题与解析

Java 程序员 后端

不是吧阿sir,你这计算机网络也太熟了,震惊面试官一整年

Java 程序员 后端

七、Redis持久化的两种方式RDB和AOF理解,mybatis接口实现原理

Java 程序员 后端

为什么说简历很重要?怎么才能写出一份完美的简历?这篇文章你值得拥有

Java 程序员 后端

世界顶级SQL技术专家呕心沥血半年才整理出这份:SQL优化核心思想笔记,手慢无

Java 程序员 后端

More than React(一)为什么ReactJS不适合复杂交互的前端项目?_JavaScript_杨博_InfoQ精选文章