Vue2.x响应式原理, Vue 与 React 响应式简单对比
文章目录
👉 配合 PPT 食用更佳 👈
实现的最终目标
|
|
对应的虚拟DOM会从
|
|
变成
|
|
第一步,监听data下边的所有属性,转换为响应式
思路
- 当data下的某个属性变化时,如何触发相应的函数?
方案:ES5中新添加了一个方法:[Object.defineProperty](),通过这个方法,可以自定义getter和setter函数,那么在获取对象属性或者设置对象属性时就能够执行相应的回调函数

代码如下:
|
|
引发了第二个问题,如果
data中的属性是一个对象还能触发我们的回掉函数么?比如说下边的demo1 2 3 4 5 6 7 8 9 10 11 12var demo2 = new Vue({ el: '#demo', data: { text: "before", o: { text: "o-before" } }, render(){ console.log("我要render了") } })
方案:用递归完善上边的响应式,需要在它开始对属性进行响应式转换的时候,前边加个判断,即如下
|
|
- 实际写的过程中发现调用data的属性时需要这样写
demo._data.text,肯定是没有demo.text这样写来的方便,所以就需要加一层代理进行转换
代码如下:
|
|
然后在构造函数中加上这么一句话
|
|
到此,我们的data属性已经变为响应式的了,只要data的属性发生变化,那么就会触发render函数。这也是为什么只有vue组件中的data属性才是响应式的,其他地方声明的值均不是响应式的原因。但是这里有个问题,即触发render函数的准确度问题!
第二步,解决准确度问题,引出虚拟dom
比如下边的demo
|
|
template中只用到了data中的name属性,但是当修改age属性的时候,会不会触发渲染呢?答案是:会。但实际是不需要触发渲染机制的
解决这个问题,先要简单说下虚拟dom。vue有两种写法:
|
|
由于vue2.x引入了虚拟dom的原因,这两种写法最终都会被解析成虚拟dom,但在这之前,他们会先被解析函数转换成同一种表达方式,即如下:
|
|
透过上边的render函数中的this.__h__方法,可以简单了解下虚拟dom
|
|
写一个简单的虚拟dom:
|
|
to do
回头看问题,也就是说,我需要知道render函数中依赖了data中的哪些属性,只有这些属性变化,才需要去触发render函数
第三步,依赖收集,准确渲染
思路:在这之前,我们已经把data中的属性改成响应式了,当去获取或者修改这些变量时便能够触发相应函数。那这里就可以利用这个相应的函数做些手脚了。当声明一个vue对象时,在执行render函数获取虚拟dom的这个过程中,已经对render中依赖的data属性进行了一次获取操作,这次获取操作便可以拿到所有依赖。
其实不仅是render,任何一个变量的改别,是因为别的变量改变引起(观察者模式),都可以用上述方法,也就是computed和watch的原理
首先需要写一个依赖收集的类,每一个data中的属性都有可能被依赖,因此每个属性在响应式转化(defineReactive)的时候,就初始化它。代码如下:
|
|
那么执行过程就是:
- 当执行render函数的时候,依赖到的变量的get就会被执行,然后就把这个 render函数加到subs里面去。
- 当set的时候,就执行notify,将所有的subs数组里的函数执行,其中就包含render的执行。
注:代码中有一个
Dep.target值,这个值时用来区分是普通的get还是收集依赖时的get
最后完整代码如下:
|
|
vue react响应式简单对比
综上发现,利用Object.defineProperty这个特性可以精确的写出订阅发布模式,从这点来说,vue是优于react的,在没经过优化之前,vue的渲染机制一定是比react更加准确的,为了验证这一说法,我用两个框架同时写了两个相同的简单项目进行对比。
没有对比就没有伤害:
- react项目地址:http://sirm2z.github.io/a_project/react-vue-test/react/index.html

- vue项目地址:http://sirm2z.github.io/a_project/react-vue-test/vue/index.html

通过对比发现,react在正常使用的过程中产生了多余的渲染,在移动端或者组件嵌套非常深的情况下会产生非常大的性能消耗,因此在使用react的过程中,写好react生命周期中的shouldComponentUpdate是非常重要的!
文章作者 ryan
上次更新 2017-05-04