从底层实现来看修改数据:在react中,组件的状态是不能被修改的,setState没有修改原来那块内存中的变量,而是去新开辟一块内存; 而vue则是直接修改保存状态的那块原始内存。
所以经常能看到react相关的文章里经常会出现一个词"immutable",翻译过来就是不可变的。
数据修改了,接下来要解决视图的更新:react中,调用setState方法后,会自顶向下重新渲染组件,自顶向下的含义是,该组件以及它的子组件全部需要渲染;而vue使用Object.defineProperty(vue@3迁移到了Proxy)对数据的设置(setter)和获取(getter)做了劫持,也就是说,vue能准确知道视图模版中哪一块用到了这个数据,并且在这个数据修改时,告诉这个视图,你需要重新渲染了。
首先需要强调的是,上文提到的“渲染”“render”“更新“都不是指浏览器真正渲染出视图。而是框架在javascript层面上,调用自身实现的render方法,生成一个普通的对象,这个对象保存了真实dom的属性,也就是常说的虚拟dom。本文会用组件渲染和页面渲染对两者做区分。
每次的视图更新流程是这样的:
- 组件渲染生成一棵新的虚拟dom树;
- 新旧虚拟dom树对比,找出变动的部分;(也就是常说的diff算法)
- 为真正改变的部分创建真实dom,把他们挂载到文档,实现页面重渲染;
由于react和vue的响应式实现原理不同,数据更新时,第一步中react组件会渲染出一棵更大的虚拟dom树。
fiber是什么
上面说了这么多,都是为了方便讲清楚为什么需要react fiber:在数据更新时,react生成了一棵更大的虚拟dom树,给第二步的diff带来了很大压力——我们想找到真正变化的部分,这需要花费更长的时间。js占据主线程去做比较,渲染线程便无法做其他工作,用户的交互得不到响应,所以便出现了react fiber。
react fiber没法让比较的时间缩短,但它使得diff的过程被分成一小段一小段的,因为它有了“保存工作进度”的能力。js会比较一部分虚拟dom,然后让渡主线程,给浏览器去做其他工作,然后继续比较,依次往复,等到最后比较完成,一次性更新到视图上。