Mundarija (34)
- Bu darsda
- 1. Nega bu kerak?
- 2. SPA'da sahifa ko'rishlari
- 2.1 Muammo: sahifa qayta yuklanmaydi
- 2.2 Nega yo'l umumlashtiriladi
- 2.3 Kanon nega effekt emas, router.subscribe ishlatdi
- 3. Hodisalar va yuborish
- 3.1 Hodisa — fe'l, matnsiz
- 3.2 Navbat va sendBeacon
- 3.3 UTM: odam qayerdan keldi
- 4. Tayyor xizmatlar
- 4.1 Beshta mashhur yo'l
- 4.2 SPA uchun ulanish kodi
- 4.3 Biz sinab ko'rdik: posthog-js
- 4.4 Reklama blokerlari
- 5. Rozilik va qonunchilik
- 5.1 Qachon oldindan rozilik shart
- 5.2 Kanon qarori: sukut bo'yicha yoqilgan — sharti bilan
- 5.3 GPC va Do Not Track
- 5.4 Rozilik oynasi
- 6. Hujumchi nigohi: ma'lumot sizishi
- 7. Vazifalar qadami: o'z yengil analitikasi (holat 18)
- 7.1 Fayllar
- 7.2 Navbatga qo'shish
- 7.3 Natija
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): bo'sh joylarni to'ldiring
- 2-mashq (o'rta): UTM belgilarini olish va tozalash
- 3-mashq (qiyin): rozilikni kutadigan treker
- 4-mashq: Vazifalar qadami
- 10. Real ishda
- Xulosa
- Manbalar
Analitika React'da: sahifa ko'rishlari, hodisalar, rozilik va maxfiylik
Qisqacha: Analitika — qaysi sahifa ochilgani va qaysi tugma bosilganini sanash. SPA'da sahifa qayta yuklanmaydi, shuning uchun "sahifa ko'rildi" ni router o'zgarganda o'zingiz yuborasiz. Hodisalar navbatda yig'ilib, sahifa yashirilganda
navigator.sendBeaconbilan ketadi. Tayyor xizmatlar — GA4, Yandex Metrika, PostHog, Plausible, Umami — tashqi skript, hisob va kalit talab qiladi; posthog-js (+98.3 KB)vazifalarning Trusted Types siyosati ostida ochilmadi. Eng muhimi — maxfiylik: shaxsiy ma'lumot yubormang, yo'lni umumlashtiring (/vazifa/7→/vazifa/:id), GPC/DNT ni hurmat qiling. Cookie yoki identifikator ishlatsangiz — Yevropada oldindan rozilik (opt-in) shart. O'zbekistonda shaxsga doir ma'lumot uchun rozilik va mahalliy saqlash talablari bor.
Bu darsda
- SPA'da sahifa ko'rishlarini router bilan to'g'ri sanaysiz va yo'lni umumlashtirasiz.
- Hodisalarni navbatga yig'ib,
sendBeaconbilan yuborasiz; UTM belgilarini o'qiysiz. - GA4, Yandex Metrika, PostHog, Plausible va Umami'ni hajm, CSP va maxfiylik bo'yicha solishtirasiz.
- Rozilik oynasini qorong'u naqshlarsiz yasaysiz, GPC va Do Not Track'ni hurmat qilasiz.
- Qachon oldindan rozilik (opt-in) shartligini GDPR va O'zbekiston qonuni bo'yicha tushuntirasiz.
Oldin bilishingiz kerak: React Router I, Performance API va Core Web Vitals, Cookie'lar JS'dan, localStorage va sessionStorage, Brauzer xavfsizlik modeli.
1. Nega bu kerak?
Oldingi darsda faylni foydalanuvchiga berdik. Endi foydalanuvchidan nimadir olamiz — u ilovadan qanday foydalanishi haqida ma'lumot. Jasur akaning savollari:
- Qaysi taom sahifasi eng ko'p ochiladi?
- «Buyurtma berish» ni bosganlardan nechtasi to'lovgacha yetadi?
- Instagram'dagi reklama ko'proq odam olib keldimi yoki Telegram kanal?
Taxmin bilan javob berish mumkin, lekin qaror pul turadi. Analitika (analytics) — foydalanuvchilar harakatini sanash va jamlash: qancha, qayerdan, qaysi yo'l bilan. Xuddi do'kon eshigidagi hisoblagich kabi: kim kirganini emas, nechta odam kirganini sanaydi.
O'xshatishdagi farq muhim. Hisoblagich odamning yuzini suratga olmaydi. Analitika ham shunday bo'lishi kerak: "nechta", "qaysi sahifa", "qaysi tugma" — ha; "kim", "nima yozdi" — yo'q. Bu darsning yarmi — texnika, yarmi — shu chegarani ushlab turish.
2. SPA'da sahifa ko'rishlari
2.1 Muammo: sahifa qayta yuklanmaydi
Oddiy saytda har havola — yangi sahifa: analitika skripti har safar qayta ishga tushadi va "sahifa ko'rildi" yuboradi. SPA'da esa (SPA va client-side routing) skript faqat bir marta yuklanadi. Keyingi o'tishlarni router history.pushState bilan qiladi — brauzer uchun bu yangi sahifa emas. Tashqi skript buni o'zi bilmasa, analitikada butun tashrif — bitta sahifa.
Yechim: router o'zgarganda o'zingiz "sahifa ko'rildi" yuborasiz. React'da eng qisqa yo'l — useLocation va effekt. Havolalarni bosib ko'ring:
import { useEffect } from "react";
import {
createMemoryRouter,
RouterProvider,
Link,
Outlet,
useLocation,
} from "react-router";
function generalizePath(path) {
return path.replace(/\/\d+(?=\/|$)/g, "/:id") || "/";
}
function track(event) {
console.log("yuborildi:", JSON.stringify(event));
}
function PageViews() {
const { pathname } = useLocation();
useEffect(() => {
track({ type: "sahifa", path: generalizePath(pathname) });
}, [pathname]);
return null;
}
function Layout() {
return (
<>
<PageViews />
<nav>
<Link to="/">Bosh sahifa</Link>{" "}
<Link to="/vazifa/7">Vazifa 7</Link>{" "}
<Link to="/statistika">Statistika</Link>
</nav>
<Outlet />
</>
);
}
const router = createMemoryRouter([
{
path: "/",
element: <Layout />,
children: [
{ index: true, element: <p>Ro'yxat</p> },
{ path: "vazifa/:id", element: <p>Vazifa</p> },
{ path: "statistika", element: <p>Statistika sahifasi</p> },
],
},
]);
export default function App() {
return <RouterProvider router={router} />;
}Konsolda:
yuborildi: {"type":"sahifa","path":"/"}
yuborildi: {"type":"sahifa","path":"/vazifa/:id"}
yuborildi: {"type":"sahifa","path":"/statistika"}
yuborildi: {"type":"sahifa","path":"/"}Natija oynasida manzil satri yo'q, shuning uchun createMemoryRouter (React Router I darsidagi kabi). To'rtta o'tish — to'rtta "sahifa". Ikkinchi qatorga qarang: /vazifa/7 emas, /vazifa/:id.
2.2 Nega yo'l umumlashtiriladi
generalizePath raqamli bo'laklarni :id ga aylantiradi. Ikki sabab:
- Foydali hisobot. Jasur akaga "vazifa sahifasi 340 marta ochildi" kerak, "
/vazifa/1— 3,/vazifa/2— 1, …" degan 300 qatorli ro'yxat emas. - Maxfiylik. Kim qaysi vazifani (buyurtmani, bemor kartasini, foydalanuvchi profilini) ochgani — shaxsga doir ma'lumotga juda yaqin. Analitikaga u kerak emas — demak, yuborilmaydi.
Shu sababli so'rov qatori (?filtr=…&q=…) va # ham tashlanadi: qidiruv matnida ism yoki telefon bo'lishi mumkin. vazifalar kanonining maxfiylik.ts dagi funksiyalari (kanon kodi):
function yolniUmumlashtir(yol: string): string {
const [faqatYol = "/"] = yol.split(/[?#]/);
return faqatYol.replace(/\/\d+(?=\/|$)/g, "/:id") || "/";
}
function kuzatmaslikSoralgan(nav: {
globalPrivacyControl?: boolean;
doNotTrack?: string | null;
}): boolean {
return nav.globalPrivacyControl === true || nav.doNotTrack === "1";
}
console.log(yolniUmumlashtir("/vazifa/12/tahrir"));
console.log(yolniUmumlashtir("/?filtr=faol&q=non#royxat"));
console.log(kuzatmaslikSoralgan({ globalPrivacyControl: true }));
console.log(kuzatmaslikSoralgan({ doNotTrack: null }));Konsolda:
/vazifa/:id/tahrir
/
true
false(?=\/|$) — oldinga qarash (Lookahead va lookbehind): raqamdan keyin / yoki yo'l oxiri bo'lsin. Shuning uchun /v2/menyu dagi 2 tegilmaydi — u alohida bo'lak emas. kuzatmaslikSoralgan haqida — «Rozilik» bo'limida.
2.3 Kanon nega effekt emas, router.subscribe ishlatdi
PageViews komponenti qulay, lekin bitta tuzog'i bor: dev rejimidagi StrictMode effektni ikki marta ishga tushiradi — birinchi sahifa ikki marta sanaladi. Kanon sahifa ko'rishlarini React'dan tashqarida, router holatiga obuna bo'lib yuboradi (main.tsx, kanon kodi):
// 21/#18: sahifa ko'rishlari — router holatidan (navigatsiya
// tugaganda, yo'l o'zgargan bo'lsa). Basename'siz yo'l: til
// alohida maydonda
let oxirgiYol = router.state.location.pathname;
sahifaKorildi(oxirgiYol);
router.subscribe((holat) => {
if (
holat.navigation.state === "idle" &&
holat.location.pathname !== oxirgiYol
) {
oxirgiYol = holat.location.pathname;
sahifaKorildi(oxirgiYol);
}
});navigation.state === "idle"— loader'li sahifa ma'lumoti yuklanib bo'lgandan keyin. Yuklanayotgan (hali ko'rinmagan) sahifa sanalmaydi.pathname !== oxirgiYol— faqat yo'l o'zgarsa. Filtr o'zgarishi (?filtr=bajarilgan) yangi sahifa emas.- Basename'siz —
/ru/vazifa/2emas,/vazifa/2: til alohida maydonda (til: document.documentElement.lang). Bitta sahifa to'rt tilda to'rtta sahifa bo'lib ketmaydi.
3. Hodisalar va yuborish
3.1 Hodisa — fe'l, matnsiz
Sahifa ko'rishdan tashqari, mahsulot uchun muhim hodisalar (custom events) sanaladi: "vazifa qo'shildi", "CSV eksport qilindi", "buyurtma berildi". Kanon ularni snake_case nomlar bilan yuboradi va hech qanday tafsilot qo'shmaydi:
hodisa("vazifa_qoshildi"); // 21/#18: matnsiz — faqat "nima bo'ldi"Vazifa matni («Shifokorga yozilish — +998 90 …») yuborilmaydi. Analitikaga "nechta vazifa qo'shildi" kerak, "qanday vazifa" emas.
3.2 Navbat va sendBeacon
Har hodisani darhol yuborish — har bosishda tarmoq so'rovi. Kanon hodisalarni navbatga yig'adi va ikki holatda yuboradi: 10 tasi to'planganda yoki sahifa yashirilganda (boshqa tabga o'tildi, telefon qulflandi, sahifa yopilmoqda). Navbat g'oyasi — kichik model (chegara 3, yuborish — konsolga):
import { useRef, useState } from "react";
function createTracker(send, limit = 3) {
let queue = [];
return {
event(name) {
queue.push({ name, time: Date.now() });
if (queue.length >= limit) this.flush();
},
flush() {
if (queue.length === 0) return;
send(queue);
queue = [];
},
};
}
export default function App() {
const [sent, setSent] = useState(0);
const tracker = useRef(null);
if (tracker.current === null) {
tracker.current = createTracker((batch) => {
console.log("yuborildi:", batch.map((e) => e.name).join(", "));
setSent((n) => n + batch.length);
});
}
const track = (name) => tracker.current.event(name);
return (
<>
<button onClick={() => track("vazifa_qoshildi")}>
Vazifa qo'shish
</button>{" "}
<button onClick={() => track("eksport_csv")}>
CSV eksport
</button>
<p>Yuborilgan hodisalar: {sent}</p>
</>
);
}Konsolda:
yuborildi: vazifa_qoshildi, vazifa_qoshildi, eksport_csvUch hodisa — bitta so'rov. tracker.current === null tekshiruvi — obyekt bir marta yaratiladi (useRef I darsidagi "dangasa boshlash" naqshi), har renderda yangisi emas.
Haqiqiy yuborish — navigator.sendBeacon(url, blob). Bu POST so'rov, lekin ikki xususiyati bor:
- Sahifa yopilayotganda ham ketadi. Oddiy
fetchsahifa yopilsa uzilishi mumkin. Beacon esa brauzer navbatiga qo'yiladi va sahifa yopilgandan keyin ham yuboriladi. - Javob yo'q. Natija kerak emas — faqat
true/false("navbatga qo'yildimi"). Hajmi cheklangan (bir necha o'n KB) — katta ma'lumot uchun emas.
Qachon yuborish kerak? unload va beforeunload hodisalari telefonda ishonchsiz (ko'pincha kelmaydi). To'g'ri hodisa — visibilitychange va document.visibilityState === "hidden". Kanon tekshiruvi bu yerda haqiqiy xato topdi: tinglovchi avval window ga qo'yilgan edi. visibilitychange esa document da keladi — window dagi tinglovchi hech qachon ishlamadi, navbat ketmay qolardi. Tuzatilgan kod (kanon):
if (MANZIL !== undefined) {
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
yubor();
}
});
}3.3 UTM: odam qayerdan keldi
Instagram reklamasi yoki Telegram post'idagi havolaga UTM belgilar qo'shiladi: ?utm_source=telegram&utm_medium=post&utm_campaign=kuz_menyu. Ular analitikaga "bu tashrif qayerdan" deydi. Ilova ularni birinchi sahifada o'qiydi va manzildan olib tashlaydi — foydalanuvchi havolani do'stiga ulashsa, do'st ham "Telegram'dan keldi" bo'lib sanalmasin. Bu — 2-mashq.
Tekshirib ko'ring: Nega navbatni yuborish uchun
visibilitychangetanlangan,beforeunloademas?
Javob
Telefonda foydalanuvchi ko'pincha sahifani "yopmaydi" — boshqa ilovaga o'tadi, keyin tizim tabni xotiradan o'chiradi. Bunda beforeunload ham, unload ham kelmaydi. visibilitychange (hidden) esa sahifa ekrandan ketgan zahoti keladi — bu yuborish uchun oxirgi ishonchli imkoniyat. Bundan tashqari, beforeunload tinglovchisi brauzerning "orqaga-oldinga keshi" ni (bfcache) buzishi mumkin.
4. Tayyor xizmatlar
4.1 Beshta mashhur yo'l
O'z analitikangiz — sanash va yuborish. Hisobot, grafiklar, "voronka" (funnel), foydalanuvchi yo'llari esa ancha katta ish. Shuning uchun ko'pchilik tayyor xizmat ishlatadi:
| Xizmat | Kim saqlaydi | Cookie/identifikator |
|---|---|---|
| Google Analytics 4 (GA4) | Google serverlari | ha (_ga) |
| Yandex Metrika | Yandex serverlari | ha; Vebvizor — sessiya yozuvi |
| PostHog | bulut yoki o'z serveringiz | ha (sukut); replay, flag'lar |
| Plausible | bulut (EI) yoki o'z serveringiz | yo'q |
| Umami | o'z serveringiz (ochiq kod) | yo'q |
Yandex Metrika O'zbekiston va MDH saytlarida keng tarqalgan. Uning Vebvizor funksiyasi foydalanuvchi sahifada nima qilganini videoga o'xshab yozib oladi — sichqoncha, bosishlar, ba'zan kiritilgan matn ham. Bu kuchli vosita, lekin maxfiylik nuqtai nazaridan eng og'iri. Plausible va Umami — "cookie'siz" analitika: kunlik aylanadigan tasodifiy qiymatlar bilan unik tashrifni taxminlaydi, shaxsni kuzatmaydi.
Biz bu xizmatlarda hisob ochmadik va kalit yaratmadik. Quyidagi kodlar hujjatlar bo'yicha yozilgan, ishga tushirilmagan.
4.2 SPA uchun ulanish kodi
GA4 (gtag.js). Sahifa ko'rishni o'zingiz yuborasiz — aks holda GA4 ning "brauzer tarixi o'zgarishi" bo'yicha avtomatik sanashi bilan ikki marta sanaladi:
// index.html'da: <script async src="https://www.googletagmanager.com
// /gtag/js?id=G-XXXXXXX"> (bir qatorda)
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag("consent", "default", {
analytics_storage: "denied", // rozilikkacha cookie yo'q
ad_storage: "denied",
ad_user_data: "denied", // Consent Mode v2
ad_personalization: "denied",
});
gtag("js", new Date());
gtag("config", "G-XXXXXXX", { send_page_view: false });
// router o'zgarganda (GA4 parametri — to'liq manzil):
gtag("event", "page_view", {
page_location: location.origin + generalizePath(pathname),
});
// rozilik berilganda:
gtag("consent", "update", { analytics_storage: "granted" });consent buyruqlari — Google'ning Consent Mode rejimi: rozilikkacha GA4 cookie yozmaydi. 2024-yildan beri u ikkinchi versiyada: hujjatga ko'ra to'rtta kalit — analytics_storage, ad_storage, ad_user_data, ad_personalization. G-XXXXXXX — sizning o'lchov identifikatoringiz.
Ikki tuzoq. Birinchisi — parametr nomi: GA4 da sahifa manzili page_location (to'liq URL). Eski maqolalardagi page_path — oldingi Universal Analytics'dan qolgan. Ikkinchisi — send_page_view: false faqat birinchi avtomatik sahifa ko'rishni o'chiradi. GA4 sozlamalaridagi "Enhanced measurement → brauzer tarixi o'zgarishlari" bandi yoqiq qolsa, router o'tishlari baribir ikki marta sanaladi — uni GA4 panelida o'chirasiz.
Yandex Metrika:
ym(XXXXXXXX, "init", {
defer: true,
clickmap: true,
webvisor: false,
});
// router o'zgarganda — "hit":
ym(XXXXXXXX, "hit", generalizePath(pathname));
// maqsad (hodisa):
ym(XXXXXXXX, "reachGoal", "vazifa_qoshildi");defer: true — birinchi sahifani avtomatik sanamaslik (SPA'da o'zingiz hit yuborasiz). webvisor: false — sessiya yozuvini ataylab o'chirdik.
PostHog (React):
import posthog from "posthog-js";
posthog.init("phc_XXXXXXXX", {
api_host: "https://eu.i.posthog.com",
capture_pageview: false, // router bilan o'zingiz
disable_session_recording: true,
persistence: "memory", // cookie'siz rejim
});
posthog.capture("$pageview", {
$current_url: generalizePath(pathname),
});
posthog.capture("vazifa_qoshildi");Plausible — bitta skript teg, maxsus hodisa: plausible("Vazifa qo'shildi"). SPA'dagi tarix o'zgarishini skript o'zi sanaydi.
4.3 Biz sinab ko'rdik: posthog-js
Kanon posthog-js 1.438.7 ni soxta kalit bilan o'lchadi — api_host o'z origin'imizga yo'naltirilgan, ya'ni hech qayerga ketmaydi. Natija (kutubxonalar-21.txt):
analitika-posthog JS 168423 (+ 98.3 KB) CSS 0 | CSP require-trusted-types-for×1 | xato: This document requires 'TrustedScriptURL' assignment. The action has been blocked. | ochiq: tashqi yo'qIkki xulosa:
- +98.3 KB gzip —
vazifalarning butun birinchi yuklashining deyarli yarmi. Analitika uchun bu juda qimmat: u har bir sahifada, eng birinchi yuklanadi. TrustedScriptURL— kutubxona qo'shimcha skriptlarni (masalan, qo'shimcha modullarni)<script src>bilan yuklashga urindi va Trusted Types siyosati to'sdi. GA4 va Metrika ham asosiy skriptni tashqi domendan yuklaydi: CSP'gascript-src https://www.googletagmanager.comyokihttps://mc.yandex.ruvaconnect-srcqo'shiladi. Bu — har ikki domenga sahifangiz ustidan to'liq ishonch berish.
Kanon qarori: tashqi xizmat yo'q, o'z yengil analitikasi — +0.5 KB.
4.4 Reklama blokerlari
Ko'p foydalanuvchilarda reklama blokerlari bor (uBlock Origin, brauzerlarning o'z himoyasi). Ular GA4, Metrika va PostHog domenlarini to'sadi — bu foydalanuvchilar analitikada umuman ko'rinmaydi. Ba'zi jamoalar so'rovlarni o'z domeni orqali "proksi" qiladi (/ingest/…). Texnik jihatdan bu ishlaydi. Lekin bloker — foydalanuvchining tanlovi. Uni aylanib o'tishdan oldin o'ylang: maqsad — mahsulotni yaxshilash, foydalanuvchini aldash emas. Raqamlar to'liq bo'lmasligini qabul qilib, trendga qarang.
5. Rozilik va qonunchilik
5.1 Qachon oldindan rozilik shart
Analitika — foydalanuvchi haqida ma'lumot yig'ish. Qonun buni ikki savol bilan o'lchaydi: shaxsga doir ma'lumot yig'ilyaptimi va qurilmada narsa saqlanyaptimi (cookie, localStorage dagi identifikator)?
flowchart TB
A{"Cookie yoki identifikator bormi?"} -- ha --> O["Oldindan rozilik (opt-in)"]
A -- yo'q --> B{"Shaxsga doir ma'lumot? (IP, email, ID)"}
B -- ha --> O
B -- yo'q --> C{"Tashqi xizmatmi? (GA4, Metrika)"}
C -- ha --> O
C -- yo'q --> D["Shaxssiz analitika: o'chirish + GPC/DNT"]Diagrammaga qarang. Yevropa qoidalari (GDPR va ePrivacy direktivasi): qurilmaga "qat'iy zarur bo'lmagan" narsani yozish yoki o'qish uchun — oldindan, aniq, erkin rozilik (opt-in). Oldindan belgilangan katakcha rozilik emas (Yevropa Ittifoqi sudi, 2019, Planet49 ishi). Rozilikni qaytarib olish berish kabi oson bo'lishi kerak. Ba'zi Yevropa nazoratchilari (masalan, Fransiyaning CNIL'i) qat'iy cheklangan, shaxssiz statistika uchun istisno qiladi — shartlari bilan.
O'zbekiston: "Shaxsga doir ma'lumotlar to'g'risida"gi Qonun (O'RQ-547, 2019). Asosiy talablar:
- shaxsga doir ma'lumotni qayta ishlash uchun — subyektning roziligi (qonunda sanab o'tilgan istisnolardan tashqari);
- 2021-yilda qo'shilgan 27¹-modda (O'RQ-666, 2021-yil aprelidan kuchda): O'zbekiston fuqarolarining shaxsga doir ma'lumotlari internet orqali yig'ilsa, ular O'zbekiston hududidagi va davlat reyestrida ro'yxatdan o'tgan bazalarda saqlanishi kerak.
Ikkinchi band analitika uchun muhim: GA4 yoki Metrika ma'lumotni xorijdagi serverlarda saqlaydi. Agar u ma'lumot shaxsni aniqlashga imkon bersa, talab bilan to'qnashishi mumkin. Bu bo'lim — huquqiy maslahat emas. Qonunning amaldagi tahririni lex.uz'da o'qing, jiddiy loyihada yurist bilan maslahatlashing. Batafsil — Shaxsiy ma'lumotlar qonunchiligi darsida (31-qism).
5.2 Kanon qarori: sukut bo'yicha yoqilgan — sharti bilan
vazifalar da analitika sukut bo'yicha yoqilgan (opt-out), lekin buning uchun hamma shartlar bajarilgan (kanon qarori):
- cookie yo'q, foydalanuvchi identifikatori yo'q, IP saqlanmaydi;
- yuboriladi faqat: umumlashtirilgan yo'l, til, hodisa nomi, vaqt;
- GPC yoki DNT bo'lsa — hech narsa yuborilmaydi;
- Sozlamalarda bir bosishda o'chiriladi;
- production'da yuborish manzili yo'q — dev server'dagi qabul qiluvchidan boshqa hech qayerga ketmaydi.
Bulardan birortasi o'zgarsa — masalan, tashqi xizmat qo'shilsa yoki foydalanuvchi ID'si paydo bo'lsa — rozilik oldindan so'ralishi kerak.
5.3 GPC va Do Not Track
Ba'zi brauzerlar va kengaytmalar har saytga "meni kuzatma" signalini yuboradi:
- GPC (Global Privacy Control) —
navigator.globalPrivacyControl === truevaSec-GPC: 1sarlavhasi. AQShning bir qator shtatlarida (masalan, Kaliforniya va Kolorado) qonuniy kuchga ega signal. - Do Not Track — eski signal,
navigator.doNotTrack === "1". Rasman eskirgan, lekin ba'zi brauzerlar hali yuboradi.
Kanon ikkalasini ham hurmat qiladi (kuzatmaslikSoralgan): bunday foydalanuvchining Sozlamalaridagi katakcha o'chiq va o'zgartirib bo'lmaydi, yonida sababi yozilgan. Qonun talab qilmasa ham, bu — foydalanuvchi aniq aytgan istak.
5.4 Rozilik oynasi
Agar rozilik (consent) kerak bo'lsa, uni halol so'rang. Asosiy qoidalar:
- Tanlovdan oldin analitika skripti yuklanmaydi, cookie yozilmaydi.
- «Rad etish» «Qabul qilish» kabi ko'rinadi va bir bosishda. Rad etishni kichik kulrang havolaga yashirish — qorong'u naqsh (dark pattern).
- Tanlovni keyin o'zgartirish mumkin (Sozlamalarda yoki pastki havolada).
- Matn aniq: nima yig'iladi, kim saqlaydi.
import { useState } from "react";
const KEY = "rozilik";
function readConsent() {
try {
return localStorage.getItem(KEY);
} catch {
return null; // saqlab bo'lmasa — so'raymiz
}
}
export default function App() {
const [consent, setConsent] = useState(readConsent);
function choose(value) {
localStorage.setItem(KEY, value);
setConsent(value);
const state = value === "ha" ? "yoqildi" : "o'chiq";
console.log("tanlov:", value, "| analitika", state);
}
if (consent === null) {
return (
<section aria-label="Maxfiylik">
<p>
Analitika uchun ruxsat berasizmi? Faqat sahifa turi va
hodisa nomi, cookie'siz.
</p>
<button onClick={() => choose("ha")}>
Ruxsat beraman
</button>{" "}
<button onClick={() => choose("yoq")}>Faqat zarurlari</button>
</section>
);
}
return (
<p>
Analitika: {consent === "ha" ? "yoqilgan" : "o'chiq"}{" "}
<button
onClick={() => {
localStorage.removeItem(KEY);
setConsent(null);
}}
>
O'zgartirish
</button>
</p>
);
}Konsolda:
tanlov: yoq | analitika o'chiqEkranda:
Analitika: o'chiq O'zgartirishIkki tugma bir xil <button> — teng ko'rinishda. Tanlov localStorage da saqlanadi — bu "qat'iy zarur" saqlash: foydalanuvchining o'z tanlovi, uni kuzatish uchun emas. readConsent dagi try — ba'zi brauzer rejimlarida localStorage xato tashlaydi (localStorage va sessionStorage). Natija oynasidagi localStorage vaqtinchalik — sahifa yangilansa tozalanadi.
Tekshirib ko'ring: Rozilik oynasi bor, lekin GA4 skripti
index.htmlda<script async>bilan turibdi va rozilikdan oldin yuklanadi. Bu to'g'rimi?
Javob
Consent Mode'siz — yo'q: skript yuklanishi bilan cookie yozadi va ma'lumot yuboradi, rozilik oynasi esa faqat bezak bo'lib qoladi. Ikki to'g'ri yo'l bor. Birinchisi — skriptni faqat «Ruxsat beraman» dan keyin dinamik qo'shish. Ikkinchisi — Consent Mode bilan analytics_storage: "denied" ni skriptdan oldin e'lon qilish (yuqoridagi GA4 kodi). Ikkinchi holatda ham Google'ga cookie'siz signallar ketadi — Yevropada bu ham munozarali, shuning uchun ehtiyotkor jamoalar birinchi yo'lni tanlaydi.
6. Hujumchi nigohi: ma'lumot sizishi
Analitikadagi eng ko'p uchraydigan muammo — hujum emas, beixtiyor sizish. Uchinchi tomon xizmatiga yuborilgan ma'lumotni qaytarib bo'lmaydi.
- URL'dagi sirlar. Parolni tiklash havolasi:
/parol?token=8f3a…&email=ali@example.com. Analitika to'liq manzilni yuborsa — token va email Google yoki Yandex serverida. Kanon so'rov qatorini butunlay tashlaydi. - Sarlavhadagi ism.
<title>Ali Valiyev — profil</title>— ko'p xizmatlar sahifa sarlavhasini ham yuboradi. - Hodisa tafsilotlari.
track("qidiruv", { matn: query })— qidiruvga odamlar telefon, manzil, kasallik nomini yozadi. - Sessiya yozuvi (Vebvizor, PostHog replay) — kiritish maydonlarini yashirish sozlamasi o'chiq bo'lsa, yozilgan hamma narsa ketadi.
- Begona skript — to'liq kirish. Tashqi analitika skripti sahifangizdagi hamma narsani o'qiy oladi. Agar uning serveri buzilsa, zararli kod sizning sahifangizda ishlaydi. 2024-yilda mashhur polyfill.io domeni yangi egasi qo'liga o'tgach, uni ulagan ko'plab saytlarga zararli kod tarqatilgan. Bu mavzu Uchinchi tomon skriptlar va embedlar darsida davom etadi.
Himoya — "minimallashtirish": yubormaslik mumkin bo'lgan narsani yubormang. Kanon qoidasi: yo'l umumlashtiriladi, so'rov qatori tashlanadi, hodisa — faqat nom. Kelasi darsdagi xato kuzatuvida bunga matndagi email va telefonni yashiradigan shaxsiyniYashir qo'shiladi.
7. Vazifalar qadami: o'z yengil analitikasi (holat 18)
7.1 Fayllar
git switch -c feat/analitika| Fayl | Nima |
|---|---|
src/kuzatuv/analitika.ts |
navbat, sendBeacon, sahifaKorildi, hodisa |
src/kuzatuv/maxfiylik.ts |
yolniUmumlashtir, kuzatmaslikSoralgan (+ 19-dars uchun shaxsiyniYashir) |
komponentlar/MaxfiylikSozlamasi.tsx |
«Anonim statistika» katakchasi |
skriptlar/kuzatuv.js |
qabul qiluvchi — faqat dev server plagini |
main.tsx, App.tsx |
router.subscribe; hodisa("vazifa_qoshildi"), hodisa("eksport_csv") |
.env.development, vite-env.d.ts |
VITE_ANALITIKA_URL — faqat dev |
Butun tizimning kaliti — manzil. analitika.ts boshidagi izoh (kanon):
// kuzatuv/analitika.ts — o'z yengil analitikamiz (21/#18):
// sahifa ko'rishlari va bir nechta hodisa. Tashqi skript yo'q
// (GA4/Metrika/PostHog — o'lchandi: posthog-js +98.3 KB va
// Trusted Types ostida ochilmadi,
// natijalar/tajriba/kutubxonalar-21.txt), cookie yo'q,
// foydalanuvchi identifikatori yo'q. Yuboriladi:
// umumlashtirilgan yo'l, til, hodisa nomi, vaqt.
// Manzil — VITE_ANALITIKA_URL (dev: skriptlar/kuzatuv.js;
// production'da yo'q — funksiyalar hech narsa qilmaydi).VITE_ANALITIKA_URL faqat .env.development da. Production build'da import.meta.env.VITE_ANALITIKA_URL — undefined va har funksiya birinchi qatorda qaytadi. Analitika serveri 25-qismda (HTTP, Express va REST API) yoziladi — o'shanda manzil production'ga qo'shiladi.
7.2 Navbatga qo'shish
function qosh(
hodisa: Omit<Hodisa, "vaqt" | "til" | "yol"> & { yol?: string },
): void {
if (MANZIL === undefined || !analitikaYoqilganmi()) {
return;
}
navbat.push({
...hodisa,
yol: yolniUmumlashtir(hodisa.yol ?? location.pathname),
til: document.documentElement.lang,
vaqt: Date.now(),
});
if (navbat.length >= NAVBAT_CHEGARASI) {
yubor();
}
}Tekshiruv har hodisada: foydalanuvchi Sozlamalarda o'chirsa, keyingi hodisa navbatga tushmaydi (o'chirilganda navbat ham bo'shatiladi). Omit<…> & { yol?: string } — chaqiruvchi faqat tur va nom beradi, qolganini funksiya o'zi to'ldiradi (Utility types).
7.3 Natija
Testlar: maxfiylik.test.js — 3 ta (yo'l, shaxsiy ma'lumot, GPC/DNT); node:test 233 → 236, Vitest 65. Dev server tekshiruvi (dev-21, haqiqiy Chrome va dev qabul qiluvchi):
✅ yuborildi (bitta beacon): sahifalar va hodisa: ["sahifa:/","hodisa:eksport_csv","sahifa:/statistika","sahifa:/sozlamalar"]
✅ yuborilganda vazifa matni yo'q; til bor; maydonlar: [false,true,["nom","til","tur","vaqt","yol"]]
✅ o'chirilgach — hech narsa yuborilmadi; localStorage: [0,"yoq"]
✅ CSP buzilishi yo'q: []
✅ GPC: katak o'chiq va faol emas, izoh, yuborilmadi: [[false,true],true,0]Ikkinchi qator — maxfiylik isboti: yuborilgan ma'lumotda faqat beshta maydon, vazifa matni yo'q. Production build tekshiruvi (e2e-21, 55 ta):
✅ production: analitika so'rovi yo'q (VITE_ANALITIKA_URL yo'q): []
✅ build'da qabul qiluvchi manzili yo'q: falseBirinchi yuklash JS — 222.8 → 223.3 KB gzip (+0.5). axe — 0.
git add -A
git commitCommit (kanon): feat: o'z yengil analitika — sahifa va hodisa, shaxssiz, GPC; dev qabul qiluvchi.
8. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
| butun tashrif — 1 sahifa | SPA'da router o'zgarishi yuborilmaydi | router bilan page_view/hit |
| har sahifa ikki marta | avtomatik + qo'lda sanash; StrictMode effekt | bittasini o'chiring; router.subscribe |
300 ta /vazifa/N qatori |
yo'l umumlashtirilmagan | /vazifa/:id |
| yopilganda hodisalar yo'qoladi | unload/beforeunload, window da tinglash |
document + visibilitychange + sendBeacon |
| analitikada email va token | to'liq URL yuborilgan | so'rov qatorini tashlash |
requires 'TrustedScriptURL' |
analitika skripti <script src> qo'shadi |
siyosat yoki o'z yechim |
| rozilikdan oldin cookie paydo bo'ldi | skript index.html da |
rozilikdan keyin yuklash / Consent Mode |
| GPC foydalanuvchisi ham sanaldi | signal tekshirilmagan | globalPrivacyControl, doNotTrack |
9. Mashqlar
Mashqlar kurs/mashqlar/21/18-analitika/ da. Yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): bo'sh joylarni to'ldiring
- Sahifa yopilayotganda ham ketadigan so'rov:
navigator. - Navbatni yuborish uchun ishonchli hodisa:
/vazifa/12/tahrirumumlashtirilgandan keyin:- "Meni kuzatma" degan zamonaviy brauzer signali:
- Yevropada cookie'li analitika uchun kerak bo'lgan rozilik turi:
Yechim
- sendBeacon — brauzer navbatiga qo'yiladi, javob kutilmaydi.
- visibilitychange —
documentda,visibilityState === "hidden"bo'lganda. - /vazifa/:id/tahrir — raqamli bo'lak
:idga. - GPC —
navigator.globalPrivacyControl. - opt-in — oldindan, aniq rozilik; jim qolish rozilik emas.
2-mashq (o'rta): UTM belgilarini olish va tozalash
takeUtm(url) funksiyasini yozing: manzildan utm_ bilan boshlanadigan parametrlarni obyekt qilib olsin va ularsiz toza manzil qaytarsin. Boshqa parametrlar (?filtr=faol) joyida qolsin. Sinov: https://example.com/menyu?utm_source=telegram&filtr=faol&utm_campaign=kuz.
Ishora: new URL(url) va uning searchParams (URL va URLSearchParams); o'chirishdan oldin kalitlarni massivga oling — aylanish paytida o'chirish elementlarni o'tkazib yuboradi.
Yechim
function takeUtm(url) {
const u = new URL(url);
const utm = {};
const keys = [...u.searchParams.keys()];
for (const key of keys) {
if (key.startsWith("utm_")) {
utm[key] = u.searchParams.get(key);
u.searchParams.delete(key);
}
}
return { utm, clean: u.href };
}
const { utm, clean } = takeUtm(
"https://example.com/menyu?utm_source=telegram" +
"&filtr=faol&utm_campaign=kuz",
);
console.log(utm);
console.log(clean);Konsolda:
{ utm_source: 'telegram', utm_campaign: 'kuz' }
https://example.com/menyu?filtr=faolBrauzerda toza manzil history.replaceState(null, "", clean) bilan o'rnatiladi — sahifa qayta yuklanmaydi, tarixga yangi qadam qo'shilmaydi. UTM esa birinchi "sahifa ko'rildi" hodisasiga qo'shilib, bir marta yuboriladi.
3-mashq (qiyin): rozilikni kutadigan treker
createConsentTracker(send, { gpc }) yozing. Uch holat: "kutilmoqda", "ruxsat", "rad".
- Kutilayotganda hodisalar buferga yig'iladi (eng ko'pi 5 ta — eskilari tashlanadi).
grant()— bufer bitta yuborishda ketadi, keyingi hodisalar darhol yuboriladi.deny()— bufer o'chiriladi, keyingi hodisalar e'tiborsiz.gpc: truebo'lsa — boshidan"rad",grant()ham uni o'zgartirmaydi.
Sinov: 7 ta hodisa → grant() → yana 1 ta; alohida — GPC'li treker.
Ishora: buferni cheklash — buffer.slice(-5) (Kesish, birlashtirish va matnga aylantirish).
Yechim
function createConsentTracker(send, { gpc = false } = {}) {
let state = gpc ? "rad" : "kutilmoqda";
let buffer = [];
return {
event(name) {
if (state === "ruxsat") send([name]);
else if (state === "kutilmoqda") {
buffer = [...buffer, name].slice(-5);
}
},
grant() {
if (state !== "kutilmoqda") return;
state = "ruxsat";
if (buffer.length) send(buffer);
buffer = [];
},
deny() {
state = "rad";
buffer = [];
},
get state() {
return state;
},
};
}
const log = (batch) => console.log("yuborildi:", batch.join(", "));
const t = createConsentTracker(log);
for (let i = 1; i <= 7; i++) t.event(`h${i}`);
t.grant();
t.event("h8");
console.log(t.state);
const g = createConsentTracker(log, { gpc: true });
g.event("x");
g.grant();
g.event("y");
console.log(g.state);Konsolda:
yuborildi: h3, h4, h5, h6, h7
yuborildi: h8
ruxsat
radBirinchi ikkita hodisa bufer chegarasidan chiqib ketdi. Bu ataylab: foydalanuvchi uzoq o'ylasa, xotira cheksiz o'smasin. GPC'li trekerdan hech narsa chiqmadi — grant() birinchi qatorda qaytdi. get state() — getter: tashqaridan o'qiladi, lekin t.state = "ruxsat" bilan o'zgartirib bo'lmaydi (Getter va setter).
4-mashq: Vazifalar qadami
feat/analitika branch'ida «Vazifalar qadami» dagi o'zgarishlarni qiling: kuzatuv/ papkasi, MaxfiylikSozlamasi.tsx va to'rt tildagi matnlari, skriptlar/kuzatuv.js plagini, main.tsx dagi router.subscribe, ikkita hodisa(…) chaqiruvi, .env.development va vite-env.d.ts.
Yechim
Tekshiruv ro'yxati:
npm test— node 236, Vitest 65;npm run tip,lint,knip— toza.npm run dev: bir nechta sahifani oching, CSV eksport qiling, boshqa tabga o'ting. Terminalda[analitika] {"tur":"sahifa","nom":"sahifa","yol":"/",…}qatorlari;http://localhost:5173/__kuzatuv— hammasi JSON'da. Vazifa matni yo'q.- Sozlamalar → «Anonim statistika…» katagini o'chiring → yana harakat qiling → yangi qator kelmaydi.
npm run buildvanpm run preview: Network'da analitika so'rovi yo'q; birinchi yuklash ~223.3 KB.- Brauzeringizda GPC'ni yoqing (masalan, kengaytma bilan) — katak o'chiq va faol emas, izohda sababi.
10. Real ishda
- Mahsulot jamoalari — analitika qaror asosi: qaysi funksiya ishlatiladi, qayerda foydalanuvchi "yo'qoladi" (voronka). Product analyst va frontend dasturchi birga hodisalar ro'yxatini (tracking plan) yozadi.
- O'zbekiston saytlari — Yandex Metrika va GA4 eng ko'p uchraydi; shaxsga doir ma'lumot va mahalliy saqlash talablari tufayli o'z serveridagi yechimlar (Umami, Plausible, PostHog self-host) ham tanlanadi.
- Kod ko'rigida so'raladi: "bu hodisada shaxsiy ma'lumot bormi?", "SPA sahifa ko'rishlari ikki marta sanalmayaptimi?", "rozilikdan oldin nima yuklanadi?".
- Intervyu savollari: "SPA'da page view qanday yuboriladi?", "
sendBeaconnima va nega kerak?", "opt-in va opt-out farqi?", "analitikaga nimani yubormaslik kerak?".
Xulosa
- SPA'da sahifa ko'rishi — router o'zgarganda o'zingiz; yo'l umumlashtiriladi (
/vazifa/:id), so'rov qatori tashlanadi. - Hodisa — nom, matnsiz; navbat +
documentdagivisibilitychange+sendBeacon. - GA4, Metrika, PostHog — tashqi skript, cookie, CSP va TT muammolari (posthog +98.3 KB, TT to'sdi); Plausible/Umami — cookie'siz.
- Cookie/identifikator yoki shaxsga doir ma'lumot — Yevropada oldindan rozilik; O'zbekistonda rozilik va mahalliy saqlash talabi (O'RQ-547).
- GPC/DNT ni hurmat qiling; rozilik oynasida "Rad etish" — teng va oson.
- Kanon: o'z analitikasi (+0.5 KB), shaxssiz, o'chirsa bo'ladi, production'da manzil yo'q.
Keyingi dars: Xatolarni kuzatish: Sentry frontendda — foydalanuvchida yuz bergan xatolarni yig'ish: window, Promise va React xatolari, reliz, iz va matndagi shaxsiy ma'lumotni yashirish.
Manbalar
- MDN:
Navigator.sendBeacon(),Document: visibilitychange event,Navigator.globalPrivacyControl,Navigator.doNotTrack - React Router:
useLocation,router.subscribe— reactrouter.com - Google: "Measure single-page applications", "Consent mode" — developers.google.com/analytics
- Yandex Metrika: "Counter initialization",
hit,reachGoal— yandex.ru/support/metrica - PostHog: "JavaScript Web SDK" (
capture_pageview,persistence) — posthog.com/docs; Plausible — plausible.io/docs; Umami — umami.is/docs - GDPR (Regulation (EU) 2016/679), ePrivacy Directive 2002/58/EC 5(3)-modda; CJEU C-673/17 (Planet49, 2019); CNIL: audience measurement exemption
- O'zbekiston Respublikasining "Shaxsga doir ma'lumotlar to'g'risida"gi Qonuni (O'RQ-547, 2019-07-02) va 27¹-moddani qo'shgan O'RQ-666 (2021-01-14) — lex.uz
- Global Privacy Control — globalprivacycontrol.org
- Kanon:
vazifalarholat 18,natijalar/tekshiruv-18.txt,brauzer-tekshiruv/natija-dev-18.txt,natija-21-18.txt,natijalar/tajriba/kutubxonalar-21.txt(2026-10-17)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!