Integrace s Vite

Moderní JavaScriptové aplikace vyžadují sofistikované build nástroje. Nette Assets poskytuje prvotřídní integraci s Vite, nástrojem pro frontend build nové generace. Získejte bleskově rychlý vývoj s Hot Module Replacement (HMR) a optimalizované produkční buildy bez potíží s konfigurací.

  • Nulová konfigurace – automatické propojení mezi Vite a PHP šablonami
  • Kompletní správa závislostí – jeden tag zpracovává všechny assety
  • Hot Module Replacement – okamžité aktualizace JavaScriptu a CSS
  • Optimalizované produkční buildy – code splitting a tree shaking

Nette Assets se bezproblémově integruje s Vite, takže získáte všechny tyto výhody, zatímco budete psát své šablony jako obvykle.

Nastavení Vite

Pojďme nastavit Vite krok za krokem. Nebojte se, pokud jste v build nástrojích noví – vše vysvětlíme!

Krok 1: Instalace Vite

Nejprve nainstalujte Vite a Nette plugin do vašeho projektu:

npm install -D vite @nette/vite-plugin

Tím se nainstaluje Vite a speciální plugin, který pomáhá Vite perfektně fungovat s Nette.

Krok 2: Struktura projektu

Standardní přístup je umístit zdrojové soubory assetů do složky assets/ v kořenovém adresáři projektu a zkompilované verze do www/assets/:

web-project/
├── assets/                   ← zdrojové soubory (SCSS, TypeScript, zdrojové obrázky)
│   ├── public/               ← statické soubory (kopírovány tak, jak jsou)
│   │   └── favicon.ico
│   ├── images/
│   │   └── logo.png
│   ├── app.js                ← hlavní vstupní bod
│   └── style.css             ← vaše styly
└── www/                      ← veřejný adresář (document root)
	├── assets/               ← zde budou zkompilované soubory
	└── index.php

Složka assets/ obsahuje vaše zdrojové soubory – kód, který píšete. Vite tyto soubory zpracuje a umístí zkompilované verze do www/assets/.

Krok 3: Konfigurace Vite

Vytvořte soubor vite.config.ts v kořenovém adresáři projektu. Tento soubor říká Vite, kde má najít vaše zdrojové soubory a kam má umístit zkompilované.

Nette Vite plugin přichází s chytrými výchozími nastaveními, která zjednodušují konfiguraci. Předpokládá, že vaše front-end zdrojové soubory jsou v adresáři assets/ (možnost root) a zkompilované soubory jdou do www/assets/ (možnost outDir). Potřebujete pouze specifikovat vstupní bod:

import { defineConfig } from 'vite';
import nette from '@nette/vite-plugin';

export default defineConfig({
	plugins: [
		nette({
			entry: 'app.js',
		}),
	],
});

Plugin pod kapotou kromě root a outDir nastavuje ještě několik dalších voleb Vite, aby vše do sebe zapadlo: base na '' (assety se servírují přímo z document rootu), build.manifest na true (aby Nette Assets uměly namapovat hashované názvy souborů) a build.assetsDir na '' (zkompilované soubory jdou přímo do outDir, bez podsložky static/). Kteroukoli z nich můžete přepsat.

Výchozí outDir (www/assets) vyžaduje, aby adresář www/ už existoval. Pokud neexistuje, plugin skončí chybou „The output directory … does not exist“.

Pokud chcete zadat jiný název adresáře pro sestavení vašich assetů, budete muset změnit několik možností:

export default defineConfig({
	root: 'assets', // kořenový adresář zdrojových assetů

	build: {
		outDir: '../www/assets',  // kam jdou zkompilované soubory
	},

	// ... další konfigurace ...
});

Cesta outDir je považována za relativní k root, proto je na začátku ../.

Krok 4: Konfigurace Nette

Řekněte Nette Assets o Vite ve vašem common.neon:

assets:
	mapping:
		default:
			type: vite      # říká Nette, aby použilo ViteMapper
			path: assets

Krok 5: Přidání skriptů

Přidejte tyto skripty do vašeho package.json:

{
	"scripts": {
		"dev": "vite",
		"build": "vite build"
	}
}

Nyní můžete:

  • npm run dev – spustí vývojový server s hot reloadingem
  • npm run build – vytvoří optimalizované produkční soubory

Vstupní body

Vstupní bod je hlavní soubor, kde začíná vaše aplikace. Z tohoto souboru importujete další soubory (CSS, JavaScript moduly, obrázky), čímž vytváříte strom závislostí. Vite sleduje tyto importy a vše zabalí dohromady.

Příklad vstupního bodu assets/app.js:

// Importovat styly
import './style.css'

// Importovat JavaScript moduly
import netteForms from 'nette-forms';
import naja from 'naja';

// Inicializovat vaši aplikaci
netteForms.initOnLoad();
naja.initialize();

V šabloně můžete vložit vstupní bod následovně:

{asset 'app.js'}

Nette Assets automaticky generuje všechny potřebné HTML tagy – JavaScript, CSS a jakékoli další závislosti.

Více vstupních bodů

Větší aplikace často potřebují samostatné vstupní body:

export default defineConfig({
	plugins: [
		nette({
			entry: [
				'app.js',      // veřejné stránky
				'admin.js',    // administrační panel
			],
		}),
	],
});

Použijte je v různých šablonách:

{* Na veřejných stránkách *}
{asset 'app.js'}

{* V administračním panelu *}
{asset 'admin.js'}

Důležité: Zdrojové vs. zkompilované soubory

Je klíčové pochopit, že v produkci můžete načíst pouze soubory, které Vite zpřístupní – buď přes svůj manifest, nebo tím, že je beze změny zkopíruje z veřejné složky:

  1. Vstupní body definované v entry (včetně modulů, které dynamicky importují) a assety odkazované z JavaScriptu nebo CSS (obrázky, fonty, …) – to vše je zaznamenáno v manifestu
  2. Soubory z adresáře assets/public/ – ty v manifestu nejsou; kopírují se beze změny a {asset} je najde filesystémovým fallbackem

Nemůžete načítat pomocí {asset} libovolné soubory z assets/ – pokud soubor není nikde odkazován, nebude zkompilován. Pokud chcete, aby Vite věděl o dalších assetech, můžete je přesunout do veřejné složky.

Vezměte prosím na vědomí, že Vite ve výchozím nastavení vloží všechny assety menší než 4KB, takže tyto soubory nebudete moci přímo odkazovat. (Viz dokumentace Vite).

{* ✓ Toto funguje – je to vstupní bod *}
{asset 'app.js'}

{* ✓ Toto funguje – je to v assets/public/ *}
{asset 'favicon.ico'}

{* ✗ Toto nebude fungovat – náhodný soubor v assets/ *}
{asset 'components/button.js'}

Vývojový režim

Vývojový režim je zcela volitelný, ale po aktivaci poskytuje značné výhody. Hlavní výhodou je Hot Module Replacement (HMR) – okamžitě vidíte změny bez ztráty stavu aplikace, což činí vývoj mnohem plynulejším a rychlejším.

Vite je moderní build nástroj, který činí vývoj neuvěřitelně rychlým. Na rozdíl od tradičních bundlerů Vite během vývoje posílá váš kód přímo do prohlížeče, což znamená okamžitý start serveru bez ohledu na velikost vašeho projektu a bleskově rychlé aktualizace.

Spuštění vývojového serveru

Spusťte vývojový server:

npm run dev

Uvidíte:

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose

Nechte tento terminál otevřený během vývoje.

Dokud dev server běží, plugin zapisuje malý signální soubor www/assets/.vite/nette.json s jeho URL. Nette Assets na straně PHP tento soubor čtou a přepnou na načítání z dev serveru, když jsou splněny obě podmínky:

  1. Vite dev server běží (signální soubor existuje), a
  2. vaše Nette aplikace je v debug režimu.

Výsledek:

{asset 'app.js'}
{* Ve vývoji: <script src="http://localhost:5173/@vite/client" type="module"></script>
              <script src="http://localhost:5173/app.js" type="module"></script> *}
{* V produkci: <script src="/assets/app-4f3a2b1c.js" type="module" crossorigin></script> *}

Není potřeba žádná konfigurace – prostě to funguje! Pokud chcete detekci vypnout nebo zadat URL dev serveru ručně, podívejte se na volbu devServer.

Signální soubor je uložen v www/assets/.vite/nette.json (hned vedle produkčního manifest.json). Přejmenovat ho můžete volbou pluginu infoFile, která má výchozí hodnotu .vite/nette.json. Pokud Nette běžící dev server nezachytí, zkontrolujte, že tento soubor existuje a odkazuje na správnou URL.

Práce na různých doménách

Pokud váš vývojový server běží na něčem jiném než localhost (například myapp.local), můžete narazit na problémy s CORS (Cross-Origin Resource Sharing). CORS je bezpečnostní funkce ve webových prohlížečích, která ve výchozím nastavení blokuje požadavky mezi různými doménami. Když vaše PHP aplikace běží na myapp.local, ale Vite běží na localhost:5173, prohlížeč je vnímá jako různé domény a blokuje požadavky.

Máte dvě možnosti, jak to vyřešit:

Možnost 1: Konfigurace CORS

Nejjednodušší řešení je povolit cross-origin požadavky z vaší PHP aplikace:

export default defineConfig({
	// ... další konfigurace ...

	server: {
		cors: {
			origin: 'http://myapp.local',  // URL vaší PHP aplikace
		},
	},
});

Možnost 2: Spusťte Vite na vaší doméně

Dalším řešením je nechat Vite běžet na stejné doméně jako vaše PHP aplikace.

export default defineConfig({
	// ... další konfigurace ...

	server: {
		host: 'myapp.local',  // stejné jako vaše PHP aplikace
	},
});

Ve skutečnosti i v tomto případě musíte nakonfigurovat CORS, protože dev server běží na stejném hostiteli, ale na jiném portu. V tomto případě je však CORS automaticky konfigurován Nette Vite pluginem.

Vývoj s HTTPS

Pokud vyvíjíte na HTTPS, potřebujete certifikáty pro váš Vite vývojový server. Nejjednodušší způsob je použití pluginu, který automaticky generuje certifikáty:

npm install -D vite-plugin-mkcert

Zde je, jak to nakonfigurovat v vite.config.ts:

import mkcert from 'vite-plugin-mkcert';

export default defineConfig({
	// ... další konfigurace ...

	plugins: [
		mkcert(),  // automaticky generuje certifikáty a povolí https
		nette(),
	],
});

Všimněte si, že pokud používáte konfiguraci CORS (možnost 1 výše), musíte aktualizovat URL původu tak, aby používala https:// namísto http://.

Vývoj s Dockerem

Když spouštíte Vite uvnitř Docker kontejneru, je potřeba ohlídat dvě věci: prohlížeč na vašem počítači musí být schopen dosáhnout na vývojový server a Vite musí rozpoznat změny souborů přes hranici kontejneru.

Nejprve z kontejneru zpřístupněte port Vite a navažte vývojový server na všechna rozhraní, aby byl dostupný zvenčí kontejneru:

export default defineConfig({
	// ... další konfigurace ...

	plugins: [
		nette(),
	],
	server: {
		host: '0.0.0.0',      // naslouchá na všech rozhraních (v kontejneru nutné)
		port: 5173,           // musí odpovídat zpřístupněnému portu
		strictPort: true,     // raději selže, než by zvolil jiný port
		watch: {
			usePolling: true, // zapněte, pokud se změny souborů na připojených svazcích nedetekují
		},
	},
});

Plugin zapíše URL vývojového serveru do nette.json pro PHP stranu. Protože host: '0.0.0.0' není použitelný v prohlížeči (u každého assetu přesměruje na localhost), plugin ho v této URL automaticky přepíše na localhost, takže se assety načtou správně.

Pokud aplikaci otevíráte na vlastní doméně místo localhost, nastavte host option pluginu na tuto doménu:

	plugins: [
		nette({ host: 'myapp.local' }),  // stejná doména jako vaše PHP aplikace
	],

Plugin pak tento host použije pro URL vývojového serveru, přidá ho mezi CORS origins a povolí ho ve Vite allowedHosts – takže to funguje bez ruční konfigurace CORS.

U složitějších nasazení – například když Vite běží za reverzní proxy, kde se veřejný host, port i protokol liší od interní adresy – nastavte ve Vite server.origin na úplnou veřejnou URL. Plugin ji respektuje a zapíše do nette.json tak, jak je, místo aby URL odvozoval z lokálního socketu:

	server: {
		origin: 'https://myapp.local:8443',  // veřejná URL, na které prohlížeč dosáhne na Vite
	},

Produkční buildy

Vytvořte optimalizované produkční soubory:

npm run build

Vite bude:

  • Minifikovat veškerý JavaScript a CSS
  • Rozdělit kód do optimálních chunků
  • Generovat hashované názvy souborů pro cache-busting
  • Vytvořit soubor manifestu pro Nette Assets

Příklad výstupu:

www/assets/
├── app-4f3a2b1c.js       # Váš hlavní JavaScript (minifikovaný)
├── app-7d8e9f2a.css      # Extrahované CSS (minifikované)
├── vendor-8c4b5e6d.js    # Sdílené závislosti
└── .vite/
	└── manifest.json     # Mapování pro Nette Assets

Hashované názvy souborů zajišťují, že prohlížeče vždy načtou nejnovější verzi.

Veřejná složka

Soubory v adresáři assets/public/ jsou kopírovány do výstupu bez zpracování:

assets/
├── public/
│   ├── favicon.ico
│   ├── robots.txt
│   └── images/
│       └── og-image.jpg
├── app.js
└── style.css

Odkazujte na ně normálně:

{* Tyto soubory jsou kopírovány tak, jak jsou *}
<link rel="icon" href={asset 'favicon.ico'}>
<meta property="og:image" content={asset 'images/og-image.jpg'}>

Pro veřejné soubory můžete použít funkce FilesystemMapperu. Volba extension se uplatní u referencí bez přípony (např. {asset 'images/og-image'} najde nejdřív og-image.webp):

assets:
	mapping:
		default:
			type: vite
			path: assets
			extension: [webp, jpg, png]  # pro reference bez přípony
			versioning: true             # Přidat cache-busting

V konfiguraci vite.config.ts můžete změnit veřejnou složku pomocí možnosti publicDir.

Dynamické importy

Vite automaticky rozděluje kód pro optimální načítání. Dynamické importy vám umožňují načítat kód pouze tehdy, když je skutečně potřeba, čímž se snižuje počáteční velikost balíčku:

// Načíst náročné komponenty na vyžádání
button.addEventListener('click', async () => {
	let { Chart } = await import('./components/chart.js')
	new Chart(data)
})

Dynamické importy vytvářejí samostatné chunky, které se načítají pouze v případě potřeby. Tomu se říká „code splitting“ a je to jedna z nejvýkonnějších funkcí Vite. Když používáte dynamické importy, Vite automaticky vytváří samostatné JavaScriptové soubory pro každý dynamicky importovaný modul.

Tag {asset 'app.js'} automaticky nepřednačítá tyto dynamické chunky. Toto je záměrné chování – nechceme stahovat kód, který by se nikdy nemusel použít. Chunky se stahují pouze při provedení dynamického importu.

Pokud však víte, že určité dynamické importy jsou kritické a budou brzy potřeba, můžete je přednačíst:

{* Hlavní vstupní bod *}
{asset 'app.js'}

{* Přednačíst kritické dynamické importy *}
{preload 'components/chart.js'}

To říká prohlížeči, aby stáhl komponentu grafu na pozadí, takže je okamžitě připravena, když je potřeba.

Podpora TypeScriptu

TypeScript funguje bez další konfigurace:

// assets/main.ts
interface User {
	name: string
	email: string
}

export function greetUser(user: User): void {
	console.log(`Hello, ${user.name}!`)
}

Odkazujte na soubory TypeScriptu normálně (jako u každého souboru musí být main.ts vstupním bodem):

{asset 'main.ts'}

Pro plnou podporu TypeScriptu jej nainstalujte:

npm install -D typescript

Další konfigurace Vite

Zde jsou některé užitečné konfigurační možnosti Vite s podrobnými vysvětleními:

export default defineConfig({
	// Kořenový adresář obsahující zdrojové assety
	root: 'assets',

	// Složka, jejíž obsah je kopírován do výstupního adresáře tak, jak je
	// Výchozí: 'public' (relativně k 'root')
	publicDir: 'public',

	build: {
		// Kam umístit zkompilované soubory (relativně k 'root')
		outDir: '../www/assets',

		// Vyprázdnit výstupní adresář před sestavením?
		// Užitečné pro odstranění starých souborů z předchozích buildů
		emptyOutDir: true,

		// Podadresář uvnitř outDir pro generované chunky a assety
		// To pomáhá organizovat výstupní strukturu
		assetsDir: 'static',

		rollupOptions: {
			// Vstupní bod(y) – může být jeden soubor nebo pole souborů
			// Každý vstupní bod se stane samostatným balíčkem
			input: [
				'app.js',      // hlavní aplikace
				'admin.js',    // administrační panel
			],
		},
	},

	server: {
		// Hostitel, na který se má dev server navázat
		// Použijte '0.0.0.0' pro vystavení do sítě
		host: 'localhost',

		// Port pro dev server
		port: 5173,

		// Konfigurace CORS pro cross-origin požadavky
		cors: {
			origin: 'http://myapp.local',
		},
	},

	css: {
		// Povolit CSS source mapy ve vývoji
		devSourcemap: true,
	},

	plugins: [
		nette(),
	],
});

Pozor na cesty ke vstupním bodům v rollupOptions.input výše: Rollup resolvuje relativní cesty vůči aktuálnímu pracovnímu adresáři (kořeni projektu), ne vůči root: 'assets'. Holé 'app.js' proto při buildu neexistuje. Buď použijte volbu pluginu entry (ta řeší cesty relativně ke root), nebo cesty pište relativně ke kořeni projektu, např. 'assets/app.js'.

To je vše! Nyní máte moderní build systém integrovaný s Nette Assets.

verze: 1.x