问题,vue很好上手,但是vue是如果做到双向数据绑定,最终又是如何渲染到dom上的呢。
vue
1、初始化vue的时候,在dom处理方面,vue会把template转化成render方法,拿到render方法后,转化成虚拟dom,在数据方面,会把data里的数据变成响应式的数据,每个节点都是响应式的。
2、对于普通对象的更新:
Object.defineProperty({
get: () => {
/**
* 1、依赖收集,
* 2、收集是watch里完成的,new Watch的时候就会触发get
* 3、在watch构造函数里,会把一个全局的target对象指向this,也就是一个watch实例
* 4、把watch放进dep队列
* 5、把target=undefined
*/
},
set: () => {
/**
* 1、数据发生改变的时候,触发此钩子,
* 2、遍历当前dep队列里的watch对象,以此执行,watch.update,更新对象在process.nextTick里放着,所以是批量更新
* 3、在更新的过程中,vue根据这些数据,生成虚拟dom,新的虚拟dom和旧的虚拟dom进行比较,将差异的部分,更新到真实的dom树上
* 4、虚拟dom直接的diff比较算法,vue参考了linux文件系统的文件比较算法
*/
}
})
3、对于数组的更新
依赖收集:和普通对象一样在get里收集变化,把watcher放入dep,在下发更新:vue劫持了原生数组的push,slice,splice,shift,unshift等引起数组变化的方法,在高版本浏览器中,通过数组实例的__proto__指向Array.protype,劫持,在低版本的浏览器里直接拿到实例,array.push = Array.prototype.push;然后再下发更新;
4、patch
1)、下发更新,vue会遍历dep里的watch,由watch执行update方法
2)、为什么要引入虚拟dom,在vue1.0时代,没有引入虚拟dom,vue通过watch准确的知道每一个节点的变化,及时更新,但是这样做,更新粒度精确到了每一个数据节点,在大型项目里内存消耗严重。在vue2.0时代,vue把每一个组件只对应一个watch,组件里的数据变化,只会进一个watch,执行dep里的watch本身是在proceess.nextTick里,会批量更新操作,最后更新到数据会形成一个虚拟dom,然后和旧树进行比较,拿得到的差异,更新到真实dom上。
react
1、react是单向数据流,react在初始化的时候,也会把真实的dom转化成虚拟的dom,vnode是fiber对象节点。
2、react的setState,是通过setState来完成的,setState的更新机制,离不开react的事务机制,Traction对象就是react的事务,Traction整理采用了AOP(装饰者模式)
Traction.before(() => {isBatchingUpdate = true})
Traction.after(() => {isBatchingUpdate = false})
Traction执行的时候,中间的setState处于isBatchingUpdate = true,所以,处理批量更新阶段的state不能立即patch,会放到任务队列里,等都执行完了,触发after,然后,可以真正的更新dom
3、react的视图更新,在事务结束时触发,如果更新是一个同步的过程,会影响渲染,造成卡顿(放到异步队列里一样会卡顿),react在16版本之后模拟了浏览器的requestIdlecallback,做任务调度,这样在渲染时候不会有任何影响,在渲染的每一帧后如果还有时间在做数据的计算更新。
总结,vue可以做到更小粒度的更新,而react做不到,这是由数据驱动决定的,vue的底层diff算法参考linux的文件比较算法,在效率上比react的diff更好一点,但是react实现了requsetIdleCallback,让计算不影响渲染。更加流畅。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。