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

精确数据绑定的优势

本系列的上一篇文章组件对复用性有害?探讨了如何在前端开发中编写可复用的界面元素。本篇文章中将从性能和算法的角度比较 Binding.scala 和其他框架的渲染机制。

\\

Binding.scala 实现了一套精确数据绑定机制,通过在模板中使用 bind 和 for/yield 来渲染页面。你可能用过一些其他 Web 框架,大多使用脏检查或者虚拟 DOM 机制。和它们相比,Binding.scala 的精确数据绑定机制使用更简单、代码更健壮、性能更高。

\\

ReactJS 虚拟 DOM 的缺点

\\

比如, ReactJS 使用虚拟 DOM 机制,让前端开发者为每个组件提供一个 render 函数。render 函数把 props 和state 转换成 ReactJS 的虚拟 DOM,然后 ReactJS 框架根据 render 返回的虚拟 DOM 创建相同结构的真实 DOM.

\\

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

\\

这样做有两大缺点:

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

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

\\

AngularJS 的脏检查

\\

除了类似 ReactJS 的虚拟 DOM 机制,其他流行的框架,比如 AngularJS 还会使用基于脏检查的定值算法来渲染页面。

\\

脏检查算法和 ReactJS 有一样的缺点,无法得知状态修改的意图,这使得 AugularJS 必须反复执行$digest轮循、反复检查各个ng-controller中的各个变量。除此之外,AngularJS 更新 DOM 的范围往往会比实际所需大得多,所以会比 ReactJS 还要慢。

\\

Binding.scala 的精确数据绑定

\\

Binding.scala 使用精确数据绑定算法来渲染 DOM 。

\\

在 Binding.scala 中,你可以用 @dom 注解声明数据绑定表达式。@dom 会自动把 = 之后的代码包装成 Binding 类型。

\\

比如:

\\
\@dom val i: Binding[Int] = 1\@dom def f: Binding[Int] = 100\@dom val s: Binding[String] = \"content\"
\\

@dom 既可用于 val 也可以用于 def ,可以表达包括 Int 、 String 在内的任何数据类型。

\\

除此之外,@dom 方法还可以直接编写 XHTML,比如:

\\
\@dom val comment: Binding[Comment] = \u0026lt;!-- This is a HTML Comment --\u0026gt;\@dom val br: Binding[HTMLBRElement] = \u0026lt;br/\u0026gt;\@dom val seq: Binding[BindingSeq[HTMLBRElement]] = \u0026lt;br/\u0026gt;\u0026lt;br/\u0026gt;
\\

这些 XHTML 生成的 CommentHTMLBRElement 是 HTML Node 的派生类。而不是 XML Node

\\

每个 @dom 方法都可以依赖其他数据绑定表达式:

\\
\val i: Var[Int] = Var(0)\@dom val j: Binding[Int] = 2\@dom val k: Binding[Int] = i.bind * j.bind\@dom val div: Binding[HTMLDivElement] = \u0026lt;div\u0026gt;{ k.bind.toString }\u0026lt;/div\u0026gt;
\\

通过这种方式,你可以编写 XHTML 模板把数据源映射为 XHTML 页面。这种精确的映射关系,描述了数据之间的关系,而不是 ReactJS 的 render 函数那样描述运算过程。所以当数据发生改变时,只有受影响的部分代码才会重新计算,而不需要重新计算整个 @dom 方法。

\\

比如:

\\
\val count = Var(0)\\@dom def status: Binding[String] = {\  val startTime = new Date\  \"本页面初始化的时间是\" + startTime.toString + \"。按钮被按过\" + count.bind.toString + \"次。按钮最后一次按下的时间是\" + (new Date).toString\}\\@dom def render = {\  \u0026lt;div\u0026gt;\    { status.bind }\    \u0026lt;button onclick={ event: Event =\u0026gt; count := count.get + 1 }\u0026gt;更新状态\u0026lt;/button\u0026gt;\  \u0026lt;/div\u0026gt;\}
\\

以上代码可以在ScalaFiddle实际运行一下试试。

\\

注意,status 并不是一个普通的函数,而是描述变量之间关系的特殊表达式,每次渲染时只执行其中一部分代码。比如,当 count 改变时,只有位于 count.bind 以后的代码才会重新计算。由于 val startTime = new Date 位于count.bind 之前,并不会重新计算,所以会一直保持为打开网页首次执行时的初始值。

\\

有些人在学习 ReactJS 或者 AngularJS 时,需要学习 key 、 shouldComponentUpdate 、 $apply 、 $digest 等复杂概念。这些概念在 Binding.scala 中根本不存在。因为 Binding.scala 的 @dom 方法描述的是变量之间的关系。所以,Binding.scala 框架知道精确数据绑定关系,可以自动检测出需要更新的最小部分。

\\
结论
\\

本文比较了虚拟 DOM 、脏检查和精确数据绑定三种渲染机制。

\\
 \\t\t\t

AngularJS

\\t\t\t
\\t\t\t

ReactJS

\\t\t\t
\\t\t\t

Binding.scala

\\t\t\t
\\t\t\t

渲染机制

\\t\t\t
\\t\t\t

基于脏检查的定值算法

\\t\t\t
\\t\t\t

虚拟DOM

\\t\t\t
\\t\t\t

精确数据绑定

\\t\t\t
\\t\t\t

数据变更时的运算步骤

\\t\t\t
\\t\t\t
  • \\t\t\t\t

    重复检查数据是否更改

    \\t\t\t\t\\t\t\t\t
  • \\t\t\t\t

    大范围更新页面,哪怕这部分页面根本没有修改

    \\t\t\t\t\\t\t\t
\\t\t\t
  • \\t\t\t\t

    重新生成整个虚拟DOM

    \\t\t\t\t\\t\t\t\t
  • \\t\t\t\t

    比较新旧虚拟DOM的差异

    \\t\t\t\t\\t\t\t\t
  • \\t\t\t\t

    根据差异更新页面

    \\t\t\t\t\\t\t\t
\\t\t\t
  • \\t\t\t\t

    直接根据数据映射关系,更新最小范围页面

    \\t\t\t\t\\t\t\t
\\t\t\t

检测页面更新范围的准确性

\\t\t\t
\\t\t\t

不准

\\t\t\t
\\t\t\t

默认情况下不准,需要人工提供`key`和`shouldComponentUpdate`才能准一点

\\t\t\t
\\t\t\t

\\t\t\t
\\t\t\t

需要前端工程师理解多少API和概念才能正确更新页面

\\t\t\t
\\t\t\t

很多

\\t\t\t
\\t\t\t

很多

\\t\t\t
\\t\t\t

只有`@dom`和`bind`两个概念

\\t\t\t
\\t\t\t

总体性能

\\t\t\t
\\t\t\t

非常差

\\t\t\t
\\t\t\t

\\t\t\t
\\t\t\t

\\t\t\t

这三种机制中,Binding.scala 的精确数据绑定机制概念更少,功能更强,性能更高。我将在下一篇文章中介绍 Binding.scala 如何在渲染 HTML 时静态检查语法错误和语义错误,从而避免 bug 。

\\

相关链接

\\

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)关注我们。

### 回答1: Vue和React虚拟DOM有一些不同之处,尽管它们的目的都是提高渲染性能和抽象渲染过程。 Vue使用双向数据绑定来实现虚拟DOM,这意味着当应用数据发生更改时,Vue会自动更新虚拟DOM以反映数据的变化,并且当用户更改界面时,Vue也会将这些更改同步到应用数据中。 React使用单向数据流来实现虚拟DOM,这意味着React只关注如何渲染虚拟DOM,而不关注如何将这些更改同步回应用数据中。当应用数据发生变化时,React会重新渲染虚拟DOM,以确保界面与应用数据同步。 总的来说,Vue的虚拟DOM实现更加方便和直观,但React的实现更加灵活,可以提供更多的控制和定制选项。 ### 回答2: Vue和React都采用了虚拟DOM(Virtual DOM)作为其底层实现之一,用以提高页面渲染的效率。虚拟DOM是一个轻量级的内存对象树结构,用于表示网页的DOM结构。它在实际的DOM更新之前,将所有的变更以最小的成本应用到虚拟DOM中,然后再与实际的DOM进行对比,找出需要更新的节点并进行相应的操作,以降低DOM操作的成本。 尽管Vue和React都使用了虚拟DOM,但它们在实际的实现上有一些不同之处: 1. 更新策略:Vue使用了双向绑定的机制,当数据发生变化时,它会自动更新相关的DOM节点。而React采用了单向数据流的原则,即数据的变化只能由上至下,从组件的父节点传递到子节点,当数据变化时,需要手动更新组件。因此在Vue中,虚拟DOM的更新更加自动化,而React需要手动管理虚拟DOM的更新。 2. 组件实现:Vue中的组件是通过配置对象来定义的,这使得组件可以在内部定义自己的模板和逻辑。而React则使用了JSX语法来定义组件的结构,JSX允许在JS代码中编写类似XML的结构。因此在Vue的组件中,虚拟DOM的实现更加灵活,可以在组件内部自由组织结构和逻辑。 3. 性能优化:Vue通过侦听数据的变化来自动更新虚拟DOM,而React则通过Diff算法来高效地计算出需要更新的节点。Vue在数据量较小的情况下具有更高的性能,但在数据量较大的情况下,React的Diff算法相对更为高效。 综上所述,Vue和React虚拟DOM在更新策略、组件实现和性能优化等方面存在一些差异。选择Vue还是React,最终要根据实际项目需求和个人喜好来决定。 ### 回答3: Vue和React都使用了虚拟DOM(Virtual DOM)来优化页面的渲染效率,但它们在实现细节上有一些不同。 首先是更新机制的不同。在Vue中,每个组件都有自己的虚拟DOM树,当组件状态变化时,Vue会通过比较前后两颗虚拟DOM树的差异来更新真实的DOM。而在React中,所有组件共享同一个虚拟DOM树,当组件状态变化时,React会通过比较前后两个虚拟DOM树的差异来更新真实的DOM。 其次是数据绑定的不同。在Vue中,可以使用双向数据绑定,即当数据发生变化时,视图会自动更新;而在React中,数据的变化只能通过显式的改变状态来触发更新,没有Vue中的自动更新机制。 另外,Vue的虚拟DOM中使用了一些特殊技术来优化性能,如模板编译、静态节点优化、异步渲染等。而React虚拟DOM则相对简单,更加灵活,可以配合各种工具和库进行更多的自定义操作。 最后,Vue和React在使用上也有差异。Vue通常使用单文件组件的形式,将HTML、CSS和JavaScript写在同一个文件中,更加便于编写和维护;而React则更加灵活,可以与其他工具和库进行组合使用。 综上所述,虽然Vue和React都使用了虚拟DOM来提高性能,但它们在实现细节和使用上存在一些区别。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值