IlmHamroh
JavaScript Full-stack/21-qism. React: media, integratsiyalar, PWA27/32-dars18 daqiqa
Mundarija (28)

Uchinchi tomon skriptlar React'da: dinamik yuklash, facade, iframe sandbox, CSP va rozilik

Qisqacha: Chat vidjeti, piksel, YouTube va xarita — boshqa kompaniyaning kodi sizning sahifangizda. Uchta qoida: (1) kerak bo'lgandagina yuklang — dinamik <script>, facade (avval rasm, bosilganda iframe); (2) cheklang — iframe sandbox, CSP'ga faqat kerakli host; (3) so'rang — kuzatuvchi skript faqat rozilikdan keyin. Kanon tajribasi: react-lite-youtube-embed +2.6 KB, lekin vazifalar CSP'si ostida 4 ta rasm va 1 ta iframe to'sildi — CSP'ga ikki host qo'shish kerak bo'lardi.

Bu darsda

  • Begona skriptni React'da bir marta va kerak bo'lganda yuklaydigan hook yozasiz.
  • YouTube facade'ini React komponenti qilib, title, loading va sandbox bilan ulaysiz.
  • async/defer, dinamik yuklash va Partytown qachon kerakligini ajratasiz.
  • Har vidjet uchun CSP'ga nima qo'shilishini va bu qanday maxfiylik narxi ekanini o'lchov bilan ko'rasiz.
  • Kuzatuvchi skriptni rozilikdan keyin yuklashni yozasiz.

Oldin bilishingiz kerak: Skriptlarni ulash: defer, async, module, iframe xavfsizligi va unumdorligi, HTML darajasidagi xavfsizlik, Brauzer xavfsizlik modeli, Analitika.

1. Nega bu kerak?

Jasur aka «Bahor» saytiga to'rt narsa qo'shmoqchi: oshxona haqida YouTube video, Yandex xaritasi, onlayn yordam chati va reklama uchun piksel. To'rttasining ham "o'rnatish" yo'riqnomasi bir xil: "shu <script> ni <head> ga qo'ying".

Har biri alohida qaraganda kichik. Hammasi birga — sahifaga yuzlab kilobayt begona JS, o'nlab tashqi so'rov, va sizning nazoratingizdan tashqaridagi kod. U sekinlashtiradi, CSP'ni buzadi va mehmon haqida ma'lumot yig'adi.

Hayotiy o'xshatish: uyga mehmon chaqirish. Mehmon kerak — lekin uni qachon chaqirasiz (kerak bo'lganda), qaysi xonaga kiritasiz (cheklangan joy) va uy egasidan (mehmondan) ruxsat so'raysizmi — shuni o'zingiz hal qilasiz.

Bu darsda uchta savolga javob beramiz: qachon yuklash, qayerda cheklash va kimdan ruxsat so'rash. Facade va sandbox ni iframe xavfsizligi darsida HTML'da ko'rgan edik — u yerda va'da qilganimizdek, endi ularni JavaScript va React bilan yozamiz.

2. Qachon yuklash: uch yo'l

2.1 Eslab olamiz: async va defer

Skriptlarni ulash darsida to'rt usulni solishtirgan edik. Begona skript uchun qisqa xulosa:

Usul Qachon bajariladi Begona skript uchun
oddiy <script src> darhol, HTML o'qishni to'xtatadi deyarli hech qachon
async yuklanishi bilan, tartibsiz mustaqil vidjet (analitika)
defer HTML tugagach, tartib bilan sahifaga bog'liq skript
dinamik (JS'dan) siz aytganda kerak bo'lgandagina

Birinchi uchtasi baribir har mehmon uchun yuklanadi. Chat vidjetini 100 mehmondan 3 tasi ochadi — qolgan 97 tasi uchun u bekor yuk. To'rtinchi yo'l shu muammoni hal qiladi.

2.2 Dinamik yuklash: <script> ni JS'dan yaratish

Skriptni kodda yaratib, <head> ga qo'shsangiz — brauzer o'shanda yuklaydi. Bunday skript sukut bo'yicha async kabi ishlaydi. React'da buni hook'ga o'raymiz. Ikki talab bor:

  1. Bir marta. Ikki komponent bir xil skriptni so'rasa, <head> ga ikkita teg tushmasin.
  2. Promise. Komponent "yuklandimi?" ni kutishi kerak.

Shuning uchun yuklash modul darajasidagi xaritada eslab qolinadi — komponentlar emas, skript manzili kalit:

jsx
const yuklanganlar = new Map(); // manzil → Promise

export function skriptYukla(manzil) {
  if (!yuklanganlar.has(manzil)) {
    const vada = new Promise((hal, rad) => {
      const s = document.createElement("script");
      s.src = manzil;
      s.async = true;
      s.onload = () => hal();
      s.onerror = () => {
        yuklanganlar.delete(manzil); // keyinroq qayta urinish
        rad(new Error(`Yuklanmadi: ${manzil}`));
      };
      document.head.append(s);
    });
    yuklanganlar.set(manzil, vada);
  }
  return yuklanganlar.get(manzil);
}

Bu blok natija oynasida ishlamaydi — u yerda tarmoq yo'q, tashqi skript yuklanmaydi. Mexanizmni esa jonli ko'ramiz: script o'rniga soxta yuklovchi (setTimeout) qo'yamiz. Qolgani — xuddi shu xarita va xuddi shu hook:

jsx
import { useEffect, useState } from "react";

const yuklanganlar = new Map();
function soxtaYukla(manzil) {
  if (!yuklanganlar.has(manzil)) {
    console.log(`yuklanmoqda: ${manzil}`);
    yuklanganlar.set(
      manzil,
      new Promise((hal) => setTimeout(hal, 200)),
    );
  }
  return yuklanganlar.get(manzil);
}

function useSkript(manzil, kerak) {
  const [holat, setHolat] = useState("kutmoqda");
  useEffect(() => {
    if (!kerak) return;
    let bekor = false;
    setHolat("yuklanmoqda");
    soxtaYukla(manzil).then(
      () => !bekor && setHolat("tayyor"),
      () => !bekor && setHolat("xato"),
    );
    return () => {
      bekor = true;
    };
  }, [manzil, kerak]);
  return holat;
}

function Vidjet({ nom, kerak }) {
  const holat = useSkript("https://example.com/chat.js", kerak);
  return <p>{nom}: {holat}</p>;
}

export default function App() {
  const [ochiq, setOchiq] = useState(false);
  return (
    <>
      <button onClick={() => setOchiq(true)}>Chatni ochish</button>
      <Vidjet nom="Pastki tugma" kerak={ochiq} />
      <Vidjet nom="Yordam sahifasi" kerak={ochiq} />
    </>
  );
}

Ekranda:

text
Chatni ochish
Pastki tugma: tayyor
Yordam sahifasi: tayyor

Konsolda:

text
yuklanmoqda: https://example.com/chat.js

Ikki komponent, bitta yuklash — konsolda bitta qator. Tugma bosilmaguncha hech narsa yuklanmadi: ikkalasi kutmoqda edi. bekor bayrog'i — komponent yuklash tugashidan oldin yo'qolsa, state yozilmasin (effektda ma'lumot olish dagi naqsh).

Maslahat: Next.js'da buning tayyor varianti bor — next/script va uning strategy (afterInteractive, lazyOnload, worker) qiymatlari. Uni 23-qismda ko'ramiz. G'oya xuddi shu: qachon yuklashni siz aytasiz.

2.3 Facade: avval rasm, bosilganda haqiqiysi

Dinamik yuklashning eng foydali turi — facade ("fasad"): og'ir vidjet o'rnida uning yengil "rasmi" turadi. Foydalanuvchi bosgandagina haqiqiy vidjet yuklanadi. iframe xavfsizligi darsida g'oyani HTML'da ko'rgandik. React komponenti:

jsx
import { useEffect, useRef, useState } from "react";

function YouTubeFasad({ id, nom }) {
  const [ochiq, setOchiq] = useState(false);
  const iframe = useRef(null);
  useEffect(() => {
    if (ochiq) console.log("iframe:", iframe.current.src);
  }, [ochiq]);
  if (!ochiq) {
    return (
      <button
        className="fasad"
        aria-label={`Videoni ko'rish: ${nom}`}
        onClick={() => setOchiq(true)}
      >
        ▶ {nom}
      </button>
    );
  }
  return (
    <iframe
      ref={iframe}
      title={nom}
      src={`https://www.youtube-nocookie.com/embed/${id}?autoplay=1`}
      allow="autoplay; encrypted-media; picture-in-picture"
      allowFullScreen
      width="320"
      height="180"
    />
  );
}

export default function App() {
  return <YouTubeFasad id="VIDEO_ID" nom="Toshkent oshi" />;
}

Konsolda:

text
iframe: https://www.youtube-nocookie.com/embed/VIDEO_ID?autoplay=1

VIDEO_ID — o'rniga haqiqiy video kodini qo'yasiz; natija oynasida tarmoq yo'q, shuning uchun iframe bo'sh qoladi. Kodda to'rt tafsilot:

  • Tugma — <div onClick> emas. Bobur Tab bilan keladi va Enter bilan ochadi (accessibility). aria-label ichida video nomi — ekran o'quvchi "Videoni ko'rish: Toshkent oshi" deydi.
  • autoplay=1 — foydalanuvchi allaqachon bir marta bosdi. Ikkinchi marta "▶" ni bosishga majburlamaymiz.
  • youtube-nocookie.com — YouTube'ning "kengaytirilgan maxfiylik" manzili: video ijro etilmaguncha cookie qo'ymaydi (YouTube yordam sahifasi bo'yicha).
  • title — iframe uchun majburiy (iframe): ekran o'quvchi ichki hujjatni shu nom bilan e'lon qiladi.

Haqiqiy facade'da tugma ichida muqova rasmi bo'ladi: https://i.ytimg.com/vi/<id>/hqdefault.jpg. Tayyor kutubxona — react-lite-youtube-embed — aynan shuni qiladi. Uni «Biz sinab ko'rdik» bo'limida o'lchaymiz.

Tekshirib ko'ring: Facade'dagi tugma bosilgandan keyin fokus qayerda qoladi va bu nega muammo bo'lishi mumkin?

Javob

Tugma DOM'dan olib tashlandi — uning o'rniga iframe keldi. Fokuslangan element yo'qolganda brauzer fokusni <body> ga tashlaydi. Bobur Tab bossa, sahifa boshidan boshlaydi. Yechim — iframe chizilgach unga fokus berish (iframe.current.focus() effektda) yoki tayyor kutubxonaning shu xulqini tekshirish. Bu — fokus boshqaruvi darsidagi "yo'qolgan fokus" tuzog'ining yana bir ko'rinishi.

2.4 Partytown: skriptni worker'ga ko'chirish

Ba'zi skriptni kechiktirib bo'lmaydi — masalan, analitika birinchi sahifa ko'rishni ham o'lchashi kerak. Lekin u asosiy thread'ni band qiladi va INP yomonlashadi.

Partytown g'oyasi: begona skriptni Web Worker ichida bajarish. Worker'da DOM yo'q — shuning uchun Partytown skriptning document va window ga murojaatlarini asosiy thread'ga "proksi" qiladi. Hujjat bo'yicha ulanishi — skript turini o'zgartirish:

html
<script
  type="text/partytown"
  src="https://example.com/analitika.js"
></script>

text/partytown turini brauzer bajarmaydi — Partytown uni topib, worker'ga beradi. Skript window.dataLayer.push(…) kabi global funksiya chaqirsa, uni forward sozlamasida ko'rsatasiz.

Halol holat: kanon Partytown'ni o'lchamadi — hajm ham, tezlik ham, CSP natijasi ham yo'q. Sabab: u build vositasi va worker'dan DOM'ga proksi — vazifalar CSP'sida worker-src va Trusted Types bilan qanday chiqishi alohida savol. Uchinchi tomon kodi worker'da ham sizning origin'ingizda ishlaydi, faqat boshqa thread'da. Shuning uchun Partytown tezlik vositasi, xavfsizlik vositasi emas.

3. Qayerda cheklash: iframe sandbox va CSP

3.1 Skript yoki iframe?

Begona kod ikki xil keladi va xavfi turlicha:

<script> <iframe>
Qayerda ishlaydi sizning sahifangizda alohida hujjatda
DOM, cookie, localStorage'ga to'liq kiradi kirmaydi (boshqa origin)
Cheklash faqat CSP va ishonch sandbox, allow, CSP frame-src

<script> bilan ulangan chat vidjeti localStorage dagi tokenni o'qiy oladi, forma maydonlariga yozilganini ko'radi. iframe ichidagi vidjet esa yo'q. Tanlov bo'lsa — iframe varianti xavfsizroq.

3.2 sandbox — React'da

iframe xavfsizligi darsidagi qoidalar React'da ham o'sha: bo'sh sandbox — eng qattiq, kerakli ruxsatlarni bittalab qaytarasiz. React'da atribut nomi bir xil, allowFullScreen esa camelCase:

jsx
<iframe
  title="«Bahor» manzili xaritada"
  src="https://example.com/xarita?nuqta=bahor"
  sandbox="allow-scripts allow-popups"
  loading="lazy"
  referrerPolicy="no-referrer"
  width="100%"
  height="300"
/>

allow-scripts — xarita o'z JS'ini ishlatadi. allow-popups — "Yo'nalish" tugmasi yangi oynada ochiladi. allow-same-origin YO'Q: busiz iframe o'z cookie'lariga ham kira olmaydi. allow-scripts va allow-same-origin ni birga berish xavfli juftlik — ichki sahifa o'zining sandbox atributini olib tashlay oladi (o'sha darsda ko'rganmiz). Ba'zi vidjetlar allow-same-origin siz ishlamaydi — u holda u sizning domeningizdan emas, o'z domenidan kelayotganini tekshiring.

3.3 CSP: har vidjet — yangi host

vazifalar CSP'si qattiq: default-src 'self', img-src 'self' blob:, Trusted Types. Begona vidjet qo'shish — CSP'ni kengaytirish. Qaysi direktivaga nima? Kanon har kutubxonani ikki marta ochdi: CSP bilan (nima to'sildi) va CSP'siz (qaysi hostlarga bordi). «Biz sinab ko'rdik» bo'limida aniq raqamlar. Umumiy xarita:

flowchart LR
  V["Begona vidjet"] --> S["script-src: skript hosti"]
  V --> C["connect-src: API so'rovlari"]
  V --> I["img-src: rasmlar, piksel"]
  V --> F["frame-src: iframe"]
  V --> T["trusted-types: innerHTML / script URL"]
  V --> ST["style-src: o'z style tegi"]

Diagrammaga qarang: bitta vidjet oltita direktivaga tegishi mumkin. Har biri — "shu hostga ishonaman" degan imzo. * yoki https: kabi keng qiymat qo'yish oson, lekin u CSP'ni deyarli o'chiradi.

Tekshirib ko'ring: Chat vidjeti script-src ga https://widget.example.com qo'shishni so'radi. Jasur aka "bitta host — xavfsiz" deydi. Nimani tekshirish kerak?

Javob

Uch narsa. Birinchisi — o'sha hostdagi boshqa fayllar ham endi ruxsatli: hostda foydalanuvchi yuklagan fayllar yoki eski zaif versiyalar bo'lsa, hujumchi ulardan foydalanadi. Ikkinchisi — skript qaysi boshqa hostlarga boradi (connect-src, img-src) — kanon jadvalidagi "ochiq" ustuni aynan shu. Uchinchisi — Trusted Types: skript innerHTML yoki o'zi skript yaratsa, siyosatga ham o'zgartirish kerak. "Bitta host" — faqat boshlanishi.

4. Kimdan so'rash: rozilik

4.1 Piksel nima

Piksel (tracking pixel) — reklama tizimi beradigan kichik skript yoki 1×1 o'lchamli ko'rinmas rasm. U sahifa ochilganda reklama serveriga "bu mehmon shu sahifani ko'rdi" deb xabar yuboradi. Keyin o'sha mehmonga boshqa saytlarda «Bahor» reklamasi ko'rsatiladi.

Analitika darsida rozilik qonunchiligini ko'rgandik: shaxsni boshqa saytlar bo'ylab kuzatish — rozilik talab qiladigan ish. Demak, piksel rozilikdan oldin yuklanmasligi kerak. "Yuklab, keyin o'chirib qo'yamiz" ishlamaydi — birinchi so'rov allaqachon ketgan.

4.2 Rozilik → yuklash

Yuqoridagi useSkript ning kerak parametri shu yerda ishlaydi: kerak = "foydalanuvchi rozi". Jonli model — soxta yuklovchi bilan:

jsx
import { useEffect, useState } from "react";

let yuklandi = false;
function pikselniYukla() {
  if (yuklandi) return;
  yuklandi = true;
  console.log("piksel yuklandi");
}

export default function App() {
  const [rozi, setRozi] = useState(null); // null — hali so'ralmagan
  useEffect(() => {
    if (rozi === true) pikselniYukla();
  }, [rozi]);
  return (
    <>
      {rozi === null && (
        <section aria-label="Cookie va reklama">
          <p>Reklama uchun tashriflaringizni hisoblaymizmi?</p>
          <button onClick={() => setRozi(true)}>Roziman</button>{" "}
          <button onClick={() => setRozi(false)}>Yo'q</button>
        </section>
      )}
      <p>
        Reklama pikseli: {rozi === true ? "yoqilgan" : "o'chiq"}
      </p>
    </>
  );
}

Ekranda:

text
Reklama pikseli: yoqilgan

Konsolda:

text
piksel yuklandi

Rozilikdan oldin ekranda savol va «o'chiq» bor edi (oraliq nuqta), piksel yuklanmagan. «Roziman» dan keyin — bitta yuklash. Uchta muhim tafsilot:

  • «Yo'q» tugmasi «Roziman» bilan teng. Rad etishni yashirish ("Sozlamalar → 3 qadam → O'chirish") — qorong'i naqsh (dark pattern). Ko'p qonunlar rad etishni rozilik bilan bir xil oson bo'lishini talab qiladi.
  • Tanlov saqlanadi — real loyihada localStorage da. Har sahifada qayta so'ramang.
  • Rozilikni qaytarib olish — sozlamalarda. Kanonning analitika sozlamasi (Analitika) aynan shunday: o'chirilgach hech narsa yuborilmaydi.

Sukut bo'yicha null — "so'ralmagan". Uni false bilan aralashtirmang: false — "rad etdi", qayta so'ramaymiz.

5. Biz sinab ko'rdik: lite-youtube va boshqa vidjetlar CSP ostida

5.1 react-lite-youtube-embed

Kanon react-lite-youtube-embed 3.7.0 ni eng kichik misolda o'lchadi. Misol kodi (kanon):

tsx
// #27 react-lite-youtube-embed 3.7.0 (fasad: bosilmaguncha iframe yo'q)
import LiteYouTubeEmbed from "react-lite-youtube-embed";
import "react-lite-youtube-embed/dist/LiteYouTubeEmbed.css";
import { createRoot } from "react-dom/client";
createRoot(document.getElementById("ilova")!).render(<main><h1>lite-youtube</h1><LiteYouTubeEmbed id="aqz-KE-bpKQ" title="Video" /></main>);

Natija (kutubxonalar-21.txt):

text
embed-youtube-lite     JS   70426 (+   2.6 KB) CSS   1308 | CSP img-src×4,frame-src×1 | xato: Loading the image 'https://i.ytimg.com/vi/aqz-KE-bpKQ/hqdefault.jpg' violates the following Content  ; Loading the image 'https://i.ytimg.com/vi/aqz-KE-bpKQ/hqdefault.jpg' violates the following Content  | ochiq: tashqi fonts.gstatic.com,i.ytimg.com,www.youtube-nocookie.com

Uni o'qiymiz:

  • Hajm — +2.6 KB JS va 1.3 KB CSS. Arzon: YouTube pleyerining o'zi bosilgunga qadar umuman yuklanmaydi.
  • CSP bilan — 5 ta buzilish. img-src ×4: muqova rasmi i.ytimg.com dan (ikki marta) va ikki data: rasm (ijro belgisi SVG va soya PNG). frame-src ×1: bosilgach youtube-nocookie.com iframe'i to'sildi.
  • CSP'siz — sahifa uch hostga bordi: i.ytimg.com, www.youtube-nocookie.com va fonts.gstatic.com (Google shriftlari — iframe ichidan).

Demak, vazifalar ga video qo'shish uchun CSP shunday kengayardi:

text
img-src 'self' blob: data: https://i.ytimg.com;
frame-src https://www.youtube-nocookie.com;

data: rasmlarga ruxsat — kutubxona ikonkalari uchun. Uning o'rniga ikonkani o'z CSS'ingizda bersangiz ham bo'ladi.

Maxfiylik narxi: muqova rasmi ham Google serveridan keladi. Ya'ni bosilmagan video ham mehmonning IP manzilini Google'ga ko'rsatadi. Iloji bo'lsa muqovani o'z serveringizga ko'chiring. Bosilgandan keyin esa — Google bilan to'g'ridan-to'g'ri aloqa, nocookie bo'lsa ham.

5.2 Boshqa vidjetlar — oldingi darslardan

Shu qismda kanon o'nlab tashqi kutubxonani xuddi shu usulda o'lchadi. Uchinchi tomon nuqtai nazaridan xulosa:

Vidjet Nima to'sildi Dars
posthog (analitika) TrustedScriptURL — skript yuklaydi Analitika
Turnstile, reCAPTCHA, hCaptcha TrustedScriptURL; hCaptcha 5 host CAPTCHA
Monaco muharriri jsDelivr CDN'dan yuklaydi Muharrirlar
Leaflet, MapLibre innerHTML / worker; plitka hostlari Xaritalar

Umumiy qonuniyat: skript yuklaydigan vidjetlar (CAPTCHA, analitika SDK, Monaco) Trusted Types'da TrustedScriptURL buzilishini beradi. Ular uchun CSP'ga host qo'shishning o'zi yetmaydi — Trusted Types siyosatiga ham o'zgartirish kerak. Bu — eng qimmat kengaytirish.

5.3 Nega vazifalar da embed yo'q

Bu darsda vazifalar kodi o'zgarmaydi. Ilovada video ham, xarita ham, chat vidjeti ham yo'q — qo'shish uchun sabab yo'q. Analitika va xatolar kuzatuvi esa kanonda o'zimizniki (Analitika, Xatolarni kuzatish) — begona skriptsiz, CSP o'zgarmagan. Saboq: eng arzon uchinchi tomon skripti — qo'shilmagani.

6. Hujumchi nigohi

Ta'minot zanjiri. Begona skript sizning sahifangizda sizning huquqlaringiz bilan ishlaydi. Skriptlarni ulash darsidagi polyfill.io voqeasini eslang: 2024-yilda domen yangi egaga o'tdi va 100 mingdan ortiq saytda zararli kod ishladi. Sayt egalari hech narsani o'zgartirmagan edi.

Himoya qatlamlari:

  1. Kamroq skript. Har yangi vidjetdan oldin: "busiz bo'ladimi?"
  2. SRI (integrity) — fayl bir xil bo'lsa (HTML xavfsizligi). Chat va analitika SDK'lari ko'pincha har safar yangi versiya beradi — ularga SRI qo'yib bo'lmaydi.
  3. iframe varianti — mavjud bo'lsa, <script> o'rniga. Iframe sizning DOM va localStorage ga kira olmaydi.
  4. CSP — aniq hostlar, * emas. Trusted Types — innerHTML va skript manzillarini nazorat qiladi.
  5. Rozilik — kuzatuvchi kod foydalanuvchi rozi bo'lmaguncha umuman yuklanmaydi.

Forma ma'lumoti. <script> bilan ulangan vidjet sahifadagi har maydonni o'qiy oladi — parol va karta raqami ham. Shuning uchun to'lov va kirish sahifalarida begona skript bo'lmasligi kerak. Analitika bo'lsa ham — o'sha sahifalardan olib tashlang.

7. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
<head> da bir skript ikki marta har komponent o'zi script qo'shadi modul darajasidagi xarita (Map)
konsolda Refused to load the script CSP'da host yo'q ataylab qo'shing yoki vidjetdan voz keching
video bosilsa ham ochilmaydi frame-src yo'q frame-src https://www.youtube-nocookie.com
piksel rozilikdan oldin so'rov yubordi <head> dagi statik <script> rozilikdan keyin dinamik yuklash
iframe ekran o'quvchida "frame" deb o'qiladi title yo'q title majburiy
sandbox bilan vidjet buzildi kerakli ruxsat yo'q bittalab qo'shing; allow-same-origin + allow-scripts ga ehtiyot

8. Mashqlar

Mashqlar kurs/mashqlar/21/27-uchinchi-tomon/ da.

1-mashq (oson): qaysi yo'l?

To'ldiring (async, defer, dinamik, facade, iframe):

  • Analitika — sahifaga bog'liq emas, imkon qadar erta:
  • Chat vidjeti — faqat "Yordam" tugmasi bosilganda:
  • YouTube video — bosilmaguncha faqat rasm:
  • Begona to'lov formasi — sahifangiz DOM'iga kira olmasin:
  • O'z skriptingiz, HTML tayyor bo'lgach, tartib bilan:
Yechim
  • async — mustaqil, tartib muhim emas; lekin kuzatuvchi bo'lsa — rozilikdan keyin.
  • dinamik — 97% mehmon uchun yuklanmaydi.
  • facade — pleyer kodi faqat bosilganda.
  • iframe — boshqa origin: sizning DOM, cookie va localStorage ga yo'l yo'q.
  • defer — HTML o'qilib bo'lgach, yozilgan tartibda.

2-mashq (o'rta): xarita facade'i

«Bahor» manzili uchun facade yozing:

  1. Avval tugma: «Xaritani ochish: Chilonzor filiali».
  2. Bosilganda <iframe> — title="«Bahor» Chilonzor filiali xaritada", src — https://example.com/xarita?filial=chilonzor, sandbox="allow-scripts allow-popups", loading="lazy".
  3. Iframe chizilgach unga fokus berilsin va konsolga fokus: IFRAME yozilsin.

Ishora: useRef + useEffect — «Facade» bo'limidagi kabi. Fokusni tekshirish — document.activeElement.tagName.

Yechim
jsx
import { useEffect, useRef, useState } from "react";

function XaritaFasad({ filial, nom }) {
  const [ochiq, setOchiq] = useState(false);
  const iframe = useRef(null);
  useEffect(() => {
    if (!ochiq) return;
    iframe.current.focus();
    console.log("fokus:", document.activeElement.tagName);
  }, [ochiq]);
  if (!ochiq) {
    return (
      <button onClick={() => setOchiq(true)}>
        Xaritani ochish: {nom}
      </button>
    );
  }
  return (
    <iframe
      ref={iframe}
      title={`«Bahor» ${nom} xaritada`}
      src={`https://example.com/xarita?filial=${filial}`}
      sandbox="allow-scripts allow-popups"
      loading="lazy"
      width="320"
      height="200"
    />
  );
}

export default function App() {
  return <XaritaFasad filial="chilonzor" nom="Chilonzor filiali" />;
}

Konsolda:

text
fokus: IFRAME

Tugma yo'qolgach fokus <body> ga tushmadi — iframe'ga o'tdi. title da filial nomi bor: bir sahifada bir nechta xarita bo'lsa, ekran o'quvchi ularni ajratadi.

3-mashq (qiyin): rozilik turlari

Ikki turdagi skript bor: «Statistika» (o'z analitikangiz) va «Reklama» (piksel). Komponent yozing:

  1. Ikki katakcha (checkbox) — sukut bo'yicha ikkalasi ham belgilanmagan — va «Saqlash» tugmasi.
  2. «Saqlash» bosilganda faqat belgilanganlar "yuklanadi": konsolga yuklandi: statistika va/yoki yuklandi: reklama.
  3. Har tur bir martadan ortiq yuklanmasin (ikki marta «Saqlash» bosilsa ham).
  4. Ostida holat: Statistika: ha, Reklama: yo'q ko'rinishida.

Ishora: yuklanganlar — modul darajasidagi Set. Katakcha holati — useState, saqlangan tanlov — alohida state.

Yechim
jsx
import { useEffect, useState } from "react";

const yuklangan = new Set();
function yukla(tur) {
  if (yuklangan.has(tur)) return;
  yuklangan.add(tur);
  console.log(`yuklandi: ${tur}`);
}

export default function App() {
  const [tanlov, setTanlov] = useState({
    statistika: false,
    reklama: false,
  });
  const [saqlangan, setSaqlangan] = useState(null);
  useEffect(() => {
    if (saqlangan === null) return;
    for (const [tur, ha] of Object.entries(saqlangan)) {
      if (ha) yukla(tur);
    }
  }, [saqlangan]);
  const belgi = (tur) => (
    <label>
      <input
        type="checkbox"
        checked={tanlov[tur]}
        onChange={(e) =>
          setTanlov({ ...tanlov, [tur]: e.target.checked })
        }
      />
      {tur === "statistika" ? "Statistika" : "Reklama"}
    </label>
  );
  const soz = (x) => (x ? "ha" : "yo'q");
  return (
    <>
      {belgi("statistika")} {belgi("reklama")}{" "}
      <button onClick={() => setSaqlangan({ ...tanlov })}>
        Saqlash
      </button>
      <p>
        Statistika: {soz(saqlangan?.statistika)}, Reklama:{" "}
        {soz(saqlangan?.reklama)}
      </p>
    </>
  );
}

Konsolda:

text
yuklandi: statistika

Ekranda:

text
Statistika Reklama Saqlash
Statistika: ha, Reklama: yo'q

«Saqlash» ikki marta bosildi, lekin Set tufayli statistika bir marta yuklandi. { ...tanlov } — har bosishda yangi obyekt, shuning uchun effekt qayta ishlaydi; takrorni Set to'xtatadi. Reklama belgilanmadi — uning kodi umuman yuklanmadi.

4-mashq: tajriba qadami (ixtiyoriy)

vazifalar da alohida branch oching (masalan tajriba/youtube) va bitta sahifaga react-lite-youtube-embed qo'shing. npm run build va npm run preview bilan oching, DevTools Console'da CSP xabarlarini sanang. Keyin index.html dagi CSP'ga «Biz sinab ko'rdik» bo'limidagi ikki qatorni qo'shib, qayta tekshiring. Branch'ni main'ga qo'shmang — o'chiring.

Yechim

Kutiladigan natija (kanon o'lchovi bilan bir xil bo'lishi kerak):

  • CSP'siz o'zgarishda — Console'da Loading the image 'https://i.ytimg.com/…' violates … (rasm) xabarlari; bosilgach Framing 'https://www.youtube-nocookie.com/' violates ….
  • Ikki qator qo'shilgach — muqova ko'rinadi, bosilganda video ochiladi.
  • npm run build — sahifa chunk'i ~2.6 KB ga, CSS ~1.3 KB ga oshadi.
  • Network panelida — bosilmaguncha youtube-nocookie.com ga so'rov yo'q (facade ishlayapti), lekin i.ytimg.com ga bor.

9. Real ishda

  • Marketing va dasturchi orasidagi bahs — har loyihada. "Shu pikselni qo'yib bering" iltimosiga to'g'ri javob: "qaysi sahifaga, rozilikdan keyin, CSP'ga shu hostlar qo'shiladi, narxi shuncha KB". Raqam bilan gapiring.
  • Tag manager (Google Tag Manager kabi) — marketologlar dasturchisiz skript qo'shadigan vosita. Qulay, lekin CSP nuqtai nazaridan "har qanday skript" degani. Uni ulash — jamoa qarori.
  • Intervyu savollari: "facade nima?", "async va defer farqi?", "begona skriptni qanday xavfsiz ulaysiz?", "iframe sandbox da qaysi juftlik xavfli?".

Xulosa

  • Begona skript — sizning sahifangizda, sizning huquqlaringiz bilan. Har birini o'lchang: KB, hostlar, CSP buzilishlari.
  • Qachon: async/defer — har mehmonga; dinamik yuklash va facade — faqat kerak bo'lganda. Bir marta yuklash — modul darajasidagi Map.
  • Qayerda: iframe skriptdan xavfsizroq; sandbox — minimal ruxsat; CSP — aniq hostlar.
  • Kimdan: kuzatuvchi kod — rozilikdan keyin; «Yo'q» «Roziman» bilan teng.
  • Kanon: lite-youtube +2.6 KB, lekin CSP'ga i.ytimg.com, data: va youtube-nocookie.com kerak; Partytown o'lchanmadi.

Keyingi dars: PWA React loyihada: vite-plugin-pwa, Workbox va Serwist — o'z sw.js imizni ikki tayyor kutubxona bilan o'lchab solishtiramiz va deploydan keyingi yashirin xatoni tuzatamiz.

Manbalar

  • MDN: <script> (async, defer), HTMLScriptElement, <iframe> sandbox, Content-Security-Policy (frame-src, img-src, trusted-types)
  • web.dev: "Lazy load third-party resources with facades"; "Efficiently load third-party JavaScript"
  • YouTube yordam markazi: kengaytirilgan maxfiylik rejimi (youtube-nocookie.com)
  • Partytown hujjati: type="text/partytown", forward
  • Kanon: natijalar/tajriba/kutubxonalar-21.txt, tajriba/kutubxonalar/src/k/embed-youtube-lite.tsx (2026-10-17)
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Uchinchi tomon skriptlar React'da: dinamik yuklash, facade, iframe sandbox, CSP va rozilik — IlmHamroh