@alexsab-ru/scripts 0.14.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/campaign-guide.md +234 -0
- package/examples/campaign-examples.js +126 -0
- package/examples/nobounce-examples.js +280 -0
- package/index.js +2 -0
- package/lib/campaign/index.js +5 -0
- package/lib/campaign/persist-campaign-data.js +162 -0
- package/lib/campaign/types.d.ts +290 -0
- package/lib/cookie.js +9 -4
- package/lib/form.js +3 -1
- package/lib/nobounce/index.js +5 -0
- package/lib/nobounce/nobounce.js +336 -0
- package/lib/nobounce/types.d.ts +255 -0
- package/package.json +20 -1
- package/lib/calltouch/fixed_calltouch_module.js +0 -312
- package/lib/calltouch/fixed_create_request.js +0 -176
- package/lib/fixed_analytics.js +0 -240
- package/lib/fixed_form.js +0 -443
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
# Persist Campaign Data
|
|
2
|
+
|
|
3
|
+
Функционал для сохранения данных рекламных кампаний (UTM параметры, gclid и др.) в cookies браузера и отправки в dataLayer. Основан на популярном Google Tag Manager шаблоне.
|
|
4
|
+
|
|
5
|
+
## Возможности
|
|
6
|
+
|
|
7
|
+
- 🎯 Автоматическое сохранение UTM параметров и других данных кампании в cookies
|
|
8
|
+
- 📊 Отправка данных в dataLayer для интеграции с аналитикой
|
|
9
|
+
- 🔗 Сохранение referrer когда пользователь приходит с внешнего домена
|
|
10
|
+
- ⚙️ Гибкая настройка параметров и поведения
|
|
11
|
+
- 🚀 Простая интеграция с React, Vue, Next.js и другими фреймворками
|
|
12
|
+
- 📱 Поддержка SPA (Single Page Applications)
|
|
13
|
+
|
|
14
|
+
## Установка
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @alexsab-ru/scripts
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Быстрый старт
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
import { initPersistCampaignData } from '@alexsab-ru/scripts';
|
|
24
|
+
|
|
25
|
+
// Автоматическая инициализация при загрузке страницы
|
|
26
|
+
initPersistCampaignData();
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Установка отдельных модулей
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
// Всё сразу
|
|
33
|
+
import { persistCampaignData } from '@alexsab-ru/scripts';
|
|
34
|
+
|
|
35
|
+
// Только campaign модуль
|
|
36
|
+
import { persistCampaignData } from '@alexsab-ru/scripts/campaign';
|
|
37
|
+
|
|
38
|
+
// Конкретный файл
|
|
39
|
+
import { persistCampaignData } from '@alexsab-ru/scripts/campaign/persist';
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## API
|
|
43
|
+
|
|
44
|
+
### `persistCampaignData(options)`
|
|
45
|
+
|
|
46
|
+
Основная функция для сохранения данных кампании.
|
|
47
|
+
|
|
48
|
+
**Параметры:**
|
|
49
|
+
|
|
50
|
+
| Параметр | Тип | По умолчанию | Описание |
|
|
51
|
+
|----------|-----|--------------|----------|
|
|
52
|
+
| `storeInCookie` | boolean | `true` | Сохранять ли данные в cookies |
|
|
53
|
+
| `pushToDataLayer` | boolean | `true` | Отправлять ли данные в dataLayer |
|
|
54
|
+
| `triggerParameters` | string[] | `['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'utm_id', 'gclid', 'yclid', 'ysclid']` | Параметры URL, которые триггерят сохранение |
|
|
55
|
+
| `urlCookieName` | string | `'__gtm_campaign_url'` | Имя cookie для URL кампании |
|
|
56
|
+
| `referrerCookieName` | string | `'__gtm_referrer'` | Имя cookie для referrer |
|
|
57
|
+
| `dataLayerName` | string | `'dataLayer'` | Имя массива dataLayer |
|
|
58
|
+
| `dataLayerKey` | string | `'originalLocation'` | Ключ в dataLayer для оригинального URL |
|
|
59
|
+
| `cookieExpireDays` | number | `null` | Дни жизни cookie (null = session cookie) |
|
|
60
|
+
|
|
61
|
+
**Возвращает:** `boolean` - успешность операции
|
|
62
|
+
|
|
63
|
+
### `getCampaignData(options)`
|
|
64
|
+
|
|
65
|
+
Получает сохраненные данные кампании из cookies.
|
|
66
|
+
|
|
67
|
+
**Параметры:**
|
|
68
|
+
|
|
69
|
+
| Параметр | Тип | По умолчанию | Описание |
|
|
70
|
+
|----------|-----|--------------|----------|
|
|
71
|
+
| `urlCookieName` | string | `'__gtm_campaign_url'` | Имя cookie для URL кампании |
|
|
72
|
+
| `referrerCookieName` | string | `'__gtm_referrer'` | Имя cookie для referrer |
|
|
73
|
+
|
|
74
|
+
**Возвращает:**
|
|
75
|
+
```javascript
|
|
76
|
+
{
|
|
77
|
+
campaignUrl: string | undefined,
|
|
78
|
+
referrer: string | undefined
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### `extractUtmParameters(url)`
|
|
83
|
+
|
|
84
|
+
Извлекает UTM параметры из URL.
|
|
85
|
+
|
|
86
|
+
**Параметры:**
|
|
87
|
+
|
|
88
|
+
| Параметр | Тип | По умолчанию | Описание |
|
|
89
|
+
|----------|-----|--------------|----------|
|
|
90
|
+
| `url` | string | `window.location.href` | URL для анализа |
|
|
91
|
+
|
|
92
|
+
**Возвращает:**
|
|
93
|
+
```javascript
|
|
94
|
+
{
|
|
95
|
+
utm_source: string | null,
|
|
96
|
+
utm_medium: string | null,
|
|
97
|
+
utm_campaign: string | null,
|
|
98
|
+
utm_term: string | null,
|
|
99
|
+
utm_content: string | null,
|
|
100
|
+
utm_id: string | null,
|
|
101
|
+
gclid: string | null,
|
|
102
|
+
yclid: string | null,
|
|
103
|
+
ysclid: string | null
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### `initPersistCampaignData(options)`
|
|
108
|
+
|
|
109
|
+
Автоматическая инициализация при загрузке страницы.
|
|
110
|
+
|
|
111
|
+
**Параметры:** те же, что у `persistCampaignData`
|
|
112
|
+
|
|
113
|
+
## Примеры использования
|
|
114
|
+
|
|
115
|
+
### Базовое использование
|
|
116
|
+
|
|
117
|
+
```javascript
|
|
118
|
+
import { persistCampaignData } from '@alexsab-ru/scripts';
|
|
119
|
+
|
|
120
|
+
// Запуск с настройками по умолчанию
|
|
121
|
+
persistCampaignData();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Кастомная настройка
|
|
125
|
+
|
|
126
|
+
```javascript
|
|
127
|
+
persistCampaignData({
|
|
128
|
+
storeInCookie: true,
|
|
129
|
+
pushToDataLayer: true,
|
|
130
|
+
triggerParameters: ['utm_source', 'utm_medium', 'fbclid', 'custom_param'],
|
|
131
|
+
cookieExpireDays: 30,
|
|
132
|
+
urlCookieName: 'my_campaign_url'
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### React приложение
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
import React, { useEffect } from 'react';
|
|
140
|
+
import { initPersistCampaignData } from '@alexsab-ru/scripts';
|
|
141
|
+
|
|
142
|
+
function App() {
|
|
143
|
+
useEffect(() => {
|
|
144
|
+
initPersistCampaignData({
|
|
145
|
+
cookieExpireDays: 30
|
|
146
|
+
});
|
|
147
|
+
}, []);
|
|
148
|
+
|
|
149
|
+
return <div>Your App</div>;
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Next.js
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
// pages/_app.js
|
|
157
|
+
import { useEffect } from 'react';
|
|
158
|
+
import { initPersistCampaignData } from '@alexsab-ru/scripts';
|
|
159
|
+
|
|
160
|
+
function MyApp({ Component, pageProps }) {
|
|
161
|
+
useEffect(() => {
|
|
162
|
+
initPersistCampaignData();
|
|
163
|
+
}, []);
|
|
164
|
+
|
|
165
|
+
return <Component {...pageProps} />;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export default MyApp;
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Vue.js
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
// main.js или App.vue
|
|
175
|
+
import { initPersistCampaignData } from '@alexsab-ru/scripts';
|
|
176
|
+
|
|
177
|
+
export default {
|
|
178
|
+
mounted() {
|
|
179
|
+
initPersistCampaignData();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Получение сохраненных данных
|
|
185
|
+
|
|
186
|
+
```javascript
|
|
187
|
+
import { getCampaignData, extractUtmParameters } from '@alexsab-ru/scripts';
|
|
188
|
+
|
|
189
|
+
// Получить данные из cookies
|
|
190
|
+
const campaignData = getCampaignData();
|
|
191
|
+
console.log('Campaign URL:', campaignData.campaignUrl);
|
|
192
|
+
console.log('Referrer:', campaignData.referrer);
|
|
193
|
+
|
|
194
|
+
// Извлечь UTM параметры
|
|
195
|
+
const utmParams = extractUtmParameters();
|
|
196
|
+
console.log('UTM Source:', utmParams.utm_source);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Как это работает
|
|
200
|
+
|
|
201
|
+
1. **Сохранение URL кампании**: Если в URL есть параметры из списка `triggerParameters`, полный URL сохраняется в cookie
|
|
202
|
+
2. **Сохранение referrer**: Если пользователь пришел с внешнего домена, referrer сохраняется в отдельный cookie
|
|
203
|
+
3. **DataLayer**: Текущий URL отправляется в dataLayer с событием `originalLocation`
|
|
204
|
+
4. **Session cookies**: По умолчанию cookies создаются как session (удаляются при закрытии браузера)
|
|
205
|
+
|
|
206
|
+
## Интеграция с аналитикой
|
|
207
|
+
|
|
208
|
+
Данные из dataLayer можно использовать в Google Analytics, Adobe Analytics и других системах:
|
|
209
|
+
|
|
210
|
+
```javascript
|
|
211
|
+
// Google Analytics 4
|
|
212
|
+
gtag('config', 'GA_MEASUREMENT_ID', {
|
|
213
|
+
page_title: 'Custom Page',
|
|
214
|
+
page_location: dataLayer.find(item => item.originalLocation)?.originalLocation
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Google Tag Manager
|
|
218
|
+
// Используйте dataLayer переменные в тегах и триггерах
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Совместимость
|
|
222
|
+
|
|
223
|
+
- ✅ Vanilla JavaScript
|
|
224
|
+
- ✅ React
|
|
225
|
+
- ✅ Vue.js
|
|
226
|
+
- ✅ Next.js
|
|
227
|
+
- ✅ Nuxt.js
|
|
228
|
+
- ✅ Angular
|
|
229
|
+
- ✅ SPA (Single Page Applications)
|
|
230
|
+
- ✅ SSR (Server Side Rendering)
|
|
231
|
+
|
|
232
|
+
## Лицензия
|
|
233
|
+
|
|
234
|
+
MIT
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Примеры использования Persist Campaign Data
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
persistCampaignData,
|
|
5
|
+
getCampaignData,
|
|
6
|
+
extractUtmParameters,
|
|
7
|
+
initPersistCampaignData
|
|
8
|
+
} from '@alexsab-ru/scripts';
|
|
9
|
+
|
|
10
|
+
// 1. Простой запуск с настройками по умолчанию
|
|
11
|
+
// Сохранит UTM параметры и gclid в cookies, отправит данные в dataLayer
|
|
12
|
+
persistCampaignData();
|
|
13
|
+
|
|
14
|
+
// 2. Автоматическая инициализация при загрузке страницы
|
|
15
|
+
initPersistCampaignData();
|
|
16
|
+
|
|
17
|
+
// 3. Настройка с кастомными параметрами
|
|
18
|
+
persistCampaignData({
|
|
19
|
+
storeInCookie: true,
|
|
20
|
+
pushToDataLayer: true,
|
|
21
|
+
triggerParameters: ['utm_source', 'utm_medium', 'utm_campaign', 'fbclid', 'custom_param'],
|
|
22
|
+
urlCookieName: 'my_campaign_url',
|
|
23
|
+
referrerCookieName: 'my_referrer',
|
|
24
|
+
dataLayerName: 'customDataLayer',
|
|
25
|
+
dataLayerKey: 'originalPageUrl',
|
|
26
|
+
cookieExpireDays: 30 // cookie будет жить 30 дней
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// 4. Только сохранение в cookies (без dataLayer)
|
|
30
|
+
persistCampaignData({
|
|
31
|
+
storeInCookie: true,
|
|
32
|
+
pushToDataLayer: false,
|
|
33
|
+
cookieExpireDays: 90
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// 5. Только отправка в dataLayer (без cookies)
|
|
37
|
+
persistCampaignData({
|
|
38
|
+
storeInCookie: false,
|
|
39
|
+
pushToDataLayer: true,
|
|
40
|
+
dataLayerName: 'myDataLayer'
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// 6. Получение сохраненных данных кампании
|
|
44
|
+
const campaignData = getCampaignData();
|
|
45
|
+
console.log('Campaign URL:', campaignData.campaignUrl);
|
|
46
|
+
console.log('Referrer:', campaignData.referrer);
|
|
47
|
+
|
|
48
|
+
// 7. Получение данных с кастомными именами cookies
|
|
49
|
+
const customCampaignData = getCampaignData({
|
|
50
|
+
urlCookieName: 'my_campaign_url',
|
|
51
|
+
referrerCookieName: 'my_referrer'
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// 8. Извлечение UTM параметров из текущего URL
|
|
55
|
+
const utmParams = extractUtmParameters();
|
|
56
|
+
console.log('UTM Source:', utmParams.utm_source);
|
|
57
|
+
console.log('UTM Medium:', utmParams.utm_medium);
|
|
58
|
+
console.log('UTM Campaign:', utmParams.utm_campaign);
|
|
59
|
+
|
|
60
|
+
// 9. Извлечение UTM параметров из конкретного URL
|
|
61
|
+
const specificUtmParams = extractUtmParameters('https://example.com/?utm_source=google&utm_medium=cpc');
|
|
62
|
+
console.log('Specific UTM params:', specificUtmParams);
|
|
63
|
+
|
|
64
|
+
// 10. Инициализация для SPA (Single Page Application)
|
|
65
|
+
// Запускать только на первоначальной загрузке страницы
|
|
66
|
+
if (!window.campaignDataPersisted) {
|
|
67
|
+
persistCampaignData();
|
|
68
|
+
window.campaignDataPersisted = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 11. Интеграция с React
|
|
72
|
+
import React, { useEffect } from 'react';
|
|
73
|
+
|
|
74
|
+
function App() {
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
// Запускаем только при первом рендере компонента
|
|
77
|
+
initPersistCampaignData({
|
|
78
|
+
cookieExpireDays: 30,
|
|
79
|
+
triggerParameters: ['utm_source', 'utm_medium', 'utm_campaign', 'gclid', 'fbclid']
|
|
80
|
+
});
|
|
81
|
+
}, []);
|
|
82
|
+
|
|
83
|
+
return <div>Your App</div>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 12. Интеграция с Vue.js
|
|
87
|
+
// В main.js или App.vue
|
|
88
|
+
import { initPersistCampaignData } from '@alexsab-ru/scripts';
|
|
89
|
+
|
|
90
|
+
export default {
|
|
91
|
+
mounted() {
|
|
92
|
+
initPersistCampaignData();
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// 13. Интеграция с Next.js
|
|
97
|
+
// В _app.js
|
|
98
|
+
import { useEffect } from 'react';
|
|
99
|
+
import { initPersistCampaignData } from '@alexsab-ru/scripts';
|
|
100
|
+
|
|
101
|
+
function MyApp({ Component, pageProps }) {
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
initPersistCampaignData();
|
|
104
|
+
}, []);
|
|
105
|
+
|
|
106
|
+
return <Component {...pageProps} />;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 14. Обработка ошибок
|
|
110
|
+
const success = persistCampaignData({
|
|
111
|
+
storeInCookie: true,
|
|
112
|
+
pushToDataLayer: true
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
if (!success) {
|
|
116
|
+
console.error('Failed to persist campaign data');
|
|
117
|
+
// Можно отправить ошибку в систему мониторинга
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 15. Условное выполнение
|
|
121
|
+
// Запускать только для определенных страниц
|
|
122
|
+
if (window.location.pathname.includes('/landing/')) {
|
|
123
|
+
persistCampaignData({
|
|
124
|
+
cookieExpireDays: 7 // Короткий срок жизни для лендингов
|
|
125
|
+
});
|
|
126
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// Примеры использования NoBounce модуля
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
initNoBounce,
|
|
5
|
+
startNoBounce,
|
|
6
|
+
getNoBounceStats,
|
|
7
|
+
stopNoBounce,
|
|
8
|
+
sendNoBounceNow
|
|
9
|
+
} from '@alexsab-ru/scripts/nobounce';
|
|
10
|
+
|
|
11
|
+
// ============================================
|
|
12
|
+
// БАЗОВОЕ ИСПОЛЬЗОВАНИЕ
|
|
13
|
+
// ============================================
|
|
14
|
+
|
|
15
|
+
// 1. Простой запуск с настройками по умолчанию (15 секунд)
|
|
16
|
+
startNoBounce();
|
|
17
|
+
|
|
18
|
+
// 2. Запуск с кастомной задержкой
|
|
19
|
+
startNoBounce(30); // 30 секунд
|
|
20
|
+
|
|
21
|
+
// 3. Запуск с кастомным названием цели
|
|
22
|
+
startNoBounce(20, 'EngagedUser');
|
|
23
|
+
|
|
24
|
+
// ============================================
|
|
25
|
+
// ПРОДВИНУТАЯ НАСТРОЙКА
|
|
26
|
+
// ============================================
|
|
27
|
+
|
|
28
|
+
// 4. Полная настройка
|
|
29
|
+
initNoBounce({
|
|
30
|
+
delay: 25, // 25 секунд
|
|
31
|
+
goalName: 'QualityVisit', // Кастомное название цели
|
|
32
|
+
goalParams: { // Дополнительные параметры
|
|
33
|
+
page_type: 'landing',
|
|
34
|
+
source: 'organic'
|
|
35
|
+
},
|
|
36
|
+
oncePerSession: true, // Один раз за сессию
|
|
37
|
+
checkVisibility: true, // Учитывать видимость страницы
|
|
38
|
+
debug: true // Включить отладку
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// 5. Настройка для разных типов страниц
|
|
42
|
+
const pageType = document.body.dataset.pageType;
|
|
43
|
+
|
|
44
|
+
switch(pageType) {
|
|
45
|
+
case 'homepage':
|
|
46
|
+
initNoBounce({
|
|
47
|
+
delay: 10,
|
|
48
|
+
goalName: 'HomepageEngagement',
|
|
49
|
+
goalParams: { page_type: 'homepage' }
|
|
50
|
+
});
|
|
51
|
+
break;
|
|
52
|
+
|
|
53
|
+
case 'product':
|
|
54
|
+
initNoBounce({
|
|
55
|
+
delay: 20,
|
|
56
|
+
goalName: 'ProductPageEngagement',
|
|
57
|
+
goalParams: {
|
|
58
|
+
page_type: 'product',
|
|
59
|
+
product_id: document.body.dataset.productId
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case 'blog':
|
|
65
|
+
initNoBounce({
|
|
66
|
+
delay: 30,
|
|
67
|
+
goalName: 'BlogEngagement',
|
|
68
|
+
goalParams: { page_type: 'blog' }
|
|
69
|
+
});
|
|
70
|
+
break;
|
|
71
|
+
|
|
72
|
+
default:
|
|
73
|
+
startNoBounce(15, 'DefaultEngagement');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ============================================
|
|
77
|
+
// УСЛОВНЫЙ ЗАПУСК
|
|
78
|
+
// ============================================
|
|
79
|
+
|
|
80
|
+
// 6. Запуск только для новых пользователей
|
|
81
|
+
if (!localStorage.getItem('returning_user')) {
|
|
82
|
+
initNoBounce({
|
|
83
|
+
delay: 20,
|
|
84
|
+
goalName: 'NewUserEngagement',
|
|
85
|
+
goalParams: { user_type: 'new' }
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 7. Запуск только для трафика с рекламы
|
|
90
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
91
|
+
if (urlParams.get('utm_source') || urlParams.get('gclid')) {
|
|
92
|
+
initNoBounce({
|
|
93
|
+
delay: 15,
|
|
94
|
+
goalName: 'PaidTrafficEngagement',
|
|
95
|
+
goalParams: {
|
|
96
|
+
traffic_type: 'paid',
|
|
97
|
+
utm_source: urlParams.get('utm_source') || 'unknown'
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 8. Разная задержка в зависимости от источника трафика
|
|
103
|
+
const utmSource = urlParams.get('utm_source');
|
|
104
|
+
let delay = 15; // по умолчанию
|
|
105
|
+
|
|
106
|
+
if (utmSource === 'google') {
|
|
107
|
+
delay = 10; // быстрее для Google трафика
|
|
108
|
+
} else if (utmSource === 'facebook') {
|
|
109
|
+
delay = 20; // медленнее для Facebook
|
|
110
|
+
} else if (document.referrer.includes('organic')) {
|
|
111
|
+
delay = 25; // еще медленнее для органики
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
initNoBounce({
|
|
115
|
+
delay,
|
|
116
|
+
goalName: 'SourceBasedEngagement',
|
|
117
|
+
goalParams: { utm_source: utmSource || 'direct' }
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// ============================================
|
|
121
|
+
// КОНТРОЛЬ И МОНИТОРИНГ
|
|
122
|
+
// ============================================
|
|
123
|
+
|
|
124
|
+
// 9. Мониторинг статистики
|
|
125
|
+
setInterval(() => {
|
|
126
|
+
const stats = getNoBounceStats();
|
|
127
|
+
if (stats) {
|
|
128
|
+
console.log('Время на странице:', stats);
|
|
129
|
+
|
|
130
|
+
// Отправляем промежуточные цели
|
|
131
|
+
if (stats.visibleTime === 60 && !window.oneMinuteSent) {
|
|
132
|
+
reachGoal('OneMinuteOnPage', { visible_time: 60 });
|
|
133
|
+
window.oneMinuteSent = true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (stats.visibleTime === 300 && !window.fiveMinutesSent) {
|
|
137
|
+
reachGoal('FiveMinutesOnPage', { visible_time: 300 });
|
|
138
|
+
window.fiveMinutesSent = true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}, 10000); // каждые 10 секунд
|
|
142
|
+
|
|
143
|
+
// 10. Принудительная отправка при определенных действиях
|
|
144
|
+
document.addEventListener('scroll', () => {
|
|
145
|
+
const scrollPercent = (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100;
|
|
146
|
+
|
|
147
|
+
if (scrollPercent > 75 && !window.deepScrollSent) {
|
|
148
|
+
sendNoBounceNow(); // Принудительно отправляем NoBounce
|
|
149
|
+
window.deepScrollSent = true;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// 11. Остановка таймера при определенных условиях
|
|
154
|
+
window.addEventListener('beforeunload', () => {
|
|
155
|
+
const stats = getNoBounceStats();
|
|
156
|
+
|
|
157
|
+
// Если пользователь быстро покидает страницу, не отправляем цель
|
|
158
|
+
if (stats && stats.visibleTime < 5) {
|
|
159
|
+
stopNoBounce();
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// ============================================
|
|
164
|
+
// ИНТЕГРАЦИЯ С ФРЕЙМВОРКАМИ
|
|
165
|
+
// ============================================
|
|
166
|
+
|
|
167
|
+
// 12. React Hook
|
|
168
|
+
import { useEffect } from 'react';
|
|
169
|
+
|
|
170
|
+
function useNoBounce(options = {}) {
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
const manager = initNoBounce({
|
|
173
|
+
delay: 15,
|
|
174
|
+
...options
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return () => {
|
|
178
|
+
if (manager) {
|
|
179
|
+
manager.stop();
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}, []);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// В компоненте
|
|
186
|
+
function MyComponent() {
|
|
187
|
+
useNoBounce({
|
|
188
|
+
goalName: 'ReactPageEngagement',
|
|
189
|
+
goalParams: { framework: 'react' }
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return <div>My Component</div>;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 13. Vue.js Composable
|
|
196
|
+
import { onMounted, onUnmounted } from 'vue';
|
|
197
|
+
|
|
198
|
+
function useNoBounce(options = {}) {
|
|
199
|
+
let manager = null;
|
|
200
|
+
|
|
201
|
+
onMounted(() => {
|
|
202
|
+
manager = initNoBounce({
|
|
203
|
+
delay: 15,
|
|
204
|
+
...options
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
onUnmounted(() => {
|
|
209
|
+
if (manager) {
|
|
210
|
+
manager.stop();
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
return { manager };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 14. Next.js App integration
|
|
218
|
+
// В _app.js
|
|
219
|
+
import { useRouter } from 'next/router';
|
|
220
|
+
import { useEffect } from 'react';
|
|
221
|
+
|
|
222
|
+
function MyApp({ Component, pageProps }) {
|
|
223
|
+
const router = useRouter();
|
|
224
|
+
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
const handleRouteChange = () => {
|
|
227
|
+
// Новая страница - инициализируем NoBounce
|
|
228
|
+
setTimeout(() => {
|
|
229
|
+
initNoBounce({
|
|
230
|
+
goalName: 'NextJSPageEngagement',
|
|
231
|
+
goalParams: {
|
|
232
|
+
route: router.pathname,
|
|
233
|
+
framework: 'nextjs'
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}, 100);
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
handleRouteChange(); // Для первой загрузки
|
|
240
|
+
router.events.on('routeChangeComplete', handleRouteChange);
|
|
241
|
+
|
|
242
|
+
return () => {
|
|
243
|
+
router.events.off('routeChangeComplete', handleRouteChange);
|
|
244
|
+
};
|
|
245
|
+
}, [router]);
|
|
246
|
+
|
|
247
|
+
return <Component {...pageProps} />;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ============================================
|
|
251
|
+
// ТЕСТИРОВАНИЕ И ОТЛАДКА
|
|
252
|
+
// ============================================
|
|
253
|
+
|
|
254
|
+
// 15. Тестовый режим
|
|
255
|
+
if (window.location.search.includes('nobounce_test=true')) {
|
|
256
|
+
initNoBounce({
|
|
257
|
+
delay: 3, // 3 секунды для тестирования
|
|
258
|
+
goalName: 'TestNoBounce',
|
|
259
|
+
debug: true,
|
|
260
|
+
oncePerSession: false // Позволяем многократную отправку
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// 16. Отладочная информация
|
|
265
|
+
window.debugNoBounce = () => {
|
|
266
|
+
const stats = getNoBounceStats();
|
|
267
|
+
console.table(stats);
|
|
268
|
+
|
|
269
|
+
console.log('Manager:', getNoBounceManager());
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// Добавляем в глобальный объект для отладки
|
|
273
|
+
if (typeof window !== 'undefined') {
|
|
274
|
+
window.noBounceDebug = {
|
|
275
|
+
getStats: getNoBounceStats,
|
|
276
|
+
sendNow: sendNoBounceNow,
|
|
277
|
+
stop: stopNoBounce,
|
|
278
|
+
reset: resetNoBounce
|
|
279
|
+
};
|
|
280
|
+
}
|
package/index.js
CHANGED