新老React版本的架构差异点

1,202 阅读6分钟

在学习react的过程中,我始终有一个疑问,那就是react为什么要升级,在升级前后在我的日常工作中并没有体会到新框架带来的更高的价值呀,无非就是更换和新增了很多api,非要给function component加上一个hooks到底有什么意义呢?

带着这样的疑问,我才想起去阅读了解react架构和源码的东西,不然我可能真的找不到动力去阅读。

在阅读的过程中,疑问越来越多,这也让我对react的兴趣越来越大,同时,也踩了很多坑,react思想很高深,确实有一定的难度。

本文将从宏观的层面了解一下react的架构。

如果只是单纯的告诉大家我理解的react是什么,未免太过于苍白无力,各位小伙伴可能也不是很想去看。所以我仔细想了想,最终我想提出一个问题,给大家一个阅读下去的道理:我们为什么需要react?或者说:react到底解决了什么问题。

我也是为了解决这个疑问,开始去了解react是如何从零到一被开发出来的。

我一直向着上游寻找,大家都知道的是:在三大框架发展起来之前,我们通常会使用jquery来进行前端的开发,jquery是一个js框架,它仅仅是封装了原生js的api,带来的价值就是兼容性问题再也不足考虑了,可是,似乎它和三大框架似乎没有什么矛盾的地方。那就是,方向偏了。

我之后又注意到伴随着三大框架发展起来的一个概念mvvm,我觉得问题肯定出在这里了。我回想刚开始学习的时候经典的mvc架构升级到mvvm的过程,正如这两个缩写中的区别一样,这两个架构最大的区别也在于c和vm。最开始的c负责在view和model之间充当胶水层,同时处理复杂的逻辑过程和渲染过程,不过m-v-c的职责总是会有各种难以区分的混合。这导致mvc模式变种或者说具体实现很混乱,后来,又提出了mvp的概念,也就是说,v只做页面渲染,m只做数据获取和转换等底层处理,其他的事通通交给c处理,再给c起个外号-p。在发展下去,人们就发现了一个非常头疼的事情,尽管逻辑总是变化,但是我们总是要频繁的去调用与业务无关的渲染api(DOM)把数据展示在页面上,这种大量的重复的体力劳动,需要我们不断的去做。于是人们首先就会想到,把数据渲染到页面上,这是不是能够封装一下。最后,前辈们完成了这件事,并把这一层操作叫做vm。于是,架构最终变成了m-v-vm。这就是架构的演变过程,这和三大框架有什么关系呢?三大框架其实是为了更好的实现和服务vm。这样,我们就理解了三大框架的由来了。

这样我就知道了,react是为了更好的实现vm,那么它和其他框架相比又有什么不同呢。

其实我自己也实现过template,使用js实现的渲染模板。对于template来说,他的公式真的很简单就是data->render(data)的过程,在这个过程中,首次渲染几乎没什么问题,关键在于更新的逻辑如何些。

有两种方式:1. 函数式编程,render之后返回一个函数,这个函数就是update方法,使用的时候将newData传递进去,newData会替代之前的data。2. 响应式编程,在data传递进去之后,利用js的api(defineProperty或proxy)进行数据监听,当数据发生变化的时候,自动响应。我没能想出第三种更好的方法。

但是我当时做template的时候,选用了第一种方式,因为当时担心jsapi的兼容性问题。一开始,我都没有考虑更新的问题,只是完全使用新的data重新渲染,大家可以想象我更新了一个小小的数据带来的页面渲染那壮观的场景。后来我也在寻找如何实现碎片更新的方法,所以我就了解了一下虚拟dom,这给了我很大的启发,这个想法真的伟大。后来,我发现,react正是这么做的,我就非常开心。只不过,react处理的比我好多了而已。

react将架构分为两层,第一层叫做reconciler,第二层叫做renderer。欠着负责找出变化的组件,后者负责将变化的组件渲染到页面上。这样做相比于我的template好在哪呢,其实玄机全在reconciler的逻辑里面,我在做template的时候,就是使用的html语法的字符串,读取innerHTML-->替换字符串-->设置innerHTML,这样做不仅安全性得不到保障,维护也很困难。reconcoler的处理方法就比较好,使用createElement来创建虚拟DOM,同时设计出了jsx语法,使用者就几乎感觉不到自己与写html的差异了。虚拟DOM与真实的DOM一一对应,当页面发生变化的时候,可以通过对比虚拟DOM树,就可以标记出变化和需要执行的动作。最后,将变化列表交给renderer,renderer在不同的平台有不同的实现,但是API是统一的,拿到变化列表的renderer就会递归执行相应的动作(插入,更新,删除等等)。经过这样的一系列算法,最终就可以实现哪个数据变了,就对应的更新哪个标签。

这就是react15的核心逻辑了。是不是很完美,既实现了vm,又能动态更新。追求完美的大佬们可不这么认为,他们认为,这个react只是实现了功能,但是细节还能完善,问题就出在了我们讨论不多的renderer上,仔细一想也是,这种递归的方式有点暴力,如果是一个上千条数据的列表,那更新一次岂不是会卡顿。如果,我是说如果,再遇到这种情况的时候,我们可以中断一下数据的渲染,先处理用户觉得更高优的事情,比如用户点击了按钮,一定想要更快得到响应,这岂不是很好。没错,react大佬们也是这样想的,于是,他们对react进行了重构和升级。

既然想要在渲染的时候可以暂停和恢复,那就不能让渲染过程是同步的了,需要引入一个异步机制。这个异步机制叫做schduler。架构就变成了schduler,reconciler和renderer了,分别负责管理任务优先级,进行调度任务,使高优先级任务先进入reconciler进行协调。schduler和reconciler都工作在内存中,可能会随时中断,所以,renderer不会再和reconciler交替执行,而是等到reconciler更新完所有的数据后,统一将动作提交给renderer进行同步的渲染操作。这样,中断的可能性也不必担心了。