Vue 响应式原理是怎么实现的?
答:
-
响应式的核心是通过
Object.defineProperty拦截对数据的访问和设置 -
响应式的数据分为两类:
-
对象,循环遍历对象的所有属性,为每个属性设置 getter、setter,以达到拦截访问和设置的目的,如果属性值依旧为对象,则递归为属性值上的每个 key 设置 getter、setter
- 访问数据时(obj.key)进行依赖收集,在 dep 中存储相关的 watcher
- 设置数据时由 dep 通知相关的 watcher 去更新
-
数组,增强数组的那 7 个可以更改自身的原型方法,然后拦截对这些方法的操作
- 添加新数据时进行响应式处理,然后由 dep 通知 watcher 去更新
- 删除数据时,也要由 dep 通知 watcher 去更新
-
methods、computed 和 watch 有什么区别?
-
使用场景
- methods 一般用于封装一些较为复杂的处理逻辑(同步、异步)
- computed 一般用于封装一些简单的同步逻辑,将经过处理的数据返回,然后显示在模版中,以减轻模版的重量
- watch 一般用于当需要在数据变化时执行异步或开销较大的操作
-
区别
-
methods VS computed
通过示例会发现,如果在一次渲染中,有多个地方使用了同一个 methods 或 computed 属性,methods 会被执行多次,而 computed 的回调函数则只会被执行一次。
通过阅读源码我们知道,在一次渲染中,多次访问 computedProperty,只会在第一次执行 computed 属性的回调函数,后续的其它访问,则直接使用第一次的执行结果(watcher.value),而这一切的实现原理则是通过对 watcher.dirty 属性的控制实现的。而 methods,每一次的访问则是简单的方法调用(this.xxMethods)。
-
computed VS watch
通过阅读源码我们知道,computed 和 watch 的本质是一样的,内部都是通过 Watcher 来实现的,其实没什么区别,非要说区别的化就两点:1、使用场景上的区别,2、computed 默认是懒执行的,切不可更改。
-
methods VS watch
methods 和 watch 之间其实没什么可比的,完全是两个东西,不过在使用上可以把 watch 中一些逻辑抽到 methods 中,提高代码的可读性。
-
Vue其核心原理
Vue其核心原理就是其数据的响应式,讲到Vue的响应式原理,我们可以从它的兼容性说起,
Vue不支持IE8以下版本的浏览器,因为Vue是基于 Object.defineProperty 来实现数据响应的,而 Object.defineProperty 是 ES5 中一个无法 shim 的特性,这也就是为什么 Vue 不支持 IE8 以及更低版本浏览器的原因;
Vue通过Object.defineProperty的 getter/setter 对收集的依赖项进行监听, 在属性被访问和修改时通知变化,进而更新视图数据;
由于JavaScript 的限制 (以及废弃 Object.observe),Vue不能检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化过程,所以属性必须在 data 对象上存在才能让Vue转换它,这样才能让它是响应的。
数组响应式
src/core/observer/array.js
/**
* 定义 arrayMethods 对象,用于增强 Array.prototype
* 当访问 arrayMethods 对象上的那七个方法时会被拦截,以实现数组响应式
*/
import { def } from '../util/index'
// 备份 数组 原型对象
const arrayProto = Array.prototype
// 通过继承的方式创建新的 arrayMethods
export const arrayMethods = Object.create(arrayProto)
// 操作数组的七个方法,这七个方法可以改变数组自身
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* 拦截变异方法并触发事件
*/
methodsToPatch.forEach(function (method) {
// cache original method
// 缓存原生方法,比如 push
const original = arrayProto[method]
// def 就是 Object.defineProperty,拦截 arrayMethods.method 的访问
def(arrayMethods, method, function mutator (...args) {
// 先执行原生方法,比如 push.apply(this, args)
const result = original.apply(this, args)
const ob = this.__ob__
// 如果 method 是以下三个之一,说明是新插入了元素
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
// 对新插入的元素做响应式处理
if (inserted) ob.observeArray(inserted)
// 通知更新
ob.dep.notify()
return result
})
})
作者:李永宁
链接:juejin.cn/post/695082…
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。