@mizchi | Workers Tech Talk
- SSR 前提 で最適化を行うUIライブラリ
- クライアントの処理が最小限
- すべてが非同期チャンク
- すべてがチャンク化された遅延ハイドレーション
- 必要なコードのみ動的ロード
- 静的解析で関数単位でチャンク化
- (使いこなせれば)理論上最強
import { component$ } from "@builder.io/qwik";
export default component$(() => {
console.log("render");
return <button onClick$={() => console.log("hello")}>Hello Qwik</button>;
});
...$()
で常にチャンクが分割されるcomponent$(...)
で別チャンクonClick$(...)
で別チャンク
// 実際に生成されるコード
// app.js
import { componentQrl, qrl } from "@builder.io/qwik";
const App = /*#__PURE__*/ componentQrl(
qrl(
() => import("./app_component_akbu84a8zes.js"),
"App_component_AkbU84a8zes"
)
);
// app_component_akbu84a8zes.js
import { jsx as _jsx } from "@builder.io/qwik/jsx-runtime";
import { qrl } from "@builder.io/qwik";
export const App_component_AkbU84a8zes = () => {
console.log("render");
return /*#__PURE__*/ _jsx("p", {
onClick$: qrl(
() => import("./app_component_p_onclick_01pegc10cpw"),
"App_component_p_onClick_01pEgC10cpw"
),
children: "Hello Qwik",
});
};
// app_component_p_onclick_01pegc10cpw.js
export const App_component_p_onClick_01pEgC10cpw = () => console.log("hello");
export const Counter = component$(
/*chunk: onMount*/ () => {
const count = useSignal(0);
return (
/* chunk: onRender */
<button
type="button"
onClick$={
/* chunk: onClick */
() => count.value++
}
>
{count.value}
</button>
);
}
);
- onMount: 関数の body 部分
- onRender:
return <>...</>
- 実体は複数チャンクで構成されるツリー構造
- クロージャにみえる参照は QRL 経由で親子間で解決
- Qwik 内 URL 参照オブジェクト
$(...)
で自分で生成することもできる
import {$, component$} from "@builder.io/qwik";
const C = component$(() => {
const V = 1; // Serialize できるので他から参照できる
const f1 = $(() => console.log(V)); // QRL化された関数
const f2 = $(() => f1()); // QRL 同士なので参照できる
// handler として QRL 化された関数を受け取ることができる
return <button onClick$={f2}>btn</button>
})
const C = component$(() => {
const v1 = new (class {})();
let v2 = 1;
const f1 = $(() => {
// NG: class はシリアライズできない
console.log(v1);
// NG: 別チャンクの let を書き換えることができない。signal が必要
v2++;
});
});
// NG: QRL化されてない関数ハンドラは受け取れない
const C = component$(() => {
const f1 = () => { console.log(1) };
return <div onClick$={f1}>btn</button>
});
// OK: 関数リテラルなら静的解析でチャンク化される
const C = component$(() => {
return <div onClick$={() => { console.log(1) }}>btn</button>
});
- スコープ解析により signal と QRL は子チャンクに引き継がれる
const C = hcomponent$(() => {
// --- onMount chunk ---
const singal = useSignal(0);
const handler = $(() => {
// ---- handler chunk ----
singal.value++
});
// --- onRender chunk ---
return <div onClick$={handler}>{signal.value}</button>
});
const Greeter_onMount = (props) => {
const salutation = "Hello";
return qrl(
"./chunk-b.js",
"Greeter_onRender",
// スコープ解析によって参照可能なものを渡す
[salutation, props]
);
};
- チャンク分割でスコープがぶつ切りになるのでシリアライズ/QRL化が必須
- let が引き継げないのはコンテキストが消失するため
- https://qwik.builder.io/docs/advanced/qwikloader/
- 最小ランタイム(1kb)
- 親
q:base
とon:*
からチャンクを解決するハンドラを登録 - (Astro に似ている)
<body q:base="/build/">
<button on:click="./myHandler.js#clickHandler">push me</button>
</body>
- Qwik 版 Next.js (Vite SSR Plugin)
- Adapter: node, deno(-deploy), cloudflare, vercel, cloudrun
// src/routes/with-loader/index.tsx => /with-loader
import { component$ } from "@builder.io/qwik";
import { routeLoader$ } from "@builder.io/qwik-city";
export const useServerData = routeLoader$(async (requestEvent) => {
return { serverTime: Date.now() };
});
export default component$(() => {
const signal = useServerData();
return <p>ServerTime: {signal.value.serverTime}</p>;
});
- Qwik 内で React コンポーネントが使える
- Qwik で遅延ハイドレーション
/** @jsxImportSource react */
import { qwikify$ } from "@builder.io/qwik-react";
// An existing React component
function Greetings() {
return <div>Hello from React</div>;
}
// Qwik component wrapping the React component
export const QGreetings = qwikify$(Greetings);
- ほとんどは React と同じ雰囲気で書ける
- チャンク分割を意識しないとハマる
- The $ dollar sign | Advanced 📚 Qwik Documentation 必読
- React 初見で props 書き換えちゃいけない!ぐらいの不自由感
- 運用上、Qwik City ほぼ必須
- CSS フレームワークはなんでも使える
- Qwik City は強い Astro みたいな感じで使える
- UI ライブラリが足りてない
- Qwik UI ぐらい?
- ほぼフルスクラッチで書く気概が必要
- 複雑な箇所は qwik-react を使ったほうがよさそう
- QwikCity
- qwik-auth で auth.js が使える
- プラットフォームへの adapter が充実
- node, deno, cloudflare, cloudrun, firebase, StaticSite...
- QwikCity で超高速フロントエンド
- CloudflarePages で低コスト運用
- Qwik の低遅延要求に応える
- QwikCity のビルドサイズも 200k ぐらい
- あとは永続層どうするか問題
- d1 + prisma いつ動くんですかねェ...