Qwik в двух словах

Компоненты

Компоненты Qwik очень похожи на компоненты React. Это функции, которые возвращают JSX. Однако, необходимо использовать component$(...), обработчики событий должны иметь суффикс $, состояние создается с помощью useSignal(), class используется вместо className и некоторые другие отличия.

  • Компоненты всегда объявляются с помощью функции component$;
  • Компоненты могут использовать хук useSignal для создания реактивного состояния;
  • Обработчики событий объявляются с суффиксом $;
  • Для <input> событие onChange в Qwik называется onInput$;
  • JSX предпочитает атрибуты HTML: class вместо className, for вместо htmlFor;
  • Условный рендер осуществляется с помощью тернарного оператора ? и оператора &&, как и в React;
src/components/my-other-component/index.tsx
import { component$, Slot } from '@builder.io/qwik';
import type { ClassList } from '@builder.io/qwik'
 
export component$((props: { class?: ClassList }) => { // ✅
  return <div class={class}><Slot /></div>;
});
import { component$, useSignal } from '@builder.io/qwik';
 
// Другие компоненты могут быть импортированы и использованы в JSX.
import { MyOtherComponent } from './my-other-component';
 
interface MyComponentProps {
  step: number;
}
 
// Компоненты всегда объявляются с помощью функции `component$`.
export const MyComponent = component$((props: MyComponentProps) => {
  // Компоненты используют хук `useSignal` для создания реактивного состояния.
  const count = useSignal(0); // { value: 0 }
  return (
    <>
      <button
        onClick$={() => {
          // Обработчики событий имеют суффикс `$`.
          count.value = count.value + props.step;
        }}
      >
        Увеличить на {props.step}
      </button>
      <main
        class={{
          even: count.value % 2 === 0,
          odd: count.value % 2 === 1,
        }}
      >
        <h1>Счёт: {count.value}</h1>
        <MyOtherComponent class="correct-way" /> {/* ✅ */}
          {count.value > 10 && <p>Счёт больше 10</p>}
          {count.value > 10 ? <p>Счёт больше 10</p> : <p>Счёт меньше 10</p>}
        </MyOtherComponent>
      </main>
    </>
  );
});

Вывод списка элементов

Как и в React, вы можете вывести список элементов с помощью функции map, однако каждый элемент в списке должен иметь уникальное свойство key. Ключ key должен быть строкой или числом, и должен быть уникальным в пределах списка.

import { component$, useSignal } from '@builder.io/qwik';
import { US_PRESIDENTS } from './presidents';
 
export const PresidentsList = component$(() => {
  return (
    <ul>
      {US_PRESIDENTS.map((president) => (
        <li key={president.number}>
          <h2>{president.name}</h2>
          <p>{president.description}</p>
        </li>
      ))}
    </ul>
  );
});

Переиспользование обработчиков событий

Обработчики событий в элементах JSX можно переиспользовать. Это делается путём создания обработчика с помощью $(...handler...).

import { $, component$, useSignal } from '@builder.io/qwik';
 
interface MyComponentProps {
  step: number;
}
 
// Компоненты всегда объявляются с помощью функции `component$`.
export const MyComponent = component$(() => {
  const count = useSignal(0);
 
  // Обратите внимание на `$(...)` вокруг функции обработчика события.
  const inputHandler = $((event, elem) => {
    console.log(event.type, elem.value);
  });
 
  return (
    <>
      <input name="name" onInput$={inputHandler} />
      <input
        name="password"
        onInput$={inputHandler}
      />
    </>
  );
});

Проекция содержимого

Проекция содержимого осуществляется компонентом <Slot/>, который экспортируется из @builder.io/qwik. Слотам можно присваивать имена, и они будут спроецированы с помощью атрибута q:slot.

// Файл: src/components/Button/Button.tsx
import { component$, Slot } from '@builder.io/qwik';
import styles from './Button.module.css';
 
export const Button = component$(() => {
  return (
    <button class={styles.button}>
      <div class={styles.start}>
        <Slot name="start" />
      </div>
      <Slot />
      <div class={styles.end}>
        <Slot name="end" />
      </div>
    </button>
  );
});
 
export default component$(() => {
  return (
    <Button>
      <span q:slot="start">📩</span>
      Привет, мир
      <span q:slot="end">🟩</span>
    </Button>
  );
});

Правила использования хуков

Методы, начинающиеся с use, являются специальными в Qwik, например, useSignal(), useStore(), useOn(), useTask$(), useLocation() и т.д. Очень похоже на React-хуки.

  • Они могут быть вызваны только внутри component$.
  • Они могут быть вызваны только на верхнем уровне component$-а, но не внутри условия или цикла.

Стилизация

Qwik из коробки поддерживает CSS-модули, или даже tailwind, глобальный импорт CSS и ленивую загрузку локальных стилей с помощью useStylesScoped$(). Модули CSS - это рекомендуемый способ стилизации компонентов Qwik.

CSS-модули

Чтобы использовать CSS-модули, просто создайте файл .module.css. Например, src/components/MyComponent/MyComponent.module.css.

src/components/MyComponent/MyComponent.module.css
.container {
  background-color: red;
}

Затем импортируйте CSS-модуль в свой компонент.

src/components/MyComponent/MyComponent.tsx
import { component$ } from '@builder.io/qwik';
import styles from './MyComponent.module.css';
 
export default component$(() => {
  return (
    <div class={styles.container}>Привет, мир</div>
  );
});

Помните, что Qwik использует class вместо className для классов CSS.

$(...) rules

Функция $(...) и любые функции, заканчивающиеся на $, являются специальными в Qwik, например: $(), useTask$(), useVisibleTask$()... Символ $ в конце обозначает границу ленивой загрузки. Существуют некоторые правила, которые применяются к первому аргументу любой функции $. Это вообще НЕ связано с jQuery.

  • Первый аргумент должен быть импортированной переменной.
  • Первый аргумент должен быть объявленной переменной на верхнем уровне того же модуля.
  • Первый аргумент должен быть выражением любых значений.
  • Если первым аргументом является функция, то она может захватывать только переменные, которые объявлены на верхнем уровне того же модуля или значение которых сериализуемо. Сериализуемые значения включают: string, number, boolean, null, undefined, Array, Object, Date, RegExp, Map, Set, BigInt, Promise, Error, JSX nodes, Signal, Store и даже HTML-элементы.
// Допустимые примеры `$`-функций.
import { $, component$, useSignal } from '@builder.io/qwik';
import { importedFunction } from './my-other-module';
 
export function exportedFunction() {
  console.log('exported function');
}
 
export default component$(() => {
  // Первый аргумент - функция.
  const valid1 = $((event, elem) => {
    console.log(event.type, elem.value);
  });
 
  // Первым аргументом является импортированный идентификатор.
  const valid2 = $(importedFunction);
 
  // Первым аргументом является идентификатор, объявленный на верхнем уровне того же модуля.
  const valid3 = $(exportedFunction);
 
  // Первый аргумент - выражение без локальных переменных.
  const valid4 = $([1, 2, { a: 'hello' }]);
 
  // Первый аргумент - это функция, которая захватывает локальную переменную.
  const localVariable = 1;
  const valid5 = $((event) => {
    console.log('local variable', localVariable);
  });
});

Вот несколько примеров недопустимых $-функций.

import { $, component$, useSignal } from '@builder.io/qwik';
import { importedVariable } from './my-other-module';
 
export default component$(() => {
  const unserializable = new CustomClass();
  const localVariable = 1;
 
  // Первый аргумент - локальная переменная.
  const invalid1 = $(localVariable);
 
  // Первый аргумент - это функция, которая захватывает несериализуемую локальную переменную.
  const invalid2 = $((event) => {
    console.log('custom class', unserializable);
  });
 
  // Первый аргумент - это выражение, которое использует локальную переменную.
  const invalid3 = $(localVariable + 1);
 
  // Первый аргумент - это выражение, которое использует импортированную переменную.
  const invalid4 = $(importedVariable + 'hello');
});

Реактивное состояние

useSignal(initialValue?)

useSignal() является основным способом создания реактивного состояния. Сигналы могут быть общими для всех компонентов, и любой компонент или задача, считывающая сигнал (выполняющая: signal.value) будет рендериться при изменении сигнала.

// Typescript definition for `Signal<T>` and `useSignal<T>`
 
export interface Signal<T> {
  value: T;
}
 
export const useSignal: <T>(value?: T | (() => T)): Signal<T>;

useSignal(initialValue?) принимает необязательное начальное значение и возвращает объект Signal<T>. Объект Signal<T> имеет свойство value, которое можно читать и записывать. Когда компонент или задача обращается к свойству value, то автоматически создаётся подписка, поэтому при изменении value каждый компонент, задача или другой вычисляемый сигнал, который читает value, будет вычислен заново.

useStore(initialValue?)

useStore(initialValue?) похож на useSignal, за исключением того, что он создаёт реактивный javascript-объект, делая каждое свойство объекта реактивным, как и value сигнала. Под капотом useStore реализуется с помощью объекта Proxy, который перехватывает все обращения к свойствам, делая их реактивными.

// Typescript definition `useStore<T>`
 
// The `Reactive<T>` is a reactive version of the `T` type, every property of `T` behaves like a `Signal<T>`.
export interface Reactive<T extends Record<string, any>> extends T {}
 
export interface StoreOptions {
  // If `deep` is true, then nested property of the store will be wrapped in a `Signal<T>`.
  deep?: boolean;
}
export const useStore: <T>(value?: T | (() => T), options?: StoreOptions): Reactive<T>;

На практике useSignal и useStore очень похожи -- useSignal(0) === useStore({ value: 0 }) -- но useSignal в большинстве случаев предпочтительнее. Некоторые случаи использования useStore:

  • Когда вам нужна реактивность в массиве.
  • Когда вам нужен реактивный объект, к которому можно легко добавлять свойства.
import { component$, useStore } from '@builder.io/qwik';
 
export const Counter = component$(() => {
 
  // Хук `useStore` используется для создания реактивного состояния.
  const todoList = useStore(
    {
      array: [],
    },
    { deep: true }
  );
 
  // todoList.array - это реактивный массив, поэтому при его изменении компонент перерендерится.
 
  return (
    <>
      <h1>Todo List</h1>
      <ul>
        {todoList.array.map((todo) => (
          <li key={todo.id}>
            <input
              type="checkbox"
              checked={todo.completed}
              onInput$={() => {
                // todoList - реактивное состояние
                // из-за использования `deep: true`, `todo`-объект тоже реактивен,
                // поэтому мы можем изменить свойство `completed`, и компонент будет перерисован.
                todo.completed = !todo.completed;
              }}
            />
            {todo.text}
          </li>
        ))}
      </ul>
    </>
  );
});

useTask$(() => { ... })

useTask$ используется для создания асинхронной задачи. Задачи полезны для реализации побочных эффектов, выполнения тяжёлых вычислений и асинхронного кода в рамках жизненного цикла рендера. Задачи useTask$ выполняются перед первым рендером, и в последствии, когда отслеживаемый сигнал или состояние изменяются, задача выполняется заново.

import { component$, useSignal, useTask$ } from '@builder.io/qwik';
 
export const Counter = component$(() => {
  const page = useSignal(0);
  const listOfUsers = useSignal([]);
 
  // Хук `useTask$` используется для создания задачи.
  useTask$(() => {
    // Задача выполняется перед первым рендером.
    console.log('Задача, выполняемая перед первым рендером');
  });
 
  // Вы можете создавать несколько задач, и они могут быть асинхронными.
  useTask$(async (taskContext) => {
    // Поскольку мы хотим повторно запускать задачу всякий раз, когда меняется `page`,
    // нам нужно его отслеживать.
    taskContext.track(() => page.value);
    console.log('Задача выполняется перед первым рендерингом И при изменении страницы');
    console.log('Текущая страница:', page.value);
 
    // Задачи могут выполнять асинхронный код, например, получать данные.
    const res = await fetch(`https://api.randomuser.me/?page=${page.value}`);
    const json = await res.json();
 
    // Изменение значения сигнала вызовет повторный рендер.
    listOfUsers.value = json.results;
  });
 
  return (
    <>
      <h1>Страница {page.value}</h1>
      <ul>
        {listOfUsers.value.map((user) => (
          <li key={user.login.uuid}>
            {user.name.first} {user.name.last}
          </li>
        ))}
      </ul>
      <button onClick$={() => page.value++}>Следующая страница</button>
    </>
  );
});

useTask$() будет выполняться на сервере в процессе SSR, и в браузере, если компонент изначально монтируется на клиенте. Из-за этого не стоит обращаться к DOM API в задаче, так как они не будут доступны на сервере. Вместо этого следует использовать обработчики событий или useVisibleTask$() для запуска задачи только на клиенте/браузере.

useVisibleTask$(() => { ... })

useVisibleTask$ используется для создания задачи, которая выполняется сразу после того, как компонент впервые установлен в DOM. Он похож на useTask$, за исключением того, что он запускается только на клиенте, и выполняется после первого рендера. Поскольку он выполняется после рендера, можно обращаться к DOM или использовать API браузера.

import { component$, useSignal, useVisibleTask$ } from '@builder.io/qwik';
 
export const Clock = component$(() => {
  const time = useSignal(0);
 
  // Хук `useVisibleTask$` используется для создания задачи, которая сразу запускается на клиенте.
  useVisibleTask$((taskContext) => {
 
    // Поскольку useVisibleTask не отслеживает никаких сигналов, она будет запущена только один раз.
    const interval = setInterval(() => {
      time.value = new Date();
    }, 1000);
 
    // Функция `cleanup` вызывается при удалении компонента или при повторном запуске задачи.
    taskContext.cleanup(() => clearInterval(interval));
  });
 
  return (
    <>
      <h1>Часы</h1>
      <h1>Прошло секунд: {time.value}</h1>
    </>
  );
});

Поскольку Qwik НЕ выполняет Javascript в браузере до взаимодействия с пользователем, useVisibleTask$() - это единственный API, который будет запускать выполнение сразу на клиенте, вот почему это хорошее место для таких вещей, как:

  • Обращение к DOM API;
  • Инициализация библиотек, предназначенных только для браузера;
  • Запуск кода аналитик;
  • Запуск анимации или таймера.

Обратите внимание, что useVisibleTask$() не следует использовать для получения данных, поскольку она не будет выполняться на сервере. Вместо этого для получения данных следует использовать useTask$(), а затем использовать useVisibleTask$() для выполнения таких действий, как запуск анимации. Злоупотребление useVisibleTask$() может привести к низкой производительности.

Маршрутизация

Qwik поставляется с маршрутизатором на основе файлов, который похож на Next.js, но имеет несколько отличий. Маршрутизатор основан на файловой системе, точнее, на src/routes/. Создание нового файла index.tsx в src/routes/ приведет к созданию нового маршрута. Например, src/routes/home/index.tsx создаст маршрут по адресу /home/.

src/routes/home/index.tsx
import { component$ } from '@builder.io/qwik';
 
export default component$(() => {
  return <h1>Home</h1>;
});

Очень важно экспортировать компонент по умолчанию, иначе маршрутизатор не сможет его найти.

Параметры маршрута

Вы можете создавать динамические маршруты, добавляя папку с [param] в путь маршрута. Например, src/routes/user/[id]/index.tsx создаст маршрут по адресу /user/:id/. Для доступа к параметру маршрута можно использовать хук useLocation, экспортированный из @builder.io/qwik-city.

src/routes/user/[userID]/index.tsx
import { component$ } from '@builder.io/qwik';
import { useLocation } from '@builder.io/qwik-city';
 
export default component$(() => {
  const loc = useLocation();
  return (
    <main>
      {loc.isNavigating && <p>Загрузка...</p>}
      <h1>Пользователь: {loc.params.userID}</h1>
      <p>Текущий URL: {loc.url.href}</p>
    </main>
  );
});

useLocation() возвращает реактивный объект RouteLocation, что означает, что он будет ререндериться каждый раз при изменении маршрута. Объект RouteLocation имеет следующие свойства:

/**
 * Текущее местоположение маршрута, возвращаемое функцией `useLocation()`.
 */
export interface RouteLocation {
  readonly params: Readonly<Record<string, string>>;
  readonly url: URL;
  readonly isNavigating: boolean;
}

Связь с другими маршрутами

Для связи с другими маршрутами можно использовать компонент Link, экспортированный из @builder.io/qwik-city. Компонент Link принимает все свойства элемента <a> HTMLAnchorElement. Единственное отличие заключается в том, что он будет использовать маршрутизатор Qwik для SPA-навигации к маршруту вместо перехода на страницу.

src/routes/index.tsx
import { component$ } from '@builder.io/qwik';
import { Link } from '@builder.io/qwik-city';
 
export default component$(() => {
  return (
    <>
      <h1>Home</h1>
      <Link href="/about/">SPA-навигация в раздел /about/</Link>
      <a href="/about/">Переход на страницу /about/</a>
    </>
  );
});

Получение / загрузка данных

Рекомендуемый способ загрузки данных с сервера - использовать функцию routeLoader$(), экспортированную из @builder.io/qwik-city. Функция routeLoader$() используется для создания загрузчика данных, который будет выполняться на сервере перед рендером маршрута. Возврат routeLoader$() должен быть экспортирован как именованный экспорт из файла маршрута, т.е. он может быть использован только в файле index.tsx, внутри src/routes/.

src/routes/user/[userID]/index.tsx
import { component$ } from '@builder.io/qwik';
import { routeLoader$ } from '@builder.io/qwik-city';
 
// Функция `routeLoader$()` используется для создания загрузчика данных, который будет выполняться на сервере перед рендером маршрута.
// Возврат `routeLoader$()` - это пользовательский хук use, который можно использовать для доступа к данным, возвращаемым из `routeLoader$()`.
export const useUserData = routeLoader$(async (requestContext) => {
  const user = await db.table('users').get(requestContext.params.userID);
  return {
    name: user.name,
    email: user.email,
  };
});
 
export default component$(() => {
  // Хук `useUserData` будет возвращать `Signal`, содержащий данные, возвращённые из `routeLoader$()`, который будет ререндерить компонент, когда навигация изменится, и routeLoader$() будет повторно запущен.
  const userData = useUserData();
  return (
    <main>
      <h1>Данные пользователя</h1>
      <p>Имя пользователя: {userData.value.name}</p>
      <p>Email пользователя: {userData.value.email}</p>
    </main>
  );
});
 
// Экспортируемая функция `head` используется для установки заголовка документа для маршрута.
export const head: DocumentHead = ({resolveValue}) => {
  // Она может использовать метод `resolveValue()` для разрешения значения из `routeLoader$()`.
  const user = resolveValue(useUserData);
  return {
    title: `User: "${user.name}"`,
    meta: [
      {
        name: 'description',
        content: 'User page',
      },
    ],
  };
};

Функция routeLoader$() принимает функцию, которая возвращает промис. Промис разрешается на сервере, и разрешённое значение передается в хук useCustomLoader$(). Хук useCustomLoader$() - это пользовательский хук, который создается функцией routeLoader$(). Хук useCustomLoader$() возвращает Signal, который содержит разрешённое значение промиса, возвращённого из функции routeLoader$(). Хук useCustomLoader$() будет рендерить компонент при каждом изменении маршрута и повторном запуске функции routeLoader$().

Обработка отправленных форм

Qwik предоставляет API routeAction$(), экспортированный из @builder.io/qwik-city для обработки запросов формы на сервере. routeAction$() выполняется на сервере только в момент отправки формы.

src/routes/user/[userID]/index.tsx
import { component$ } from '@builder.io/qwik';
import { routeAction$, Form, zod$, z } from '@builder.io/qwik-city';
 
// Функция `routeAction$()` используется для создания загрузчика данных, который будет выполняться на сервере при отправке формы.
// `routeAction$()` возвращает пользовательский хук, который можно использовать для доступа к данным, возвращаемым из `routeAction$()`.
export const useUserUpdate = routeAction$(async (data, requestContext) => {
  const user = await db.table('users').get(requestContext.params.userID);
  user.name = data.name;
  user.email = data.email;
  await db.table('users').put(user);
  return {
    user,
  };
}, zod$({
  name: z.string(),
  email: z.string(),
}));
 
export default component$(() => {
  // Хук `useUserUpdate` вернёт `ActionStore<T>`, содержащий `value`, возвращённое из `routeAction$()`, и некоторые другие свойства, такие как `submit()`, которое используется для программной отправки формы, и свойство `isRunning`. Все эти свойства являются реактивными и будут перерисовывать компонент всякий раз, когда они изменяются.
  const userData = useUserUpdate();
  // userData.value — это значение, возвращаемое функцией `routeAction$()`, которое является неопределённым перед отправкой формы.
  // userData.formData — это данные формы, которые были отправлены, они `undefined` перед отправкой формы.
  // userData.isRunning — это логическое значение, которое принимает значение `true` при отправке формы.
  // userData.submit() — это функция, которую можно использовать для отправки формы программно.
  // userData.actionPath — это путь к действию, которое используется для отправки формы.
  return (
    <main>
      <h1>Данные пользователя</h1>
      <Form action={userData}>
        <div>
          <label>Имя пользователя: <input name="name" defaultValue={userData.formData?.get('name')} /></label>
        </div>
        <div>
          <label>Электронная почта: <input name="email" defaultValue={userData.formData?.get('email')} /></label>
        </div>
        <button type="submit">Обновить</button>
      </Form>
    </main>
  );
});

routeAction$() используется в паре с компонентом Form, экспортированным из @builder.io/qwik-city. Компонент Form представляет собой оболочку вокруг HTML-элемента <form>. Компонент Form принимает ActionStore<T> в качестве свойства action. ActionStore<T> — это возвращаемое значение функции routeAction$().

Запуск кода только в браузере

Поскольку Qwik выполняет один и тот же код на сервере и в браузере, вы не можете использовать window или другие API браузера в своём коде, поскольку они не существуют, когда код выполняется на сервере.

Если вы хотите получить доступ к API браузера, таким как window, document, localStorage, sessionStorage, webgl и т.д., вам необходимо проверить, запущен ли код в браузере, прежде чем обращаться к ним.

import { component$, useTask$, useVisibleTask$, useSignal } from '@builder.io/qwik';
import { isBrowser } from '@builder.io/qwik/build';
 
export default component$(() => {
  const ref = useSignal<Element>();
 
  // useVisibleTask$ будет выполняться только в браузере.
  useVisibleTask$(() => {
    // Нет необходимости проверять `isBrowser` перед доступом к DOM, потому что `useVisibleTask$` будет выполняться только в браузере.
    ref.value?.focus();
    document.title = 'Привет, мир';
  });
 
  // useTask может выполняться на сервере, поэтому вам необходимо проверить `isBrowser` перед доступом к DOM.
  useTask$(() => {
    if (isBrowser) {
      // Этот код будет выполняться в браузере только при первом отображении компонента.
      ref.value?.focus();
      document.title = 'Привет, мир';
    }
  });
 
  return (
    <button
      ref={ref}
      onClick$={() => {
        // Все обработчики событий выполняются только в браузере, поэтому доступ к DOM безопасен.
        ref.value?.focus();
        document.title = 'Привет, мир';
      }}
    >
      Нажми меня
    </button>
  );
});

useVisibleTask$(() => { ... })

Этот API объявит функцию VisibleTask, которая будет выполняться только на клиенте/браузере. Она никогда не будет запущена на сервере.

Обработчики событий JSX

Обработчики JSX, такие как onClick$ и onInput$, выполняются только на клиенте. Это потому, что они являются событиями DOM, поскольку на сервере нет DOM, они не будут выполняться на сервере.

Условное выражение isBrowser

Qwik предоставляет логическое значение isBrowser, экспортируемое из @builder.io/qwik/build. Вы можете использовать его, чтобы убедиться, что код работает только в браузере.

Запуск кода только на сервере

Иногда вам нужно выполнять код только на сервере, например, получать данные или обращаться к базе данных. Для решения этой проблемы Qwik предоставляет несколько API для запуска кода только на сервере.

import { component$, useTask$ } from '@builder.io/qwik';
import { server$, routeLoader$ } from '@builder.io/qwik/qwik-city';
import { isServer } from '@builder.io/qwik/build';
 
export const useGetProducts = routeLoader$((requestEvent) => {
  // Этот код будет работать только на сервере.
  const db = await openDB(requestEvent.env.get('DB_PRIVATE_KEY'));
  const product = await db.table('products').select();
  return product;
});
 
const encryptOnServer = server$(function(message: string) {
  // `this` - это `requestEvent`
  const secretKey = this.env.get('SECRET_KEY');
  const encryptedMessage = encrypt(message, secretKey);
  return encryptedMessage;
});
 
export default component$(() => {
  useTask$(() => {
    if (isServer) {
      // Этот код будет работать только на сервере и только тогда, когда компонент будет впервые отрендерен на сервере.
    }
  });
 
  return (
    <>
      <button
        onClick$={server$(() => {
          // Этот код будет выполняться на сервере только при нажатии кнопки.
        })}
      >
        Нажми меня
      </button>
 
      <button
        onClick$={() => {
          // Этот код вызовет серверную функцию и дождётся результата.
          const encrypted = await encryptOnServer('Привет, мир');
          console.log(encrypted);
        }}
      >
        Нажми меня
      </button>
    </>
  );
});

routeAction$()

routeAction$() - это специальный компонент, который выполняется только на сервере. Он используется для обработки отправки форм и других действий. Например, вы можете использовать его для добавления пользователя в базу данных, а затем перенаправить на страницу профиля пользователя.

routeLoader$()

routeLoader$() — это специальный компонент, который выполняется только на сервере. Он используется для получения данных, а затем рендера страницы. Например, вы можете использовать его для получения данных из API, а затем рендерить страницу с данными.

server$((...args) => { ... })

server$() — это особый способ объявления функций, которые выполняются только на сервере. При вызове с клиента они будут вести себя как RPC-вызов и будут выполняться на сервере. Они могут принимать любые сериализуемые аргументы и возвращать любое сериализуемое значение.

Условное выражение isServer

Qwik предоставляет логическое значение isServer, экспортированное из @builder.io/qwik/build. Вы можете использовать его, чтобы убедиться, что код работает только на сервере.

Участники

Спасибо всем участникам, которые помогли сделать эту документацию лучше!

  • manucorporat
  • AnthonyPAlicea
  • the-r3aper7
  • igorbabko
  • mrhoodz
  • hbendev
  • willnguyen1312
  • julianobrasil
  • devagja
  • vanvuongngo
  • iancharlesdouglas
  • adamdbradley
  • hamatoyogi
  • aendel