这里提一点,前端三大框架(Angular,React,Vue)的数据驱动来更新视图的原理,即 MVVM 的实现。
为什么数据发生变化,绑定的视图就会刷新了呢?
以下是我的个人理解,仅供参考:
在还是 jQuery 的时代,当在 js 中改变了某个变量的数据,而这个变量是需要在 Html 中显示出来的。那么,这个时候,我们的做法也就是通过 DOM 先获取到显示该变量的视图元素,然后借助 DOM API 来更新这个视图元素,是吧。这是原始的方式。
那么,不管三大框架都做了些什么,MVVM 的实现原理是什么,它们最终其实也都还是要通过操纵 DOM API 来更新视图元素,与原始方式的区别就是,这部分操纵 DOM 的工作,由框架来负责,我们无需关心了,只需要关心数据的变化工作即可。
好处就是,我们可以更关注于业务逻辑的编程,而无须再去为如何操纵 DOM 树而烦恼。
那么,既然框架要来帮我们处理这部分工作,它们实现的关键点就在于,如何知道,我们对数据进行了更新?
什么意思?也就是说,这部分工作由我们自己来做时,我们是能够明确的知道什么时候该去操纵 DOM 树了,不就是我们对数据进行更新的时刻吗。但,框架并不知道我们什么时刻会对数据进行更新。
所以,回想一下,你在使用三大框架时,是不是每个框架基本都有一些注意事项,或者说它的规定?
比如说:
react 要求修改 state 局部变量时,得通过 this.setState(...)
vue 要求得声明在 data 中的变量,当它变化时才会被追踪到,以更新视图
为什么这些框架会有这些要求,或者说这些规定?
因为它需要知道我们到底什么时刻会去对数据进行更新啊。
对于 react 来说,当我们需要更新变量的数据值时,都通过调用它的方法,那么,它自然就知道我们什么时候更新了数据了。
对于 vue 来说,虽然我们更新数据时是直接对变量进行赋值操作,但实际上,声明在 data 中的这些变量,都会被转换成存取器属性,也就是 set 和 get。那么,当我们直接对变量的赋值操作,其实会去执行 set 的内部逻辑,而 vue 只需要在这里就可以获取我们更新数据的时机了。
那么,对于 Angular 呢?好像使用 Angular 过程中,并没有需要遵循什么规定。
这是因为,Angular 的实现原理并不类似于 react 和 vue。
react 和 vue 的原理类似于主动通知的模式,也就是,当我发生变化了,那我就通知你一下,你就需要去做些更新处理了。
而 Angular 的原理,类似于被动轮询的模式。也就是,你不知道我什么时候会变化,那么你就在我有可能会变化的情况下,不断的读取我的值,比对一下,看看有没有发生变化。
验证 Angular 的这种原理的猜测很简单,你在页面上某个元素绑定个方法,方法内打个日志,然后你滑动下页面试试看,看看日志是不是一直在输出。
总结一下:
三大框架实现的原理其实有所差异
react 是通过调用 setState()
方式来告知视图刷新;
vue 是通过将声明在 data 中的数据属性转换为存取器数据(set 和 get)的方式,来监听数据变化的时机;
angular 则是在会触发视图变化的情况下,主动去检测绑定的数据源,比对下是否有发生变化来判断是否需要刷新视图。
当然,以上的理解仅仅是很浅的层面,只是理清了三大框架是如何知道我们数据更新的时机这个问题。
对于三大框架来说,他们的视图刷新并非是这么简单的实现。比如说:
对于 vue,当它监听到某个数据源发生变化了,但它并不会立马去刷新视图,而是将相关的信息先记录起来,等待一个固定频率的下个帧信号,在这期间发生变化的数据源都会被记录起来。直到信号来的时候,再一起去处理这次的视图刷新。
这也是为什么一些 vue 的书中或者项目中,会有要求说某些代码需要放在下一个 tick 中去执行,因为数据源刚发生变化时,页面不一定就更新了。
原理跟 Android 的屏幕刷新机制很像,就都是以一个固定频率来刷新页面,在每个帧信号之间,只是收集发生变化的视图,或者说,只更新虚拟 DOM,并不会去更新真实的页面。直到帧信号到的时候,再一次性的批处理地刷新页面。
对于 Angular 来说,虽然它是不断轮询的方式来检测数据源是否发生变化,但并不意味着时时刻刻都在轮询检测,而只在一些有可能导致视图更新的场景下才会去检测。比如说,滑动页面,比如说 settimeout 事件。
这也是为什么在 Angular 项目中,经常会看到一些 settimeout(..., 0) 这样的操作。
以上,个人的理解,如有错误,欢迎指点一下。