Příručka:Kódovací konvence/JavaScript

This page is a translated version of the page Manual:Coding conventions/JavaScript and the translation is 100% complete.

Tato stránka popisuje kódovací konvence pro JavaScript v kódová základně MediaWiki. Viz také obecné konvence.

Odkazování

Jako náš nástroj pro kvalitu kódu používáme ESLint s přednastavením eslint-config-wikimedia pro kódování většiny našich stylů kódování a pravidel kvality kódu. Na stránce Integrations na eslint.org najdete mnoho textových editorů nebo IDE s pluginy, které poskytují živou zpětnou vazbu při psaní.

Konfigurace Linter

Chcete-li z analýzy vyloučit soubory nebo adresáře (např. knihovny třetích stran), můžete konfigurovat vzory ignorování v ESLint prostřednictvím souboru .eslintignore.[1] Všimněte si, že node_modules je ve výchozím nastavení vyloučen, takže většina repozitářů nemusí nastavovat žádná pravidla ignorování.

Každé repo potřebuje soubor .eslintrc.json v kořenovém adresáři úložiště. Následuje příklad konfiguračního souboru ESLint:

{
	"root": true,
	"extends": [
		"wikimedia/client",
		"wikimedia/jquery",
		"wikimedia/mediawiki"
	],
	"globals": {
		// Celoprojektové globální.
	},
	"rules": {
		// Variace pravidel pro celý projekt. Omezte na minimum.
	}
}

Živé příklady viz .eslintrc.json v MediaWiki a VisualEditoru.

Nezapomeňte nastavit "root": true, abyste předešli nechtěnému "magickému dědění" konfigurací ESLint v nesouvisejících nadřazených adresářích, které jste vy nebo server CI mohli nastavit na disku (například mezi rozšířením MediaWiki a jádrem MediaWiki nebo mezi vaším projektovým adresářem a něčím v váš domovský adresář).

Přednastavení eslint-config-wikimedia poskytuje několik profilů, ze kterých si mohou projekty vybrat, jak uznají za vhodné, jako jsou různé jazykové příchutě nebo globální prostředí běhového prostředí. Například:

  • wikimedia/client - pro kód prohlížeče v aktuálním výchozím prostředí ES (aktuálně ES6, tedy alias wikimedia/client-es6)
    • wikimedia/client-es5 - pro kód prohlížeče očekává podporu ES5
    • wikimedia/client-es6 - pro kód prohlížeče, který očekává podporu ES6
  • wikimedia/server - pro kód Node.js 10+ a očekává podporu ES2018
  • wikimedia/qunit - mix pro testy QUnit

Měli byste očekávat použití více souborů .eslintrc.json v repo pro nastavení různých environmentálních očekávání pro různé podadresáře. Tím je zajištěno, že nepoužijete náhodou metody window v kódu serveru, který očekáváte na serveru, nebo že v produkčním kódu neodkážete na QUnit. .eslintrc.json soubory v podadresářích automaticky dědí z konfigurace nadřazeného adresáře, takže musí obsahovat pouze věci, které jsou specifické pro daný adresář (example file).


Průběžná integrace

Projektům se doporučuje, aby prosadily předávání ESLint tím, že do něj zahrnou svůj npm test skript pomocí příkazu "test" do package.json. Více informací o tom najdete na Continuous integration/Entry points.

Pokud má váš projekt poměrně náročný testovací kanál, lze definovat skript npm run lint nebo dokonce npm run lint:js v package.json, aby bylo snadné spouštět pouze ty z příkazového řádku během místního vývoje (abyste nemuseli otevírat každý soubor v editoru a/nebo čekat na CI).

Chcete-li odhalit další funkce příkazového řádku z ESLint (jako je lintování jednotlivých souborů mimo textový editor nebo použití funkce --fix), definujte "eslint" jako vlastní skript v package.json bez jakéhokoli argumentu. Poté jej můžete vyvolat z příkazového řádku následovně:

# Celý adresář, rekurzivní
$ npm run eslint -- .
$ npm run eslint -- resources/

# Jeden soubor
$ npm run eslint -- resources/ext.foo/bar.js

# Ustálený
$ npm run eslint -- --fix resources/ext.foo/

Prázdné místo

Mezery

Používáme následující konvence:

  • Odsazení s tabulátory.
  • Žádné mezery na konci.
  • Oddělení jednoho bloku logicky souvisejícího kódu od druhého pomocí prázdných řádků.
  • Jedna mezera na obou stranách binárních operátorů a operátorů přiřazení.
  • Klíčová slova následovaná "(" (levá závorka) musí být oddělena jednou mezerou. To poskytuje vizuální rozlišení mezi klíčovými slovy a vyvoláním funkcí.
  • Mezi názvem funkce a levou závorkou seznamu argumentů by neměla být žádná mezera.
  • Uvnitř závorek (jako jsou příkazy if, volání funkcí a seznamy argumentů) by měla být jedna mezera.
  • Nepoužívejte operátory, jako by to byly funkce (například delete, void, typeof, new, return, ..).

Tyto a další aspekty našeho průvodce stylem jsou vynuceny pomocí ESLint.

Příklady mezer
Správně Špatně
a.foo = bar + baz;

if ( foo ) {
	foo.bar = doBar();
}

function foo() {
	return bar;
}

foo = function () {
	return 'bar';
};

foo = typeof bar;

function baz( foo, bar ) {
	return 'gaz';
}

baz( 'banana', 'pear' );

foo = bar[ 0 ];
foo = bar[ baz ];
foo = [ bar, baz ];
a.foo=bar+baz;

if( foo ){
	foo.bar = doBar () ;
}

function foo () {
	return bar;
};

foo = function() {
	return('bar');
};

foo = typeof( bar );

function baz(foo, bar) {
	return 'gaz';
}

baz('banana', 'pear');

foo = bar[0];
foo = bar[baz];
foo = [bar,baz];

Délka řádku

Řádky by neměly být delší než 80–100 znaků. Pokud se příkaz nevejde na jeden řádek, rozdělte příkaz na více řádků. Pokračování prohlášení by mělo být odsazeno o jednu úroveň navíc.

Volání funkcí a objekty by měly být buď na jednom řádku, nebo rozděleny na více řádků s jedním řádkem pro každý segment. Vyvarujte se zavírání volání funkce nebo objektu v jiném odsazení, než je jeho otevření.

Příklady zalamování řádků
Ano
// Jeden řádek
if ( mw.foo.hasBar() && mw.foo.getThis() === 'that' ) {
	return { first: 'Who', second: 'What' };
} else {
	mw.foo( 'first', 'second' );
}

// Víceřádkový (jedna komponenta na řádek)
if (
	// Oproti hlavičce odsazené o jednu úroveň.
	mw.foo.hasBar() &&
	mw.foo.getThis() === 'that' &&
	!mw.foo.getThatFrom( 'this' )
) {
    // ↖ Uzavírací závorka na stejné úrovni jako otevírání.
	return {
		first: 'Who',
		second: 'What',
		third: 'I don\'t know'
	};
} else {
	mw.foo(
		[ 'first', 'nested', 'value' ],
		'second'
	);
}
Ne
// Ne: Smíšené jednořádkové a víceřádkové
if ( mw.foo.hasBar() && mw.foo.getThis() === 'that' &&
	!mw.foo.getThatFrom( 'this' ) ) {

	// Ne: různý počet segmentů na řádek.
	return { first: 'Who', second: 'What',
		third: 'I don\'t know' };

} else {
	mw.foo( 'first', 'second',
		'third' );

	// Ne: Výpisy vypadají, jako by byly rozděleny na více řádků, ale stále jsou v řádku.
	// Visualy vypadá jako volání s jedním parametrem nebo s polem jako prvním parametrem.
	mw.foo(
		'first', 'second', 'third'
	);

	mw.foo(
		[ 'first', 'nested', 'value' ], 'second'
	);
}

Struktura

Ve zkratce:

// Proměnné, které mají doslovné a levné počáteční hodnoty
const baz = 42;
const quux = 'apple';

// Lokální funkce
function local( x ) {
	return x * quux.length;
}

// Hlavní prohlášení
const foo = local( baz );
const bar = baz + foo;

Uzavření

Pokud balíček modulů nelze nebo z jiného důvodu ještě není registrován pomocí souborů balíčku , pak by jeho jednotlivé soubory JavaScriptu měly mít kolem kódu uzávěr na úrovni souboru.[2] To dává kódu jeho vlastní rozsah a zabraňuje úniku proměnných z nebo do jiných souborů, včetně režimu ladění, a způsobem, který je srozumitelný pro statickou analýzu. Tento vzor je známý jako ihned vyvolaný funkční výraz (nebo "iffy").[3]

U souborů balíčků to není potřeba, protože se spouštějí jako soubor "modul" spíše než soubor "script", který má přirozeně svůj vlastní lokální rozsah. ESLint by měl být také nakonfigurován odpovídajícím způsobem (nastavte "parserOptions": { "sourceType": "commonjs" }, jako v tomto příkladu).

Deklarování

Proměnné musí být deklarovány před použitím. Každé zadání musí být na samostatném řádku. Proměnné mohou být deklarovány blízko nebo při jejich prvním přiřazení.

const waldo = 42;
const quux = 'apple';

let foo, bar;
const flob = [ waldo ];
if ( isFound( waldo ) ) {
    foo = 1;
    bar = waldo * 2;
} else {
    foo = 0;
    bar = 0;
}

for ( let i = 0; i < flob.length; i++ ) {
    // ...
}

Funkce by měly být deklarovány před použitím. V těle funkce by deklarace funkcí měly následovat po deklaracích proměnných a před jakýmikoli hlavními příkazy.

Komentáře

Komentáře by měly být na samostatném řádku a měly by být nad kódem, který popisují.

V rámci komentáře by měla být úvodní syntaxe (např. lomítko-lomítko nebo lomítko-hvězda) oddělena od textu jednou mezerou a text by měl začínat velkým písmenem. Pokud je komentář platnou větou, měla by být na jejím konci umístěna tečka.

Používejte řádkové komentáře (// foo) ve funkcích a dalších blocích kódu (včetně víceřádkových komentářů).

Používejte blokové komentáře (/* foo */) pouze pro bloky dokumentace. To pomáhá udržovat konzistentní formátování vkládaných komentářů (např. ne některé jako bloky a některé jako víceřádkové komentáře nebo nutnost převádět z jednoho na druhý). Vyhnete se také matoucím dokumentačním nástrojům. Usnadňuje také deaktivaci částí kódu během vývoje pouhým posunutím zápisu koncového komentáře o několik řádků dolů, aniž by byl zkratován vloženým blokovým komentářem.

Buďte v komentářích liberální a nebojte se kvůli tomu velikosti souboru. Veškerý kód je před odesláním automaticky minimalizován o ResourceLoader .

Komentáře k dokumentaci

  • Text v blocích volného tvaru by měl být velkými a malými písmeny (např. popis metod, parametrů, návratových hodnot atd.)
  • Věty začínejte velkým písmenem.
  • V popisu pokračujte na dalším řádku, odsazeném o jednu mezeru navíc.
/**
 * Získejte uživatelské jméno.
 *
 * Vypracujte další odstavec po prvním jednořádkovém shrnutí
 * imperativní nálada.
 *
 * @param {string} foo Popis parametru, který se nachází na
 *  dalším řádku komentáře.
 * @param {number} bar
 * @return {string} Uživatelské jméno
 */

Třídy ES5

Chcete-li zdokumentovat třídu, která používá syntaxi ES5 s třídou a konstruktorem definovanými společně jako function MyClass(…) {…}, použijte:

  • tag @classdesc pro dokumentování třídy
  • tag @description pro dokumentaci konstruktoru
/**
 * @classdesc Popis třídy.
 *
 * @description Popis konstruktoru.
 *
 * @param {string} můj parametr
 * @return {string} Popis
 */

Třídy ES6

Chcete-li zdokumentovat třídu, která používá syntaxi ES6 s konstruktorem definovaným pomocí constructor(), použijte samostatné komentáře k dokumentaci třídy a konstruktoru.

/**
 * Popis třídy.
 */
class myClass {
	/**
	 * Popis konstruktoru.
	 *
     * @param {string} můj parametr
     * @return {string} Popis
	 */
	constructor() {...}
}

Vygenerovaná dokumentace

K vytvoření dokumentace použijte JSDoc (viz doc.wikimedia.org). Chcete-li nastavit a publikovat dokumentaci JSDoc, podívejte se na stránku JSDoc .

Rovnost

  • Použijte operátory přísné rovnosti (=== a !==) místo (volné) rovnosti (== a !=). Ten druhý dělá typ nátlaku.
  • Nepoužívejte Yodovy podmínky.

Kontroly typů

  • string: typeof val === 'string'
  • number: typeof val === 'number'
  • boolean: typeof val === 'boolean'
  • Function: typeof val === 'function'
  • object: val === Object( val )
  • plain object: jQuery.isPlainObject( val )
  • array: Array.isArray( val )
  • HTMLElement: obj.nodeType === Node.ELEMENT_NODE
  • null: val === null
  • undefined: val === undefined


Řetězce

Pro řetězcové literály používejte jednoduché uvozovky místo dvojitých. Pamatujte, že v JavaScriptu nejsou žádné "kouzelné uvozovky", tj. \n a \t fungují všude.

Chcete-li extrahovat část řetězce, použijte pro konzistenci metodu slice(). Vyhněte se metodám substr() nebo substring(), které jsou nadbytečné, snadno zaměnitelné a mohou mít neočekávané vedlejší účinky.[4][5][6]

Export

Použijte globální hodnoty vystavené prohlížečem (například document, location, navigator) přímo a ne jako vlastnosti objektu window. To zvyšuje důvěru v kód prostřednictvím statické analýzy a může také umožňovat další funkce IDE. Kromě globálů prohlížeče jsou bezpečné k použití pouze mw, $ a OO .

Vyhněte se vytváření nových globálních proměnných. Vyhněte se úpravám globálních prvků, které "nevlastní" váš kód. Například vestavěné globály, jako je String nebo Object, nesmí být rozšířeny o další obslužné metody a podobně by těmto globálům neměly být přiřazeny funkce související s OOjs nebo jQuery.

Chcete-li veřejně vystavit funkce pro opětovné použití, použijte module.exports ze souborů balíčku anebo přiřaďte vlastnosti v rámci hierarchie mw, např. mw.echo.Foo.

Všimněte si, že konfigurační proměnné vystavené MediaWiki musí být přístupné přes mw.config .

Prostředí

Úprava vestavěných prototypů, jako je Object.prototype, je považována za škodlivou. Toto není podporováno v kódu MediaWiki a pravděpodobně to povede k poškození nesouvisejících funkcí.

Pojmenovávání

Všechny proměnné musí být pojmenovány pomocí CamelCase začínající malým písmenem, nebo pokud proměnná představuje nějaký druh konstantní hodnoty, použijte velká písmena (s podtržítky pro oddělení).

Všechny funkce musí být pojmenovány pomocí CamelCase, obvykle začínající malým písmenem, pokud není funkce konstruktorem třídy, v takovém případě musí začínat velkým písmenem. Funkce Metod jsou preferovány se začátečním slovesem, např. getFoo() místo foo().

Zkratky

Jména, která obsahují zkratky, by měla zkratku považovat za normální slovo a podle potřeby používat pouze velké první písmeno. To platí i pro dvoupísmenné zkratky, například Id. Například getHtmlApiSource na rozdíl od "getHTMLAPISource".

jQuery

Odlište uzly DOM od objektů jQuery předponou proměnným znakem dolaru, pokud budou obsahovat objekt jQuery, např. $foo = $( '#bar' ). To pomáhá omezit chyby tam, kde podmínky používají nesprávné podmíněné kontroly, jako je if ( foo ) namísto if ( $foo.length ). Tam, kde metody DOM často vracejí hodnotu null (což je nepravda), metody jQuery vracejí prázdný objekt kolekce (které jsou, stejně jako nativní pole a další objekty v JavaScriptu, pravdivé).

npm

Při publikování samostatného projektu na npmjs.org zvažte jeho publikování pod jmenným prostorem @wikimedia. Všimněte si, že některé samostatné projekty, které jsou zaměřeny na použití mimo komunitu Wikimedia a mají dostatečně jedinečný název, v současnosti používají název balíčku bez jmenných prostorů (např. "oojs" a "visualeditor"). T239742

Vytváření prvků

Chcete-li vytvořit prostý prvek, použijte jednoduchou syntaxi ‎<tag> v konstruktoru jQuery:

$hello = $( '<div>' )
	.text( 'Hello' );

Při vytváření prvků na základě názvu značky z proměnné (která může obsahovat libovolný html):

// Načíst 'span' nebo 'div' atd.
tag = randomTagName();
$who = $( document.createElement( tag ) );

$('<a title="valid html" href="#syntax">like this</a>'); používejte pouze tehdy, když potřebujete analyzovat HTML (na rozdíl od vytváření prostého prvku).

Sbírky

Různé typy kolekcí někdy vypadají podobně, ale mají odlišné chování a mělo by se s nimi tak zacházet. Tento zmatek je většinou způsoben tím, že pole v JavaScriptu vypadají hodně jako pole v jiných jazycích, ale ve skutečnosti jsou pouze rozšířením Object. Používáme následující konvence:

Nepoužívejte smyčku for-in k opakování přes pole (na rozdíl od prostého objektu), protože for-in bude mít za následek mnoho neočekávaných chování, včetně: Klíčů jako řetězců, nestabilního pořadí iterací, indexů může přeskakovat mezery, opakování může zahrnovat jiné nečíselné znaky vlastnosti.

Úložiště

Klíče v localStorage anebo sessionStorage by měly být přístupné přes mw.storage nebo mw.storage.session.

Klíče

eys by měl začínat mw a používat Camel Case anebo spojovníky. Nepoužívejte podtržítka ani jiné oddělovače. Příklady skutečných klíčů:

  • mwuser-sessionId
  • mwedit-state-templatesUsed
  • mwpreferences-prevTab

Dejte si pozor na to, že na rozdíl od souborů cookie přes mw.cookie není ve výchozím nastavení přidána žádná wiki předpona nebo předpona cookie. Pokud se hodnoty musí lišit podle wiki, musíte ručně zahrnout wgCookiePrefix jako součást klíče.

Hodnoty

Hodnoty musí být řetězce. Pozor, pokus o uložení jiných typů hodnot se tiše přetypuje na řetězec (např. z false se stane "false").

Prostor je omezený. Kde je to možné, používejte krátké a výstižné hodnoty před objektovými strukturami. Několik příkladů:

  • Pro booleovský stav (pravda/nepravda, rozbaleno/sbaleno) použijte "1" nebo "0".
  • Hodnoty, které jsou vždy čísly, je uložte tak, jak jsou, a na cestě ven přetypujte s Number (vyhněte se parseInt).
  • Hodnoty, které jsou vždy řetězce, uložte tak, jak jsou.
  • U seznamů softwarově definovaných hodnot zvažte řetězce oddělené čárkami nebo svislou čarou, abyste snížili prostor a náklady na zpracování.
  • Pro seznamy hodnot, které mohou být vytvořeny uživatelem nebo jsou jinak složité povahy, použijte JSON.

Strategie vylučování

Pamatujte, že místní úložiště nemá ve výchozím nastavení žádnou strategii vylučování. Proto je třeba se vyhnout následujícímu:

  • Nepoužívejte jako součást názvu klíče vstup generovaný uživatelem.
  • Vyhněte se klíčům obsahujícím identifikátory entit generovaných uživateli (např. uživatelská jména, názvy kategorií, ID stránek nebo jiné proměnné poskytnuté uživatelem nebo systémem).
  • Obecně se vyhněte přístupům, které zahrnují vytváření potenciálně velkého počtu klíčů úložiště.

Pokud například funkce potřebuje uložit stav proměnné entity (např. aktuální stránky), může mít smysl použít jeden klíč pro tuto funkci jako celek a omezit uložené informace pouze na několik posledních iterací (LRU). . Mírné zvýšení nákladů na vyhledání (celý klíč namísto samostatných menších) se považuje za vhodné, protože jinak by počet klíčů nekontrolovatelně rostl.

I když klíče nezávisí na vstupu uživatele, možná budete chtít pro svou funkci použít jeden klíč, protože jinak budou mít předchozí verze vašeho softwaru uložená data, která nebudete moci vyčistit. Použitím jediného klíče jej můžete přepínat způsobem, který přirozeně nepřetrvává neznámé dílčí vlastnosti.

Použití sledovacího klíče je také anti-vzor a nevyhne se výše uvedeným problémům, protože by mohlo dojít k úniku kvůli závodním podmínkám ve webovém úložišti HTML5, které je sdíleno a není atomické mezi více otevřenými kartami prohlížeče.

Když má být funkce místního úložiště odstraněna, nezapomeňte nejprve implementovat strategii vystěhování, abyste odstranili staré hodnoty. Obvykle se mw.requestIdleCallback používá k elegantnímu vyhledání klíče a jeho odstranění. Viz T121646 pro škálovatelnější přístup.

Osobní údaje

Neukládejte do místního úložiště osobní údaje, které zůstanou, když se uživatel odhlásí nebo zavře prohlížeč. Použijte místo toho úložiště relací. Podívejte se na stránku T179752.

Asynchronní kód

Asynchronní kód by se měl řídit standardem Promise a být s ním kompatibilní.

Pokud definujete asynchronní metodu pro volání jiného kódu, můžete interně vytvořit vrácený objekt thenable pomocí $.Deferred nebo nativního Promise.

Když zavoláte asynchronní metodu, použijte pouze standardní metody kompatibilní s Promise, jako jsou then() a catch(). Vyhněte se používání metod specifických pro jQuery, jako je done() nebo fail(), které by mohly přestat fungovat bez varování, pokud metoda, kterou voláte, interně přejde z $.Deferred na nativní Promise.

Všimněte si, že ve zpětných voláních done a fail existují také jemné starší chování. Při migraci stávajícího kódu z done() na then() věnujte zvýšenou pozornost, protože to může způsobit, že kód přestane správně fungovat. Konkrétně, zpětná volání done a fail vyvolají vaše zpětné volání synchronně, pokud bylo odložené volání již vyřízeno v době, kdy připojíte zpětné volání. To znamená, že vaše zpětné volání může být vyvoláno před dokončením příkazu připojení.

Například:

function getSqrt( num ) { return $.Deferred().resolve( Math.sqrt( num ) ); }

console.log( "A" );
getSqrt( 49 ).done( ( val ) => {
    console.log( "C" ); // může být A C B nebo A B C
} );
console.log( "B" );

console.log( "A" );
getSqrt( 49 ).then( ( val ) => {
    console.log( "C" ); // vždy A B C
} );
console.log( "B" );

const y = getSqrt( 49 ).then( ( val ) => {
    console.log( y.state(), val ); // "vyřešeno", 7
} );
const x = getSqrt( 49 ).done( ( val ) => {
    console.log( x.state(), val ); // Nezachycený TypeError: x je nedefinováno
} );
const z = getSqrt( 49 );
z.done( ( val ) => {
    console.log( x.state(), val ); // "vyřešeno", 7
} );

Závěrečné poznámky

Opětovné použití modulů ResourceLoader

Neobjevujte znovu kolo. Mnoho funkcí JavaScriptu a utilit souvisejících s MediaWiki se dodává s jádrem MediaWiki, které jsou stabilní a můžete se na ně (doslova) spolehnout. Než nasadíte svůj vlastní kód, podívejte se na ResourceLoader/Core modules .

Úskalí

  • Buďte opatrní, abyste zachovali kompatibilitu s jazyky psanými zleva doprava a zprava doleva (tj. float: right nebo text-align: left), zejména při stylování textových kontejnerů. Vložení těchto deklarací do souboru CSS umožní jejich automatické převrácení pro jazyky RTL z CSSJanus na ResourceLoader .
  • Použijte přiměřeně attr() a prop().
    Přečtěte si více:
  • Konzistentní hodnoty selektoru citovat atribut: [foo="bar"] místo [foo=bar] (jqbug 8229).
  • Od jQuery 1.4 má konstruktor jQuery novou funkci, která umožňuje předat objekt jako druhý argument, například: $( '<div>', { foo: 'bar', click: () => {}, css: { .. } } );. Toto nepoužívejte. Ztěžuje sledování kódu, selhává u atributů (jako je 'size' (velikost)), které jsou také metodami, a je nestabilní kvůli tomuto míšení metod jQuery s atributy prvků. Budoucí metoda jQuery nebo plugin nazvaný "title" může převést prvek na nadpis, což znamená, že atribut title již nelze nastavit touto metodou. Buďte explicitní a volejte .attr(), .prop(), .on() atd. přímo.

Použijte CSS pro úpravu stylů mnoha prvků

Neaplikujte styl na mnoho prvků najednou. Oslabuje to výkon. Místo toho použijte společnou rodičovskou třídu (nebo jednu přidejte) a použijte CSS v souboru .css nebo .less. Díky ResourceLoaderu se to vše načte ve stejném požadavku HTTP, takže za samostatný soubor CSS nedochází k žádnému snížení výkonu. Nenastavujte CSS do inline atributů "style", nevkládejte ani prvky "style" z JS.

Poznámky pod čarou