Բոլոր դասերը
FrontendՄիջին13 րոպե ընթերցումԻմ ստեկում

SEO, արագագործություն և հասանելիություն

Core Web Vitals, տեխնիկական SEO և WCAG-ի հիմունքներ՝ չափված, ոչ թե գուշակված։

  • Core Web Vitals
  • JSON-LD
  • hreflang
  • Sitemaps
  • Accessibility
Քայլ 1/5
Որոնողական crawler-ը այցելում է էջ, որը պարզապես դատարկ JS «կմախք» է՝ <div id=root> ու spinner։ Կարդալու բան դեռ չկա։

30 վայրկյանում

Կայքը պետք է գտնվի, արագ բեռնվի և աշխատի բոլորի համար։ Տեխնիկական SEO-ն օգնում է որոնողական համակարգերին crawl անել և հասկանալ ձեր էջերը. Core Web Vitals-ը չափում է, թե որքան արագ և կայուն են դրանք զգացվում իրական օգտատերերի համար. հասանելիությունը (accessibility) երաշխավորում է, որ ստեղնաշար, screen reader կամ zoom օգտագործող մարդիկ կարողանան օգտվել դրանցից։ Երեքը մեծապես համընկնում են, և բոլորն էլ լավագույնս լուծվում են իրական տվյալները չափելով և նախ ամենամեծ խնդիրը շտկելով։

Ինչու եմ օգտագործում

  • Սա ինձ համար կարևոր է, որովհետև վատ դիրքավորվող կամ դանդաղ բեռնվող առցանց խանութը կորցնում է հաճախորդներին՝ նախքան նրանք որևէ ապրանք կտեսնեն։
  • SEO-ն CMS-ի մեջ ներդնում եմ հենց սկզբից՝ ամեն էջի metadata, sitemap-ներ, redirect-ներ և structured data, որպեսզի խմբագիրներին երբեք ծրագրավորող պետք չլինի title ուղղելու համար։
  • Աշխատում եմ բազմալեզու կայքերի վրա, որտեղ hreflang-ը և մաքուր locale URL-ները որոշում են, թե արդյոք յուրաքանչյուր լեզվական տարբերակ կհասնի ճիշտ լսարանին։
  • Հասանելիությունը համարում եմ որակի մաս, և այն նաև օգնում է SEO-ին, որովհետև սեմանտիկ, լավ կառուցված էջերն ավելի հեշտ են հասկանալի թե՛ մարդկանց, թե՛ crawler-ների համար։

Ինչպես է աշխատում

01Core Web Vitals. LCP, INP և CLS

Core Web Vitals-ը երեք մետրիկ է, որոնցով Google-ը նկարագրում է իրական օգտատիրոջ փորձառությունը։ Largest Contentful Paint-ը (LCP) այն պահն է, երբ հայտնվում է հիմնական բովանդակությունը, սովորաբար hero պատկերը կամ վերնագիրը. լավ է 2.5 վայրկյանը կամ պակասը։ Interaction to Next Paint-ը (INP) չափում է, թե որքան արագ է էջը տեսողականորեն արձագանքում սեղմումներին, հպումներին և ստեղների սեղմումներին ամբողջ այցի ընթացքում. լավ է 200 միլիվայրկյանը կամ պակասը։ INP-ն 2024-ի մարտին փոխարինեց First Input Delay-ին։ Cumulative Layout Shift-ը (CLS) չափում է, թե որքան է բովանդակությունն անսպասելիորեն «ցատկոտում». լավ է 0.1-ը կամ պակասը։ Էջն անցնում է, երբ իրական այցերի 75%-ը բավարարում է յուրաքանչյուր շեմին՝ mobile-ի և desktop-ի համար առանձին չափված։ Դրանք ranking ազդանշան են, բայց համեստ. դրանց ավելի մեծ արժեքը conversion-ներն են և օգտատերերի վստահությունը։

  • LCP ≤ 2.5 s. բեռնում։
  • INP ≤ 200 ms. արձագանքողություն։
  • CLS ≤ 0.1. տեսողական կայունություն։
  • Գնահատվում է իրական օգտատերերի 75-րդ պերցենտիլով։

02Ինչպես շտկել յուրաքանչյուր մետրիկը

LCP-ն ունի չորս մաս՝ սերվերի պատասխանի ժամանակը (TTFB), ուշացումը մինչև browser-ը կհայտնաբերի LCP ռեսուրսը, դրա ներբեռնման ժամանակը և render-ի ուշացումը։ Շտկեք դրանք այդ հերթականությամբ. HTML-ը cache արեք edge-ում, hero պատկերը դարձրեք հայտնաբերելի HTML-ում (ոչ թե JavaScript-ով կամ CSS background-ով դրված), երբեք lazy-load մի արեք այն, տվեք fetchpriority="high" և մատուցեք AVIF կամ WebP ձևաչափով՝ ճիշտ չափսով։ INP-ն main thread-ի աշխատանքի մասին է. ուղարկեք ավելի քիչ JavaScript, երկար task-երը բաժանեք, որպեսզի browser-ը կարողանա դրանց արանքում paint անել (scheduler.yield() կամ setTimeout), ոչ հրատապ React թարմացումների համար օգտագործեք startTransition և խուսափեք ծանր third-party script-ներից։ CLS-ը նախապես պահված տարածքի մասին է. պատկերների և embed-ների վրա միշտ սահմանեք width և height կամ aspect-ratio, տարածք պահեք banner-ների և գովազդների համար և օգտագործեք համապատասխան մետրիկաներով font fallback-ներ։

html
<!-- LCP image: discoverable, eager, high priority, right size -->
<img
  src="/img/hero-1200.avif"
  srcset="/img/hero-800.avif 800w, /img/hero-1200.avif 1200w, /img/hero-1800.avif 1800w"
  sizes="(max-width: 768px) 100vw, 1200px"
  width="1200" height="675"
  fetchpriority="high"
  alt="Fresh coffee being poured into a cup" />
 
<!-- below-the-fold images -->
<img src="/img/team.avif" width="800" height="600" loading="lazy" decoding="async" alt="Our team" />

03Crawling, indexing և canonical-ներ

Որոնողական համակարգերն աշխատում են երեք քայլով՝ crawl (URL-ների ստացում), index (դրանց պահպանում և հասկացում) և rank։ robots.txt-ը կառավարում է crawling-ը, ոչ թե indexing-ը. արգելափակված URL-ը միևնույն է կարող է հայտնվել արդյունքներում, եթե այլ կայքեր հղում են դրան։ Էջը index-ից դուրս պահելու համար թույլ տվեք crawling-ը և վերադարձրեք noindex meta tag կամ X-Robots-Tag header։ Canonical link-ը որոնողական համակարգերին ասում է, թե որ URL-ն է հիմնական տարբերակը, երբ նույն բովանդակությունը հասանելի է մի քանի հասցեով, օրինակ՝ tracking պարամետրերով կամ ֆիլտրերով. դա ուժեղ հուշում է, ոչ թե հրաման։ Վերադարձրեք իրական status code-եր՝ 301 մշտական տեղափոխությունների համար, 404 կամ 410 հեռացված էջերի համար, և երբեք 200-ով «not found» էջ։ Համոզվեք, որ կարևոր բովանդակությունը սերվերում render արված HTML-ի մեջ է, ոչ թե միայն կլիենտի JavaScript-ով է բեռնվում։

text
# robots.txt
User-agent: *
Disallow: /cart
Disallow: /account
Disallow: /*?sort=
 
Sitemap: https://example.com/sitemap.xml

04Sitemap-ներ, structured data և hreflang

Sitemap-ը թվարկում է այն URL-ները, որոնք ուզում եք index անել, ճշգրիտ lastmod ամսաթվով. Google-ն անտեսում է priority-ն և changefreq-ը։ Մեկ ֆայլը պարունակում է մինչև 50,000 URL կամ 50 MB՝ չսեղմված, ուստի մեծ կայքերն օգտագործում են sitemap index։ Structured data-ն, սովորաբար JSON-LD script tag-ի մեջ, նկարագրում է էջը schema.org-ի բառապաշարով՝ Organization, Product՝ Offer-ով, BreadcrumbList, Article, Event։ Այն կարող է բացել rich result-ներ, օրինակ՝ գներ և վարկանիշներ։ Բազմալեզու կայքերի համար hreflang-ը որոնողական համակարգերին ասում է, թե որ URL-ն է որ լեզվի կամ տարածաշրջանի համար։ Յուրաքանչյուր տարբերակ պետք է թվարկի բոլոր տարբերակները, ներառյալ ինքն իրեն, հղումները պետք է լինեն փոխադարձ, իսկ x-default-ը ցույց է տալիս fallback-ը։ Օգտագործեք լեզվի կոդեր՝ en, hy, ru, ըստ ցանկության տարածաշրջանով, օրինակ՝ en-GB։

html
<link rel="canonical" href="https://example.com/en/products/espresso" />
<link rel="alternate" hreflang="en" href="https://example.com/en/products/espresso" />
<link rel="alternate" hreflang="hy" href="https://example.com/hy/products/espresso" />
<link rel="alternate" hreflang="ru" href="https://example.com/ru/products/espresso" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/products/espresso" />
 
<script type="application/ld+json">
{ "@context": "https://schema.org", "@type": "Product", "name": "Espresso beans 1 kg",
  "offers": { "@type": "Offer", "price": "24.90", "priceCurrency": "EUR",
              "availability": "https://schema.org/InStock" } }
</script>

05Պատկերների և font-երի օպտիմալացում

Պատկերները սովորաբար էջի ամենածանր մասն են։ Մատուցեք ժամանակակից ձևաչափեր (AVIF, հետո WebP), srcset-ով և sizes-ով չափսը հարմարեցրեք ցուցադրվող չափսին, սեղմեք և lazy-load արեք ամեն ինչ, որ առաջին էկրանից ներքև է։ Framework-ները օգնում են. Next.js-ի Image կոմպոնենտը ավտոմատ գեներացնում է չափսերն ու ձևաչափերը. Next.js 16-ում հին priority prop-ը փոխարինվել է preload-ով, և փաստաթղթերը LCP պատկերի համար խորհուրդ են տալիս loading="eager" կամ fetchPriority="high"։ Font-երը առաջացնում են թե՛ դանդաղ տեքստ, թե՛ layout shift։ Self-host արեք դրանք, subset արեք միայն անհրաժեշտ նիշերով (առանձին ֆայլեր լատինական, կիրիլյան կամ հայկական գրերի համար), preload արեք միայն մեկ-երկու կրիտիկական ֆայլ, օգտագործեք font-display: swap և fallback font՝ size-adjust-ով, որպեսզի swap-ը տեքստը տեղից չշարժի։ Variable font-երը կարող են մի քանի weight ֆայլ փոխարինել մեկով։

css
@font-face {
  font-family: "Brand Sans";
  src: url("/fonts/brand-sans-latin.woff2") format("woff2");
  font-display: swap;
  unicode-range: U+0000-00FF;
}
 
/* metric-matched fallback reduces layout shift on swap */
@font-face {
  font-family: "Brand Sans Fallback";
  src: local("Arial");
  size-adjust: 104%;
  ascent-override: 92%;
}

06Հասանելիության հիմունքներ (WCAG)

WCAG-ը (Web Content Accessibility Guidelines) ստանդարտն է. 2.2 տարբերակը AA մակարդակով սովորական թիրախն է, և հենց դրան են հղում այնպիսի օրենքներ, ինչպիսին European Accessibility Act-ն է, որը կիրառվում է 2025-ի հունիսից։ Հիմունքները ծածկում են իրական խնդիրների մեծ մասը։ Օգտագործեք սեմանտիկ HTML՝ մեկ h1, վերնագրեր՝ հերթականությամբ, landmark-ներ (header, nav, main, footer), button՝ գործողությունների համար և a՝ հղումների համար։ Յուրաքանչյուր պատկերի պետք է իմաստալից alt տեքստ, կամ alt="", եթե այն դեկորատիվ է։ Տեքստի կոնտրաստը պետք է լինի առնվազն 4.5:1, կամ 3:1՝ մեծ տեքստի և UI կոմպոնենտների համար։ Ամեն ինչ պետք է աշխատի ստեղնաշարով՝ տեսանելի focus ցուցիչով։ Ֆորմաներին իրական label-ներ և հստակ սխալի հաղորդագրություններ են պետք։ Սեղմվող տարրերը պետք է լինեն առնվազն 24 x 24 CSS պիքսել։ Յուրաքանչյուր էջի համար սահմանեք lang ատրիբուտը, ինչը կարևոր է screen reader-ների համար բազմալեզու կայքերում։

  • Նախ սեմանտիկ տարրեր, ARIA՝ միայն երբ HTML-ում համարժեք չկա։
  • Ստեղնաշար. Tab-ի հերթականություն, տեսանելի focus, ոչ մի «թակարդ», Escape-ը փակում է dialog-ները։
  • Կոնտրաստ՝ 4.5:1 տեքստի համար, 3:1՝ մեծ տեքստի և կառավարման տարրերի համար։
  • Թեստավորեք screen reader-ով (VoiceOver, NVDA) և 200% zoom-ով։

07Չափում. Lighthouse, PageSpeed և CrUX

Տվյալները երկու տեսակի են։ Lab data-ն գալիս է ֆիքսված սարքի և ցանցի վրա սիմուլյացված բեռնումից՝ Lighthouse Chrome DevTools-ում կամ CI-ում։ Այն կրկնելի է և հիանալի է debugging-ի համար, բայց չի կարող ուղղակիորեն չափել INP-ն. Total Blocking Time-ը նրա փոխարինող ցուցանիշն է։ Field data-ն գալիս է իրական Chrome օգտատերերից՝ Chrome UX Report-ից (CrUX), որն ամփոփում է նախորդ 28 օրը։ PageSpeed Insights-ը URL-ի համար ցույց է տալիս երկուսն էլ, իսկ Search Console-ը խմբավորում է field data-ն ամբողջ կայքով։ Ձեր սեփական մանրամասն թվերի համար ավելացրեք web-vitals գրադարանը և մետրիկաներն ուղարկեք ձեր analytics, ներառյալ, թե որ տարրն էր LCP-ն և որ ինտերակցիան էր դանդաղ։ Օպտիմալացրեք field data-ի հիման վրա, շտկումները ստուգեք lab-ում, հետո սպասեք, որ field data-ն հաստատի։

ts
import { onLCP, onINP, onCLS } from "web-vitals/attribution";
 
function send(metric: { name: string; value: number; rating: string }) {
  navigator.sendBeacon("/api/vitals", JSON.stringify(metric));
}
 
onLCP(send);
onINP(send);
onCLS(send);

Օգտագործեք, երբ

  • Յուրաքանչյուր հանրային կայք՝ առաջին sprint-ից, ոչ թե որպես թողարկման շաբաթվա շտկում։
  • E-commerce և բովանդակային կայքեր, որտեղ եկամուտը կախված է օրգանիկ որոնումից և conversion-ից։
  • Բազմալեզու կայքեր, որտեղ յուրաքանչյուր լեզու պետք է դիրքավորվի իր շուկայում։
  • Պրոդուկտներ, որոնք վաճառվում են հասանելիության կարգավորում ունեցող տարածաշրջաններում, օրինակ՝ ԵՄ-ում։

Մի՛ օգտագործեք, երբ

  • Login-ի հետևում գտնվող ներքին գործիքներին SEO աշխատանք պետք չէ, թեև դրանց միևնույն է պետք են արագագործություն և հասանելիություն։
  • Մի հետապնդեք կատարյալ Lighthouse score՝ օգտատերերին անհրաժեշտ ֆունկցիաների հաշվին. շտկեք այն, ինչ field data-ն ցույց է տալիս որպես դանդաղ։

Այլընտրանքներ

  • Next.js Metadata API

    App Router-ում կոդից գեներացնում է title-ներ, canonical-ներ, hreflang alternate-ներ, sitemap-ներ և robots։

  • WebPageTest

    Խորը lab թեստավորում՝ filmstrip-ներով, waterfall-ներով և իրական սարքերով բազմաթիվ վայրերում։

  • Real user monitoring գործիքներ

    Vercel Speed Insights-ը, Cloudflare Web Analytics-ը կամ Sentry-ն հավաքում են field Web Vitals ըստ route-ի։

  • axe DevTools / Lighthouse accessibility

    Հասանելիության ավտոմատ ստուգումներ. դրանք բռնում են խնդիրների մոտ մեկ երրորդը, ուստի ձեռքով թեստավորումը մնում է անհրաժեշտ։

Լավագույն պրակտիկաներ

  1. Կարևոր բովանդակությունը և metadata-ն render արեք սերվերում։
  2. LCP պատկերը դարձրեք հայտնաբերելի, eager և բարձր առաջնահերթությամբ. մնացածը lazy-load արեք։
  3. Տարածք պահեք յուրաքանչյուր պատկերի, embed-ի և banner-ի համար՝ layout shift-ը կանխելու համար։
  4. Բաժանեք երկար JavaScript task-երը և պարբերաբար audit արեք third-party script-ները։
  5. Ամեն էջի համար պահեք մեկ canonical URL՝ յուրաքանչյուր locale-ի համար փոխադարձ hreflang-ով։
  6. Sitemap-ները և structured data-ն գեներացրեք CMS-ից, որպեսզի դրանք երբեք չհնանան։
  7. Field Web Vitals-ին և հասանելիությանը հետևեք CI-ում, ոչ թե տարին մեկ անգամ։

Տարածված սխալներ

  • robots.txt-ով էջը որոնման արդյունքներից թաքցնելու փորձ. այն միայն արգելափակում է crawling-ը։
  • Hero պատկերի lazy-load, ինչը ուշացնում է LCP-ն։
  • hreflang tag-եր, որոնք փոխադարձ չեն կամ ցույց են տալիս redirect արված URL-ներ, ուստի որոնողական համակարգերն անտեսում են դրանք։
  • Rich result-ների համար FAQ կամ HowTo markup-ի վրա հույս դնել. Google-ը FAQ արդյունքները սահմանափակեց մի քանի հեղինակավոր կայքերով և 2023-ին հանեց HowTo արդյունքները։
  • Գեղեցկության համար focus outline-ների հեռացում, ինչը կայքը ստեղնաշարով անօգտագործելի է դարձնում։

Խորը վերլուծություն

08INP-ի ներսում՝ input delay, processing և presentation

INP-ն մոտավորապես հաղորդում է այցի ամենավատ ինտերակցիան. շատ ինտերակցիաներով էջերում յուրաքանչյուր 50-ից մեկ outlier անտեսվում է։ Յուրաքանչյուր ինտերակցիա ունի երեք մաս, և յուրաքանչյուրն ունի իր լուծումը։ Input delay-ը այն ժամանակն է, մինչև ձեր handler-ն ընդհանրապես կսկսի աշխատել, սովորաբար որովհետև մեկ այլ long task է ընթանում, օրինակ՝ hydration, tag manager կամ analytics batch։ Processing time-ը հենց ձեր event handler-ներն են։ Presentation delay-ը handler-ներից հետո մինչև հաջորդ frame-ի նկարվելը կատարվող աշխատանքն է. փոխվածի style, layout և paint, որն աճում է DOM-ի չափի և մեծ React re-render-ների հետ։ Long Animation Frames API-ն (LoAF) հաղորդում է, թե որ սկրիպտներն են frame-ը դանդաղեցրել՝ մինչև source URL և ֆունկցիա, իսկ web-vitals attribution build-ը այդ գրառումները կցում է INP-ին։ Այսպիսով՝ մասնատեք մրցակցող long task-երը input delay-ը կրճատելու համար, նախ նկարեք տեսողական արձագանքը և մնացածը հետաձգեք processing-ը կրճատելու համար, և DOM-ը փոքր պահեք ու թարմացումները՝ լոկալ, presentation delay-ը կրճատելու համար։

ts
const yieldToMain = () =>
  "scheduler" in globalThis && "yield" in scheduler
    ? scheduler.yield()
    : new Promise((resolve) => setTimeout(resolve, 0));
 
button.addEventListener("click", async () => {
  showAddedState();      // 1. visual feedback in this frame
  await yieldToMain();   // 2. let the browser paint
  await saveCart();      // 3. heavy work afterwards
  sendAnalytics();
});

09Ակնթարթային նավիգացիաներ՝ bfcache և speculation rules

Ամենաարագ էջի բեռնումն այն է, որը երբեք տեղի չի ունենում։ Back/forward cache-ը (bfcache) հիշողության մեջ պահում է էջի ամբողջական snapshot, ուստի Back-ն ու Forward-ը վերականգնում են այն ակնթարթորեն՝ առանց ցանցի և առանց JavaScript-ի կրկնակի գործարկման։ Էջը կորցնում է այդ առավելությունը, եթե գրանցում է unload handler, օգտատիրոջ հեռանալու պահին պահում է որոշ բաց կապեր, կամ բացառված է header-ներով, ինչպես Cache-Control: no-store-ը։ DevTools-ում՝ Application և Back/forward cache բաժնում, կարելի է ստուգել էջը և տեսնել, թե կոնկրետ ինչու է այն բլոկավորված։ Առաջ նավիգացիաների համար Speculation Rules API-ն թույլ է տալիս Chromium-ի վրա հիմնված բրաուզերներին prefetch կամ ամբողջությամբ prerender անել հավանական հաջորդ էջերը՝ ցուցակից կամ հղումների համընկնմամբ, eagerness կարգավորմամբ, օրինակ՝ moderate (սկսել hover-ի ժամանակ)։ Prerender արված էջը բացվելիս ցույց է տալիս գրեթե զրոյական LCP։ Մյուս բրաուզերներն անտեսում են կանոնները, ուստի սա անվտանգ progressive enhancement է։ Prerender արեք միայն side effect-եր չունեցող GET էջեր, բացառեք logout կամ add-to-cart հղումները և analytics-ը հետաձգեք մինչև էջն իրականում ցուցադրվի՝ օգտագործելով document.prerendering-ը։

html
<script type="speculationrules">
{
  "prerender": [{
    "where": { "and": [
      { "href_matches": "/*" },
      { "not": { "href_matches": "/logout" } },
      { "not": { "href_matches": "/cart/*" } },
      { "not": { "selector_matches": "[data-no-prerender]" } }
    ]},
    "eagerness": "moderate"
  }]
}
</script>

10Crawl budget և ռենդերինգ մեծ կայքերում

Մի քանի հարյուր էջով կայքը crawl է արվում ամբողջությամբ։ Հարյուր հազարավոր URL-ների դեպքում, օրինակ՝ faceted կատալոգում, crawl budget-ը դառնում է իրական. Googlebot-ը մեկ կայքի վրա ծախսում է սահմանափակ քանակով fetch-եր, որոնք կախված են նրանից, թե որքան արագ և առողջ է ձեր սերվերը և որքան պահանջարկ կա ձեր URL-ների նկատմամբ։ Կորուստը գալիս է անվերջ URL տարածքներից. ֆիլտրերի համակցություններ, սորտավորման կարգեր, session կամ tracking պարամետրեր, անվերջ օրացույցներ։ Վերահսկեք այն շերտերով։ Ակնառու հղումներ դրեք միայն այն facet էջերին, որոնք ուզում եք ինդեքսավորել։ Մյուս համակցությունները canonical արեք դեպի հիմնական էջը, կամ disallow արեք պարամետրերը robots.txt-ում, եթե դրանք նույնիսկ չպետք է fetch արվեն. հիշեք, որ բլոկավորված URL-ը չի կարող ցույց տալ իր noindex-ը։ Sitemap-ներում պահեք միայն canonical, ինդեքսավորվող URL-ներ՝ ազնիվ lastmod-ով։ Ստուգված Googlebot-ով ֆիլտրված server log-երը ցույց են տալիս, թե իրականում ուր է գնում crawling-ը։ Ռենդերինգը նույնպես կարևոր է. Google-ը կատարում է JavaScript, բայց որպես առանձին, ավելի թանկ քայլ, ուստի կոնտենտը և հղումները, որոնք հայտնվում են միայն client-side fetch-ից հետո, գտնվում են ավելի ուշ և ավելի քիչ հուսալի։

  • Արագ, կայուն պատասխանները (ցածր TTFB, քիչ 5xx սխալներ) թույլ են տալիս Googlebot-ին ավելի շատ crawl անել։
  • Դատարկ ֆիլտրերի համակցությունների համար վերադարձրեք 404՝ բարակ 200 էջի փոխարեն։
  • Search Console-ի Crawl stats հաշվետվությունը ցույց է տալիս պատասխանի կոդերը, ֆայլերի տիպերը և host-ի վիճակը։

11Accessibility checklist-ից այն կողմ՝ անուններ, դերեր և live թարմացումներ

Ավտոմատ գործիքները բռնում են WCAG խախտումների միայն մի մասը. մնացածի համար պետք է հասկանալ accessibility tree-ն։ Բրաուզերը DOM-ը վերածում է օբյեկտների ծառի, որոնցից յուրաքանչյուրն ունի role (button, link, dialog), accessible name (տեքստային բովանդակությունից, label-ից, aria-label-ից կամ aria-labelledby-ից), վիճակներ, ինչպես expanded կամ checked, և ըստ ցանկության նկարագրություն։ Screen reader-ները կարդում են այս ծառը, ոչ թե ձեր պիքսելները. ստուգեք այն DevTools-ի Accessibility պատուհանում։ Այնտեղ երևում են ավելի խորը խնդիրներ. icon button-ներ առանց անվան, custom dropdown-ներ, որոնք չեն աշխատում ստեղնաշարով, single-page նավիգացիաներ, որոնք փոխում են կոնտենտը առանց focus-ը տեղափոխելու, ուստի screen reader-ի օգտատերերը չգիտեն, որ էջը փոխվել է (տեղափոխեք focus-ը նոր h1-ի վրա կամ հայտարարեք վերնագիրը), status հաղորդագրություններ, ինչպես «added to cart»-ը, որոնք տեսողապես հայտնվում են, բայց երբեք չեն հայտարարվում։ Հայտարարությունների համար օգտագործեք live region, օրինակ՝ role='status', որն արդեն գոյություն ունի DOM-ում, նախքան դրա տեքստը փոխելը։ Native dialog էլեմենտը showModal()-ով ինքն է trap անում focus-ը և մշակում Escape-ը։ Հիմնական flow-երը ստուգեք VoiceOver-ով կամ NVDA-ով։

html
<button type="button" aria-label="Open cart" aria-expanded="false" aria-controls="cart">
  <svg aria-hidden="true" focusable="false"><!-- icon --></svg>
</button>
 
<!-- present on page load; changing its text is announced politely -->
<p role="status" class="visually-hidden" id="cart-status"></p>
 
<dialog id="cart" aria-labelledby="cart-title">
  <h2 id="cart-title">Your cart</h2>
  <!-- opened with cart.showModal(): focus is trapped, Escape closes -->
</dialog>

Իրական իրավիճակներ

01

Օրգանիկ տրաֆիկը նվազեց կայքի միգրացիայից հետո

Խնդիր

5,000 էջանոց կայքը տեղափոխվեց նոր framework՝ նոր URL-ներով։ Երեք շաբաթ անց օրգանիկ տրաֆիկը մոտ 40 տոկոսով ցածր է, և Search Console-ը ցույց է տալիս բազմաթիվ «Not found» և «Duplicate without user-selected canonical» սխալներ։

Ինչպես լուծել

Վերակառուցեք redirect map-ը հին URL-ների բոլոր աղբյուրներից. հին sitemap-ներ, analytics-ի landing էջեր և backlink հաշվետվություններ։ Յուրաքանչյուր հին URL պետք է 301 անի իր ամենամոտ նոր համարժեքին մեկ քայլով՝ առանց շղթաների և առանց գլխավոր էջ կատարվող զանգվածային redirect-ների։ Crawl արեք հին URL-ների ցուցակը site crawler-ով՝ status code-երը ստուգելու համար։ Հետո ստուգեք staging-ից մնացած հետքերը. noindex tag-եր, staging host-երին ուղղված canonical-ներ, սահմանափակող robots.txt։ Թարմացրեք hreflang-ը և ներքին հղումները, որպեսզի դրանք ուղղակիորեն ցույց տան նոր URL-ները։ Ներկայացրեք նոր sitemap-ը և հիմնական էջերի վրա օգտագործեք URL Inspection՝ ռենդերված HTML-ը հին կայքի հետ համեմատելու համար։ Վերականգնումը սովորաբար շաբաթներ է տևում, ուստի հետևեք դրան ըստ բաժինների։

02

Lighthouse-ն ասում է 95, Search Console-ն ասում է Poor

Խնդիր

Lab թեստերը ցույց են տալիս գերազանց միավորներ, բայց Search Console-ը հաղորդում է վատ LCP և INP մոբայլ URL-ների մեծ մասի համար։ Թիմը շփոթված է, թե որ թվին վստահել։

Ինչպես լուծել

Վստահեք field տվյալներին, հետո բացատրեք տարբերությունը։ Lab թեստերը բեռնում են մեկ մաքուր էջ սիմուլյացված սարքի վրա. իրական օգտատերերը բերում են դանդաղ հեռախոսներ, consent banner-ներ, A/B testing սկրիպտներ, logged-in անհատականացում և սառը cache։ Ավելացրեք web-vitals attribution build-ը և analytics ուղարկեք LCP էլեմենտը, INP ինտերակցիայի թիրախը և պատասխանատու սկրիպտը։ Հաճախ field-ում LCP էլեմենտը cookie banner-ն է կամ ըստ երկրի տարբեր hero, իսկ TTFB-ն դանդաղ է, որովհետև անհատականացված էջերը շրջանցում են edge cache-ը։ Վերարտադրեք իրական պայմանները lab-ում՝ throttling-ով, consent banner-ով և միացված third-party tag-երով։ Ուղղեք գլխավոր պատճառը, հետո սպասեք, մինչև CrUX-ի 28-օրյա պատուհանը թարմանա, նախքան արդյունքը գնահատելը։

03

Faceted կատեգորիաների էջերը ողողում են ինդեքսը

Խնդիր

E-commerce կայքն ունի մոտ 300 կատեգորիա, բայց Search Console-ը ցույց է տալիս հարյուր հազարավոր հայտնաբերված URL-ներ, հիմնականում ֆիլտրերի և սորտավորման համակցություններ։ Նոր ապրանքները որոնման մեջ հայտնվելու համար շաբաթներ են պահանջում։

Ինչպես լուծել

Որոշեք, թե որ facet-ներն ունեն իրական որոնման պահանջարկ, օրինակ՝ կատեգորիա գումարած բրենդ, և տվեք դրանց մաքուր ստատիկ URL-ներ, եզակի վերնագրեր և տեքստ, ներքին հղումներ և sitemap գրառումներ։ Մնացած բոլոր համակցությունների համար ֆիլտրերը պահեք օգտագործելի, բայց canonical արեք դեպի հիմնական կատեգորիան, և robots.txt-ում disallow արեք զուտ sort, view և pagination-size պարամետրերը, որպեսզի Googlebot-ը դադարի դրանք fetch անել։ Նույն URL-ի վրա մի համատեղեք robots.txt բլոկը noindex-ի հետ, որովհետև noindex-ը երբեք չի երևա։ Առանց ապրանքների համակցությունների համար վերադարձրեք 404։ Հրապարակեք առանձին ապրանքային sitemap ճշգրիտ lastmod-ով՝ հայտնաբերումն արագացնելու համար, և հետևեք Crawl stats-ին, մինչև պարամետրային URL-ների բաժինը նվազի։

Արագ ուղեցույց

Նախապես միանալ կրիտիկական third-party origin-ին
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
Yield անել main thread-ին երկար աշխատանքի ժամանակ
await scheduler.yield(); // fallback: await new Promise(r => setTimeout(r, 0))
Հետևել դանդաղ frame-երին և դրանց սկրիպտներին
new PerformanceObserver((l) => console.log(l.getEntries())).observe({ type: "long-animation-frame", buffered: true });
Հետաձգել աշխատանքը, մինչև prerender արված էջը ցուցադրվի
if (document.prerendering) document.addEventListener("prerenderingchange", init, { once: true }); else init();
Գտնել, թե ինչն է բլոկավորում bfcache-ը
DevTools > Application > Back/forward cache > Test back/forward cache
PDF-ը կամ նկարը ինդեքսից դուրս պահել
X-Robots-Tag: noindex (HTTP պատասխանի header)
Գործարկել Lighthouse-ը CI-ում
npx @lhci/cli autorun
Տեսնել էջը այնպես, ինչպես Google-ն է ռենդերել
Search Console > URL Inspection > View crawled page
Screen reader-ներին հայտարարել վիճակի փոփոխության մասին
<p role="status">Added to cart</p>
Accessible modal՝ focus trapping-ով
document.querySelector("dialog").showModal();

Իմ իրական նախագծերում

  • Ռեստորանային պատվերների պլատֆորմում և կենդանիների ապրանքների խանութում իմ կառուցած CMS-ը ներառում է SEO դաշտեր ամեն մոդելի համար և գեներացվող sitemap։
  • Սրճարանային ցանցի կայքում կարգավորեցի sitemap-ը, robots-ը, OG պատկերները և app-link association ֆայլերը, որոնք թույլ են տալիս հղումները բացել մոբայլ հավելվածում։
  • Թվային գործակալության կայքը, որի վրա աշխատել եմ, ունի sitemap-ի, robots-ի և llms.txt-ի գեներացում, ինչպես նաև SEO redirect-ներ ամբողջական վերագրումից հետո։
  • 8 լեզվով տուրերի ամրագրման պլատֆորմում իմ ներդրումը ներառում էր SEO schema markup, redirect-ներ և արագագործության աշխատանք։

Հարցեր և պատասխաններ

Q1Որո՞նք են Core Web Vitals-ը և դրանց շեմերը։

Դրանք երեքն են։ Largest Contentful Paint-ը չափում է բեռնումը՝ երբ render է լինում ամենամեծ տեսանելի տարրը, սովորաբար hero պատկերը կամ հիմնական վերնագիրը. լավ է 2.5 վայրկյանը կամ պակասը։ Interaction to Next Paint-ը չափում է արձագանքողությունը այցի բոլոր ինտերակցիաների ընթացքում. լավ է 200 միլիվայրկյանը կամ պակասը։ Այն 2024-ի մարտին փոխարինեց First Input Delay-ին։ Cumulative Layout Shift-ը չափում է բովանդակության անսպասելի շարժը. լավ է 0.1-ը կամ պակասը։ Էջն անցնում է, երբ իրական օգտատերերի 75-րդ պերցենտիլը բավարարում է յուրաքանչյուր շեմին՝ mobile-ի և desktop-ի համար առանձին։ Տվյալները գալիս են Chrome UX Report-ից՝ 28 օրվա սահող պատուհանով, ուստի բարելավումները երևում են մի քանի շաբաթում։

Q2Մեր LCP-ն 4 վայրկյան է։ Ինչպե՞ս կմոտենաք դրան։

Նախ գտնում եմ LCP տարրը և ժամանակը բաժանում չորս մասի՝ time to first byte, ռեսուրսի բեռնման ուշացում, ռեսուրսի բեռնման տևողություն և render-ի ուշացում։ Սա ցույց են տալիս PageSpeed Insights-ը և DevTools-ի Performance panel-ը։ Եթե TTFB-ն բարձր է, HTML-ը cache եմ անում CDN-ում կամ օգտագործում static կամ incremental rendering։ Եթե ուշացումն է բարձր, պատկերը հավանաբար ուշ է հայտնաբերվում՝ JavaScript-ի, CSS background-ի կամ lazy-loading-ի պատճառով, ուստի այն դնում եմ HTML-ի մեջ՝ fetchpriority high-ով։ Եթե ներբեռնումն է դանդաղ, մատուցում եմ AVIF կամ WebP՝ ճիշտ չափսով։ Եթե render-ի ուշացումն է բարձր, փնտրում եմ render-blocking CSS, font-եր կամ hydration։ Հետո հաստատում եմ field data-ով։

Q3Ինչպե՞ս եք բարելավում INP-ն React հավելվածում։

INP-ն դանդաղ է, երբ main thread-ը զբաղված է ինտերակցիայի պահին, կամ երբ դրանից հետո եկող թարմացումը թանկ է։ Դանդաղ ինտերակցիան profile եմ անում DevTools-ում և փնտրում երկար task-եր։ Շտկումները՝ ըստ ազդեցության. ավելի քիչ JavaScript ուղարկել code splitting-ի և server component-ների միջոցով, հեռացնել կամ հետաձգել ծանր third-party script-ները, անմիջական արձագանքը պահել փոքր, օրինակ՝ նախ թարմացնել կոճակի վիճակը և թանկ աշխատանքը փաթաթել startTransition-ի մեջ, երկար loop-երը կոտրել scheduler.yield()-ով, երկար ցուցակները virtualise անել և խուսափել հսկայական ծառերի re-render-ից՝ state-ը լոկալ պահելով։ web-vitals-ի attribution build-ը production-ում ասում է, թե որ տարրն ու որ փուլն է դանդաղ։

Q4Ինչպե՞ս եք կարգավորում SEO-ն բազմալեզու կայքի համար։

Յուրաքանչյուր լեզու ստանում է իր URL-ը, սովորաբար path prefix, օրինակ՝ /en/ կամ /hy/, երբեք միայն cookie կամ browser-ի լեզվի վրա հիմնված ավտոմատ redirect, որովհետև crawler-ները այդ ազդանշանները չեն ուղարկում։ Յուրաքանչյուր էջ ներառում է hreflang alternate-ներ բոլոր լեզվական տարբերակների համար, ներառյալ ինքն իրեն, գումարած x-default, և դրանք պետք է լինեն փոխադարձ և ցույց տան վերջնական, index-վող URL-ներ։ Յուրաքանչյուր տարբերակ ունի ինքն իրեն հղվող canonical. երբեք թարգմանությունները մի canonicalise արեք անգլերենին։ Title-ները, description-ները, slug-երը և structured data-ն թարգմանված են, html lang ատրիբուտը համապատասխանում է, և sitemap-ը թվարկում է յուրաքանչյուր տեղայնացված URL։ Առաջարկեք լեզվի փոխարկիչ՝ redirect պարտադրելու փոխարեն, որպեսզի և՛ օգտատերերը, և՛ crawler-ները կարողանան հասնել յուրաքանչյուր տարբերակի։

Q5Ո՞րն է տարբերությունը robots.txt-ի, noindex-ի և canonical-ի միջև։

Դրանք լուծում են տարբեր խնդիրներ։ robots.txt-ը կառավարում է crawling-ը. այն bot-երին խնդրում է չստանալ որոշ path-եր, ինչը խնայում է crawl budget-ը այնպիսի բաների վրա, ինչպիսիք են cart-ը, account-ը կամ ֆիլտրերի անվերջ համակցությունները։ Այն էջերը index-ից չի հեռացնում, և եթե էջը արգելափակեք, bot-ը չի կարող նույնիսկ տեսնել դրա վրայի noindex-ը։ noindex-ը, որպես meta tag կամ X-Robots-Tag header, կառավարում է indexing-ը. էջը կարող է crawl արվել, բայց արդյունքներում չի հայտնվի։ Canonical-ը միավորում է կրկնօրինակները. այն որոնողական համակարգերին ասում է, թե որ URL-ը պետք է ներկայացնի մի քանի URL-ով հասանելի բովանդակությունը, և ազդանշանները փոխանցում է այդ URL-ին։ Դա ուժեղ հուշում է, ոչ թե երաշխիք։

Q6Հասանելիության ո՞ր հիմունքները պետք է ամեն կայք ճիշտ անի։

Սկսում եմ այն խնդիրներից, որոնք արգելափակում են ամենաշատ մարդկանց։ Սեմանտիկ HTML՝ իրական կոճակներ և հղումներ, վերնագրեր՝ հերթականությամբ, landmark-ներ և մեկ main։ Ստեղնաշարով հասանելիություն՝ յուրաքանչյուր կառավարման տարր հասանելի է Tab-ով, տեսանելի focus ցուցիչ, ոչ մի keyboard trap, dialog-ներ, որոնք տեղափոխում և վերադարձնում են focus-ը։ Տեքստային այլընտրանքներ՝ իմաստալից alt տեքստ տեղեկատվական պատկերների համար և դատարկ alt՝ դեկորատիվների համար։ Կոնտրաստ՝ առնվազն 4.5:1 սովորական տեքստի համար։ Label-ներով ֆորմայի դաշտեր, որոնց սխալները բացատրված են տեքստով, ոչ թե միայն գույնով։ Ճիշտ lang ատրիբուտներ, reduced motion-ի հարգում և layout-ներ, որոնք աշխատում են 200% zoom-ով։ Հետո գործարկում եմ axe կամ Lighthouse և հիմնական flow-երը թեստավորում screen reader-ով, որովհետև ավտոմատ գործիքները բաց են թողնում խնդիրների մեծ մասը։

Սենիոր մակարդակի հարցեր

S1Ինչպե՞ս են ռենդերինգի ընտրությունները (SSG, ISR, SSR, client rendering) ազդում SEO-ի և Web Vitals-ի վրա։

Static generation-ը և incremental regeneration-ը տալիս են լավագույն ելակետը. HTML-ը նախապես կառուցված է և cache արված edge-ում, ուստի TTFB-ն ցածր է, LCP-ն շուտ է սկսվում, և crawler-ները ստանում են ամբողջական կոնտենտ։ Փոխզիջումը թարմությունն է, որը լուծվում է revalidation-ով։ Յուրաքանչյուր request-ի համար server rendering-ը հարմար է անհատականացված կամ արագ փոփոխվող էջերի համար, բայց TTFB-ն այժմ կախված է ձեր ամենադանդաղ data call-ից, ուստի նախ stream եմ անում shell-ը և ագրեսիվ cache եմ անում տվյալները։ Զուտ client rendering-ը ամենաթույլն է SEO-ի և LCP-ի համար. կոնտենտը հայտնվում է միայն JavaScript-ի ներբեռնումից, գործարկումից և fetch-ից հետո, և որոնողական համակարգերը այն ռենդերում են հետաձգված քայլով։ Server-ով ռենդերվող յուրաքանչյուր տարբերակ արժե նաև hydration, որը վնասում է INP-ին, եթե մեծ է, ուստի server component-ներով ինտերակտիվ island-ները փոքր եմ պահում։ Գործնականում խառնում եմ ըստ route-ի. static՝ marketing և կատալոգի էջերի համար, server rendering՝ որոնման և հաշվի էջերի համար, client-only՝ մասնավոր dashboard-ների համար։

S2Ինչպե՞ս եք կանխում, որ մեծ թիմը դանդաղորեն վատացնի արտադրողականությունը։

Արտադրողականությունը տեսանելի և տեր ունեցող դարձնելով, ոչ թե միանգամյա աուդիտով։ Budget-ներ եմ սահմանում ըստ էջի տիպի. field LCP, INP և CLS 75-րդ percentile-ում, գումարած JavaScript-ի և նկարների քաշը։ CI-ում Lighthouse CI-ը գործարկվում է preview deployment-ների վրա assertion-ներով, իսկ bundle-size ստուգումը յուրաքանչյուր pull request-ի վրա comment է թողնում delta-ով։ Production-ում web-vitals գրադարանով real-user monitoring-ը սնուցում է dashboard՝ բաժանված ըստ էջի տիպի, սարքի և երկրի, alert-ներով, երբ 75-րդ percentile-ը հատում է շեմը։ Third-party սկրիպտները ավելացնելուց առաջ պետք է review և տեր ունենան, որովհետև tag-երը ամենատարածված լուռ regression-ն են։ Փոխզիջումը feature թիմերի համար որոշակի շփումն է. այն փոքր եմ պահում՝ budget-ները սկզբում դարձնելով զգուշացումներ և բլոկավորելով միայն հստակ, չափելի regression-ների դեպքում։

S3Ինչպե՞ս եք աշխատում third-party սկրիպտների հետ, ինչպիսիք են tag manager-ները, chat widget-ները և A/B testing-ը։

Յուրաքանչյուրին վերաբերվում եմ որպես տեր ունեցող ծախսի։ Նախ՝ գույքագրում. ինչ է բեռնվում, որտեղից, քանի կիլոբայթ և որքան main-thread ժամանակ՝ օգտագործելով Performance վահանակը և network waterfall-ը։ Հեռացրեք այն ամենը, ինչն այժմ տեր չունի։ Մնացածի համար ընտրեք բեռնման ռազմավարություն։ Analytics-ը և tag manager-ները բեռնվում են էջի ինտերակտիվ դառնալուց հետո։ Chat widget-ները բեռնվում են ըստ մտադրության՝ թեթև placeholder կոճակով, որը կտտոցի ժամանակ բեռնում է իրական widget-ը։ Client-side A/B testing-ը ամենադժվարն է, որովհետև anti-flicker snippet-ները թաքցնում են էջը և ոչնչացնում LCP-ն, ուստի նախընտրում եմ server-side կամ edge էքսպերիմենտներ։ Consent management-ը պետք է արագ լինի, որովհետև հաճախ այն դառնում է LCP էլեմենտը։ Որոշ թիմեր սկրիպտները տեղափոխում են web worker՝ Partytown-ի նման գործիքով. դա կարող է օգնել, բայց համատեղելիությունը տարբեր է, ուստի յուրաքանչյուր tag ստուգում եմ։

Բառարան

Input delay
Ժամանակը օգտատիրոջ ինտերակցիայի և այն պահի միջև, երբ դրա event handler-ը սկսում է աշխատել։
Presentation delay
Ժամանակը event handler-ների ավարտից մինչև բրաուզերի կողմից հաջորդ frame-ի նկարվելը։
Long Animation Frame (LoAF)
Բրաուզերային API, որը հաղորդում է 50 ms-ից երկար տևած frame-երը՝ դրանք առաջացրած սկրիպտներով։
bfcache
Back/forward cache. հիշողության մեջ snapshot, որը Back և Forward նավիգացիաները դարձնում է ակնթարթային։
Speculation rules
JSON սկրիպտ, որը թույլ է տալիս Chromium բրաուզերներին prefetch կամ prerender անել այն էջերը, որոնք օգտատերը հավանաբար կբացի։
Crawl budget
Քանի URL է որոնողական համակարգը պատրաստ և ունակ fetch անել կայքից որոշակի ժամանակում։
Faceted navigation
Ֆիլտրերի վրա հիմնված դիտում (չափ, գույն, բրենդ), որը կարող է առաջացնել URL համակցությունների հսկայական քանակ։
Accessibility tree
Բրաուզերի էջի մոդելը օժանդակ տեխնոլոգիաների համար՝ դերեր, անուններ, վիճակներ և կապեր։
Accessible name
Տեքստը, որը screen reader-ը հայտարարում է էլեմենտի համար՝ հաշվարկված բովանդակությունից, label-ներից կամ ARIA-ից։
Live region
Էլեմենտ, որի տեքստի փոփոխություններն ավտոմատ հայտարարվում են, օրինակ՝ role='status' կամ aria-live։

Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։

Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։