Прокси, которого вы не знаете

внешний интерфейс
Прокси, которого вы не знаете

существуетОт шаблона Observer к принципам адаптивного дизайнаВ этой статье брат Абао представилobserver-utilЭта библиотека использует прокси для реализации ответа. И дляvue-nextв проекте@vue/reactivityМодули также используют прокси для реализации отзывчивости. Итак, если вы хотите научиться@vue/reactivityДля модулей вам нужно сначала освоить прокси.

Далее Брат Абао начнет с 6 аспектов и шаг за шагом проведет вас, чтобы раскрыть тайну объекта Прокси. Прочитав эту статью, вы будете знать следующее:

  • роль агента;
  • Знание объектов Proxy и объектов Reflect;
  • 6 сценариев использования Proxy объектов;
  • Некоторые примечания по использованию прокси-объектов;
  • Применение прокси в проектах с открытым исходным кодом.

1. Расскажите об агенте

Я полагаю, что в повседневной работе многие мелкие партнеры использовали прокси-инструменты для веб-отладки, такие какFiddlerилиCharles. Используя прокси-инструмент веб-отладки, мы можем сканировать запросы протокола HTTP/HTTPS, а также можем вручную изменять параметры запроса и результаты ответа. Мало того, при отладке онлайн-проблем с помощью прокси-инструмента веб-отладки вы также можетеСжатие запутаноФайлы JS сопоставляются с локальнымиНесжатый и запутанныйJS-файл.

После краткого ознакомления с основными функциями прокси-инструмента веб-отладки давайте рассмотрим процесс HTTP-запроса с использованием прокси-инструмента веб-отладки:

Как видно из приведенного выше рисунка, после введения прокси-инструмента веб-отладки инициируемые нами HTTP-запросы будут пересылаться и обрабатываться через веб-прокси. Добавление уровня прокси-сервера Web Proxy позволяет нам лучше контролировать поток HTTP-запросов. Для одностраничных приложений после получения данных с сервера мы будем считывать соответствующие данные и отображать их на странице:

Вышеприведенный процесс похож на получение данных браузером напрямую с сервера:

Чтобы иметь возможность гибко управлять потоком HTTP-запросов, мы добавили уровень веб-прокси. Так можем ли мы контролировать процесс чтения объекта данных? Ответ — да, мы можем воспользоваться такими веб-API, какObject.definePropertyилиProxyAPI. После введения Web API процесс доступа к данным показан на следующем рисунке:

Далее брат Абао сосредоточится на введении.ProxyAPI, это «герой», стоящий за реализацией Vue3 реакции на данные. Друзья, кому это интересно, давайте научимся этому с братом Абао.

Следуйте «Дороге развития полного стека», чтобы прочитать 4 бесплатные электронные книги (в общей сложности более 27 000 загрузок) и 10 серий руководств по анализу исходного кода, изначально созданных А Баоге.

Во-вторых, профиль

ProxyОбъект используется для создания прокси-объекта, чтобы добиться перехвата и пользовательских основных операций (таких как поиск свойств, оценка, перечисление, вызовы функций и т. д.). Синтаксис конструктора прокси:

const p = new Proxy(target, handler)

Соответствующие параметры описываются следующим образом:

  • цель: использоватьProxyОбернутый целевой объект (может быть любым объектом, включая собственные массивы, функции или даже другой прокси).
  • обработчик: объект, который обычно имеет функции в качестве атрибутов.Функции в каждом атрибуте определяют прокси при выполнении различных операций.pповедение.

Прежде чем представить пример использования объекта Proxy, давайте посмотрим на его совместимость:

(Источник изображения:потрите newser.com/?search=pro…

Как видно из рисунка выше, совместимость Proxy API не очень хорошая, поэтому каждый должен обращать внимание на его совместимость при его использовании.

2.1 Пример использования прокси-объекта

Разобравшись с конструктором Proxy, давайте рассмотрим простой пример:

const man = {
  name: "阿宝哥",
};

const proxy = new Proxy(man, {
  get(target, property, receiver) {
    console.log(`正在访问${property}属性`);
    return target[property];
  },
});

console.log(proxy.name);
console.log(proxy.age);

В приведенном выше примере мы использовали конструктор Proxy какmanобъект, создается прокси-объект.Когда вы создаете прокси-объект, мы определяем ловушку get для захвата считанных рабочих атрибутов.Функция ловушки состоит в том, чтобы перехватывать связанные операции пользователя с целевым объектом. Прежде чем эти операции будут распространены на целевой объект, сначала будет вызвана соответствующая функция ловушки, тем самым перехватывая и изменяя соответствующее поведение.

После установки ловушки получения, когда приведенный выше пример кода будет успешно запущен, консоль выведет следующий результат:

正在访问name属性
阿宝哥
正在访问age属性
undefined

Наблюдая за приведенным выше выводом, мы можем найти, чтополучить ловцаМожно перехватить не только операцию чтения известных свойств, но и операцию чтения неизвестных свойств. При создании объекта Proxy помимо определенияполучить ловцаКроме того, мы также можем определить другие ловушки, такие как has, set, delete, apply или ownKeys и т. д.

2.2 Ловцы, поддерживаемые объектами-обработчиками

Объект-обработчик поддерживает 13 видов ловушек, здесь Abaoge перечисляет только следующие 5 часто используемых ловушек:

  • handler.get(): ловушка для операций чтения свойств.
  • handler.set(): ловушка для операций с наборами свойств.
  • handler.deleteProperty(): ловушка для оператора удаления.
  • handler.ownKeys(): ловушка для методов Object.getOwnPropertyNames и Object.getOwnPropertySymbols.
  • Handler.has (): ловец для оператора.

Обратите внимание, что все ловушки являются необязательными. Если ловушка не определена, поведение исходного объекта по умолчанию сохраняется.Прочитав введение к ловушке выше, вы думаете, что объект Proxy очень мощный?

3. Знакомство с объектом Reflect

Reflect— это встроенный объект, предоставляющий методы для перехвата операций JavaScript. Эти методы такие же, как у обработчиков прокси. Reflect не является функциональным объектом, поэтому его нельзя построить.

Прежде чем представить пример использования объекта Reflect, давайте посмотрим на его совместимость:

(Источник изображения:потрите newser.com/?search=ref…

3.1 Пример использования объекта Reflect

const man = {
  name: "阿宝哥",
  city: "Xiamen",
};

console.log(Reflect.set(man, "sex", 1)); // true
console.log(Reflect.has(man, "name")); // true
console.log(Reflect.has(man, "age")); // false
console.log(Reflect.ownKeys(man)); // [ 'name', 'city', 'sex' ]

В дополнение к тому, что описано в примереset,hasа такжеownKeysВ дополнение к методам объект Reflect также поддерживаетget,definePropertyа такжеdeletePropertyи другие методы. Ниже кратко Бао братReflectНекоторые статические методы, поддерживаемые объектом.

3.2 Статические методы, поддерживаемые объектами Reflect

Все свойства и методы Reflect являются статическими, и объект предоставляет 13 методов, связанных с объектом-обработчиком Proxy. Точно так же здесь Abaoge перечисляет только следующие 5 часто используемых методов:

  • Reflect.get(target, propertyKey[ Receiver]): получить значение свойства объекта, аналогичное target[name].
  • Reflect.set(target, propertyKey, value[ Receiver]): функция, которая присваивает значение свойству. Возвращает логическое значение или true, если обновление прошло успешно.
  • Reflect.deleteProperty(target, propertyKey): удалить указанное свойство целевого объекта, что эквивалентно выполнению удаления target[name].
  • Reflect.has(target, propertyKey): определяет, имеет ли объект свойство, точно такое же, как у оператора in.
  • Reflect.ownKeys(target): возвращает массив, содержащий все собственные свойства (за исключением унаследованных свойств).

В реальных сценариях использования прокси мы часто комбинируем статические методы, предоставляемые объектом Reflect, для реализации некоторых конкретных функций. Чтобы каждый мог лучше понять и освоить прокси-объект, в следующей ссылке брат А Бао перечислит прокси-объекты.6сценарий использования.

4. Сценарии использования прокси

Здесь мы впервые представляем первый сценарий использования объекта Proxy —Расширенный массив.

4.1 Расширенные массивы

Определить расширенную функцию Array
function enhancedArray(arr) {
  return new Proxy(arr, {
    get(target, property, receiver) {
      const range = getRange(property);
      const indices = range ? range : getIndices(property);
      const values = indices.map(function (index) {
        const key = index < 0 ? String(target.length + index) : index;
        return Reflect.get(target, key, receiver);
      });
      return values.length === 1 ? values[0] : values;
    },
  });

  function getRange(str) {
    var [start, end] = str.split(":").map(Number);
    if (typeof end === "undefined") return false;

    let range = [];
    for (let i = start; i < end; i++) {
      range = range.concat(i);
    }
    return range;
  }

  function getIndices(str) {
    return str.split(",").map(Number);
  }
}
Использование расширенной функции Array
const arr = enhancedArray([1, 2, 3, 4, 5]);

console.log(arr[-1]); //=> 5
console.log(arr[[2, 4]]); //=> [ 3, 5 ]
console.log(arr[[2, -2, 1]]); //=> [ 3, 4, 2 ]
console.log(arr["2:4"]); //=> [ 3, 4]
console.log(arr["-2:3"]); //=> [ 4, 5, 1, 2, 3 ]

Из приведенных выше выходных результатов видно, что расширенный объект массива может поддерживать такие функции, как отрицательный индекс и индекс фрагмента. Помимо увеличения массивов, мы также можем увеличивать простые объекты с помощью Proxy API.

4.2 Улучшенные объекты

Создать функцию расширенного объекта
const enhancedObject = (target) =>
  new Proxy(target, {
    get(target, property) {
      if (property in target) {
        return target[property];
      } else {
        return searchFor(property, target);
      }
    },
  });

let value = null;
function searchFor(property, target) {
  for (const key of Object.keys(target)) {
    if (typeof target[key] === "object") {
      searchFor(property, target[key]);
    } else if (typeof target[property] !== "undefined") {
      value = target[property];
      break;
    }
  }
  return value;
}
Используйте функцию EnhancedObject
const data = enhancedObject({
  user: {
    name: "阿宝哥",
    settings: {
      theme: "dark",
    },
  },
});

console.log(data.user.settings.theme); // dark
console.log(data.theme); // dark

После запуска кода консоль выведет следующий код:

dark
dark

Наблюдая за вышеуказанными результатами выхода, можно увидеть, что использованиеenhancedObjectОбъект, обработанный функцией, мы можем легко получить доступ к глубинным свойствам внутри обычного объекта.

4.3 Создание объектов только для чтения

Создайте прокси-объект
const man = {
  name: "semlinker",
};

const handler = {
  set: "Read-Only",
  defineProperty: "Read-Only",
  deleteProperty: "Read-Only",
  preventExtensions: "Read-Only",
  setPrototypeOf: "Read-Only",
};

const proxy = new Proxy(man, handler);
использовать прокси-объект
console.log(proxy.name);
proxy.name = "kakuqo";

После запуска кода консоль выведет следующий код:

semlinker
proxy.name = "kakuqo";
           ^
TypeError: 'Read-Only' returned for property 'set' of object '#<Object>' is not a function

Наблюдая за приведенной выше информацией об исключении, мы видим, что причина исключения заключается в том, чтоhandlerобъектsetЗначение свойства не является функцией. Если мы не хотим генерировать исключение во время выполнения, мы можем определитьfreezeфункция:

function freeze (obj) {
  return new Proxy(obj, {
    set () { return true; },
    deleteProperty () { return false; },
    defineProperty () { return true; },
    setPrototypeOf () { return true; }
  });
}

четко определенныйfreezeмы используем объект массива для проверки его функциональности:

let frozen = freeze([1, 2, 3]);
frozen[0] = 6;
delete frozen[0];
frozen = Object.defineProperty(frozen, 0, { value: 66 });
console.log(frozen); // [ 1, 2, 3 ]

После успешного выполнения приведенного выше кода консоль выведет[ 1, 2, 3 ], очевидно послеfreezeОбъект массива, обрабатываемый функцией, был "заморожен".

4.4 Перехват вызовов методов

Определите функцию traceMethodCalls
function traceMethodCalls(obj) {
  const handler = {
    get(target, propKey, receiver) {
      const origMethod = target[propKey]; // 获取原始方法
      return function (...args) {
        const result = origMethod.apply(this, args);
        console.log(
          propKey + JSON.stringify(args) + " -> " + JSON.stringify(result)
        );
        return result;
      };
    },
  };
  return new Proxy(obj, handler);
}
Использование функции traceMethodCalls
const obj = {
  multiply(x, y) {
    return x * y;
  },
};

const tracedObj = traceMethodCalls(obj);
tracedObj.multiply(2, 5); // multiply[2,5] -> 10

После успешного выполнения приведенного выше кода консоль выведетmultiply[2,5] -> 10, т.е. нам удалось успешно отследитьobjВызовите методы объекта процедуры. Фактически, в дополнение к вызову метода отслеживания, мы можем отслеживать доступом атрибута объекта, конкретные примеры следующие:

function tracePropAccess(obj, propKeys) {
  const propKeySet = new Set(propKeys);
  return new Proxy(obj, {
    get(target, propKey, receiver) {
      if (propKeySet.has(propKey)) {
        console.log("GET " + propKey);
      }
      return Reflect.get(target, propKey, receiver);
    },
    set(target, propKey, value, receiver) {
      if (propKeySet.has(propKey)) {
        console.log("SET " + propKey + "=" + value);
      }
      return Reflect.set(target, propKey, value, receiver);
    },
  });
}

const man = {
  name: "semlinker",
};
const tracedMan = tracePropAccess(man, ["name"]);

console.log(tracedMan.name); // GET name; semlinker
console.log(tracedMan.age); // undefined
tracedMan.name = "kakuqo"; // SET name=kakuqo

В приведенном выше примере мы определяемtracePropAccessфункция, которая получает два параметра: obj и propKeys, представляющие отслеживаемую цель и список отслеживаемых свойств соответственно. передачаtracePropAccessПосле функции будет возвращен прокси-объект, и когда мы получим доступ к отслеживаемому свойству, консоль выведет соответствующий журнал доступа.

4.5 Скрытые свойства

Создайте функцию hideProperty
const hideProperty = (target, prefix = "_") =>
  new Proxy(target, {
    has: (obj, prop) => !prop.startsWith(prefix) && prop in obj,
    ownKeys: (obj) =>
      Reflect.ownKeys(obj).filter(
        (prop) => typeof prop !== "string" || !prop.startsWith(prefix)
      ),
    get: (obj, prop, rec) => (prop in rec ? obj[prop] : undefined),
  });
Используйте функцию hideProperty
const man = hideProperty({
  name: "阿宝哥",
  _pwd: "www.semlinker.com",
});

console.log(man._pwd); // undefined
console.log('_pwd' in man); // false
console.log(Object.keys(man)); // [ 'name' ]

Наблюдая за приведенными выше результатами вывода, мы можем знать, что с помощью Proxy API мы добились сокрытия указанного атрибута префикса. Помимо реализации скрытых свойств, с помощью Proxy API мы также можем реализовать функцию проверки значений свойств.

4.6 Проверка значений свойств

Создайте функцию validatedUser
const validatedUser = (target) =>
  new Proxy(target, {
    set(target, property, value) {
      switch (property) {
        case "email":
          const regex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
          if (!regex.test(value)) {
            console.error("The user must have a valid email");
            return false;
          }
          break;
        case "age":
          if (value < 20 || value > 80) {
            console.error("A user's age must be between 20 and 80");
            return false;
          }
          break;
      }

      return Reflect.set(...arguments);
    },
  });
Используйте функцию validatedUser
let user = {
  email: "",
  age: 0,
};

user = validatedUser(user);
user.email = "semlinker.com"; // The user must have a valid email
user.age = 100; // A user's age must be between 20 and 80

После успешного выполнения приведенного выше кода консоль выведет следующие результаты:

The user must have a valid email
A user's age must be between 20 and 80

После представления сценариев использования объекта Proxy давайте продолжим знакомить с некоторыми вопросами, связанными с объектом Proxy.

5. Проблемы, связанные с прокси

5.1 Проблема наведения этого

const target = {
  foo() {
    return {
      thisIsTarget: this === target,
      thisIsProxy: this === proxy,
    };
  },
};

const handler = {};
const proxy = new Proxy(target, handler);
console.log(target.foo()); // { thisIsTarget: true, thisIsProxy: false }
console.log(proxy.foo()); // { thisIsTarget: false, thisIsProxy: true }

После успешного выполнения приведенного выше кода консоль выведет следующие результаты:

{ thisIsTarget: true, thisIsProxy: false }
{ thisIsTarget: false, thisIsProxy: true }

По результатам приведенного выше вывода,foothis в методе относится к текущему вызывающему объекту. Это кажется очень простым, но в некоторых сценариях, если вы не будете осторожны, могут возникнуть проблемы, такие как следующий пример:

const _name = new WeakMap();

class Person {
  constructor(name) {
    _name.set(this, name);
  }
  
  get name() {
    return _name.get(this);
  }
}

В приведенном выше примере мы используемWeakMapобъект для хранения личной информации объекта Person. определенныйPersonclass, мы можем использовать его следующими способами:

const man = new Person("阿宝哥");
console.log(man.name); // 阿宝哥

const proxy = new Proxy(man, {});
console.log(proxy.name); // undefined

Для приведенного выше кода, когда мы передаемproxyвозражать против доступаnameсвойства, вы обнаружите, что выводundefined. Это связано с тем, что при использованииproxy.nameспособ доступаnameимущество,thisуказывает наproxyобъект и_nameВ объекте WeakMap хранитсяmanобъект, поэтому выводundefined.

Однако для приведенной выше задачи, если мы определим следующим образомPersonclass, вышеуказанные проблемы не возникнут:

class Person {
  constructor(name) {
    this._name = name;
  }
  get name() {
    return this._name;
  }
}

const man = new Person("阿宝哥");
console.log(man.name); // 阿宝哥

const proxy = new Proxy(man, {});
console.log(proxy.name); // 阿宝哥

Кроме того, если вы правыWeakMapЕсли вам интересно, вы можете прочитатьWeakMap, которого вы не знаетеЭта статья.

const p = new Proxy(target, {
  get: function(target, property, receiver) {
    // receiver
  }
});

Захват GET используется для перехвата операции чтения атрибута объекта, и захват содержит три параметра:

  • цель: целевой объект.
  • свойство: имя свойства для чтения.
  • получатель: указывает на текущий объект Proxy или объект, который наследуется от текущего Proxy.

Чтобы лучше понятьreceiverОписание информации о параметрах, приведем на конкретном примере:

const proxy = new Proxy({},
  {
    get: function (target, property, receiver) {
      return receiver;
    },
  }
);

console.dir(proxy.getReceiver === proxy); // true
var inherits = Object.create(proxy);
console.dir(inherits.getReceiver === inherits); // true

Так можем ли мы изменитьreceiverА как насчет остроконечного предмета? Ответ «да», предоставляемый через объект Reflect.getметод, мы можем динамически установитьreceiverЗначение объекта, конкретное использование, выглядит следующим образом:

console.dir(Reflect.get(proxy, "getReceiver", "阿宝哥"));

фактическиreceiverНазвание получено из спецификации ECMAScript:

  • [[Get]] (propertyKey, Receiver) → любой

    Return the value of the property whose key is propertyKey from this object. If any ECMAScript code must be executed to retrieve the property value, Receiver is used as the this value when evaluating the code.

  • [[Set]]   (propertyKey, value, Receiver) → Boolean

    Set the value of the property whose key is propertyKey to value. If any ECMAScript code must be executed to set the property value, Receiver is used as the this value when evaluating the code. Returns true if the property value was set or false if it could not be set.

Над[[Get]]а также[[Set]] Известные как внутренние методы, каждый объект в механизме ECMAScript связан с набором внутренних методов, определяющих его поведение во время выполнения. Обратите внимание, что эти внутренние методы не являются частью языка ECMAScript. Для свойств доступа объектов при выполнении внутреннего кодаReceiverбудет использоваться какthisЗначение , также используя API, предоставляемый объектом Reflect, мы также можем установитьreceiverзначение параметра для измененияthisЗначение:

const obj = {
  get foo() {
    return this.bar;
  },
};

console.log(Reflect.get(obj, "foo")); // undefined
console.log(Reflect.get(obj, "foo", { bar: 2021 })); // 2021

5.3 Обёртка экземпляров встроенных конструкторов

При обертывании экземпляра встроенного конструктора прокси могут возникнуть некоторые проблемы. Например, используйте Proxy для проксирования экземпляра конструктора Date:

const target = new Date();
const handler = {};
const proxy = new Proxy(target, handler);

proxy.getDate(); // Error

Когда приведенный выше код запустится, консоль выведет следующую информацию об исключении:

proxy.getDate();
      ^
TypeError: this is not a Date object.

Причина вышеуказанных проблем заключается в том, что внутренние свойства некоторых нативных объектов могут быть переданы только через правильныйthisмогут быть получены, поэтому Proxy не может проксировать свойства этих собственных объектов. Итак, как решить эту проблему? Для решения этой проблемы мы можемgetDateМетод обязательно будет правильнымthis:

const target = new Date();
const handler = {
  get(target, property, receiver) {
    if (property === "getDate") {
      return target.getDate.bind(target);
    }
    return Reflect.get(target, property, receiver);
  },
};

const proxy = new Proxy(target, handler);
console.log(proxy.getDate());

5.4 Создание отзываемых прокси-объектов

пройти черезProxy.revocable()можно использовать для создания отзывного прокси-объекта, сигнатура метода:

Proxy.revocable(target, handler);

Соответствующие параметры описываются следующим образом:

  • цель: будет использоватьсяProxyИнкапсулированный целевой объект. Может быть объектом любого типа, включая собственные массивы, функции или даже другой прокси-объект.
  • обработчик: объект, свойства которого представляют собой массив необязательных функций, определяющих поведение агента при выполнении соответствующей операции.

передачаProxy.revocableПосле метода его возвращаемое значение представляет собой объект, структура которого следующая:{"proxy": proxy, "revoke": revoke},в:

  • прокси: представляет собой только что сгенерированный прокси-объект, и в общем случаеnew Proxy(target, handler)Созданный прокси-объект ничем не отличается, за исключением того, что его можно отозвать.
  • Revoke: метод отмены, при вызове, вам не нужно добавлять какие-либо параметры, вы можете отозвать объекты прокси, сгенерированные вместе с ним.

пониматьrevocableПосле метода давайте рассмотрим конкретный пример:

const target = {}; 
const handler = {};
const { proxy, revoke } = Proxy.revocable(target, handler);

proxy.name = "阿宝哥";
console.log(proxy.name); // 阿宝哥

revoke();
console.log(proxy.name); // TypeError: Revoked

Когда приведенный выше код запустится успешно, консоль выведет следующее:

阿宝哥
Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked
  at <anonymous>

Из приведенных выше результатов мы видим, что когдаproxyПосле отзыва объекта у нас нет возможностиproxyобъект для выполнения какой-либо операции.

6. Применение прокси в проектах с открытым исходным кодом

Поскольку объект Proxy может обеспечить мощные возможности перехвата, он используется в некоторых зрелых проектах с открытым исходным кодом для реализации чувствительных функций, таких какvue-nextа такжеobserver-utilпроект. дляobserver-utilЭтот проект, брат А Бао написал статьюОт шаблона Observer к принципам адаптивного дизайнаСтатья для ознакомления с проектом, заинтересованные друзья могут прочитать ее сами.

И дляvue-nextДля проектов адаптивная функциональность инкапсулирована в@vue/reactivityмодуль, который предоставляет намreactiveфункция для создания реактивных объектов. Давайте кратко рассмотримreactiveРеализация функции:

// packages/reactivity/src/reactive.ts
export function reactive(target: object) {
  if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
    return target
  }
  return createReactiveObject(
    target,
    false,
    mutableHandlers,
    mutableCollectionHandlers
  )
}

существуетreactiveВнутри функции он будет продолжать вызыватьcreateReactiveObjectфункция для создания реактивных объектов, которая также определена вreactive.tsВ файле конкретная реализация этой функции выглядит следующим образом:

// packages/reactivity/src/reactive.ts
function createReactiveObject(
  target: Target,
  isReadonly: boolean,
  baseHandlers: ProxyHandler<any>,
  collectionHandlers: ProxyHandler<any>
) {
  // 省略部分代码  
  const proxyMap = isReadonly ? readonlyMap : reactiveMap
  const existingProxy = proxyMap.get(target)
  if (existingProxy) {
    return existingProxy
  }
  const proxy = new Proxy(
    target,
    targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
  )
  proxyMap.set(target, proxy)
  return proxy
}

существуетcreateReactiveObjectВнутри функции мы наконец-то видим долгожданныйProxyобъект. когдаtargetЕсли объект не является объектом типа коллекции, например Map, Set, WeakMap и WeakSet, при создании объекта Proxy используйтеbaseHandlers,ДолженhandlerОбъекты определяют следующие 5 типов ловушек:

export const mutableHandlers: ProxyHandler<object> = {
  get,
  set,
  deleteProperty,
  has,
  ownKeys
}

вgetа такжеsetЛовец используется для сбора функции эффекта и запуска выполнения функции эффекта соответственно. Хорошо, брат А Бао, просто чтобы представить@vue/reactivityсерединаreactiveФункция, подробности того, как этот модуль реализует отзывчивость, здесь не будут представлены. Брат А Бао напишет отдельную статью для подробного анализа функции этого модуля.

Следуйте «Дороге полного совершенствования», чтобы прочитать 4 бесплатные электронные книги (в общей сложности более 27 000 загрузок) и 50 учебных пособий «Повторное изучение TS» от брата Абао.

7. Справочные ресурсы