4. Qwik-компоненты
В Qwik компоненты создаются с помощью функции component$(). Символ $ — ключевой: он говорит оптимизатору Qwik, что данная функция должна быть вынесена в отдельный ленивый чанк.
Синтаксис компонента
Заголовок раздела «Синтаксис компонента»import { component$ } from '@builder.io/qwik';
export const MyComponent = component$(() => { return <div>Привет, Qwik!</div>;});Отличия от React:
component$()вместо обычной функции- Символ
$делает компонент лениво загружаемым - Компонент — это QRL (Qwik Resource Locator)
QRL — Qwik Resource Locator
Заголовок раздела «QRL — Qwik Resource Locator»QRL — это URL-ссылка на фрагмент JavaScript. Вместо передачи функции напрямую, Qwik передаёт адрес функции.
// Без Qwik оптимизатора (концептуально):const MyComponent = component$( qrl('./chunk-abc.js', 'MyComponent_0BkQ6'));
// С Qwik оптимизатором (что пишем мы):export const MyComponent = component$(() => { return <div>Hello</div>;});// Оптимизатор сам вынесет функцию в отдельный чанкProps компонента
Заголовок раздела «Props компонента»import { component$ } from '@builder.io/qwik';
interface ButtonProps { label: string; variant?: 'primary' | 'secondary'; onClick$?: () => void; // Функции в props тоже QRL!}
export const Button = component$<ButtonProps>(({ label, variant = 'primary', onClick$ }) => { return ( <button class={\`btn btn--\${variant}\`} onClick$={onClick$} > {label} </button> );});Важно: функции в props должны быть QRL (с суффиксом $ в типе PropFunction<>):
import { component$, type PropFunction } from '@builder.io/qwik';
interface CardProps { title: string; onClose$: PropFunction<() => void>; // PropFunction для колбэков}
export const Card = component$<CardProps>(({ title, onClose$ }) => { return ( <div class="card"> <h2>{title}</h2> <button onClick$={onClose$}>✕</button> </div> );});Вложенные компоненты
Заголовок раздела «Вложенные компоненты»import { component$, Slot } from '@builder.io/qwik';
// Компонент с children через <Slot />export const Panel = component$<{ title: string }>(({ title }) => { return ( <div class="panel"> <h3>{title}</h3> <div class="panel-body"> <Slot /> {/* Здесь рендерятся children */} </div> </div> );});
// Использование:export const App = component$(() => { return ( <Panel title="Мой панель"> <p>Это контент внутри панели</p> </Panel> );});Именованные слоты
Заголовок раздела «Именованные слоты»export const Layout = component$(() => { return ( <div> <header> <Slot name="header" /> {/* Именованный слот */} </header> <main> <Slot /> {/* Дефолтный слот */} </main> </div> );});
// Использование:export const Page = component$(() => { return ( <Layout> <div q:slot="header">Навигация</div> <p>Основной контент</p> </Layout> );});Inline компоненты
Заголовок раздела «Inline компоненты»Для небольших компонентов без собственного состояния можно использовать component$ с встроенной функцией:
// Простой компонент без состоянияexport const Badge = component$<{ text: string; color?: string }>( ({ text, color = '#ac7ef4' }) => ( <span style={{ background: color, padding: '2px 8px', borderRadius: 4 }}> {text} </span> ));Жизненный цикл компонента
Заголовок раздела «Жизненный цикл компонента»В отличие от React, у Qwik-компонентов минимальный жизненный цикл:
import { component$, useSignal, useTask$, useVisibleTask$ } from '@builder.io/qwik';
export const MyComponent = component$(() => { const count = useSignal(0);
// Аналог useEffect — запускается на сервере и клиенте useTask$(({ track }) => { track(() => count.value); console.log('count changed:', count.value); });
// Запускается ТОЛЬКО в браузере (когда компонент виден) useVisibleTask$(() => { console.log('Component is visible in browser!'); return () => console.log('cleanup'); });
return <button onClick$={() => count.value++}>{count.value}</button>;});