首页 > 其他分享 >vue2 数组响应式一

vue2 数组响应式一

时间:2023-02-10 11:55:21浏览次数:47  
标签:key __ const vue2 dep 响应 数组 data

继上文,监听data后,data中的每个属性都有响应式效果,那么下面这种情况会触发响应式吗?

import { observe } from "./reactive";
import { Watcher } from "./watcher";

const data = {
  arr: ["a"],
};

const updateArr = () => {
  data.arr.map((item) => console.log(item));
};

observe(data);

new Watcher(updateArr);

data.arr.push("k");

运行代码会发现数组push方法执行后,updateArr函数并没有执行,因为我们没有对push方法进行处理 ,因此不会触发Watcher.

查看vue2源码发现,他们是通过重写数组的方法,通过代理模式,原有的方法执行后增加额外的操作。

vue 重写了push、pop、shift、unshift、splice、sort、reverse这7个方法,为了只有这7个呢,这些方法的共同点都是修改数组本身的,只有本身数据变化了才能触发响应式效

/*
export function def(obj, key, val, enumerable) {
    Object.defineProperty(obj, key, {
        value: val,
        enumerable: !!enumerable,
        writable: true,
        configurable: true,
    });
}
*/

import { def } from "./utils/index"; // 获取数组的原型方法 const arrayProto = Array.prototype; //创建新的对象 export const arrayMethods = Object.create(arrayProto); const methodsToPatch = [ "push", "pop", "shift", "unshift", "splice", "sort", "reverse", ]; //拦截数组方法 methodsToPatch.forEach(function (method) { const original = arrayProto[method]; def(arrayMethods, method, function mutator(...args) { // 重写数组方法 const result = original.apply(this, args); const ob = this.__ob__; // 通知依赖的函数更新 ob.dep.notify(); return result; }); });

 数组的方法已经重写,但执行后,怎样去通知依赖函数更新呢,前文每个属性都有一个dep,通过get set方法收集通知依赖函数。但我们想要的是数组本身有个dep,然后当触发重写的数组方法时,在数组方法里让dep去通知依赖函数。

  上篇做深度嵌套时,如果属性是对象继续创建Observer,在Observer添加一个dep就可以达到我们想要的效果。

//const hasProto = "__proto__" in {}; // 判断是否有__proto__属性

import Dep from "./dep"; import { hasProto, def } from "./utils/index"; import { arrayMethods } from "./array"; const arrayKeys = Object.getOwnPropertyNames(arrayMethods); const NO_INIITIAL_VALUE = {}; function defineReactive(obj, key, val, shallow) { // 获取当前属性有没有自定义方法; let property = Object.getOwnPropertyDescriptor(obj, key); // 判断当前属性有没有自定义get set 方法 let getter = property && property.get; let setter = property && property.set; // 没有val值,去obj里面的 if ( (!getter || setter) && (val = NO_INIITIAL_VALUE || arguments.length == 2) ) { val = obj[key]; } const dep = new Dep(); // 持有一个 Dep 对象,用来保存所有依赖于该变量的 Watcher // 如果有深度对象,则深度监听 let childOb = !shallow && observe(val, false); Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function () { //添加依赖函数 if (Dep.target) { // dep.addSub(Dep.target); dep.depend(); /******新增 *************************/ if (childOb) { if (Array.isArray(val)) { // 如果当前值是数组,给数组生成的observer.dev添加依赖函数 childOb.dep.depend(); } } /************************************/ } //如果有自定义方法则调用 let value = getter ? getter.call(obj) : val; return value; }, set: function (newVal) { if (setter) { setter.call(obj, newVal); } else { val = newVal; } // 如果对一个对象整体赋值后,再修改对象属性也没有响应效果,在set也监听下 childOb = !shallow && observe(newVal, false); //执行依赖当前属性的依赖函数 dep.notify(); }, }); } export class Observer { // shallow 属性判断是否需要深度监听 constructor(data, shallow = false) { this.dep = new Dep(); // 新增dep def(data, "__ob__", this); // 每个数据都新增_ob_属性绑定当前Observer实例 if (Array.isArray(data)) { // 判断当前值是否是数组类型,是否有__proto__属性,将重写的方法指向原型 if (hasProto) { data.__proto__ = arrayMethods; } else { for (let i = 0, l = arrayKeys.length; i < l; i++) { const key = arrayKeys[i]; def(data, key, arrayMethods[key]); } } } else { this.walk(data, shallow); } } walk(data, shallow) { let keys = Object.keys(data); for (let key of keys) { defineReactive(data, key, NO_INIITIAL_VALUE, shallow); } } } export function observe(data, shallow = false) { if (data !== null && typeof data === "object") { return new Observer(...arguments); } else { return false; } }

再执行前面的方法,发现数组已经为响应式。

总结

普通属性(对象属性都会循环递归为普通属性,属性为数组的区别

标签:key,__,const,vue2,dep,响应,数组,data
From: https://www.cnblogs.com/hardmeng/p/17108278.html

相关文章