React diff diff算法的作用:数据更改,生成相应的虚拟DOM,与真实DOM作对比,通过diff算法,对比出有变化的部分,通过原生的dom操作只更新有变化的部分。这样就不用想原生DOM,有一处修改就会造成整个页面的整改。
传统diff算法:循环递归地方式遍历节点,时间复杂度为O(n^3)
react的diff:通过最少的步骤,将虚拟DOM转化为真实DOM,时间复杂度为O(n)
树的diff:跨层级的dom操作很少,可以忽略不计
通过uodateDepth对虚拟数进行层级控制 只对两棵树同一层级进行比较,节点不存在就直接删掉,遍历一次就能完成比较 如果出现跨层操作,比如a从原位置移动到b位置,就删除原位置的a,在b新建a,所以a以下的树会被重建,所以官方不建议跨层操作,建议通过隐藏显示来操作,比如visibility:hidden 组件diff:相同类型组件生成相同树结构;不同类型组件生成不同树结构
同一类型的组件,按原策略(层级比较) 同一类型的组件,a变化时,虚拟DOM没有变,可以在这里进行shouldComponentUpdate操作 如果被判定为不同类型的组件,删除原组件,构建新组件 元素diff:同一层级的一组子节点,通过唯一id进行区分
插入:如果元素不在原集合,就插入
删除:d在集合中,但是d节点被更改,不能更新复用,就删除重建;或者d没有了,直接删除
移动:d在集合中,并且没有变化,只是换了位置,通过key来区分并移动。所以通过map出来的元素,如果不加key,就会报错
:warning:比较元素的新旧index:lastIndex,index;只有index<lastIndex才会移动,也就是往右移动,往左不移动。所以!!如果在一长串集合中,如果最后一个元素移动到第一个,前面的所有元素都会移动,性能不佳要尽量避免
所以不难理解为什么不要把index作为key值(当数组变化了,index也会变化,从而lastIndex和index不再能正确代表新旧index),应该用元素的唯一标识id作为key值
Vue diff 虚拟DOM是将真实的DOM的数据抽取出来,以对象的形式模拟树结构。(虚拟DOM和oldVNode都是对象) 比较新旧节点的时候,比较只会在同层级进行,不会跨层级比较。 当数据改变,set方法会通知所有订阅者watcher,订阅者会调用patch(oldVnode, Vnode)给真实的DOM打补丁,更新相应的视图。是否是同一个VNode?不是就替换,是就继续进行patch patch接收oldVnode和Vnode来代表新的节点和之前的旧节点 判断两节点是否值得比较,值得就继续比较;不值得直接替换 当确定值得比较后,会对两个节点指定patchVnode方法 找到对应的真实DOM,称el,判断Vnode和oldVnode是否指向同一对象,如果是直接return 都有文本对象且不相等,就将el的文本节点设置为Vnode的文本节点 如果oldVnode没有子节点,而Vnode有,将Vnode子节点真实化后添加到el 如果两个都有子节点,执行updateChildren函数比较子节点!!! updateChilldren(parentElement,oldCh,newCh) oldCh旧的子节点,newCh新的子节点提取出来,oldCh和newCh各有两个头尾变量,startIdx和EndIdx;2个变量互相比较。涉及到4种比较方式,如果4种都不成功,就用key来比较,比较过程中,变量会向中间靠拢,一旦startIdx>endIdx,表明oldCh和newCh至少有一个已经遍历结束。如果old先结束,那么newCh中的节点按照其index插入到DOM中去;如果newCh先遍历完,就将真实DOM中多余的节点删掉
vue和react的diff算法,都是忽略跨级比较,只做同级比较。vue diff时调动patch(runtime/index.js中将patch方法挂载到vue的原型属性__patch__上,patch的使用是当我们调用vue实例的$el时,即调用patch函数)函数,参数是vnode和oldVnode,分别代表新旧节点。
-
vue比对节点,当节点元素类型相同,但是className不同,认为是不同类型元素,删除重建,而react会认为是同类型节点,只是修改节点属性
-
vue的列表比对,采用从两端到中间的比对方式,而react则采用从左到右依次比对的方式。当一个集合,只是把最后一个节点移动到了第一个,react会把前面的节点依次移动,而vue只会把最后一个节点移动到第一个。总体上,vue的对比方式更高效。
虚拟DOM和DOM: 首先,在浏览器中js引擎和dom引擎(计算、绘制)互相独立,但是都运行在浏览器的主线程中。 js代码调用了dom api之后会挂起js引擎、转换传入参数数据、激活dom引擎。 等页面完成重绘之后再激活js引擎继续执行。 如果进行频繁的DOM API调用,引擎之间的切换会造成大量的性能损耗,若每次dom操作都引起了重排,那将引起更大的性能损耗。
VDOM和DOM的对比:
- VDOM是存在与内存中描绘真实DOM的一个对象
- 修改VDOM不会引起重绘和重排
- VDOM进行频繁的修改,最后一次性比较得出DOM中需要修改的部分,最后在真实DOM中只进行重排和重绘。
React和Vue的区别: 都是MVVM框架 且都是响应式、组件化的。 都有自己的路由react-router, vue-router 都使用了在js中写html的语法, .jsx .vue .jsx中样式需要额外引入, 而.vue中可以直接写style
不同之处: 更新过程 React通过调用组件的setstate方法之后更新组件的state之后调用render方法生成新的VDOM,与老的VDOM进行DOM DIFF得到需要变更的部分再将结果patch到页面的真实dom上来达到更新的效果。 Vue呢则是基于观察者模式在初始化vm对象的时候通过Object.defineProperty进行依赖收集和数据劫持,解析模版的时候根据指令生成wather,比如文本节点{{property}},{{}}就是一个指令,再比如input节点上的v-model='property'指令,会为这个input标签的input事件添加一个方法,把input的值赋给vm.data.property。每个wathcer都记录着一个dom元素和一个property,再将这些wather收集到一个Dependence中,在给vm.property赋值的时候就会触发set方法,同时对Dep中watcher进行遍历,对有相同的property的watcher调用它的update方法去更新它所对应的dom节点。