レンダリング
レンダリングとは、アプリケーションの状態とコンポーネントテンプレートの変更に基づいてDOMを更新するプロセスです。
Qwikは、非同期で、かつきめ細かくテンプレートをアウトオブオーダーでレンダリングする方法を知っている点でユニークです。
JSX
Reactと同様に、Qwikはコンポーネントのテンプレートを表現するためにJSXを使用します。JSXは単なる構文であり、内部的にはReactとQwikは完全に異なることに注意してください。JSX != VDOMです。
Qwikは、他のJSXフレームワークとはいくつかの違いがあります。
- コンポーネントは常に
component$
関数で宣言されます。 - コンポーネントは、
useSignal
フックを使用してリアクティブな状態を作成できます。 - イベントハンドラーは
$
サフィックスで宣言されます。 <input>
の場合、QwikではonChange
イベントはonInput$
と呼ばれます。- HTML属性が優先されます。
className
ではなくclass
。htmlFor
ではなくfor
。
import { component$, useSignal } from '@builder.io/qwik';
export const MyComponent = component$((props) => {
const count = useSignal(0);
return (
<>
<button
onClick$={() => {
count.value = count.value + props.step;
}}
>
Increment by {props.step}
</button>
<main
class={{
even: count.value % 2 === 0, // conditional class
odd: count.value % 2 === 1,
}}
>
<h1>Count: {count.value}</h1>
</main>
</>
);
});
子コンポーネントのレンダリング
Qwikは必要に応じてコンポーネントを遅延ロードします。ダウンロードするコンポーネントの数を最小限に抑えるために、Qwikはコンポーネントのpropsが変更された場合にのみ子コンポーネントを調べます。
import { component$, useSignal } from '@builder.io/qwik';
export const Parent = component$(() => {
const count = useSignal(0);
return (
<>
<button onClick$={() => (count.value += 1)}>Increment</button>
<Child name={'World_' + count.value} />
</>
);
});
export const Child = component$((props: { name: string }) => {
return <p>Hello {props.name}</p>;
});
上記の例では、Parentコンポーネントは変更される
name
プロパティをChildコンポーネントに渡しています。Childコンポーネントは、countシグナルが変更された場合にのみ再レンダリングされます。
項目のリストのレンダリング
多くの場合、items.map()
を使用してレンダリング関数で配列をマッピングすることにより、コンポーネントをレンダリングする必要があります。リストのすべての項目に、マッピング関数の最初の子に渡される一意のkey
プロパティが必要です。key
は文字列または数値で、リスト内で一意である必要があります。
import { component$ } from '@builder.io/qwik';
export const Parent = component$(() => {
return (
<>
{data.map(({ message, uniqueKey }) => (
<div key={uniqueKey}>
<p>{message}</p>
</div>
))}
</>
);
});
注: 特定のキーのデータが常に同じであることを保証できる場合を除き、配列のインデックスをキーとして使用することは推奨されません。キーとしてデータから一意の識別子を使用することが常に推奨されます。
条件付きレンダリング
条件付きレンダリングは、Javasciptの三項演算子?
、&&
演算子、またはif
ステートメントを使用するだけで行われます。
import { component$ } from '@builder.io/qwik';
export default component$(() => {
const show = useSignal(false);
return (
<>
<button onClick$={() => show.value = !show.value}>Toggle</button>
{show.value ? <h1>Visible</h1> : <h1>Hidden</h1>}
{show.value && <div>Only show when it's visible</div>}
</>
);
});
dangerouslySetInnerHTML
Qwikは、DOMでinnerHTML
を呼び出す代わりに、dangerouslySetInnerHTML
というHTML要素の属性を提供します。
信頼できないコンテンツをレンダリングする際のクロスサイトスクリプティング(XSS)の可能性があるため、この操作が危険な可能性があることを思い出させるために、dangerouslySetInnerHTML
を使用する必要があります。
const htmlString = "<strong>hello</strong>";
<div dangerouslySetInnerHTML={htmlString}></div>
bind
属性
bind
属性は、<input>
値をSignal
に双方向データバインディングするための便利なAPIです。
チェックボックス入力の場合、bind:checked
を使用できます。これにより、checked
ブール値が指定されたシグナルにバインドされます。
import { component$, useSignal } from '@builder.io/qwik';
export default component$(() => {
const firstName = useSignal('');
const acceptConditions = useSignal(false);
return (
<form>
<input type="text" bind:value={firstName} />
<input type="checkbox" bind:checked={acceptConditions} />
<div>First name: {firstName.value}</div>
</form>
);
})
APIはシグナルを返さないため、
useStore
では機能しません。以下に示すように、valueとonInput$を組み合わせることで、手動アプローチを使用できます。
bind:
は、Qwikオプティマイザーによってプロパティセットとイベントハンドラーにコンパイルされます。つまり、単なるシンタックスシュガーです。
import { component$, useSignal } from '@builder.io/qwik';
export default component$(() => {
const firstName = useSignal('');
const acceptConditions = useSignal(false);
return (
<form>
<input type="text"
value={firstName.value}
onInput$={(_, el) => firstName.value = el.value }
/>
<input type="checkbox"
checked={acceptConditions.value}
onChange$={(_, el) => acceptConditions.value = el.checked }
/>
<div>First name: {firstName.value}</div>
</form>
);
})
このAPIは、変更元に関係なく、入力の
value
が常にシグナルの値と同期していることを保証します。
非同期レンダリング
レンダリングパイプラインが子コンポーネントを遅延ロードできることは重要です。遅延ロードは非同期操作であるため、レンダリングも非同期である必要があります。実際には、これはrender()
関数がpromiseを返す必要があることを意味します。
現在のほとんどのフレームワークは、同期的なrender()
プロセスを持っています。同期的なレンダリングは非同期のコード読み込みに容易に対応できないため、レンダリングを開始する前にすべての依存コンポーネントが事前に存在している必要があります。
レンダーバッチ処理
アプリケーションの状態が変化するたびに、Qwikはレンダリング処理をスケジュールします。レンダリング処理は、マクロタスク(例:setTimeout(0)
)の後で実行されるようにスケジュールされます。これにより、アプリケーションは複数の状態変更を単一のレンダリング処理にまとめて処理できます。
さらに、Qwikの非同期的な性質のため、すべてのDOM書き込みはバッファリングされ、すべてのコンポーネントがダウンロードされ、そのJSX関数が実行された後にのみフラッシュされます。その結果、UIはアトミックな操作として更新され、たとえアプリケーションのレンダリングが遅くても、ユーザーは中間段階を見ることはありません。
最終的な目標は、急速に変化する状態と遅いネットワークの状況下でも、パフォーマンスと一貫性のあるレンダリングを可能にすることです。
きめ細かいリアクティビティ
Qwikのきめ細かいリアクティビティのおかげで、状態に依存するコンポーネントのみが更新されます。これは、次の2つの理由から大きなパフォーマンス向上につながります。
- 実行するコードが少ないほど、アプリケーションの更新が高速にレンダリングされます。
- 実行するコードが少ないほど、多くの場合、アプリケーションの起動時(またはそれ以降)にコードをダウンロードする必要がなくなります。