Qwik を試してみました

システム開発事業部

開発言語: TypeScript

はじめに

先日、DEV Community の ‍relevant を眺めていると、 ‍Resumable JavaScript with Qwik という記事を見つけました。
以前、なんでもSPAにするんじゃねぇ!という主張のその先で記載されているのを見ていたので、Qwikの存在は知っていましたが試していなかったので、今回初めて試してみました。
技術的な説明は、先の記事やQwikというフレームワークについてで詳しく書かれているため、ここでは割愛します。

試してみる

実行環境は以下の通りです。

> node -v
v16.14.0
> npm -v
8.3.1

余談ですが、Windows環境で NVM for Windows を使って今回実行していましたが、本筋とは別に細々とエラーが発生して困惑しました。
色々とやった結果、必要なことは以下の2点でした。
・NVM for Windowsのバージョンを1.1.9に上げる
・NVM for Windowsのバージョンを上げた後に、Node.jsをインストール & 利用するバージョンへ切り替え
(NVM for Windowsは1.1.8からNode.jsのバージョン切り替えの際に管理者権限でターミナルを起動するように変更されました。)

Qwikのバージョンはv0.0.16で試してみます。
リポジトリはこちらです。

Getting Started にある通りnpm init qwik@latestを実行します。
設定を聞かれますが、今回はプロジェクト名のみ変更して、以下のような設定で作成しました。

💫 Let's create a Qwik project 💫

√ Project name ... qwik-starter
√ Select a starter » Starter
√ Select a server » Express
√ Features » Prettier, Tailwindcss

⭐️ Success! Project saved in qwik-starter directory

📟 Next steps:
   cd qwik-starter
   npm install
   npm start

雛形が作成できたので、 記載されている通り、npm install して npm start して試しみましたが、残念ながら動きませんでした
正確に言うと、サーバは立ち上がり3000番ポートでリッスンしてくれますが、ブラウザでアクセスすると以下のようなエラーがサーバ側で表示されます。
なお、ブラウザはChromium系のみ動くようです。(リポジトリ上で、明記されているところは見つけられませんでした。)

  vite v2.8.0 dev server running at:

  > Local: http://localhost:3000/
  > Network: use `--host` to expose

  ready in 1069ms.

Error: failed to load module for ssr: /src/h_main_app_onmount.js
    at instantiateModule (C:\qwik-starter\node_modules\vite\dist\node\chunks\dep-c9c9d3e5.js:56042:15)

記載時点で、筆者ではこの問題を解決することができませんでした。

ということで、npm run build でビルドして試そうとしましたが、以下のようなエラーが発生し、ビルドに失敗しました。

> npm run build

> qwik-starter@0.0.0 build C:\qwik-starter
> npm run typecheck && npm run build.client && npm run build.server


> qwik-starter@0.0.0 typecheck C:\qwik-starter
> npx tsc --noEmit


> qwik-starter@0.0.0 build.client C:\qwik-starter
> vite build --outDir server/public

vite v2.8.0 building for production...
✓ 15 modules transformed.
server/public/index.html                 0.32 KiB
server/public/assets/index.cd4b6173.js   0.79 KiB / gzip: 0.48 KiB
server/public/q-afa3b79d.js              2.30 KiB / gzip: 1.07 KiB
server/public/q-4611b9d9.css             3.17 KiB / gzip: 1.29 KiB
server/public/q-ff7eed5f.js              32.15 KiB / gzip: 11.18 KiB

> qwik-starter@0.0.0 build.server C:\qwik-starter
> vite build --outDir server/build --ssr src/entry.express.tsx

vite v2.8.0 building SSR bundle for production...
✓ 2 modules transformed.
[vite:build-import-analysis] Parse error @:22:38
file: C:/qwik-starter/src/entry.server.tsx:22:37
20:       <head>
21:         <meta charSet="utf-8" />
22:         <title>Qwik Blank App</title>
                                         ^
23:       </head>
24:       <body q:base="/">
error during build:
Error: Parse error @:22:38
    at parse$g (C:\qwik-starter\node_modules\vite\dist\node\chunks\dep-c9c9d3e5.js:19322:353)
    at Object.transform (C:\qwik-starter\node_modules\vite\dist\node\chunks\dep-c9c9d3e5.js:20990:27)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! qwik-starter@0.0.0 build.server: `vite build --outDir server/build --ssr src/entry.express.tsx`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the qwik-starter@0.0.0 build.server script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\anonymous\AppData\Roaming\npm-cache\_logs\2022-03-04T08_21_15_844Z-debug.log
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! qwik-starter@0.0.0 build: `npm run typecheck && npm run build.client && npm run build.server`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the qwik-starter@0.0.0 build script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\anonymous\AppData\Roaming\npm-cache\_logs\2022-03-04T08_21_15_877Z-debug.log

前半のクライアント側(html / JS)のビルドは成功していますが、サーバ側のビルドに失敗しています。
記載時点で、筆者ではこの問題を解決することができませんでしたが、回避方法を見つけることができました。
回避方法と言っても、 src/entry.express.ts のインポートの一部に以下のように拡張子を追加するだけです。

- import { render } from './entry.server';
+ import { render } from './entry.server.tsx';

改めて npm run build でビルドすると、以下のように成功しました。

> npm run build

> qwik-starter@0.0.0 build
> npm run typecheck && npm run build.client && npm run build.server


> qwik-starter@0.0.0 typecheck
> npx tsc --noEmit


> qwik-starter@0.0.0 build.client
> vite build --outDir server/public

vite v2.8.0 building for production...
✓ 15 modules transformed.
server/public/index.html                 0.33 KiB
server/public/assets/index.7155f819.js   0.79 KiB / gzip: 0.48 KiB
server/public/q-79ea2b46.js              2.49 KiB / gzip: 1.16 KiB
server/public/q-c9292ef8.css             4.98 KiB / gzip: 1.76 KiB
server/public/q-ff7eed5f.js              32.15 KiB / gzip: 11.18 KiB

> qwik-starter@0.0.0 build.server
> vite build --outDir server/build --ssr src/entry.express.ts

vite v2.8.0 building SSR bundle for production...
✓ 12 modules transformed.
server/build/entry.express.js   7.58 KiB

ビルド後のファイルを npm run serve でサーバを起動します。
デフォルトでは、8080ポートでリッスンするのでブラウザでアクセスすると画面が表示されます。(エラーは発生しません)


画面の作りはよくあるパターンで、inputの入力値が、リアクティブに描画されるようになっています。
Qwikの良い点の1つは、遅延ロードです。
リアクティブな挙動のためのJavaScriptは、Worldから1文字 ( ! ) 加えた瞬間に取得されるようになっています。
該当部分は以下のようになっており、他に特別な実装は見受けられません。

       <li>
          Try interacting with this component by changing
          <input
            class="..."
            value={state.name}
            on$:keyup={() => {
              const event = useEvent<KeyboardEvent>();
              const input = event.target as HTMLInputElement;
              state.name = input.value;
            }}
          ></input>
          .
        </li>
        <li>
          Observe that the binding changes: <code>Hello {state.name}!</code>
        </li>

つまり、ほとんど従来通りのJSX記法で遅延ロードされるようにしてくれます。
画面が複雑になってくると単純にダウンロード量が増えるだけでなく、発火しない可能性のあるイベント分のJSもダウンロードされてくるので、パフォーマンスが出なかったり転送量が増えたりしてくるので、これらを削減してくれる仕組みが整っているのは非常にありがたいです。

1点問題なのは、Issueに挙がっているように1文字入力した後にinputからフォーカスが外れてしまうことです。(筆者は最初に触った時は遅延ロードに感動して全然気が付きませんでした。)
再度フォーカスした後は、連続入力できる(既にダウンロードされたのでJSもダウンロードはされない)ようになります。

極めて簡単なお試しとなってしまいましたが以上です。
もっと色々と試してみたいところですが、開発中ということもあり破壊的変更が多く発生してくるとドキュメントが少ない現状では追うのがかなり大変なので、もうしばらくしたらまた調査してみようと思います。

感想

JavaScript界隈のサーベイであるThe State of JS 2021が公開されました。
DVE Commnityでの記事を作成した方は、群雄割拠の front-end-frameworks で2021年のSatisfactionで首位を獲得したSolid の作者であり、Marko のコントリビュータだったりします。
Solidは5年以上開発されているようですが、State of JSのランキングに記載されたのが2021年で初めてなので、Qwikが広く認知されるにはもう少し時間がかかるかもしれません。
build-toolsではViteが同じくSatisfactionにて初登場で首位となりました。QwikもViteを使っており、これからデファクトスタンダードになってくるかもしれません。

ところで、Markoは以前試したことがありますが、EJSやJSXなどと文法が異なることから個人的には慣れるのに時間がかかる印象でした。また、関数名を文字列で指定したりTypeScript対応がまだされていないので、エディタやトランスパイラの力を活かした開発は難しいと思いました。
QwikはTypeScriptが前提で、TSX記法が使えるので、React / Next.jsユーザは特に始めやすいと思います。
Qwikはまだまだ開発中なので、実運用は少し先になるかもしれませんが、現在採用されるケースが多いであろうReact / Next.jsからのパフォーマンス改善を狙ったリプレースの可能性はあると思うため、動向は確認していきたいと思います。

関連記事

カテゴリー

アーカイブ