日记

日记

  • Diary
  • GitHub

›All Blog Posts

All Blog Posts

  • 再看高程3
  • 面试
  • 个人简历
  • react的高性能
  • webpack源码学习
  • 树莓派
  • 布局
  • 模块化
  • 攻击与安全
  • 异步
  • TODO
  • React状态管理比较
  • 性能优化
  • HTTP缓存
  • (译)JS中数据的改变与发现
  • CSS杂记
  • XML与SVG
  • 个人简历
  • Workspaces是什么
  • NPM包测试之低高级策略
  • 谈谈version
  • 减肥之道
  • 问题汇总
  • 一个nginx配置
  • 好用的工具
  • 好用的npm包
  • 流行框架学习对比

(译)JS中数据的改变与发现

June 23, 2018

Shiyon Yin

原文传送门

比较Angular,Ember,React,Backbone的角度有很多,但是也许比较他们是如何管理状态的角度是最有趣的。

投影数据

我们可以理解为将我们的数据投影到屏幕上的。比如JS中的对象,数组,字符串是源头,HTML中的forms,links, buttons,images等是显示屏幕上的内容。

我们称之为渲染过程。我们可以认为是数据到可视化界面的投影。当我们根据数据渲染模板的时候,我们得到了代表了我们 数据的DOM(HTML)。

onchange_base

平常来说这是没有什么问题的。

假如数据随着时间的改变而改变的话,这就比较有挑战性了。比如用户的操作导致了数的变化,或者什么发生了什么改变了数据。 UI需要体现出这些变化。更重要的是,重新构建DOM是花费昂贵的。我们希望最小化的更新节点

onchange_change

这可比只渲染一次UI困难多了,因为牵涉到了状态的变化。我们就从这里探讨上面框架的解决方案与不同之处。

服务端渲染:重置全局

没有改变,全局不可变

在大JS的时代之前,每一个点击,没一个表单提交,都会将页面unload掉,从后端请求整个渲染的页面,回来之后,再次渲染。 这就是所谓的服务端渲染。

onchange_reload

这种方法前端是不管理任何state的,都由后端处理,前端只是提供下html,css,或许有点点JS。

显而易见这种方法速度很慢

  1. UI需要全部渲染
  2. 需要走后端请求,一去一回要不少时间

第一代JS:手动重新渲染

我不知道哪些应该重新渲染,你来搞明白

总的来说,数据改变,发出事件,重新渲染UI你来决定。

第一代框架如backbone, ext, dojo第一次在浏览器中引入了data model,同时也是第一次 我们需要改变state在浏览器端。data model的内容改变之后需要你来获取改变然后改变UI. 数据改变的时候回触发一些事件,但是重新渲染UI是你的责任。

onchange_manual

到底是渲染一大部分还是渲染一小部分就由你来决定了。灵活性很大,但同时不要忘了性能。

手动重新渲染解决方案

Ember.js:数据绑定

我知道什么变化了,也知道哪里需要重新渲染,因为我控制了model和view

总的来说,通过我设计的API来控制Model

和backbone一样,Ember也会在数据改变的时候发出事件,不同的是,我们把UI绑定到data model上, 也就是说,有一个数据变化的监听器,在监听器里可以和UI做绑定。这个监听器知道在接收到数据变化的时候 如何更新UI。(我们可以通过watch来获得变化,并进行UI的绑定)

onchange_kvo

最大的不好的地方是,Ember必须永远知道数据的变化,这就要求我们使用Ember设计的一套API。

AngularJS:脏检查

我不知道什么发生了改变,所以我就检查下所有地方好了

虽然AngularJS也在着手解决手动重新渲染的问题,但是它是从另一个角度解决问题的。

当我们通过angular模板渲染{{foo.x}}的时候,angular不仅渲染数据,还为这个特殊的值创建了的一个watcher, 从此以后,只要有变化,它就检查watcher中的值是否变了,如果变了就在UI中重新渲染这个值。整个过程就是脏检查。

onchange_watch

不好的地方就是,当改变发生的时候,angular并没有深入的探测到具体是哪个数据发生了改变。所以,只要一有情况发生, 所有的watcher都要跑一遍。

听起来这是个性能的噩梦,但实际上还是挺快的,因为仅仅是纯JS的逻辑执行,没有牵涉到DOM的更新。但是当UI十分庞大的时候, 或者需要频繁渲染的时候,额外的优化技巧就是必须的了。

另外提一下,ES7中的Object.observe对Ember和Angular会十分有帮助,因为这给出了原生的watching在属性的改变上面。

React:VirtualDOM

我不知道发生了哪些改变,所以我就重新渲染下,看看与之前有什么不同

React和Angular类似,不需要data modal API的支持。那么React是如何根据数据的变化来解决UI的更新的呢?

React好像将我们带到了服务端渲染的方式。React的做法是从头到尾的渲染了整个UI。

这听起来是低效的,如果这就是故事的结尾,那确实是。然而,React 用了特殊的方法进行重新渲染。

当React UI进行渲染的时候,它首先渲染到Virtual DOM里面,并不是真正的DOM,而是一个轻量级的,纯粹的对象和数组的 JavaScript数据结构,这个数据结构代表了真实的DOM对象图。然后一个单独的进程采用该DOM结构在屏幕上渲染出真实的DOM结构。

onchange_vdom_initial

当数据改变的时候,一个新的Virtual DOM被从头的创建。新的Virtual DOM中包含了变化的值。React从这两个Virtual DOM执行 diff算法。来获取变化的地方。而且只有那些被改变的地方才会被真实DOM重新渲染。

onchange_vdom_change

Immutable-js

尽管React的Virtual DOM已经很快了,但是在UI很大或者需要频繁渲染的时候还是会出现瓶颈。

问题是真的没有方法能够渲染整个DOM,(UI太大渲染不过来,渲染太快,还没渲染完就需要渲染洗一次了)。除非想Ember一想引入 一套data model API。

一个有效的解决的方法就是使用immutable,这和React的Virtual DOM很般配。

immutable是这么一个原理。正如他的名字一样,你永远不能直接改变一个对象,当时我们可以基于这个对象产生一个新的版本。

使用immutable的意义就是,我们可以重复使用上次Virtual DOM的那些没有改变的Virtual DOM部分。

onchange_immutable

像Ember一样,我们不能使用原生JS对象,必须使用额外的API。但是不同点在于,这次并不是框架的需要,我们使用它因为这是一种 更好的管理state的方法。这不仅提高新性能,而且是一种文化的象征。

总结

对改变的发现是UI渲染的核心问题,JS库通过各种各样的途径来解决这个问题。

EmberJS能够检测到变化,当他们发生的时候,因为EmberJS通过API控制了Model和View,当你调用他们的时候你可以触发事件。

AngularJS在变化之后去检测变化,通过re-running在UI中所有的绑定来看看值是否发生了改变。

纯React通过re-render whole UI到一个Virtual DOM然后和就得版本比较来获得改变。不管发生了什么,都补丁到真实DOM上。

有immutable的React加强了纯React,通过让component快速标记那些没有改变来提升速度,这是性能的选择,同时也是文化的选择。

Recent Posts
  • 投影数据
  • 服务端渲染:重置全局
  • 第一代JS:手动重新渲染
  • 手动重新渲染解决方案
    • Ember.js:数据绑定
    • AngularJS:脏检查
    • React:VirtualDOM
    • Immutable-js
  • 总结
Copyright © 2018-2019 Shiyong Yin