Я пытаюсь создать систему, которая «кэширует» вызовы библиотеки до загрузки этой библиотеки.
Это похоже на то, что код настройки Google Analytics делает с _gaq
переменная - она инициализируется как массив, который «кэширует» вызовы реальных конечных точек аналитики до тех пор, пока не будет загружена библиотека ga.js
. Когда это происходит, он читает _gaq
и воспроизводит вызовы.
Мы решили сделать это, потому что наш унаследованный код содержит множество вызовов определенной библиотеки, которая загружается синхронно в <head>
. Это значительно увеличивает время до первой содержательной отрисовки, так как много JS оценивается и выполняется.
Однако в коде слишком много мест, которые необходимо изменить (обернуто в 'DOMContentLoaded'
слушатель), поэтому мы решили попробовать использовать обходной путь.
Мы решили попробовать использовать Proxy
, чтобы перехватывать вызовы методов нашей библиотеки и воспроизводить их, когда все будет готово:
// Original code:
var ourLib = new OurLib({ ... });
// Throughout the site, calls such as:
var res1 = ourLib.doThis();
var res2 = ourLib.getThat(3);
Вот своего рода «упрощенная» версия того, что делает наш новый код:
// New code:
var ourLib = new Proxy({
calls: [],
}, {
get(target, prop) {
if (prop in target) {
return Reflect.get(...arguments);
}
const callref = { prop, args: [], placeholder };
target.calls.push(callref);
return function(...args) {
const placeholder = MakeResultPlaceholder(...);
callref.args = args;
callref.placeholder = placeholder;
return placeholder;
};
},
});
// Throughout the site, calls continue as before, except now they're 'stored' in `calls`
var res1 = ourLib.doThis();
var res2 = ourLib.getThat(3);
// Much later, the original lib is loaded, and
var ourRealLib = new OurLib({ ... });
__playbackCalls(ourLib.calls, ourRealLib);
// Replace the proxy with the real thing
ourLib = ourRealLib;
После запуска выше свойство calls
будет выглядеть примерно так:
[
{
prop: 'doThis',
args: [],
reference: ResultPlaceholder
},
{
prop: 'getThat',
args: [3],
reference: ResultPlaceholder
}
]
Функция __playbackCalls
перебирает массив calls
и apply
каждый метод из ourRealLib
с args
, хранящимся в каждом объекте.
calls.forEach(({ prop, args, reference }) => {
reference._value = ourRealLib[prop].apply(ourRealLib, args);
});
Проблема возникает, когда результат вызовов прокси должен быть использован. Прямо сейчас, как вы можете видеть, вызовы возвращают объект placeholder
(который сам по себе является другим прокси). Эти заполнители содержат свойство _value
, которое заполняется во время «воспроизведения».
Итак, вот вопрос:
- Допустим,
ourLib.getThat()
предназначено для возврата number
. - Во время первого «запуска», из-за всего прокси,
res1
будет указывать на placeholder
объект: Proxy { _value: undefined }
- Загружена реальная библиотека, «воспроизведение» заканчивается,
ourRealLib.getThat(3)
возвращает 23
, поэтому res1
будет Proxy { _value: 23 }
- Могу ли я сделать что-нибудь , чтобы мы могли использовать
res1
в качестве числа? Что-то вроде:
console.log(res1 * 2); // 46