Vue 源码
你想要的 vue 源码分析 · 18
之前有写了 一篇博客 ,给出了,通过数组下标给数组添加元素、修改数组长度或者直接对对象的属性的添加或删除,但是数据没变成响应式解决方法,并且讲解了为什么 vue3.0 不再使用 Object.defineProperty 而使用 Proxy 的实现依赖收集和派发更新原因 ,那么就从源码角度分析下,为什么上述操作对象或者数组,数据没有变成响应式的,但可以通过 Vue.set 或者 this.$set…
之前有写了 一篇博客 ,给出了,通过数组下标给数组添加元素、修改数组长度或者直接对对象的属性的添加或删除,但是数据没变成响应式解决方法,并且讲解了为什么 vue3.0 不再使用 Object.defineProperty 而使用 Proxy 的实现依赖收集和派发更新原因 ,那么就从源码角度分析下,为什么上述操作对象或者数组,数据没有变成响应式的,但可以通过 Vue.set 或者 this.$set…
之前有写了一篇博客,给出了,通过数组下标给数组添加元素、修改数组长度或者直接对对象的属性的添加或删除,但是数据没变成响应式解决方法,并且讲解了为什么vue3.0 不再使用 Object.defineProperty 而使用 Proxy 的实现依赖收集和派发更新原因,那么就从源码角度分析下,为什么上述操作对象或者数组,数据没有变成响应式的,但可以通过 Vue.set 或者 this.$set 变成响应式。
// src/core/observer/index.js
// set接收三个参数,target是数组或者对象,key是数组下标或者这对象的键值,val是添加的值或修改的值
function set(target, key, val) {
// 首先判断数组,并且是一个合法的下标
if (Array.isArray(target) && isValidArrayIndex(key)) {
target.length = Math.max(target.length, key);
// 则通过splice方法添加数组
target.splice(key, 1, val);
// 然后返回
return val;
}
// 如果key已经在target中了,则直接赋值返回,因为这样的变化是可以检测到的
if (key in target && !(key in Object.prototype)) {
target[key] = val;
return val;
}
// 把target.__ob__赋给ob,__ob__是在Observer构造函数执行的时候初始化的,表示Observer是一个实例
const ob = (target: any).__ob__;
// 如果target是一个vue实例或者ob有vmCount,有vmCount代表是一个根data
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn('Avoid adding reactive properties to a Vue instance or its root $data ' + 'at runtime - declare it upfront in the data option.');
return val;
}
// 如果target.__ob__不存在,就说明target不是一个响应式对象,直接赋值并返回
if (!ob) {
target[key] = val;
return val;
}
// 通过defineReactivev把新添加的属性变成响应式对象
defineReactive(ob.value, key, val);
// 然后通过ob.dep.notify()手动的触发依赖更新
ob.dep.notify();
return val;
}
defineReactive
把属性变成响应式
// src/core/observer/index.js
export function defineReactive(obj: Object, key: string, val: any, customSetter?: ?Function, shallow?: boolean) {
// 值是对象或者数组,值是一个observer
let childOb = !shallow && observe(val);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
const value = getter ? getter.call(obj) : val;
if (Dep.target) {
dep.depend();
if (childOb) {
// 在getter中判断了childOb,调用了childOb.dep.depend() 收集了依赖,这就是为什么执行Vue.set的时候可以通过
// ob.dep.notify() 能够通知到 watcher,从而新增的对象也可以检测到变化,如果对象新增的值为一个数组,那对数组的每一个元素也进行依赖收集
childOb.dep.depend();
if (Array.isArray(value)) {
dependArray(value);
}
}
}
return value;
}
// ...
});
}
Observer 是一个类,作用是给对象属性添加 getter 和 setter
// src/core/observer/index.js
export class Observer {
constructor(value: any) {
this.value = value;
this.dep = new Dep();
this.vmCount = 0;
def(value, '__ob__', this);
// 新插入的值如果是数组
if (Array.isArray(value)) {
// protoAugment方法直接把target.__proto__ = src,原型直接修改src
// copyAugment方法先遍历keys,通过 Object.defineProperty 去定义它自身的属性值
// 现代浏览器大部分执行protoAugment,所以就把value原型指向arrayMethods
const augment = hasProto ? protoAugment : copyAugment;
augment(value, arrayMethods, arrayKeys);
this.observeArray(value);
// 对于数组则会调用observeArray 方法
// observeArray是遍历数组再次调用observe方法
// observe 的作用是用来检测数据变化
} else {
// ...
}
}
}
// src/core/observer/index.js
// protoAugment方法直接把target.__proto__ = src,在原型上直接修改src
function protoAugment(target, src: Object, keys: any) {
/* eslint-disable no-proto */
target.__proto__ = src;
/* eslint-enable no-proto */
}
// copyAugment方法先遍历keys,通过 Object.defineProperty 去定义它自身的属性值
function copyAugment(target: Object, src: Object, keys: Array<string>) {
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i];
def(target, key, src[key]);
}
}
// src/core/observer/array.js
// arrayMethods先继承了Array,然后对能改变数组的本身的数组内置方法重写
// 对能增加数组长度的 3 个方法 push、unshift、splice做了判断
// 获取到插入的值,把新添加的值变成了一个响应式对象, ob.dep.notify()手动触发依赖更新,
import { def } from '../util/index';
const arrayProto = Array.prototype;
export const arrayMethods = Object.create(arrayProto);
const methodsToPatch = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function(method) {
// cache original method
const original = arrayProto[method];
def(arrayMethods, method, function mutator(...args) {
const result = original.apply(this, args);
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);
// notify change
ob.dep.notify();
return result;
});
});
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论