Інтеграція Vite

Сучасні JavaScript-додатки вимагають складних інструментів збірки. Nette Assets надає першокласну інтеграцію з Vite, інструментом збірки фронтенду нового покоління. Отримайте блискавично швидку розробку з Hot Module Replacement (HMR) та оптимізовані виробничі збірки без проблем з конфігурацією.

  • Нульова конфігурація – автоматичний міст між Vite та PHP-шаблонами
  • Повне управління залежностями – один тег обробляє всі активи
  • Гаряча заміна модулів – миттєві оновлення JavaScript та CSS
  • Оптимізовані виробничі збірки – розділення коду та tree shaking

Nette Assets бездоганно інтегрується з Vite, тому ви отримуєте всі ці переваги, пишучи свої шаблони як зазвичай.

Налаштування Vite

Давайте налаштуємо Vite крок за кроком. Не хвилюйтеся, якщо ви новачок в інструментах збірки – ми все пояснимо!

Крок 1: Встановіть Vite

Спершу встановіть Vite та плагін Nette у вашому проекті:

npm install -D vite @nette/vite-plugin

Це встановлює Vite та спеціальний плагін, який допомагає Vite ідеально працювати з Nette.

Крок 2: Структура проекту

Стандартний підхід полягає в розміщенні вихідних файлів активів у папці assets/ у корені вашого проекту, а скомпільованих версій – у www/assets/:

web-project/
├── assets/                   ← source files (SCSS, TypeScript, source images)
│   ├── public/               ← static files (copied as-is)
│   │   └── favicon.ico
│   ├── images/
│   │   └── logo.png
│   ├── app.js                ← main entry point
│   └── style.css             ← your styles
└── www/                      ← public directory (document root)
	├── assets/               ← compiled files will go here
	└── index.php

Папка assets/ містить ваші вихідні файли – код, який ви пишете. Vite обробить ці файли та помістить скомпільовані версії в www/assets/.

Крок 3: Налаштуйте Vite

Створіть файл vite.config.ts у корені вашого проекту. Цей файл вказує Vite, де шукати ваші вихідні файли та куди поміщати скомпільовані.

Плагін Nette Vite поставляється з розумними значеннями за замовчуванням, які спрощують конфігурацію. Він припускає, що ваші вихідні файли фронтенду знаходяться в каталозі assets/ (опція root), а скомпільовані файли потрапляють до www/assets/ (опція outDir). Вам потрібно лише вказати точку входу:

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

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

Якщо ви хочете вказати іншу назву каталогу для збірки ваших активів, вам потрібно буде змінити кілька опцій:

export default defineConfig({
	root: 'assets', // root directory of source assets

	build: {
		outDir: '../www/assets',  // where compiled files go
	},

	// ... other config ...
});

Шлях outDir вважається відносним до root, тому на початку є ../.

Крок 4: Налаштуйте Nette

Повідомте Nette Assets про Vite у вашому common.neon:

assets:
	mapping:
		default:
			type: vite      # tells Nette to use the ViteMapper
			path: assets

Крок 5: Додайте скрипти

Додайте ці скрипти до вашого package.json:

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

Тепер ви можете:

  • npm run dev – запустити dev-сервер з гарячою перезавантаженням
  • npm run build – створити оптимізовані файли для продакшену

Точки входу

Точка входу – це головний файл, з якого починається ваш додаток. З цього файлу ви імпортуєте інші файли (CSS, модулі JavaScript, зображення), створюючи дерево залежностей. Vite слідує цим імпортам і об'єднує все разом.

Приклад точки входу assets/app.js:

// Import styles
import './style.css'

// Import JavaScript modules
import netteForms from 'nette-forms';
import naja from 'naja';

// Initialize your application
netteForms.initOnLoad();
naja.initialize();

У шаблоні ви можете вставити точку входу наступним чином:

{asset 'app.js'}

Nette Assets автоматично генерує всі необхідні HTML-теги – JavaScript, CSS та будь-які інші залежності.

Кілька точок входу

Більші додатки часто потребують окремих точок входу:

export default defineConfig({
	plugins: [
		nette({
			entry: [
				'app.js',      // public pages
				'admin.js',    // admin panel
			],
		}),
	],
});

Використовуйте їх у різних шаблонах:

{* In public pages *}
{asset 'app.js'}

{* In admin panel *}
{asset 'admin.js'}

Важливо: Вихідні проти скомпільованих файлів

Важливо розуміти, що на продакшені ви можете завантажувати лише:

  1. Точки входу, визначені в entry
  2. Файли з каталогу assets/public/

Ви не можете завантажувати за допомогою {asset} довільні файли з assets/ – лише активи, на які посилаються файли JavaScript або CSS. Якщо на ваш файл ніде немає посилання, він не буде скомпільований. Якщо ви хочете, щоб Vite знав про інші активи, ви можете перемістити їх до публічної папки.

Зверніть увагу, що за замовчуванням Vite вбудовуватиме всі активи розміром менше 4 КБ, тому ви не зможете посилатися на ці файли безпосередньо. (Див. документацію Vite).

{* ✓ This works - it's an entry point *}
{asset 'app.js'}

{* ✓ This works - it's in assets/public/ *}
{asset 'favicon.ico'}

{* ✗ This won't work - random file in assets/ *}
{asset 'components/button.js'}

Режим розробки

Режим розробки є повністю необов'язковим, але надає значні переваги при увімкненні. Головна перевага – це Гаряча заміна модулів (HMR) – миттєве відображення змін без втрати стану програми, що робить процес розробки набагато плавніше та швидше.

Vite – це сучасний інструмент збірки, який робить розробку неймовірно швидкою. На відміну від традиційних бандлерів, Vite під час розробки подає ваш код безпосередньо в браузер, що означає миттєвий запуск сервера незалежно від розміру вашого проекту та блискавичні оновлення.

Запуск dev-сервера

Запустіть dev-сервер:

npm run dev

Ви побачите:

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

Залишайте цей термінал відкритим під час розробки.

Плагін Nette Vite автоматично виявляє, коли:

  1. Vite dev-сервер запущений
  2. Ваш Nette-додаток знаходиться в режимі налагодження

Коли обидві умови виконані, Nette Assets завантажує файли з dev-сервера Vite замість скомпільованого каталогу:

{asset 'app.js'}
{* In development: <script src="http://localhost:5173/app.js" type="module"></script> *}
{* In production: <script src="/assets/app-4a8f9c7.js" type="module"></script> *}

Конфігурація не потрібна – просто працює!

Робота на різних доменах

Якщо ваш dev-сервер працює не на localhost (наприклад, myapp.local), ви можете зіткнутися з проблемами CORS (Cross-Origin Resource Sharing). CORS – це функція безпеки у веб-браузерах, яка за замовчуванням блокує запити між різними доменами. Коли ваш PHP-додаток працює на myapp.local, а Vite – на localhost:5173, браузер розглядає їх як різні домени та блокує запити.

У вас є два варіанти вирішення цієї проблеми:

Варіант 1: Налаштуйте CORS

Найпростіше рішення – дозволити крос-доменні запити з вашого PHP-додатку:

export default defineConfig({
	// ... other config ...

	server: {
		cors: {
			origin: 'http://myapp.local',  // URL вашого PHP-додатку
		},
	},
});

Варіант 2: Запустіть Vite на вашому домені

Інше рішення – змусити Vite працювати на тому ж домені, що й ваш PHP-додаток.

export default defineConfig({
	// ... other config ...

	server: {
		host: 'myapp.local',  // те саме, що й ваш PHP-додаток
	},
});

Насправді, навіть у цьому випадку вам потрібно налаштувати CORS, оскільки dev-сервер працює на тому ж хості, але на іншому порту. Однак у цьому випадку CORS автоматично налаштовується плагіном Nette Vite.

Розробка HTTPS

Якщо ви розробляєте на HTTPS, вам потрібні сертифікати для вашого dev-сервера Vite. Найпростіший спосіб – використовувати плагін, який автоматично генерує сертифікати:

npm install -D vite-plugin-mkcert

Ось як налаштувати його в vite.config.ts:

import mkcert from 'vite-plugin-mkcert';

export default defineConfig({
	// ... other config ...

	plugins: [
		mkcert(),  // generates certificates automatically and enables https
		nette(),
	],
});

Зверніть увагу, що якщо ви використовуєте конфігурацію CORS (Варіант 1 вище), вам потрібно оновити URL походження, щоб використовувати https:// замість http://.

Продакшен збірки

Створіть оптимізовані файли для продакшену:

npm run build

Vite зробить:

  • Мініфікувати весь JavaScript та CSS
  • Розділити код на оптимальні чанки
  • Згенерувати хешовані імена файлів для обходу кешу
  • Створити файл маніфесту для Nette Assets

Приклад виводу:

www/assets/
├── app-4f3a2b1c.js       # Your main JavaScript (minified)
├── app-7d8e9f2a.css      # Extracted CSS (minified)
├── vendor-8c4b5e6d.js    # Shared dependencies
└── .vite/
	└── manifest.json     # Mapping for Nette Assets

Хешовані імена файлів гарантують, що браузери завжди завантажують найновішу версію.

Публічна папка

Файли в каталозі assets/public/ копіюються у вихідний каталог без обробки:

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

Посилайтеся на них звичайно:

{* These files are copied as-is *}
<link rel="icon" href={asset 'favicon.ico'}>
<meta property="og:image" content={asset 'images/og-image.jpg'}>

Для публічних файлів можна використовувати функції FilesystemMapper:

assets:
	mapping:
		default:
			type: vite
			path: assets
			extension: [webp, jpg, png]  # Try WebP first
			versioning: true             # Add cache-busting

У конфігурації vite.config.ts ви можете змінити публічну папку за допомогою опції publicDir.

Динамічні імпорти

Vite автоматично розділяє код для оптимального завантаження. Динамічні імпорти дозволяють завантажувати код лише тоді, коли він дійсно потрібен, зменшуючи початковий розмір бандлу:

// Load heavy components on demand
button.addEventListener('click', async () => {
	let { Chart } = await import('./components/chart.js')
	new Chart(data)
})

Динамічні імпорти створюють окремі чанки, які завантажуються лише за потреби. Це називається “розділення коду” (code splitting) і є однією з найпотужніших функцій Vite. Коли ви використовуєте динамічні імпорти, Vite автоматично створює окремі файли JavaScript для кожного динамічно імпортованого модуля.

Тег {asset 'app.js'} не попередньо завантажує ці динамічні чанки автоматично. Це навмисна поведінка – ми не хочемо завантажувати код, який може ніколи не використовуватися. Чанки завантажуються лише тоді, коли виконується динамічний імпорт.

Однак, якщо ви знаєте, що певні динамічні імпорти є критичними і знадобляться незабаром, ви можете попередньо завантажити їх:

{* Main entry point *}
{asset 'app.js'}

{* Preload critical dynamic imports *}
{preload 'components/chart.js'}

Це вказує браузеру завантажити компонент діаграми у фоновому режимі, щоб він був готовий негайно, коли це знадобиться.

Підтримка TypeScript

TypeScript працює “з коробки”:

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

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

Посилайтеся на файли TypeScript звичайно:

{asset 'main.ts'}

Для повної підтримки TypeScript встановіть його:

npm install -D typescript

Додаткова конфігурація Vite

Ось деякі корисні опції конфігурації Vite з детальними поясненнями:

export default defineConfig({
	// Root directory containing source assets
	root: 'assets',

	// Folder whose contents are copied to output directory as-is
	// Default: 'public' (relative to 'root')
	publicDir: 'public',

	build: {
		// Where to put compiled files (relative to 'root')
		outDir: '../www/assets',

		// Empty output directory before building?
		// Useful to remove old files from previous builds
		emptyOutDir: true,

		// Subdirectory within outDir for generated chunks and assets
		// This helps organize the output structure
		assetsDir: 'static',

		rollupOptions: {
			// Entry point(s) - can be a single file or array of files
			// Each entry point becomes a separate bundle
			input: [
				'app.js',      // main application
				'admin.js',    // admin panel
			],
		},
	},

	server: {
		// Host to bind the dev server to
		// Use '0.0.0.0' to expose to network
		host: 'localhost',

		// Port for the dev server
		port: 5173,

		// CORS configuration for cross-origin requests
		cors: {
			origin: 'http://myapp.local',
		},
	},

	css: {
		// Enable CSS source maps in development
		devSourcemap: true,
	},

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

Ось і все! Тепер у вас є сучасна система збірки, інтегрована з Nette Assets.

версія: 1.0