Авторы: Лилли Цзян, Айцин Донг
задний план
Система погребенных точек
Вообще говоря, полная система скрытых точек состоит из следующих трех частей:
- заявление
- Платформа анализа данных
- SDK платформы данных
Отчет о скрытых точках — это процесс загрузки событий прикладного уровня на платформу верхнего уровня. Например, на веб-сайте магазина пользователь нажимает кнопку «Избранное». В это время генерируется событие клика, и об этом событии сообщается на платформе анализа данных. Таким образом, соответствующие аналитики данных, менеджеры по продуктам, операторы и другие студенты могут анализировать отчетные данные о событиях на платформе анализа данных, чтобы получить все аспекты, которые можно оптимизировать в приложении. Можно видеть, что отчет о скрытых точках является важной частью качества каждого продукта.
Благодаря приведенному выше описанию мы знаем двух главных героев процесса создания отчетов о скрытых точках: приложение и платформу анализа данных. С точки зрения передовых технологий нам обычно нужна помощь третьей роли, которая является SDK платформы данных.Этот SDK инкапсулирует различные интерфейсы платформы анализа данных, предоставляя нам простые методы для вызова и реализации простой загрузки точки захоронения. .
Два скрытых события
Мы можем разделить события прикладного уровня на две категории:
- «Событие страницы»: одно из них является общим «событием страницы», таким как пребывание пользователя и время его активности на определенной странице в приложении. Мы надеемся, что такого рода глобальную скрытую точку можно ввести только один раз при инициализации проекта, и не нуждается в коде.в сопровождении.
- «Триггерное событие»: другое — это настраиваемое «триггерное событие», такое как нажатие определенной кнопки для запуска определенного процесса. Для этого типа события требуется, чтобы учащиеся переднего плана вручную вводили скрытые точки в коде.
Мы разработали набор встроенных SDK загрузки для этих двух событий соответственно. Далее давайте подробно объясним технические знания этих двух SDK.
SDK для обработки "событий страницы" -monitor-tracer
monitor-tracer
Это внешний SDK, используемый для мониторинга видимой и активной продолжительности страниц и компонентов, а также основной компонент встроенной внешней системы Monitor.
задний план
Чтобы лучше понять использование различных бизнес-функций пользователями, чтобы провести соответствующую оптимизацию и настройку продукта:
-
Для общих веб-приложений нам необходимо вести соответствующую статистику пребывания пользователя и времени активности на определенной странице приложения;
-
Для страницы типа большая/канбан/панель инструментов (как показано на рисунке ниже) мы надеемся дополнительно подсчитать время видимости каждого компонента для пользователя на основе размера страницы, чтобы оптимизировать порядок их расположения и содержание.
Исходя из вышеуказанных требований, мы разработалиmonitor-tracer
SDK, предназначенный для реализации"Страница видна, активное время"а также«Видимая продолжительность компонента»Регистрация.
Глоссарий
-
Страница- Веб-страница открыта в браузере, разные страницы отмечены путем
location.pathname
различать; - время видимости страницы- Совокупное время просмотра страницы пользователями;
- время активности страницы- совокупная продолжительность эффективных действий пользователя с мышью, клавиатурой и касаниями на странице;
- Компонент- Набор элементов DOM, являющихся частью страницы. Страница может содержать несколько компонентов;
- Видимое время компонента- Совокупное количество времени, в течение которого компонент был виден пользователю.
Его отношение:
- Активная продолжительность страницы ≤ видимой продолжительности страницы;
- Продолжительность видимости компонента ≤ продолжительности видимости страницы;
- Когда страница не видна, она должна быть неактивна, и все компоненты на ней также должны быть невидимы.
Просматриваемость страниц и статистика активного времени
В нашем дизайне для измерения времени пребывания и активности страницы необходимы два важных показателя:
-
видимость
-
visible
- Страница находится в области просмотра текущего браузера, а окно браузера не свернуто; -
invisible
- Страница не находится в области просмотра текущего браузера или ее невозможно увидеть, так как браузер свернут.
-
-
Деятельность
-
active
- активность пользователя на веб-странице (например, мышь, клавиатура, прокрутка страницы и т. д.); -
inactive
- У пользователя нет активности на странице.
-
Пока можно получить временные метки этих четырех состояний, видимая и активная продолжительность страницы от загрузки до выхода может быть рассчитана кумулятивно, как показано на следующем рисунке:
Получить данные о видимости страницы
Web Lifecycle
В июле 2018 года Google предложил набор рекомендаций для описания жизненного цикла веб-страниц.Page Lifecycle APIСпецификация, этот SDK основан на этой спецификации для отслеживания изменений видимости страницы. В спецификации указано, что в процессе загрузки веб-страницы до ее уничтожения она будет трансформироваться между следующими шестью состояниями жизненного цикла посредством различных событий браузера.
состояние жизненного цикла | описывать |
---|---|
active |
Веб-страница видна и имеет фокус |
passive |
Веб-страница видна, но не в фокусе |
hidden |
Веб-страница невидима, но не зависает в браузере, обычно это происходит, когда пользователь переключается на другую вкладку или сворачивает браузер. |
frozen |
Веб-страница зависает в браузере (некоторые фоновые задачи, такие как таймеры, выборка и т. д., приостанавливаются для экономии ресурсов ЦП) |
terminated |
Веб-страницы выгружаются браузером и очищаются от памяти. Это состояние срабатывает, когда обычный пользователь активно закрывает веб-страницу. |
discarded |
Веб-страница была вынуждена очистить браузер. Обычно вызваны серьезным отсутствием системных ресурсов |
Отношения преобразования между состояниями жизненного цикла показаны на следующем рисунке:
Из вышеприведенной информации мы можем нарисовать взаимосвязь между состоянием жизненного цикла страницы и видимым состоянием страницы:
состояние жизненного цикла | Видимое состояние |
---|---|
active passive
|
visible |
hidden terminated frozen discarded
|
invisible |
Поэтому нам нужно только следить за изменениями жизненного цикла страницы и фиксировать его время, а затем мы можем получать статистику видимости страницы соответственно.
Отслеживание изменений жизненного цикла страницы
При разработке спецификации жизненного цикла страницы команда Google Chrome также разработалаPageLifecycle.js
SDK реализует мониторинг состояния жизненного цикла, описанный в этой спецификации, и совместим со всеми браузерами выше IE 9. Для простоты использования и стабильности мы решили использовать этот SDK для мониторинга жизненного цикла. из-заPageLifecycle.js
Он написан на самом JavaScript, мы добавляем в него определения типов и инкапсулируем для совместимости с TypeScript.@byted-cg/page-lifecycle-typed
SDK. PageLifecycle.js
используется следующим образом:
import lifecycleInstance, {
StateChangeEvent,
} from "@byted-cg/page-lifecycle-typed";
lifecycleInstance.addEventListener("statechange", (event: StateChangeEvent) => {
switch (event.newState) {
case "active":
case "passive":
// page visible, do something
break;
case "hidden":
case "terminated":
case "frozen":
// page invisible, do something else
break;
}
});
пройти черезPageLifecycle.js
, мы можем контролироватьstatechange
события, чтобы получать уведомления, когда изменяется жизненный цикл страницы и когда состояние жизненного циклаactive
а такжеpassive
отметить страницу какvisible
состояние, пометьте страницу как состояние жизненного цикла, несколько иноеinvisible
Статус, обновить последнюю видимую временную метку и накапливать видимое время страницы.
PageLifecycle.js
Дефекты
Для наших нужд,PageLifecycle.js
Он имеет следующие два дефекта. Мы также внесли некоторые улучшения для этих двух дефектов.
-
Невозможно отслеживать изменения страниц в одностраничном приложении (SPA)
В одностраничных приложениях страница обычно полагается на маршрут History или Hash для переключения, сама страница не перезагружается, поэтому
PageLifecycle.js
Не удалось обнаружить переключение страниц. Чтобы справиться с этой ситуацией, мы вручную добавили событие смены маршрута в monitor-tracer (popstate
replacestate
и др.) мониторинг. Если обнаружится, что маршрут страницы изменился, будет считаться, что текущая страница вошлаterminated
жизненного цикла, чтобы выполнить соответствующую обработку. Логика мониторинга изменений маршрутизации здесь повторно использует логику, которую мы разработали ранее.@byted-cg/puzzle-routerSDK, к которому могут обратиться заинтересованные студенты. -
не могу поймать
discarded
Жизненный циклdiscarded
Жизненный цикл происходит, когда страница принудительно очищается браузером. На этом сайте уничтожено и вычищено из памяти, вы не можете пройти ни одно событие снаружи, поэтомуPageLifecycle.js
тоже нельзя отправитьdiscarded
мероприятие. Как только это произойдет, это приведет к потере очищенной статистики веб-страницы. Чтобы справиться с этим сценарием,monitor-tracer
войдет на страницуinvisible
состояние, сохраните существующую статистику продолжительности страницы, используяJSON.stringify
сериализуются и хранятся вlocalStorage
среди. Если страница восстановленаvisible
государство,localStorage
Данные на странице очищаются, и если страница очищается, при следующем входе на страницуlocalStorage
Данные предыдущей страницы, хранящиеся в событии, выталкиваются наружу. Это в наибольшей степени гарантирует, что даже при принудительной очистке страницы ее данные могут быть отправлены без потерь.
Получить данные об активности страницы
По сравнению с видимостью страницы оценка активности страницы более проста. Для непосредственной оценки статуса страницы используются следующие методы:active
ещеinactive
Вот и все.
active
Стандарт суждения
Прослушивая серию событий браузера, мы можем определить, активен ли пользователь на текущей странице.monitor-tracer
Отслеживаются следующие шесть событий:
мероприятие | описывать |
---|---|
keydown |
Запускается, когда пользователь нажимает на клавиатуру |
mousedown |
Запускается, когда пользователь нажимает кнопку мыши |
mouseover |
Запускается, когда пользователь перемещает указатель мыши |
touchstart |
Запускается, когда палец пользователя касается сенсорного экрана (только для устройств с сенсорным экраном). |
touchend |
Запускается, когда палец пользователя покидает сенсорный экран (только для устройств с сенсорным экраном). |
scroll |
Запускается, когда пользователь прокручивает страницу |
После наблюдения за вышеуказанными событиямиmonitor-tracer
пометит страницу какactive
Статус и запись текущей метки времени, а также накопление активного времени.
inactive
Стандарт суждения
В следующих двух случаях страница будет помечена какinactive
условие:
- По истечении определенного временного порога (по умолчанию 30 секунд, который можно настроить при инициализации SDK) шесть событий, указывающих на активность страницы, не обнаруживаются;
- Статус страницы
invisible
.Потому что если страница не видна пользователю, то она должна быть неактивна.
страница отмечена какinactive
назад,monitor-tracer
Текущая отметка времени будет записана, а активная продолжительность будет накоплена.
Статистика видимого времени компонента
Подсчет активной длительности уровня компонента требует двух условий: одно — получить все DOM-элементы, которые необходимо подсчитать, а второе — отслеживать эти DOM-элементы в соответствии с определенными стандартами.
Получить элемент DOM, который нужно посчитать
Мониторинг изменений структуры DOM
Получение элементов DOM требует от нас отслеживания изменений во всей структуре DOM.monitor-tracer
использовалMutationObserver
API, Через этот API можно уведомлять о любых изменениях в DOM, таких как добавление или удаление узлов, изменения атрибутов, изменения текстового содержимого.
Концептуально это очень близко к событию, которое можно понимать как срабатывание при изменении DOM.MutationObserver
мероприятие. Однако он существенно отличается от событий: события запускаются синхронно, то есть изменения DOM немедленно вызывают соответствующие события, аMutationObserver
Он запускается асинхронно, и изменения DOM не будут инициированы немедленно, но не будут инициированы до тех пор, пока не будут завершены все текущие операции DOM.
Этот дизайн предназначен для работы с характеристиками частых изменений DOM и сохранения производительности. Например, если вы вставите 1000 последовательных<p></p>
тег, он будет непрерывно запускать 1000 событий вставки и выполнять функцию обратного вызова для каждого события, что может привести к зависанию браузера. а такжеMutationObserver
Совершенно другой, он срабатывает только после того, как будет вставлено 1000 тегов, и только один раз.
MutationObserver
Использование API выглядит следующим образом:
const observer = new MutationObserver(function (mutations, observer) {
mutations.forEach(function (mutation) {
console.log(mutation.target); // target: 发生变动的 DOM 节点
});
});
observer.observe(document.documentElement, {
childList: true, //子节点的变动(指新增,删除或者更改)
attributes: true, // 属性的变动
characterData: true, // 节点内容或节点文本的变动
subtree: true, // 表示是否将该观察器应用于该节点的所有后代节点
attributeOldValue: false, // 表示观察 attributes 变动时,是否需要记录变动前的属性值
characterDataOldValue: false, // 表示观察 characterData 变动时,是否需要记录变动前的值。
attributeFilter: false, // 表示需要观察的特定属性,比如['class','src']
});
observer.disconnect(); // 用来停止观察。调用该方法后,DOM 再发生变动则不会触发观察器
Отметьте элемент, который необходимо контролировать
Чтобы найти элементы для прослушивания среди множества элементов DOM, нам нужен способ пометить эти элементы.monitor-tracer
SDK предусматривает, что если компоненту необходимо подсчитывать активное время, он должен добавитьmonitor-pv
илиdata-monitor-pv
Атрибуты. В использованииMutationObserver
При сканировании изменений DOMmonitor-tracer
Элементы DOM с этими двумя свойствами будут собраны в массив для прослушивания. Например, следующие два компонента:
<div monitor-pv='{ "event": "component_one_pv", "params": { ... } }'>
Component One
</div>
<div>Component Two</div>
Компонент один, потому что добавлениеmonitor-pv
Атрибуты будут записываться и учитывать видимую продолжительность. Второй компонент - нет.
babel-plugin-tracer
плагин
Если компонент, который необходимо отслеживать, написан в какой-либо библиотеке компонентов, такой как Ant Design или ByDesign, то добавьтеmonitor-pv
илиdata-monitor-pv
Такие пользовательские атрибуты могут быть отфильтрованы самим компонентом и не будут отображаться в окончательно сгенерированном элементе DOM, в результате чего мониторинг компонента не будет работать. Для решения аналогичной задачи мы разработали@byted-cg/babel-plugin-tracer
Плагин Бабель. Этот плагин будет искать добавленныеmonitor-pv
компонент атрибута и обернуть пользовательский<monitor></monitor>
Этикетка. Например:
import { Card } from 'antd';
const Component = () => {
return <Card monitor-pv={{ event: "component_one_pv", params: { ... } }}>HAHA</Card>
}
Если плагин не добавлен, окончательный сгенерированный DOM будет следующим:
<div class="ant-card">
<!-- ... Ant Design Card 组件 -->
</div>
видимыйmonitor-pv
Атрибуты исчезли после фильтрации компонентов.
После установки плагина babel окончательная скомпилированная структура DOM выглядит так:
<monitor
is="custom"
data-monitor-pv='{ "event": "component_one_pv", "params": { ... } }'
>
<div class="ant-card">
<!-- ... Ant Design Card 组件 -->
</div>
</monitor>
monitor-pv
свойства сохраняются и плагин автоматически добавляет ихdata-
префикс, если React 16 поддерживает толькоdata-
Проблема с пользовательским атрибутом в начале, при использовании входящего объектаJSON.stringify
Преобразовано в единственный поддерживаемый атрибут элемента DOMstring
Типы.
Поскольку у пользовательского тега нет стиля, перенос тега не повлияет на стиль исходного компонента.monitor-tracer
После сканирования элемента DOM SDK собирает все<monitor></monitor>
информацию об элементе в теге и контролировать элемент, который он обертывает.
Определение видимости элемента DOM
Оценку видимости компонента можно разделить на три измерения:
- Находится ли компонент в окне просмотра браузера — используйте
IntersectionObserver
APIсудить; - Видны ли стили компонентов — на основе CSS элемента
display
visibility
а такжеopacity
оценка атрибута стиля; - Видна ли страница — на основе видимости страницы.
Определить, находится ли компонент в области просмотра браузера
Здесь мы используемIntersectionObserver
API, Этот API предоставляет способ асинхронного обнаружения целевого элемента и элементов-предков илиviewportСпособы изменения ситуации на перекрестке.
В прошлом при обнаружении пересечений часто использовались прослушиватели событий, которые нужно было часто вызывать.Element.getBoundingClientRect
метод для получения информации о границах связанного элемента. Мониторинг событий и звонкиElement.getBoundingClientRect
Все выполняются в основном потоке, поэтому частые запуски и вызовы могут вызвать проблемы с производительностью. Этот метод обнаружения чрезвычайно странный и неэлегантный.
а такжеIntersectionObserver
API регистрирует функцию обратного вызова, которая запускается и выполняется всякий раз, когда отслеживаемый элемент входит или выходит из другого элемента (или области просмотра) или когда изменяется размер пересечения двух элементов. Таким образом, главному потоку нашего веб-сайта не нужно усердно следить за пересечением элементов, а браузер сам оптимизирует управление пересечением элементов.
IntersectionObserver
Использование API выглядит следующим образом:
const observer = new IntersectionObserver(
(entries) => {
entries.forEach(function (entry) {
/**
entry.boundingClientRect // 目标元素的矩形区域的信息
entry.intersectionRatio // 目标元素的可见比例,即 intersectionRect 占 boundingClientRect 的比例,完全可见时为 1,完全不可见时小于等于 0
entry.intersectionRect // 目标元素与视口(或根元素)的交叉区域的信息
entry.isIntersecting // 标示元素是已转换为相交状态 (true) 还是已脱离相交状态 (false)
entry.rootBounds // 根元素的矩形区域的信息, getBoundingClientRect 方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回 null
entry.target // 被观察的目标元素,是一个 DOM 节点对象
entry.time // 可见性发生变化的时间,是一个高精度时间戳,单位为毫秒
**/
});
},
{
threshold: [0, 0.25, 0.5, 0.75, 1], //该属性决定了什么时候触发回调函数。它是一个数组,每个成员都是一个门槛值,默认为 [0],即交叉比例 (intersectionRatio) 达到 0 时触发回调函数
}
);
observer.observe(document.getElementById("img")); // 开始监听一个目标元素
observer.disconnect(); // 停止全部监听工作
Если компонент пересекает окно просмотра с коэффициентом меньше определенного значения (по умолчанию 0,25), то компонент помечается какinvisible
И наоборот, если масштаб больше определенного значения (по умолчанию 0,75), то компонент будет помечен какvisible
.
Определите, видны ли стили CSS компонента
Если стиль CSS элемента установлен наvisibility: hidden
илиopacity: 0
, то он невидим для пользователя, даже если он пересекает окно просмотра в масштабе 1. Поэтому нам нужно дополнительно определить, видимо ли свойство CSS целевого элемента.
Если для стиля компонента установлено одно из следующих значений, он помечается какinvisible
.
visibility: hidden
display: none
opacity: 0
Определить, видна ли страница
Когда страница не видна, все компоненты естественным образом невидимы, поэтому страницаinvisible
состояние,monitor-tracer
также пометит состояние всех компонентов, подлежащих мониторингу, какinvisible
.
SDK, который обрабатывает «инициированные события» —monitor
monitor
Позиционирование SDK
Единый метод отчетности SDK платформы данных не может соответствовать конечной цели чистого кода в нашей разработке.
SDK платформы данных часто предоставляет только функциональный метод для сообщения о скрытых точках.Хотя он может удовлетворить наши повседневные потребности в разработке, он не может решить две основные болевые точки, когда мы пишем скрытый код:
- Вы можете сообщать о скрытых точках только одну за другой.
- Сочетание скрытой логики и бизнес-логики
Мы надеемся, что встроенный код можно будет легко добавлять, изменять и удалять, и он не повлияет на бизнес-код. Поэтому мы разрабатываем нечувствительные к фреймворку фреймворки на основе TypeScript.monitor
SDK. Он поддерживает загрузку нескольких скрытых точек одну за другой и принимает функции, которые возвращают скрытые точки и сообщают об их возвращаемых значениях; он предоставляет три способа внедрения скрытых точек, охватывающих все сценарии и полностью отделяющих скрытые точки от бизнес-кода.
Как можно заметить,monitor
Это одновременно и процессор данных, и библиотека методов. Более интуитивно используйтеmonitor
После этого процесс сообщения закопанных точек в нашем приложении выглядит следующим образом:
Скрытые точки отправляются прикладным уровнем вmonitor
назад,monitor
Сначала данные будут обработаны, а затем будет вызван SDK платформы данных, чтобы сообщить платформе данных о событии закопанной точки.
справаmonitor
После предварительного понимания эта статья в основном объяснитmonitor
Как разделить бизнес-логику и логику скрытых точек с помощью следующих трех методов внедрения скрытых точек.
Давайте взглянемmonitor
а такжеmonitor-tracer
Специфический технический дизайн SDK и методы реализации.
Три метода инжекции в заглубленные точки
как императив
monitor
Предоставляет метод инструкции класса для внедрения скрытых точек. Например, в следующем коде используетсяmonitor-click
Директивы вводят скрытые точки. Когда эта кнопка нажата (щелчок),monitor-click
Соответствующее значение, то есть событие, будет сообщено.
// 指令式埋点示例
<Button
monitor-click={JSON.stringify({
type: "func_operation",
params: { value: 3 },
})}
>
Click Me
</Button>
Как это достигается? Зачем добавлять только один к компонентуmonitor-click
Атрибуты,monitor
Будет ли он сообщать о скрытой точке при нажатии этой кнопки?
Реализация и принцип
фактически,monitor
Когда SDK инициализируется, он выдаст текущийdocument
Объект плюс серия прослушивателей событий, прослушивающихhover
click
input
focus
и т.д. события. Когда слушатель уволен,monitor
вызовет событие изtarget
Объект запускается и проходит уровень за уровнем, чтобы увидеть, есть ли у текущего элемента инструкция, соответствующая этому событию.Если да, сообщайте об этом событии до тех пор, пока не встретится узел элемента без инструкции события. На следующей диаграмме показан процесс создания отчетов о скрытых точках императивного типа:
Поэтапный процесс отчетности
Возьмите следующий код в качестве примера, когда курсор наводится наButton
час,document
прослушиватели, установленные на объектеhover
Функция события выполнена. Эта функция перваяevent.target
которыйButton
узнать, есть лиhover
Директивы, связанные с событиями (т.е. свойства).Button
имеютmonitor-hover
Эта команда, в это время функция загружает событие, соответствующее этой команде, то есть{ type: 'func_operation', params: { value: 1 }}
.
Далее функция поднимается на один уровень вверх, кButton
родительский элементdiv
, повторяя описанный выше процесс, он находитdata-monitor-hover
Эта команда также сообщила о соответствующем событии скрытой точки. и прибылsection
Хотя этот слой имеетdata-monitor-click
команда, но эта команда невернаhover
Событие откликается, следовательно, процесс пошагового отчёта о закопанных точках закончен.
// 指令式埋点实现逐级上报
<section
data-monitor-click={JSON.stringify({
type: 'func_operation',
params: { value: 3 },
})}
>
<div
data-monitor-hover={JSON.stringify({
type: 'func_operation',
params: { value: 2 },
})}
>
<Button
monitor-hover={JSON.stringify({
type: 'func_operation',
params: { value: 1 },
})}
>
Click Me
</Button>
</div>
</section>
Внедрение скрытых точек в инструкции класса подходит для простого создания отчетов о скрытых точках и четко отделено от бизнес-кода. Но если нам нужно обработать сообщенные данные перед сообщением о событии, то этот метод не может быть удовлетворен. Кроме того, не все сценарии можно охватить событиями DOM. Если я хочу сообщить о скрытой точке, когда пользователь вводит определенное значение в поле поиска, мне нужно проанализировать значение, введенное пользователем, а не в поле поиска.input
О закопанных точках сообщается каждый раз, когда запускается событие.
декоратор
Декоратор — это, по сути, функция более высокого порядка. Он принимает функцию и возвращает другую декорированную функцию. Поэтому мы, естественно, думаем об использовании декораторов для внедрения логики скрытых точек в бизнес-функции, которые не только реализуют отделение скрытых точек от бизнес-кода, но и адаптируются к сложным сценариям скрытых точек.
Код ниже использует@monitorBefore
модификатор.@monitorBefore
Возвращаемое значение полученной функции является событием, о котором необходимо сообщить. существуетhandleSearch
Когда функция вызывается,monitor
Сначала сообщит о событии закопанной точки, а затем выполнитhandleSearch
Логика функции.
// @monitorBefore 使用示例
@monitorBefore((value: string) => ({
type: 'func_operation',
params: { keyword: value },
}))
handleSearch() {
console.log(
'[Decorators Demo]: this should happen AFTER a monitor event is sent.',
);
}
return (
<AutoComplete
onSearch={handleSearch}
/>
)
от@readonly
Понять, как работают декораторы
Как декоратор реализует интеграцию логики скрытых точек и бизнес-логики? Подробнее читайте в нашем@monitorBefore
Перед этим начнем с обычного декоратора@readonly
Давайте начнем.
Декораторы применяются к одному члену класса, включая свойства класса, методы, геттеры и сеттеры.При вызове функция декоратора получает 3 параметра:
-
target
- класс, в котором находится декоратор -
name
- имя декорируемой функции -
descriptor
- Дескриптор атрибута украшенной функции
// @readonly装饰器的代码实现
readonly = (target, name, descriptor) => {
console.log(descriptor);
descriptor.writable = false;
return descriptor;
};
Приведенный выше код проходитconsole.log
Результат:
с нашим общим@readonly
Например, способ его реализации такой, как указано выше. Выйдя из системы в приведенном выше кодеdescriptor
, мы узнаем, чтоdescriptor
Свойства:
-
writable
- Может ли декорированная функция быть изменена оператором присваивания; -
enumerable
- Появляется ли декорированная функция в свойстве перечисления объекта; -
configurable
- Можно ли изменить или удалить из объекта дескриптор декорируемой функции; -
value
- Значение декорированной функции, то есть соответствующий ей функциональный объект.
видимый,@readonly
декоратор будетdescriptor
изwritable
свойство установлено наfalse
и возвращает этоdescriptor
, он успешно устанавливает для члена класса, который он украшает, доступ только для чтения.
Мы используем следующим образом@readonly
Декоратор:
class Example {
@readonly
a = 10;
@readonly
b() {}
}
@monitorBefore
реализация
@monitorBefore
декоратор, чем@readonly
Это немного сложнее: как он объединяет логику скрытой точки с бизнес-логикой для создания новой функции?
Во-первых, давайте взглянем на следующий код.monitorBefore
Получить скрытое событиеevent
в качестве параметра и возвращает функцию. Параметры возвращаемой функции такие же, как упомянутые выше.@readonly
Полученные параметры совпадают.
// monitorBefore 函数源代码
monitorBefore = (event: MonitorEvent) => {
return (target: object, name: string, descriptor: object) =>
this.defineDecorator(event, descriptor, this.before);
};
// @monitorBefore 使用方式
@monitorBefore((value: string) => ({
type: 'func_operation',
params: { keyword: value },
}))
handleSearch() {
console.log(
'[Decorators Demo]: this should happen AFTER a monitor event is sent.',
);
}
return (
<AutoComplete
onSearch={handleSearch}
/>
)
Во время компиляции@monitorBefore
получилevent
параметры и возвращает следующую функцию:
f = (target: object, name: string, descriptor: object) =>
this.defineDecorator(event, descriptor, this.before);
Затем компилятор вызывает функциюf
, передать текущий класс, декорированное имя функции и дескриптор ее атрибута вf
, функцияf
вернутьthis.defineDecorator(event, descriptor, this.before)
будет проанализирован как новыйdescriptor
объект, егоvalue
будет вызываться во время выполнения, то есть он будет вызываться во время выполненияonSearch
Вызывается при срабатывании.
Теперь давайте подробно интерпретируемdefineDecorator
Как изменяется функция для создания новогоdescriptor
да.
// defineDecorator 函数源代码
before = (event: MonitorEvent, fn: () => any) => {
const that = this;
return function (this: any) {
const _event = that.evalEvent(event)(...arguments);
that.sendEvent(_event);
return fn.apply(this, arguments);
};
};
defineDecorator = (
event: MonitorEvent,
descriptor: any,
decorator: (event: MonitorEvent, fn: () => any) => any
) => {
if (isFunction(event) || isObject(event) || isArray(event)) {
const wrapperFn = decorator(event, descriptor.value);
function composedFn(this: any) {
return wrapperFn.apply(this, arguments);
}
set(descriptor, "value", composedFn);
return descriptor;
} else {
console.error(
`[Monitor SDK @${decorator}] the event argument be an object, an array or a function.`
);
}
};
monitorBefore = (event: MonitorEvent) => {
return (target: object, name: string, descriptor: object) =>
this.defineDecorator(event, descriptor, this.before);
};
defineDecorator
Функция принимает три параметра:
-
event
- Закопанные точки, о которых необходимо сообщить; -
descriptor
- дескриптор атрибута декорированной функции; -
decorator
- функция высшего порядка. Он получает событие отслеживания и обратный вызов, возвращает функцию отслеживания отслеживания, а затем выполняет обратный вызов.
decorator
Сначала возвращает функциюwrapperFn
, Когда звонили,wrapperFn
Сначала сообщит о скрытой точке, а затем выполнитdescriptor.value
Логика, то есть декорированная функция.
существуетdefineDecorator
изcomposedFn
, мы используемwrapperFn.apply(this, arguments)
Прозрачно передавать параметры, переданные при вызове декорированной функции, вwrapperFn
.
Наконец, мы будемcomposedFn
установить какdescriptor.value
, Таким образом, мы успешно создали новую функцию, которая сочетает в себе логику скрытых точек и бизнес-логику.
То, как декоратор спрятан и внедрен, очень чисто и его можно четко отличить от бизнес-кода. Будь то добавление, изменение или удаление скрытых точек, не нужно беспокоиться об изменениях в бизнес-коде. Но его ограничения также очевидны, декораторы можно использовать только для компонентов класса, и теперь наши часто используемые функциональные компоненты не могут использовать декораторы.
Реагировать на хуки
Чтобы иметь возможность реализовать функции, привнесенные скрытой точкой декоратора в функциональный компонент, мы также поддерживаем хук скрытой точки.useMonitor
, работает по тому же принципу, что и декораторы,useMonitor
Получите функцию скрытой точки, бизнес-функцию и верните новую функцию для их интеграции, которая не только обеспечивает четкое разделение на уровне кода, но также охватывает внедрение скрытой точки всей сцены.
// useMonitor 源代码
useMonitor = (fn: () => any, event: MonitorEvent) => {
if (!event) return fn;
const that = this;
return function (this: any) {
const _event = that.evalEvent(event)(...arguments);
that.sendEvent(_event);
return fn.apply(this, arguments);
};
};
useMonitor
Реализация относительно проста, это просто функция более высокого порядка, в отличие от декораторов, которые требуют разбора синтаксиса. Он возвращает функцию и при вызове сначала загружает событие скрытой точки, а затем выполняет бизнес-логику. Он используется следующим образом:
// useMonitor 使用示例
const Example = (props: object) => {
const handleChange = useMonitor(
// 业务逻辑
(value: string) => {
console.log("The user entered", value);
},
// 埋点逻辑
(value: string) => {
return {
type: "func_operation",
params: { value },
};
}
);
return <Search onSearch={handleChange} />;
};
резюме
Вышеуказанные три метода захоронения точек охватывают все сценарии использования. Используете ли вы React, Vue или собственный JavaScript, используете ли вы компоненты класса или функциональные компоненты, требует ли ваше встраивание сложной предварительной логики,monitor
Все пакеты SDK предоставляют методы использования, соответствующие вашему сценарию.
стек технологий
- Язык программирования -TypeScript
- Монитор жизненного цикла веб-страницы —
PageLifecycle.js
- Мониторинг событий, диспетчеризация -
wolfy87-eventemitter
- Монитор изменения структуры DOM -
MutationObserver
API - Элемент DOM и прослушиватель перекрестного состояния окна просмотра -
IntersectionObserver
API
Кредиты разработчиков
-
monitor
SDK - [Lilly Jiang] -
monitor-tracer
SDK- Просматриваемая и активная страница, плагин babel - [Aiqing Dong]
- Компонент «Видимое время» — [Юлин Чен]