专栏首页ThoughtWorksReact.Component损害了复用性?|TW洞见

React.Component损害了复用性?|TW洞见

本文转载自InfoQ: http://www.infoq.com/cn/articles/more-than-react-part02

本系列的上一篇文章《为什么ReactJS不适合复杂交互的前端项目》中列举了前端开发中的种种痛点。本篇文章将详细探讨其中的“复用性”痛点。

我们将用原生DHTML API、ReactJS和Binding.scala实现同一个需要复用的标签编辑器,然后比较三个标签编辑器哪个实现难度更低,哪个更好用。

标签编辑器的功能需求

在InfoQ的许多文章都有标签。比如本文的标签是“binding.scala”、“data-binding”、“scala.js”。

假如你要开发一个博客系统,你也希望博客作者可以添加标签。所以你可能会提供标签编辑器供博客作者使用。

如图所示,标签编辑器在视觉上分为两行。

第一行展示已经添加的所有标签,每个标签旁边有个“x”按钮可以删除标签。

第二行是一个文本框和一个“Add”按钮,可以把文本框的内容添加为新标签。每次点击“Add”按钮时,标签编辑器应该检查标签是否已经添加过,以免重复添加标签。而在成功添加标签后,还应清空文本框,以便用户输入新的标签。

除了用户界面以外,标签编辑器还应该提供API。标签编辑器所在的页面可以用API填入初始标签,也可以调用API随时增删查改标签。如果用户增删了标签,应该有某种机制通知页面的其他部分。

原生DHTML版

首先,我试着不用任何前端框架,直接调用原生的DHTML API来实现标签编辑器,代码如下:

点击查看清晰大图

HTML 文件中硬编码了几个 <div> 。这些 <div> 本身并不是动态创建的,但可以作为容器,放置其他动态创建的元素。

代码中的函数来会把网页内容动态更新到这些 <div> 中。所以,如果要在同一个页面显示两个标签编辑器,id 就会冲突。因此,以上代码没有复用性。

就算用 jQuery 代替 DHTML API,代码复用仍然很难。为了复用 UI ,jQuery 开发者通常必须额外增加代码,在 onload 时扫描整个网页,找出具有特定 class 属性的元素,然后对这些元素进行修改。对于复杂的网页,这些 onload 时运行的函数很容易就会冲突,比如一个函数修改了一个 HTML 元素,常常导致另一处代码受影响而内部状态错乱。

ReactJS 实现的标签编辑器组件

ReactJS 提供了可以复用的组件,即 React.Component 。如果用 ReactJS 实现标签编辑器,大概可以这样写:

以上51行ECMAScript 2015代码实现了一个标签编辑器组件,即TagPicker。虽然代码量比DHTML版长了一点点,但复用性大大提升了。

如果你不用ECMAScript 2015的话,那么代码还会长一些,而且需要处理一些JavaScript的坑,比如在回调函数中用不了 this

ReactJS开发者可以随时用 ReactDOM.render 函数把 TagPicker 渲染到任何空白元素内。此外,ReactJS框架可以在 stateprops 改变时触发 render ,从而避免了手动修改现存的DOM。

如果不考虑冗余的 key 属性,单个组件内的交互ReactJS还算差强人意。但是,复杂的网页结构往往需要多个组件层层嵌套,这种父子组件之间的交互,ReactJS就很费劲了。

比如,假如需要在 TagPicker 之外显示所有的标签,每当用户增删标签,这些标签也要自动更新。要实现这个功能,需要给 TagPicker 传入 changeHandler 回调函数,代码如下:

为了能触发页面其他部分更新,我被迫增加了一个 21 行代码的 Page 组件。

Page 组件必须实现 changeHandler 回调函数。每当回调函数触发,调用 Page自己的 setState 来触发 Page 重绘。

从这个例子,我们可以看出,ReactJS可以简单的解决简单的问题,但碰上层次复杂、交互频繁的网页,实现起来就很繁琐。使用ReactJS的前端项目充满了各种 xxxHandler用来在组件中传递信息。

我参与的某海外客户项目,平均每个组件大约需要传入五个回调函数。如果层次嵌套深,创建网页时,常常需要把回调函数从最顶层的组件一层层传入最底层的组件,而当事件触发时,又需要一层层把事件信息往外传。整个前端项目有超过一半代码都在这样绕圈子。

Bingding.scala 的基本用法

在讲解Binding.scala如何实现标签编辑器以前,我先介绍一些Binding.scala的基础知识:

Binding.scala中的最小复用单位是数据绑定表达式,即 @dom 方法。每个 @dom 方法是一段HTML模板。比如:

每个模板还可以使用bind语法包含其他子模板,比如:

你可以参见附录:Binding.scala快速上手指南,学习上手Binding.scala开发的具体步骤。

此外,本系列第四篇文章《HTML也可以静态编译》还将列出Binding.scala所支持的完整HTML模板特性。

Bingding.scala 实现的标签编辑器模版

最后,下文将展示如何用Binding.scala实现标签编辑器。

标签编辑器要比刚才介绍的HTML模板复杂,因为它不只是静态模板,还包含交互。

这个标签编辑器的HTML模板一共用了18行代码就实现好了。

标签编辑器中需要显示当前所有标签,所以此处用tags: Vars[String]保存所有的标签数据,再用for/yield循环把tags中的每个标签渲染成UI元素。

Vars 是支持数据绑定的列表容器,每当容器中的数据发生改变,UI就会自动改变。所以,在x按钮中的onclick事件中删除tags中的数据时,页面上的标签就会自动随之消失。

同样,在Add按钮的onclick中向tags中添加数据时,页面上也会自动产生对应的标签。

Binding.scala不但实现标签编辑器比ReactJS简单,而且用起来也比ReactJS简单:

只要用9行代码另写一个HTML模板,在模板中调用刚才实现好的 tagPicker 就行了。

完整的DEMO请访问:

https://thoughtworksinc.github.io/Binding.scala/#4。

在 Binding.scala 不需要像 ReactJS 那样编写 changeHandler 之类的回调函数。每当用户在 tagPicker 输入新的标签时,tags 就会改变,网页也就会自动随之改变。

对比 ReactJS 和 Binding.scala 的代码,可以发现以下区别:

  • Binding.scala 的开发者可以用类似 tagPicker 这样的 @dom 方法表示 HTML 模板,而不需要组件概念。
  • Binding.scala 的开发者可以在方法之间传递 tags 这样的参数,而不需要 props 概念。
  • Binding.scala 的开发者可以在方法内定义局部变量表示状态,而不需要 state 概念。

总的来说Binding.scala要比ReactJS精简不少。

如果你用过ASP、PHP、JSP之类的服务端网页模板语言,你会发现和Binding.scala的HTML模板很像。

使用Binding.scala一点也不需要函数式编程知识,只要把设计工具中生成的HTML原型复制到代码中,然后把会变的部分用花括号代替、把重复的部分用 for / yield 代替,网页就做好了。

结论

本文对比了在不同技术栈中实现和使用可复用的标签编辑器的难度。

Binding.scala不发明“组件”之类的噱头,而以更轻巧的“方法”为最小复用单位,让编程体验更加顺畅,获得了更好的代码复用性。

本系列下一篇文章将比较 ReactJS 的虚拟 DOM 机制和 Binding.scala 的精确数据绑定机制,揭开 ReactJS 和 Binding.scala 相似用法背后隐藏的不同算法

本文分享自微信公众号 - 思特沃克(ThoughtWorks),作者:杨博

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-09-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • TW洞见 | 可视化你的足迹

    今日洞见 文章作者来自ThoughtWorks:邱俊涛。 本文所有内容,包括文字、图片和音视频资料,版权均属ThoughtWorks公司所有,任何媒体、网站或个...

    ThoughtWorks
  • TW洞见〡getter和setter的那些事

    文章作者来自ThoughtWorks:佟达 相信每一个以Java或者C++作为编程入门语言的程序员,一定会记得一条金科玉律:字段(Filed)要声明成priv...

    ThoughtWorks
  • 虚拟DOM已死?|TW洞见

    杨博 ThoughtWorks 本文转载自InfoQ:http://www.infoq.com/cn/articles/more-than-react-part...

    ThoughtWorks
  • 比Kubernetes Dashboard更好用的K8S管理工具 Kuboard

    Kuboard 是一款免费的 Kubernetes 管理工具,提供了丰富的功能,结合代码仓库、镜像仓库、CI/CD工具等,可以便捷的搭建一个生产可用的 Kube...

    YP小站
  • LeetCode 557 Reverse Words in a String III

    首先按照空格对字符串进行分隔,然后将每个单词进行翻转后再拼接回字符串即可,需要注意拼接时记得加空格,但最后一个单词不需要加。

    一份执着✘
  • Spring周边:StringTokenizer

    JDK 已经不建议在新代码中使用 StringTokenizer 了,建议使用正则表达式功能替换;

    WEBJ2EE
  • Java解释器模式(Interpreter)

    用户4919348
  • Java漫谈8

    今天我们来聊聊字符串。 字符串,在Java中一个最接近与8大数据类型的存在。甚至于由于它太好用了,以至于在编写代码的时候都快忘了有个叫char的基本数据类型了。...

    用户1335799
  • 奇怪,Spring Security 登录成功后总是获取不到登录用户信息?

    一开始我觉得这可能是一个小概率 BUG,但是当问的人多了,我觉得这个问题对于新手来说还有一定的普遍性,有必要来写篇文章跟大家仔细聊一聊这个问题,防止小伙伴们掉坑...

    江南一点雨
  • Android版-支付宝APP支付

    补充(20170513) 支付宝APP支付可以使用沙箱环境测试。如需开启测试模式只需要在OnCreate中添加如下代码。沙箱环境测试APP支付中请使用沙箱版钱...

    Javen

扫码关注云+社区

领取腾讯云代金券