北京团建网站,网站数据统计工具,鹰潭市网站建设公司,上海高端网站建一、Vue2是如何实现数据响应式的
Vue实现响应式变化的方式是通过数据劫持和发布-订阅模式。 数据劫持#xff1a;Vue通过使用Object.defineProperty()方法对数据对象的属性进行劫持#xff0c;使其能够在属性值发生变化时触发相应的操作。 发布-订阅模式#xff1a;Vue使用…一、Vue2是如何实现数据响应式的
Vue实现响应式变化的方式是通过数据劫持和发布-订阅模式。 数据劫持Vue通过使用Object.defineProperty()方法对数据对象的属性进行劫持使其能够在属性值发生变化时触发相应的操作。 发布-订阅模式Vue使用发布-订阅模式来监听数据变化并在变化时通知相关的订阅者更新视图。当数据发生变化时会触发相应的setter方法然后通知所有订阅者进行更新。
具体实现步骤如下 在初始化Vue实例时通过遍历数据对象利用Object.defineProperty()方法将每个属性转化为getter和setter。 在getter方法中将订阅者Watcher对象添加到当前属性的依赖列表中。 在setter方法中当数据发生改变时会触发该属性的所有订阅者的更新操作即将Watcher对象的update方法加入到异步更新队列中。 当所有同步任务执行完毕后异步更新队列会依次执行各个Watcher对象的update方法更新视图。
通过数据劫持和发布-订阅模式的结合Vue能够在数据变化时及时更新视图实现了响应式变化。
export function defineReactive ( // 定义响应式数据obj: Object,key: string,val: any,customSetter?: ?Function,shallow?: boolean) {const dep new Dep()// 如果不可以配置直接returnconst property Object.getOwnPropertyDescriptor(obj, key)if (property property.configurable false) {return}// cater for pre-defined getter/settersconst getter property property.getconst setter property property.setif ((!getter || setter) arguments.length 2) {val obj[key]}// 对数据进行观测let childOb !shallow observe(val)Object.defineProperty(obj, key, {enumerable: true,configurable: true,get: function reactiveGetter () { // 取数据时进行依赖收集const value getter ? getter.call(obj) : valif (Dep.target) {dep.depend()if (childOb) { // 让对象本身进行依赖收集childOb.dep.depend() // {a:1} {} 外层对象if (Array.isArray(value)) { // 如果是数组 {arr:[[],[]]}
vm.arr取值只会让arr属性和外层数组进行收集 dependArray(value) }}}return value},set: function reactiveSetter (newVal) {const value getter ? getter.call(obj) : val/* eslint-disable no-self-compare */if (newVal value || (newVal ! newVal value ! value)) {return}/* eslint-enable no-self-compare */if (process.env.NODE_ENV ! production customSetter) {customSetter()}// #7981: for accessor properties without setterif (getter !setter) returnif (setter) {setter.call(obj, newVal)} else {val newVal}childOb !shallow observe(newVal)dep.notify()}})
}
二、vue2中如何检测数组的变化
数组考虑性能原因没有用 defineProperty 对数组的每一项进行拦截而是选择重写 数组 push,shift,pop,splice,unshift,sort,reverse 方法。 数组中如果是对象数据类型也会进行递归劫持
数组的索引和长度变化是无法监控到的
检测数组的变化是通过重写数组原型上的相关方法来实现的。具体步骤如下 首先Vue会判断当前浏览器是否支持原生的数组方法。如果支持则直接重写数组原型上的方法并在重写的方法中添加对应的变异方法。如果不支持则创建一个新的对象使用Object.defineProperty来拦截数组变异方法。 在重写数组方法时Vue会先缓存原生的数组方法比如Array.prototype.push、Array.prototype.pop等。然后在重写的方法中先调用缓存的原生方法再根据不同的变异方法类型执行不同的操作比如添加响应式元素、触发依赖更新等。 Vue还会判断是否支持__proto__属性如果支持则直接将数组的原型指向重写的原型对象以便数组的原型链能够正常查找到重写的方法。如果不支持则遍历重写的原型对象将其中的方法拷贝到数组的实例上。
示例代码如下
// 是否支持原生数组方法
const hasProto __proto__ in {};
// 缓存原生数组方法
const arrayProto Array.prototype;
// 创建重写的原型对象
const arrayMethods Object.create(arrayProto);// 定义重写的原型方法
[push, pop, shift, unshift, splice, sort, reverse].forEach(function (method) {// 缓存原生数组方法const original arrayProto[method];// 重写原型方法def(arrayMethods, method, function mutator(...args) {// 调用原生数组方法const result original.apply(this, args);// 获取当前数组的__ob__属性即Observer实例const ob this.__ob__;// 数组变异操作的类型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;});
});// 将重写的原型对象设置到数组的原型上
const arrayKeys Object.getOwnPropertyNames(arrayMethods);
if (hasProto) {protoAugment(target, arrayMethods);
} else {copyAugment(target, arrayMethods, arrayKeys);
}通过以上代码Vue实现了对数组变化的检测并能够自动追踪数组的操作以实现响应式更新。