Резюме
✨Длинный текст читается минут десять
✨На просмотр уходит больше часа
✨Около 100 строк кода
Некоторое время назад я планировал написать фейковую консоль для мобильных телефонов, с помощью которой можно было бы видетьconsole
Вывод этой функции завершен
Но потом я узнал, что есть проект команды TencentvConsole
Для справки, я обнаружил, что его функции намного богаче, вы можете видетьNetwork
панель и т.д.
Несмотря на то, что это кастрированная панель, вы все равно можете увидеть важную информацию, связанную с сетью.
Вот и подумал добавитьNetwork
Функция панели
Чтобы реализовать эту функцию, я придумал несколько решений. Одно из них — инкапсулировать ajax, но это нереально. Очень недружелюбно позволять другим использовать свой собственный инкапсулированный ajax, что очень недружественно к ситуации, которую необходимо внедрить. в середине проекта, и нужно изменить много кода.
Так что это решение не рекомендуется
После выбора схемы, как здесь, как и реализацияconsole
Как и панель, метод ниже консоли перехватывается
при блокировкеconsole
Метод под объектом фактически переопределяет его метод
Напримерconsole.log
фактическиlog
Метод переопределяется в реальном исполненииlog
Делайте то, что вы хотите сделать, прежде чем
идеи
Собственно об этом я и подумал сначалаXMLHrrpRequest
Объект имеет глобальную функцию ловушки, и документация, похоже, не находит соответствующего контента.
Позже я искал какие-то другие методы реализации, большинство из которых заключаются в том, что пряная куриная станция не знает, куда лезть, и нет ничего ценного.
Позже нашел репозиторий на github (Ajax-hook)
В этой библиотеке реализована возможность перехвата глобальных Ajax-запросов, она может использовать одноименный метод или атрибут в качестве хука для достижения эффекта перехвата.
Часть содержания относится к его идеям реализации
реально реализовано и настроеноconsole
Подобно тому, как базовая операция по-прежнему зависит от собственного объекта xhr, но была переписана как прокси на верхнем уровне.XMLHttpRequest
объект
Общая идея реализации может быть указана в следующих шагах
- сохранить собственный объект xhr
- Буду
XMLHttpRequest
объект как новый объект - Перепишите метод объекта xhr и поместите его в новый объект.
- Переопределите свойство и поместите его в новый объект
Основная цель переопределения метода — предоставить перехватчик для выполнения пользовательского содержимого перед выполнением собственного метода.
Переопределяющие свойства — это в основном некоторые свойства, запускаемые событиями, такие какonreadystatechange
В то же время есть и другие подводные камни.Многие свойства объекта xhr доступны только для чтения (этим также занимаетсяAjax-hookпонял)
Если в хуке изменено свойство только для чтения, спускаться вниз бесполезно, потому что модификация вообще не удалась.
Но теперь, когда у вас есть общая идея, давайте попробуем ее реализовать.
выполнить
Перед внедрением подумайте, как его использовать, чтобы процесс внедрения имел еще и общее направление
имя, позвони емуAny-XHR
охватывать
когда используешьnew AnyXHR()
создать экземпляр
Вы можете добавить хуки в конструкторе.Существует два типа хуков, одинbefore
(вызов перед выполнением) крючки One isafter
(вызов после выполнения) хук
Таким образом, конструктор принимает два параметра, оба из которых являются объектами: первый — ловушка, которая выполняется до вызова, а другой — после вызова.
внутри объектаkey
Методы и атрибуты, соответствующие родному xhr, могут быть согласованы
let anyxhr = new AnyXHR({
send: function(...args) {
console.log('before send');
}
}, {
send: function(...args) {
console.log('after send');
}
});
Конструктор
У нас есть цель, давайте сделаем это сейчас, как мы хотим, мы будем использовать этоnew
ключевое слово для создания экземпляра объекта, используйте конструктор илиclass
ключевое слово для создания класса
Ключевое слово class используется здесь для объявления
class AnyXHR {
constructor(hooks = {}, execedHooks = {}) {
this.hooks = hooks;
this.execedHooks = execedHooks;
}
init() {
// 初始化操作...
}
overwrite(proxyXHR) {
// 处理xhr对象下属性和方法的重写
}
}
Сосредоточьтесь только здесь构造函数
构造函数
Принимаются два объекта: хуки до выполнения и хуки после выполнения, соответствующие хукам, прикрепленным к экземпляру.hooks
а такжеexecedHooks
атрибут
Следуйте только что сказанным шагам, чтобы сохранить собственный объект xhr и добавить этот шаг в конструктор.
Затем выполните некоторые операции инициализации, чтобы переписать объект xhr во время инициализации.
constructor(hooks = {}, execedHooks = {}) {
+ this.XHR = window.XMLHttpRequest;
this.hooks = hooks;
this.execedHooks = execedHooks;
+ this.init();
}
метод инициализации
Что делать в init, чтобыXMLHttpRequest
Переопределить каждый метод и свойство экземпляра xhr для переопределения
так вnew XMLHttpRequest()
когдаnew
На выходе получается наш собственный переписанный экземпляр xhr.
init() {
window.XMLHttpRequest = function() {
}
}
поставь вот такXMLHttpRequest
назначьте его новой функции, подобной этойXMLHttpRequest
Это не оригинал, глобальный доступ пользователя - это новая функция, написанная нами.
Далее следует реализовать переписывание какopen
,onload
,send
Методы и свойства этих собственных экземпляров xhr
Но как его переписать?Теперь это пустая функция,как впустить пользователяnew XMLHttpRequest
можно использовать послеsend
,open
как насчет метода
Собственно, об этом упоминалось ранееОсновная цель переопределения метода — предоставить перехватчик для выполнения пользовательского содержимого перед выполнением собственного метода.
Поскольку мы хотим выполнить собственный метод, нам все равно нужно использовать собственный объект xhr.
просто возьмиopen
для
в пользователеnew XMLHttpRequest
В то же время необходимоnew
оригинальный, который мы сохранилиXMLHttpRequest
объект
а потом переписатьXMLHttpRequest
Повесьте один на экземплярsend
Метод Что он делает, так это сначала выполняет пользовательский контент, а затем выполняет нашnew
вне, сохраненоXMLHttpRequest
Открытый метод экземпляра объекта может одновременно передавать параметры
Звучит немного запутанно, можно рисовать картинки и внимательно смаковать
init() {
+ let _this = this;
window.XMLHttpRequest = function() {
+ this._xhr = new _this.XHR(); // 在实例上挂一个保留的原生的xhr实例
+ this.overwrite(this);
}
}
так в юзереnew XMLHttpRequest
При создании зарезервированного нативногоXMLHttpRequest
пример
например, когда пользователь звонитsend
метод, мы сначала выполняем то, что хотим сделать, а затем вызываемthis._xhr.send
Выполнить собственный экземпляр xhrsend
Это легко, не так ли?
После того, как мы закончили, мы входимoverwrite
Метод Этот метод делает то, что было сказано в предыдущем предложении, переписывая каждый метод и свойство, фактически делая что-то до и после выполнения.
передача_this.overwrite
Когда нам нужно передать это, это здесь указывает наnew XMLHttpRequest
пример после
Все атрибуты и методы связаны с экземпляром для вызова пользователем, поэтому передайте это для удобства работы.
Вот перезапись с новой функцией
window.XMLHttpRequest
Стрелочные функции нельзя использовать при использовании стрелочных функций Стрелочные функции нельзя использовать в качестве конструкторов, поэтому здесь используется практика этой оговорки.
метод перезаписи
Чтобы переопределить методы и свойства, все, что вам нужно переопределить, этоsend
,open
,responseText
,onload
,onreadystatechange
подожди этих
Вы должны сделать это один за другим... Это определенно не реально
Методы и свойства для переопределения мы можем получить, облегчив собственный экземпляр xhr.
Собственный экземпляр xhr был сохранен в перезаписанномXMLHttpRequest
экземпляр объекта_xhr
атрибут
Таким образом, пересекая_xhr
Этот собственный экземпляр xhr может получить все методы и свойства, которые необходимо переопределить.key
а такжеvalue
overwrite(proxyXHR) {
+ for (let key in proxyXHR._xhr) {
+ }
}
здесьproxyXHR
Параметр указывает на измененныйXMLHttpRequest
пример
Методы и свойства присоединены к экземпляру
прямо сейчасproxyXHR
вниз_xhr
Обход свойств может получить все обходные свойства и методы ниже него.
Затем различайте методы и свойства и выполняйте другую обработку.
overwrite(proxyXHR) {
for (let key in proxyXHR._xhr) {
+ if (typeof proxyXHR._xhr[key] === 'function') {
+ this.overwriteMethod(key, proxyXHR);
+ continue;
+ }
+ this.overwriteAttributes(key, proxyXHR);
}
}
пройти черезtypeof
Чтобы определить, является ли текущий обход свойством или методом, если это метод, вызовитеoverwriteMethod
Переопределить этот метод
Если это атрибут, то передайтеoverwriteAttributes
Переопределить это свойство
В то же время пройденное имя свойства и имя метода, а также измененныйXMLHttpRequest
экземпляр передан
Тогда давайте реализуем два метода здесь
overwriteMethod
Добавьте этот метод в класс
class AnyXHR {
+ overwriteMethod(key, proxyXHR) {
// 对方法进行重写
+ }
}
Что делает этот метод, так это переписывает метод под собственным экземпляром xhr.
На самом деле, строго говоря, не строго называть эту операцию перезаписью.
просто возьмиsend
С точки зрения метода это не влияет на собственный экземпляр xhr.send
Измените метод и напишите новый метод, который будет привязан к экземпляру xhr, реализованному вами, вместо собственного экземпляра xhr для окончательного выполнения.send
Процесс по-прежнему вызывает роднойsend
метод
Просто сделайте еще две вещи до и после звонка
Итак, вот оболочка вокруг каждого метода
overwriteMethod(key, proxyXHR) {
+ let hooks = this.hooks;
+ let execedHooks = this.execedHooks;
+ proxyXHR[key] = (...args) => {
+ }
}
первый сохранилhooks
а такжеexecedHooks
Будет использоваться часто
Затем к новому экземпляру xhr присоединяем одноименный метод, например, у нативного xhr естьsend
пройти кsend
Когда ключ здесьsend
Таким образом, он будет висеть на новом экземпляре xhr.send
на замену родномуsend
метод
При вызове метода он получает кучу параметров.Параметры подбрасывает js движок (или браузер).Здесь остальные параметры используются для их перехвата для формирования массива.При вызове хука или нативного метод, вы можете передать его снова.
Итак, что именно он здесь делает?
На самом деле, три шага
- Если текущий метод имеет соответствующий хук, выполнить хук
- Выполните соответствующий метод в собственном экземпляре xhr.
- Посмотрите, есть ли какие-либо ловушки, которые необходимо выполнить после выполнения метода, соответствующего собственному экземпляру xhr.Если да, выполните его.
overwriteMethod(key, proxyXHR) {
let hooks = this.hooks;
let execedHooks = this.execedHooks;
proxyXHR[key] = (...args) => {
+ // 如果当前方法有对应的钩子 则执行钩子
+ if (hooks[key] && (hooks[key].call(proxyXHR, args) === false)) {
+ return;
+ }
+ // 执行原生xhr实例中对应的方法
+ const res = proxyXHR._xhr[key].apply(proxyXHR._xhr, args);
+ // 看看还有没有原生xhr实例对应的方法执行后需要执行的钩子 如果有则执行
+ execedHooks[key] && execedHooks[key].call(proxyXHR._xhr, res);
+ return res;
}
}
первый шаг первыйhooks[key]
Это нужно для того, чтобы определить, имеет ли текущий метод соответствующую функцию ловушки.
Все функции или методы ловушек, которые необходимо перехватить, сохраняются в объекте ловушек, когда мы выполняемnew AnyXHR(..)
прошел, когда
Если есть, выполните его и передайте параметр хуку.Если функция хука вернет false, она прервется и не выйдет из строя.Это может иметь эффект перехвата.
В противном случае спуститесь и выполните соответствующий метод в собственном экземпляре xhr.
Собственный экземпляр xhr помещается_xhr
В этом свойстве так пройтиproxyXHR._xhr[key]
Вы можете получить к нему доступ и использовать параметры одновременноapply
Просто отбросьте его, просто передайте и одновременно поймайте возвращаемое значение.
После завершения третьего шага
Посмотрите, есть ли хук, который будет выполнен после выполнения нативного метода xhr.Если есть, выполните его, а затем передайте возвращаемое значение.
После этого верните возвращаемое значение метода, соответствующее собственному экземпляру xhr после выполнения.
На этом прокси и перехват метода завершены, можно пробовать.
не забудьте отметить
this.overwriteAttributes(key, proxyXHR);
эта линия
Ввод в эксплуатацию в первый раз
Хотя кода пока не так много, но я не обошёл всё сразу, я всё равно очень устал, можно налить стакан воды и отдохнуть.
Полный код пока выглядит следующим образом
class AnyXHR {
constructor(hooks = {}, execedHooks = {}) {
this.XHR = window.XMLHttpRequest;
this.hooks = hooks;
this.execedHooks = execedHooks;
this.init();
}
init() {
let _this = this;
window.XMLHttpRequest = function() {
this._xhr = new _this.XHR(); // 在实例上挂一个保留的原生的xhr实例
_this.overwrite(this);
}
}
overwrite(proxyXHR) {
for (let key in proxyXHR._xhr) {
if (typeof proxyXHR._xhr[key] === 'function') {
this.overwriteMethod(key, proxyXHR);
continue;
}
// this.overwriteAttributes(key, proxyXHR);
}
}
overwriteMethod(key, proxyXHR) {
let hooks = this.hooks;
let execedHooks = this.execedHooks;
proxyXHR[key] = (...args) => {
// 如果当前方法有对应的钩子 则执行钩子
if (hooks[key] && (hooks[key].call(proxyXHR, args) === false)) {
return;
}
// 执行原生xhr实例中对应的方法
const res = proxyXHR._xhr[key].apply(proxyXHR._xhr, args);
// 看看还有没有原生xhr实例对应的方法执行后需要执行的钩子 如果有则执行
execedHooks[key] && execedHooks[key].call(proxyXHR._xhr, res);
return res;
}
}
}
Попробуйте первую отладку
new AnyXHR({
open: function () {
console.log(this);
}
});
var xhr = new XMLHttpRequest();
xhr.open('GET', '/abc?a=1&b=2', true);
xhr.send();
Вы можете открыть консоль, чтобы увидеть, есть ли вывод, и вы можете наблюдать за объектом и_xhr
Сядьте и сравните содержимое в свойствах
метод overwriteAttributes
Этот метод немного сложнее реализовать, и содержимое будет обойдено.
Может быть, есть идея, зачем слушать или прокси или давать属性
Какая польза от предоставления крючков?
картинаresponseText
Это свойство на самом деле не является целью
Цель состоит в том, чтобы дать что-то вродеonreadystatechange
,onload
Такие свойства обертывают слой
Эти свойства немного похожи на события или просто события.
При его использовании нужно вручную присвоить им значение, и он будет автоматически вызываться в соответствующий момент.Можно сказать нативныйxhr
предоставленный крючок
картинаsend
,open
Может быть перехвачен при отправке запроса
а такжеonreadystatechange
,onload
Запросы, которые можно использовать для перехвата ответов от сервисов
Поэтому необходимо обернуть эти свойства
Теперь, когда вы знаете, зачем нужно оборачивать свойства, возникает вопрос, как оборачивать свойства.
Этоonload
подать пример
xhr.onload = function() {
...
};
Когда пользователь присваивает такое значение, мы должны сделать какой-то ответ
Процесс захвата этого задания
тогда иди посмотриhooks
Есть ли в этом массивеonload
крюк
Если есть, можно выполнить хук перед выполнением загрузки собственного экземпляра xhr.
Когда возникает проблема, как насчет обычных атрибутов, таких какresponseType
Тогда эти атрибуты не будут обрабатываться и напрямую связаны с новым экземпляром xhr.
Другая проблема, как отличить общие атрибуты от таких же атрибутов, как события?
На самом деле, просто наблюдайтеon
Атрибут в начале совпадает с атрибутом события.
Итак, подведем итог
- Посмотрите, начинается ли атрибут с on
- Если нет, просто повесьте трубку
- Если да, посмотрите, есть ли хук для выполнения
- Если есть, оберните его и сначала выполните хук, а затем выполните тело
- Если нет ответственности, напрямую присвойте значение и повесьте трубку
Логика понятна, легко найти?
Хорошо, давайте сделаем это
? ? Подождите, как монитору пользователя датьonload
Такое назначение атрибута а? ? ?
ты можешь остановиться и подумать об этом
Эту часть можно использовать в ES5getter
а такжеsetter
метод
Этот пункт знаний должен быть вам очень знаком. Если вы не знакомы с ним, вы можете перейти на MDN.
С помощью этих двух методов вы можете отслеживать действия пользователя по назначению и извлечению свойства, а также выполнять некоторые дополнительные действия.
Тогда как установить метод get/set для вставляемого свойства?
ES5 обеспечиваетObject.defineProperty
метод
Вы можете определить атрибуты для объекта и указать его дескриптор атрибута.Описатель атрибута может описывать, может ли атрибут быть записан, может ли он быть пронумерован, его установка/получение и т. д.
Конечно, также возможно определить методы получения/установки для свойства с помощью литералов.
После долгих разговоров давайте реализуем этот метод.
overwriteAttributes(key, proxyXHR) {
Object.defineProperty(proxyXHR, key, this.setProperyDescriptor(key, proxyXHR));
}
Вот строка кода, которая используетObject.defineProperty
Повесьте атрибут на экземпляр xhr, реализованный вами, имя атрибута - это переданный ключ, а затем используйтеsetProperyDescriptor
Метод генерирует дескриптор атрибута при передаче ключа и экземпляра.
В дескрипторе будет сгенерирован метод get/set, то есть операция, когда атрибут присваивается и извлекается.
метод setProperyDescriptor
Добавьте тот же метод в класс
setProperyDescriptor(key, proxyXHR) {
}
Вы можете получить имя атрибута (ключ), которое будет добавлено, и экземпляр объекта xhr, реализованный самостоятельно.
свойства должны быть прикреплены к этому экземпляру
setProperyDescriptor(key, proxyXHR) {
+ let obj = Object.create(null);
+ let _this = this;
}
дескриптор свойства на самом деле является объектом
использовать здесьObject.create(null)
для создания абсолютно чистого объекта, чтобы предотвратить случайное появление некоторых беспорядочных свойств в описании
тогда держи это
Затем реализовать набор
setProperyDescriptor(key, proxyXHR) {
let obj = Object.create(null);
let _this = this;
+ obj.set = function(val) {
+ }
}
Метод set будет использоваться, когда свойству будет присвоено значение (например,obj.a = 1
) и он получит параметр, который является значением присваивания (значение справа от знака равенства)
Затем вы можете выполнить шаги, перечисленные ранее
- Посмотрите, начинается ли атрибут с on
- Если нет, просто повесьте трубку
- Если да, посмотрите, есть ли хук для выполнения
- Если есть, оберните его и сначала выполните хук, а затем выполните тело
- Если нет ответственности, напрямую присвойте значение и повесьте трубку
setProperyDescriptor(key, proxyXHR) {
let obj = Object.create(null);
let _this = this;
obj.set = function(val) {
+ // 看看属性是不是on打头 如果不是 直接挂上去
+ if (!key.startsWith('on')) {
+ proxyXHR['__' + key] = val;
+ return;
+ }
+ // 如果是 则看看有没有要执行的钩子
+ if (_this.hooks[key]) {
+ // 如果有 则包装一下 先执行钩子 再执行本体
+ this._xhr[key] = function(...args) {
+ (_this.hooks[key].call(proxyXHR), val.apply(proxyXHR, args));
+ }
+ return;
+ }
+ // 如果没有 责直接赋值挂上去
+ this._xhr[key] = val;
}
+ obj.get = function() {
+ return proxyXHR['__' + key] || this._xhr[key];
+ }
+ return obj;
}
На первом этапе оценивается, включен он или нет, и если нет, то он будет прикреплен к экземпляру как есть.
Затем посмотрите, есть ли текущее свойство в списке хуков, если да, упакуйте присваиваемое значение (val) с хуком и превратите его в функцию
В функции сначала выполняется хук, а затем выполняется назначенное значение.Пока человек, который его использует, не слеп, значение здесь в основном состоит в том, что функция не запускалась, поэтому вызывайте ее напрямую, не оценивая, и параметры передаются в прошлом.
Если хука нет, просто назначьте его напрямую
Этот метод набора в порядке
Метод get проще, просто получите значение
Может быть, есть одно место, которое я не совсем понимаю, а именноproxyXHR['__' + key] = val;
также в методе get
почему есть__
Префикс на самом деле такой
Рассмотрим такой сценарий, вы можете получить его, когда запрос на перехват вернетсяresponseText
Атрибуты
Это свойство является значением, возвращаемым сервером.
может потребоваться в это времяresponseType
Единая обработка типов данных
Если это JSON, тоthis.responseText = JSON.parse(this.response)
Затем с радостью переходите к успешной функции обратного вызоваresponseText
В итоге сообщалось об ошибке при доступе к свойствам или методам на него.После печати обнаружилось, что он все-таки строка и передача не удалась.
На самом деле, потому чтоresponseText
доступен только для чтения в теге этого атрибутаwritable
даfalse
Таким образом, вы можете использовать прокси-атрибут для решения
Напримерthis.responseText = JSON.parse(this.responseText)
когда
Во-первых, получить его в соответствии с методом getresponseText
Еще нет__responseText
Атрибут, поэтому вернитесь к собственному экземпляру xhr и получите значение, возвращаемое сервером.
Затем после разбора он снова назначается
При копировании в экземпляре xhr, реализованном вами, будет еще один__responseText
Атрибут его значение обрабатывается
пройти после этогоresponseText
Значение, полученное с помощью метода get, равно__responseText
значение
Это решает проблему только для чтения собственных атрибутов экземпляра xhr через слой прокси атрибута.
Таким образом, большая часть логики завершена.this.overwriteAttributes(key, proxyXHR);
удалить комментарий
вторая отладка
Здесь может закружиться голова, не переживайте сильно, просто внимательно следите за рисунком, налейте стакан воды и успокойтесь
Это полный код на данный момент, вы можете попробовать его
class AnyXHR {
constructor(hooks = {}, execedHooks = {}) {
this.XHR = window.XMLHttpRequest;
this.hooks = hooks;
this.execedHooks = execedHooks;
this.init();
}
init() {
let _this = this;
window.XMLHttpRequest = function () {
this._xhr = new _this.XHR(); // 在实例上挂一个保留的原生的xhr实例
_this.overwrite(this);
}
}
overwrite(proxyXHR) {
for (let key in proxyXHR._xhr) {
if (typeof proxyXHR._xhr[key] === 'function') {
this.overwriteMethod(key, proxyXHR);
continue;
}
this.overwriteAttributes(key, proxyXHR);
}
}
overwriteMethod(key, proxyXHR) {
let hooks = this.hooks;
let execedHooks = this.execedHooks;
proxyXHR[key] = (...args) => {
// 如果当前方法有对应的钩子 则执行钩子
if (hooks[key] && (hooks[key].call(proxyXHR, args) === false)) {
return;
}
// 执行原生xhr实例中对应的方法
const res = proxyXHR._xhr[key].apply(proxyXHR._xhr, args);
// 看看还有没有原生xhr实例对应的方法执行后需要执行的钩子 如果有则执行
execedHooks[key] && execedHooks[key].call(proxyXHR._xhr, res);
return res;
}
}
setProperyDescriptor(key, proxyXHR) {
let obj = Object.create(null);
let _this = this;
obj.set = function (val) {
// 看看属性是不是on打头 如果不是 直接挂上去
if (!key.startsWith('on')) {
proxyXHR['__' + key] = val;
return;
}
// 如果是 则看看有没有要执行的钩子
if (_this.hooks[key]) {
// 如果有 则包装一下 先执行钩子 再执行本体
this._xhr[key] = function (...args) {
(_this.hooks[key].call(proxyXHR), val.apply(proxyXHR, args));
}
return;
}
// 如果没有 责直接赋值挂上去
this._xhr[key] = val;
}
obj.get = function () {
return proxyXHR['__' + key] || this._xhr[key];
}
return obj;
}
overwriteAttributes(key, proxyXHR) {
Object.defineProperty(proxyXHR, key, this.setProperyDescriptor(key, proxyXHR));
}
}
передача
new AnyXHR({
open: function () {
console.log('open');
},
onload: function () {
console.log('onload');
},
onreadystatechange: function() {
console.log('onreadystatechange');
}
});
$.get('/aaa', {
b: 2,
c: 3
}).done(function (data) {
console.log(1);
});
var xhr = new XMLHttpRequest();
xhr.open('GET', '/abc?a=1&b=2', true);
xhr.send();
xhr.onreadystatechange = function() {
console.log(1);
}
Введите jquery, чтобы попытаться перехватить
Посмотрите на консоль, чтобы увидеть результаты
Есть еще некоторый контент, который нужно завершить, чтобы сделать весь класс более полным, но остальное очень простое содержание.
синглтон
Потому что это глобальный перехват и есть только один глобальныйXMLHttpRequest
Объект, поэтому здесь он должен быть разработан как синглтон
Синглтон — это один из шаблонов проектирования, он не такой загадочный, он означает, что существует только один экземпляр во всем мире.
Другими словами, вы должны двигать руками и ногами.new AnyXHR
получить тот же экземпляр
Изменить конструктор
constructor(hooks = {}, execedHooks = {}) {
// 单例
+ if (AnyXHR.instance) {
+ return AnyXHR.instance;
+ }
this.XHR = window.XMLHttpRequest;
this.hooks = hooks;
this.execedHooks = execedHooks;
this.init();
+ AnyXHR.instance = this;
}
Войдите в конструктор, чтобы судить первымAnyXHR
Есть ли зависший экземпляр на
Затем, после завершения процесса создания, поместитеAnyXHR
Просто повесьте пример, так что ни на чтоnew
Все получают один и тот же экземпляр
В то же время добавьте метод, чтобы легко получить экземпляр
getInstance() {
return AnyXHR.instance;
}
добавить хук динамически
Все хуки хранятся в двух объектах. Каждое выполнение метода будет считывать эти два объекта, поэтому, пока объект изменяется, хуки могут добавляться динамически.
Итак, добавьте метод добавления
add(key, value, execed = false) {
if (execed) {
this.execedHooks[key] = value;
} else {
this.hooks[key] = value;
}
return this;
}
вkey
value
Два параметра соответствуют имени свойства или имени и значению метода.
execed
Указывает, выполняется ли собственный метод, а затем выполняется.Этот параметр используется для определения того, к какому объекту он добавляется.
Таким же образом снять крючок и опорожнить крючок очень просто.
снять крючок
rmHook(name, isExeced = false) {
let target = (isExeced ? this.execedHooks : this.hooks);
delete target[name];
}
пустой крючок
clearHook() {
this.hooks = {};
this.execedHooks = {};
}
Отменить перехват глобального мониторинга
Этот шаг на самом деле очень прост, перепишите нашу собственную реализациюXMLHttpRequest
Просто будь оригиналом
Мы сохранили оригиналthis.XHR
начальство
unset() {
window.XMLHttpRequest = this.XHR;
}
Переслушать перехват
Поскольку это синглтон, перезапустите прослушиватель, просто очистите синглтон и обновите его.
reset() {
AnyXHR.instance = null;
AnyXHR.instance = new AnyXHR(this.hooks, this.execedHooks);
}
полный код
class AnyXHR {
/**
* 构造函数
* @param {*} hooks
* @param {*} execedHooks
*/
constructor(hooks = {}, execedHooks = {}) {
// 单例
if (AnyXHR.instance) {
return AnyXHR.instance;
}
this.XHR = window.XMLHttpRequest;
this.hooks = hooks;
this.execedHooks = execedHooks;
this.init();
AnyXHR.instance = this;
}
/**
* 初始化 重写xhr对象
*/
init() {
let _this = this;
window.XMLHttpRequest = function() {
this._xhr = new _this.XHR();
_this.overwrite(this);
}
}
/**
* 添加勾子
* @param {*} key
* @param {*} value
*/
add(key, value, execed = false) {
if (execed) {
this.execedHooks[key] = value;
} else {
this.hooks[key] = value;
}
return this;
}
/**
* 处理重写
* @param {*} xhr
*/
overwrite(proxyXHR) {
for (let key in proxyXHR._xhr) {
if (typeof proxyXHR._xhr[key] === 'function') {
this.overwriteMethod(key, proxyXHR);
continue;
}
this.overwriteAttributes(key, proxyXHR);
}
}
/**
* 重写方法
* @param {*} key
*/
overwriteMethod(key, proxyXHR) {
let hooks = this.hooks;
let execedHooks = this.execedHooks;
proxyXHR[key] = (...args) => {
// 拦截
if (hooks[key] && (hooks[key].call(proxyXHR, args) === false)) {
return;
}
// 执行方法本体
const res = proxyXHR._xhr[key].apply(proxyXHR._xhr, args);
// 方法本体执行后的钩子
execedHooks[key] && execedHooks[key].call(proxyXHR._xhr, res);
return res;
};
}
/**
* 重写属性
* @param {*} key
*/
overwriteAttributes(key, proxyXHR) {
Object.defineProperty(proxyXHR, key, this.setProperyDescriptor(key, proxyXHR));
}
/**
* 设置属性的属性描述
* @param {*} key
*/
setProperyDescriptor(key, proxyXHR) {
let obj = Object.create(null);
let _this = this;
obj.set = function(val) {
// 如果不是on打头的属性
if (!key.startsWith('on')) {
proxyXHR['__' + key] = val;
return;
}
if (_this.hooks[key]) {
this._xhr[key] = function(...args) {
(_this.hooks[key].call(proxyXHR), val.apply(proxyXHR, args));
}
return;
}
this._xhr[key] = val;
}
obj.get = function() {
return proxyXHR['__' + key] || this._xhr[key];
}
return obj;
}
/**
* 获取实例
*/
getInstance() {
return AnyXHR.instance;
}
/**
* 删除钩子
* @param {*} name
*/
rmHook(name, isExeced = false) {
let target = (isExeced ? this.execedHooks : this.hooks);
delete target[name];
}
/**
* 清空钩子
*/
clearHook() {
this.hooks = {};
this.execedHooks = {};
}
/**
* 取消监听
*/
unset() {
window.XMLHttpRequest = this.XHR;
}
/**
* 重新监听
*/
reset() {
AnyXHR.instance = null;
AnyXHR.instance = new AnyXHR(this.hooks, this.execedHooks);
}
}
Заканчивать
На этом все готово, из-за отсутствия тестирования могут быть баги.
Так же есть некоторые дефекты.Все хуки должны быть синхронными.Если асинхронный то последовательность будет хаотичной.Эта проблема будет решена позже.Если вам интересно,можете попробовать сами.
Кроме того, этот метод перехвата в принципе применим к любому объекту и может использоваться гибко.
просто используйтеXMLHttpRequest
Ajax-запросы могут быть перехвачены им