BT

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

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

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

A note to our readers: As per your request we have developed a set of features that allow you to reduce the noise, while not losing sight of anything that is important. Get email and web notifications by choosing the topics you are interested in.

本系列的上一篇文章《为什么ReactJS不适合复杂的前端项目》列举了前端开发中的种种痛点。本篇文章中将详细探讨其中“复用性”痛点。我们将用原生 DHTML API 、 ReactJS 和 Binding.scala 实现同一个需要复用的标签编辑器,然后比较三个标签编辑器哪个实现难度更低,哪个更好用。

标签编辑器的功能需求

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

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

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

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

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

原生 DHTML 版

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

<!DOCTYPE html>
<html>
<head>
  <script>
    var tags = [];

    function hasTag(tag) {
      for (var i = 0; i < tags.length; i++) {
        if (tags[i].tag == tag) {
          return true;
        }
      }
      return false;
    }

    function removeTag(tag) {
      for (var i = 0; i < tags.length; i++) {
        if (tags[i].tag == tag) {
          document.getElementById("tags-parent").removeChild(tags[i].element);
          tags.splice(i, 1);
          return;
        }
      }
    }

    function addTag(tag) {
      var element = document.createElement("q");
      element.textContent = tag;
      var removeButton = document.createElement("button");
      removeButton.textContent = "x";
      removeButton.onclick = function (event) {
        removeTag(tag);
      }
      element.appendChild(removeButton);
      document.getElementById("tags-parent").appendChild(element);
      tags.push({
        tag: tag,
        element: element
      });
    }

    function addHandler() {
      var tagInput = document.getElementById("tag-input");
      var tag = tagInput.value;
      if (tag && !hasTag(tag)) {
        addTag(tag);
        tagInput.value = "";
      }
    }
  </script>
</head>
<body>
  <div id="tags-parent"></div>
  <div>
    <input id="tag-input" type="text"/>
    <button onclick="addHandler()">Add</button>
  </div>
  <script>
    addTag("initial-tag-1");
    addTag("initial-tag-2");
  </script>
</body>
</html>

为了实现标签编辑器的功能,我用了 45 行 JavaScript 代码来编写 UI 逻辑,外加若干的 HTML <div> 外加两行 JavaScript 代码填入初始化数据。

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

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

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

ReactJS 实现的标签编辑器组件

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

class TagPicker extends React.Component {

  static defaultProps = {
    changeHandler: tags => {}
  }

  static propTypes = {
    tags: React.PropTypes.arrayOf(React.PropTypes.string).isRequired,
    changeHandler: React.PropTypes.func
  }

  state = {
    tags: this.props.tags
  }

  addHandler = event => {
    const tag = this.refs.input.value;
    if (tag && this.state.tags.indexOf(tag) == -1) {
      this.refs.input.value = "";
      const newTags = this.state.tags.concat(tag);
      this.setState({
        tags: newTags
      });
      this.props.changeHandler(newTags);
    }
  }

  render() {
    return (
      <section>
        <div>{
          this.state.tags.map(tag =>
            <q key={ tag }>
              { tag }
              <button onClick={ event => {
                const newTags = this.state.tags.filter(t => t != tag);
                this.setState({ tags: newTags });
                this.props.changeHandler(newTags);
              }}>x</button>
            </q>
          )
        }</div>
        <div>
          <input type="text" ref="input"/>
          <button onClick={ this.addHandler }>Add</button>
        </div>
      </section>
    );
  }

}

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

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

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

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

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

class Page extends React.Component {

  state = {
    tags: [ "initial-tag-1", "initial-tag-2" ]
  };

  changeHandler = tags => {
    this.setState({ tags });
  };

  render() {
    return (
      <div>
        <TagPicker tags={ this.state.tags } changeHandler={ this.changeHandler }/>
        <h3>全部标签:</h3>
        <ol>{ this.state.tags.map(tag => <li>{ tag }</li> ) }</ol>
      </div>
    );
  }

}

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

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

从这个例子,我们可以看出, ReactJS 可以简单的解决简单的问题,但碰上层次复杂、交互频繁的网页,实现起来就很繁琐。使用 ReactJS 的前端项目充满了各种 xxxHandler 用来在组件中传递信息。我参与的某海外客户项目,平均每个组件大约需要传入五个回调函数。如果层次嵌套深,创建网页时,常常需要把回调函数从最顶层的组件一层层传入最底层的组件,而当事件触发时,又需要一层层把事件信息往外传。整个前端项目有超过一半代码都在这样绕圈子。

Binding.scala 的基本用法

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

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

// 两个 HTML 换行符
@dom def twoBr = <br/><br/>
// 一个 HTML 标题
@dom def myHeading(content: String) = <h1>{content}</h1>

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

@dom def render = {
  <div>
    { myHeading("Binding.scala的特点").bind }
    <p>
      代码短
      { twoBr.bind }
      概念少
      { twoBr.bind }
      功能多
    </p>
  </div>
}

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

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

Binding.scala实现的标签编辑器模板

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

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

@dom def tagPicker(tags: Vars[String]) = {
  val input: Input = <input type="text"/>
  val addHandler = { event: Event =>
    if (input.value != "" && !tags.get.contains(input.value)) {
      tags.get += input.value
      input.value = ""
    }
  }
  <section>
    <div>{
      for (tag <- tags) yield <q>
        { tag }
        <button onclick={ event: Event => tags.get -= tag }>x</button>
      </q>
    }</div>
    <div>{ input } <button onclick={ addHandler }>Add</button></div>
  </section>
}

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

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

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

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

@dom def render() = {
  val tags = Vars("initial-tag-1", "initial-tag-2")
  <div>
    { tagPicker(tags).bind }
    <h3>全部标签:</h3>
    <ol>{ for (tag <- tags) yield <li>{ tag }</li> }</ol>
  </div>
}

只要用 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 相似用法背后隐藏的不同算法。

相关链接

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 张 同学

这篇真的是太水了。
为了推广自己的项目,而去把其他项目贬得一无是处,有必要吗?
而且还是用这么没有说服力的方式,一定要在一个小的功能点上通过比较代码行数来得出结论吗?

Re: 水水水 by Yang Bo

我不知道怎么回复您的问题。我作为源码级咨询师,不比代码还能比什么?

您可能觉得这个功能点比较小,但这样的日常前端开发需求还是挺常见的。

可能您没有读完整篇文章,所以您觉得我把ReactJS贬得一无是处。但如果您读完了,您就会发现React.Component相比过去用jQuery和原生DHTML,复用性其实是更好的。

希望您在读完文章之后,能给我一些具体一点的反馈。我挺担心大家觉得我写的 ReactJS 代码不地道。您可以帮我看一下。

Re: 水水水 by 张 同学

如果在实际项目中,真的每个Component都独自管理state,要靠众多的回调才能相互通信的话,那么确实会是地狱。
但是现在flux或者redux之类的全局单一状态管理,几乎已经公认是React的最佳实践了,这时候在忽略了这些的前提下,用最手工的方法写一些代码,就说React会有回调地狱,不是太合适吧。

而且还用了『笨重』、『噱头』之类的字眼,着实会有点扎眼。也会让一些不了解细节的新人,脑中留下了这个印象就走了。

Re: 水水水 by liu channing

Component之间数据通讯方式“不爽”,应该不能归结于React本身,还有Flux、Redux等用于处理数据流的问题。
而这文章里面,恕我水平有限,我也没有看出React组件对复用性的负面影响。
另外,也有声音说组件化的目的是“分治”,而不是“复用”。但我觉得分治、复用都是我的目标。
如果本文能把重点放在“复用性”问题中,会更加清晰明了。

Re: 水水水 by Yang Bo

您说得对,用 Binding.scala 和 ReactJS 对比,对 ReactJS 太不公平了。毕竟 ReactJS 只有 View 层的功能,而 Binding.scala 则是完整的前端解决方案。

欢迎您提供一些 Flux 或者 Redux 的例子,和 Binding.scala 对比。

Re: 水水水 by Yang Bo

您好,我并没有用“笨重”这个字眼啊。

当然 ReactJS 的代码量是 Binding.scala 的三倍多,新人确实很容易留下“ReactJS很笨重”的印象。

都怪我把 Binding.scala 设计得太简洁了。对不起。

Re: 水水水 by Yang Bo

其实我挺理解您的想法,因为您可能以前用 Angular 或者 jQuery 比较多,所以 ReactJS 组件相比之下还算简洁,复用起来还算容易。

不过本文介绍的 Binding.scala 可能比 ReactJS 还要简单一点。具体在本文的例子中, ReactJS 例子中三分之二的代码都是多余的,是 Component 带来的语法噪音。

其实现代语言都属于结构化编程语言,函数和类本身就是基本复用单位,并不需要额外发明 Component 这个概念。

Re: 水水水 by Yang Bo

您二位都提到了 Flux 和 Redux 。这两个框架恐怕语法噪音更多。

如果说 Binding.scala 能够“简单实现简单交互”+“简单实现复杂交互”, ReactJS 是 “简单实现简单交互”+“复杂实现复杂交互” ,那么 Flux 和 Redux 恐怕是 “复杂实现简单交互”+“复杂实现复杂交互” 。

比如本例中的 tagPicker , Binding.scala 的例子用了 17 行代码,ReactJS 用了 51 行,Flux 和 Redux 实现的话,我估计会比 ReactJS 还要长。

值得支持的项目 by 夏 鹏

恰好我现在在用React做大型项目。仅仅从React来讲,局限性蛮大的,要想在项目中用React,基本上得选择React的整个技术栈,比如redux、react route等等,要学好多东西才能做出一个项目来。React的数据单向流动,的确在一些复杂的地方需要在外层组件传入一些函数回调,这个在很多时候也的确觉得是个麻烦事情。不过我倒是喜欢React这种打破传统的思想的。从我刚最初接触HTML的时候,对于CSS和JS都要用单独的外部文件去组织的方式不太爽,React这种思维想法对我来说是蛮爽的。

对于国人能做出Binding.scala框架,在我看来是很值得支持的,能够看到国人在技术方面独树一帜的太少了。不过对于我来说Binding.scala阻止我去使用的地方不是别的,而是scala语言,又得去学新的语言,对于前几年几乎呕心沥血学了差不多十来种语言,做了无数乱七八糟的项目,好不容易静下心来选定一个方向准备往高处攀登的时候,想起还要再去从头学别的语言,心里有万匹草泥马奔腾啊。

不过推荐其他同学详细了解和支持。

Re: 水水水 by 张 同学

既然你坚持数着代码行数来比优劣,那么React永远都不可能赢。
不过要是用Vue.js的话,可能会比你的代码更少,不知道你怎么看?

多说无益了。
就我个人来说,小项目用Vue.js,大项目用React.js,暂时应该对Scale写前端没什么兴趣。

Re: 水水水 by Yang Bo

暂时应该对Scale写前端没什么兴趣。

我觉得您最好别用 Scala 。因为对于 Scala 这种静态类型语言来说,如果您喜欢写错别字的话,是没办法通过编译的。

Re: 值得支持的项目 by Yang Bo

我理解你对技术投资的万马奔腾心情,毕竟自己花了多年学习的技术如果不流行了,可能在就业市场上自己就贬值了。对应用开发工程师来说,ReactJS是个新技术,如果用ReactJS做过项目,在简历上可是个加分项呢。

不过,我觉得应用开发工程师不必太在意一时技术得失,只要是有新意的技术,都可以学。我10年前学习C++里的Boost模板元编程之类的技术,虽然没在项目中用上,但是我对类型系统的理解就更加深刻了,对我后来开发 Binding.scala 等几个框架都大有益处。

实际上 Binding.scala 的 API 要比 ReactJS、Vue.js 简单五到十倍。oss.sonatype.org/service/local/repositories/rel...
所以,我觉得你可以学一下 Binding.scala 再评论 Binding.scala 好不好学。

不过怎么样,谢谢你推荐 Binding.scala 。

Re: 水水水 by Yang Bo

既然你坚持数着代码行数来比优劣,那么React永远都不可能赢。
不过要是用Vue.js的话,可能会比你的代码更少,不知道你怎么看?


您错了。Vue.js 的 TodoMVC 例子中的 JavaScript 代码行数比 Binding.scala 的 Scala 代码长。
github.com/tastejs/todomvc/tree/master/examples...
github.com/ThoughtWorksInc/todo/blob/master/js/...

当然这样比较,对 Binding.scala 不太公平。因为 Vue.js 除了需要编写 JavaScript 以外,还需要额外编写 HTML 模板。而 Binding.scala 的 Scala 文件中有一半左右的代码都是 HTML 模板。

不过没关系,虽然对 Binding.scala 不公平,但 Binding.scala 还是能赢。

Re: 值得支持的项目 by 夏 鹏

感谢你能和我探讨这些。我基本算是一个做全栈开发的,这些年基本是项目指哪儿我就打哪个。这些年除了前端的Javascript、PHP,微软系的C#、WPF、WCF、ASP.NET MVC、.NET Core,移动应用的java、swift,Web服务器的node、nginx,sql数据库sql server、mysql,缓存redis,nosql的mongodb、azure的nosql存储等等。这些都是我一下子能够想起来的,可能没想起来的还有很多。因为都是项目需要什么,我就做什么。我感觉除了做项目,我没有业余时间,没有休息,都是不断地学习再学习。

可是几年过去,我发觉我很凌乱。因为我觉得我做了这么多事情,居然写不出一本书,不是说我非要写书,而是我感觉我自己居然找不到一个点能达到出书的水平。我很惆怅,因为我感觉我被技术玩儿了,而不是我在玩儿技术。于是我痛定思痛,决定缩小范围,其他一切都咔掉,然后再这个范围内做出我自己满意的成绩来。

当然我不是说其他技术我不接触了,而是不想像无头苍蝇一样乱撞了,为自己理一个清晰的主线来走比较好。

我希望你的Binding.scala,能够有朝一日能够和React、Angular相提并论,我也希望自己能够尽快参与到你这个开源项目中来,能够做出一个好东西,对于参与者来说都是一份了不起的成就感。Binding.scala这个github的项目我已经保存了,如果我遇到做scala,并且喜欢参与开源项目的同学,我会尽力向他们推荐的。

Re: 值得支持的项目 by Yang Bo

感谢你能和我探讨这些。我基本算是一个做全栈开发的,这些年基本是项目指哪儿我就打哪个。这些年除了前端的Javascript、PHP,微软系的C#、WPF、WCF、ASP.NET MVC、.NET Core,移动应用的java、swift,Web服务器的node、nginx,sql数据库sql server、mysql,缓存redis,nosql的mongodb、azure的nosql存储等等。这些都是我一下子能够想起来的,可能没想起来的还有很多。因为都是项目需要什么,我就做什么。我感觉除了做项目,我没有业余时间,没有休息,都是不断地学习再学习。

可是几年过去,我发觉我很凌乱。因为我觉得我做了这么多事情,居然写不出一本书,不是说我非要写书,而是我感觉我自己居然找不到一个点能达到出书的水平。我很惆怅,因为我感觉我被技术玩儿了,而不是我在玩儿技术。于是我痛定思痛,决定缩小范围,其他一切都咔掉,然后再这个范围内做出我自己满意的成绩来。

当然我不是说其他技术我不接触了,而是不想像无头苍蝇一样乱撞了,为自己理一个清晰的主线来走比较好。

我希望你的Binding.scala,能够有朝一日能够和React、Angular相提并论,我也希望自己能够尽快参与到你这个开源项目中来,能够做出一个好东西,对于参与者来说都是一份了不起的成就感。Binding.scala这个github的项目我已经保存了,如果我遇到做scala,并且喜欢参与开源项目的同学,我会尽力向他们推荐的。

我觉得你对自己职业生涯的思考很清晰。加油吧!

Re: 水水水 by 张 同学

说实话,感觉您的优越感都透过电脑屏幕渗出来了。

Re: 水水水 by Yang Bo

我理解您可能有“你不就是写了个比ReactJS好的框架吗?有什么了不起?”这样的情绪。

不过我觉得作为专业技术人员,您如果觉得 ReactJS 就是世界上最好的框架,别人介绍的其他框架都只是“优越感”就视而不见,那么我觉得这样容易限制您的技术视野,对您的职业生涯可能不是特别好。

区别只在数据绑定而已 by chao wang

如果把react的代码使用stateless component的方式实现,并不会比binding.scala多那么多行代码。
而binding.scala的那些代码行数的优势实际上只是来自于
Vars 是支持数据绑定的列表容器
这句话而已。

但是React并没有提供数据绑定的东西,本身的定位只是View。也因此能出现react-native,react-desktop,react-canvas这样的东西。数据绑定的部分现在社区都有很多解决方案了,如redux和mobx等

而在我看来,binding.scala看起来只是提供了数据绑定的功能而已。

Tag Editor reactjs 15行版本 by chao wang

tags是一个immutablejs的set对象,所以现在你看除了数据绑定以外,还有什么差别?行数嘛?

const TagEditor = ({onChange, tags}) => (
<section>
<div>
{tags.map((tag, index) => (
<div key={index}>
{tag}
<button onClick={() => onChange(tags.delete(tag))}>x</button>
</div>
))}</div>
<div>
<input type="text" ref="input"/>
<button onClick={() => onChange(tags.add(this.refs.input.value))}>Add</button>
</div>
</section>
)

Re: Tag Editor reactjs 15行版本 by Yang Bo

你的洞察力不错!归根到底,ReactJS就是因为缺乏通用数据绑定能力,所以没办法简单复用组件。

《More than React》系列的下一篇文章就会比较ReactJS的虚拟DOM和Binding.scala的精确数据绑定机制。到时候你可以关注一下。

Re: 区别只在数据绑定而已 by Yang Bo

数据绑定的部分现在社区都有很多解决方案了,如redux和mobx等


数据绑定部分,的确JavaScript社区有很多解决方案。不过Redux可能不能算“通用”的数据绑定,对前端开发者如何使用,限制重重。实际用起来会比直接用ReactJS还要繁琐。

Reddit上也有人提到了MobX。我简单看了一下,MobX的API数量要比Binding.scala多十倍,但MobX几乎所有功能Binding.scala都可以用同一个API(即bind)解决。我不明白为什么MobX需要这么多API。

如果你很熟悉MobX的话,我觉得你可以写一篇评测文章比较一下MobX和Binding.scala,我也好学习一下MobX的作者是怎么想的。

Re: 水水水 by 吴 浩

欢迎您提供一些 Flux 或者 Redux 的例子,和 Binding.scala 对比。



github.com/gitwuhao/antd-plus

这个项目是用redux写的,还实现了拖拽,不知道算不算复杂,反正用react写成之后感觉还不错。

Re: 值得支持的项目 by Lee Jerin

因为我感觉我被技术玩儿了,而不是我在玩儿技术。于是我痛定思痛,决定缩小范围,其他一切都咔掉,然后再这个范围内做出我自己满意的成绩来。


支持!加油 ↖(^ω^)↗

支持!加兴趣十足 by zheng jemmy

看您第一篇的时候,心理还有点不爽,本人是十足的ReactJs热爱者,但是最近开发了一整个较为复杂的应用,在使用react的时候的确遇到很多您说的语法噪音,虽然我还是会继续使用React,毕竟技术转型还是要成本的(对公司而言)哈哈,不过我对您的这个框架,确切的说,您的框架让我对scala很感兴趣,而且对您的技术水平和对技术的热爱感到由衷的佩服!

这个例子举的不好 by lao kuan

我就觉得前端框架就应该“轻”一点,就实现View的功能好了,在实际的项目中,持久化数据应该是在后端实现的。
你的例子中Tags这个数据都是“写死在前端界面中的”,但是在实际项目中,这些数据大部分都应该是存放在后台数据库中的吧?
这样一来tags: [ "initial-tag-1", "initial-tag-2" ]这个变量不太可能仅仅在前端框架中持久化保存吧?所以,我认为这个数据肯定就是这个组件的一个内部数据,当编辑完成之后可能会通过Ajax之类的传到后台。因此,我觉得实现这个组件,也就需要props传入就够了,组件内部仅仅需要保存变化之后的变量。只是这个props引入的变量需要用一个“全局的json的数据”完成和后台数据的交互。如果页面其他组件显示针对这个tags的变化,则也需要引用这个“全局的json的数据”。如果是在这样的背景下,React肯定是有优势的,因为React同时支持原生态的js语言,数据交换可以封装在json等格式中传递。
我对Binding.scala不太了解,但是第一感觉是语法糖不如js清楚,我觉得如果考虑和后台的数据交互,实现起来也不一定很简单。
我觉得你这个例子完全就是针对React的“数据单向”的缺点,本身React的组件就是“单向数据设计”,出现大量的数据嵌套数据传递,我觉得不是React的问题,而是整体设计上有问题,或者就是项目不适合用React。因此,你的题目叫“组件对复用性有害?”有点夸张的感觉。
但是我在用React的时候,确实没有出现过三层组件以上的数据传递。

Re: 这个例子举的不好 by Yang Bo

Binding.scala也是单向数据设计,处理大量数据嵌套完全没问题。
Binding.scala的用户反馈说: I really like Binding.scala for making small reusable, isolated components.
本文并不是针对“单向数据设计”,而是针对ReactJS繁琐、冗余的单向数据设计。


至于您说的后台数据交换,请您关注一下More than React (五)。InfoQ的编辑说近期就会发表出来。
Binding.scala的远程数据绑定功能,相比原生态的异步编程的巨大优势,可能会让您大吃一惊。

允许的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通知我

26 讨论

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


找回密码....

Follow

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

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

Like

内容自由定制

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

Notifications

获取更新

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

BT