辛宝Otto

辛宝Otto 的玄酒清谈

北漂前端程序员儿 / 探索新事物 / Web Worker 主播之一/内向话痨
xiaoyuzhou
email

速通 - Qwik 2.0 前瞻

速通 - Qwik 2.0 前瞻

速通 - Qwik 2.0 前瞻#

原始信息#

原文链接:
迈向 Qwik 2.0:更轻、更快、更好
发布于 2024-02-10

包含的信息#

qwik は HTML 優先 HTML-first の方針を堅持しており、ページのインタラクションは徹底的に遅延読み込みされます:公式には 可復元性 resumability と呼ばれています。

2.0 バージョンの更新の節目に立ち、1.0 の可復元性には改善が必要な点があります。qwik は html コンテンツに情報を追加し、html と js ロジックのマッピング関係を実現する必要があります。

具体的には、3 つのことを行いました:

  • リスナー位置 Listener location 。html に特定の属性を追加しますが、実際の内容は少なく、改善の可能性はあまりありません。
  • コンポーネント境界 Component boundaries 。今回の核心です。
  • アプリケーションの状態。調整したい点で、今後の議論で。

すべての変更は後方互換性があり、ユーザーには影響を与えません。

Web アプリは特定のフレームワークに依存せず、抽象的なロジックと DOM ツリーの関係を構築します。

举例子#

源码#

例として、qwik コンポーネントを使用し、jsx 構文で特定の関数 component$ でラップし、カウンターを実現します。

import { component$, useSignal } from '@builder.io/qwik';

export const Counter = component$(() => {
  const count = useSignal(123);
  return (
    <>
      Count: {count.value}!
      <button onClick$={() => count.value++}>+1</button>
    </>
  );
});

export const Layout = component$(() => {
  return (
    <main>
      <Slot />
    </main>
  );
});

// ページ中でこのように呼び出します <main> <Counter/> </main>

html 产物#

Qwik の公式 Playground https://qwik.dev/examples/introduction/hello-world/ を開いて、上記の内容を app.tsx に置き換えます。

出力される html にはいくつかのコメントノードが含まれ、多くの情報が含まれています。

image

<div q:container="paused" q:render="static-ssr" q:version="dev"
     q:base="/build/" q:locale q:manifest-hash="dev">
  <main>
    <!--qv q:s q:sref=5 q:key=-->
      <!--qv q:id=7 q:key=xYL1:zl_0-->
        <!--qv q:key=H1_0-->
          Count:
          <!--t=8-->123<!---->!
          <button on:click="..." q:id="9">
            +1
          </button>
        <!--/qv-->
      <!--/qv-->
    <!--/qv-->
  </main>
  <script type="qwik/json">{...}</script>
</div>

注释节点 1#

最初のコメントノード <!--qv q:s q:sref=5 q:key=-->

ソースコードに対応する Layout コンポーネントで、slot 機能を実現します。コンテンツをルーティングするために使用されます。

注释节点 2#

2 番目のコメントノード <!--qv q:id=7 q:key=xYL1:zl_0-->

上記の Counter コンポーネントを示しています。props があれば、qwik/json コンテンツに関連付けられます。

原文は少し回りくどく、理解しにくいです:

  • The additional attributes are used to cross-reference the props with this virtual node.
  • 追加の属性は、この仮想ノードと props を相互参照するために使用されます - つまり、props と関係を築くためです。このデモの props はありません。
  • A key point of resumability is that one should be able to re-render a component without the parent component needing to be resumed as well.
  • 可復元性の重要なポイントは、親コンポーネントを復元することなく、コンポーネントを再描画できるべきであるということです。
  • But components get props from the parent, so the props need to be serialized and recoverable so that this component can render independently of others.
  • しかし、コンポーネントは親から props を取得するため、props はシリアル化され、復元可能である必要があります。このコンポーネントが他のコンポーネントとは独立して描画できるようにするためです。
  • Qwik needs a virtual node for that.
  • Qwik はそれに仮想ノードが必要です。

つまり、この qwik コンポーネントは親コンポーネントから独立して再描画を実現するためにデカップリングする必要がありますが、親コンポーネントが提供する props 情報にも依存しています。

注释节点 3#

3 番目のノード <!--qv q:key=H1_0--> は jsx の <></> ノードをマークします。

注释节点 4#

コメントノード <!--t=8-->123<!----> は更新されるテキストノードをマークします。

其他特殊属性#

q:id/ q:sref / t の 3 つは状態との関連付けに使用されます。

ノードが多く見えますが、実際にはすべて役に立ちます。

有什么问题呢?#

余分な内容があります。実際の実行時に、Counter コンポーネントはブラウザにダウンロードされることはありません。なぜなら、後続のインタラクションでは DOM を追加または削除することはなく、更新操作だけで qwik 自身が DOM を更新できるため、Counter コンポーネントの仮想ノードはあまり必要ではありません。

ストリーミング転送の場合、レンダリングの過程で他のコンポーネントが Counter に影響を与える可能性があるため、コメントノードを先に残しておき、アプリ全体が処理された後に、このコンポーネントが静的であることがわかります。この仮想ノードは不要です。

データをシリアル化した後、関連付けのために ID が必要です。しかし、ストリーミング転送 html の場合、qwik xxx

2.0 会怎么改进?#

  • 人間が読み取れないデータはすべて html の最後に置き、すべては UI の迅速なレンダリングのために。
  • より効率的な仮想ノードのエンコーディングスキーム。
  • 可復元性アルゴリズムをさらに遅延読み込みし、ユーザー入力の仮想ノードにのみ注目する。

記事は新しいバージョンの出力内容を示しており、出力結果は非常にクリーンで、余分なコメントノードは完全にありませんが、上記の出力と同等です。

<div q:container="paused" q:render="static-ssr" q:version="dev"
     q:base="/build/" q:locale q:manifest-hash="dev">
  <main>
   Count: 123!
   <button on:click="...">+1</button>
  </main>
  <script type="qwik/state">[...]</script>
  <script type="qwik/vnode">!{{HDB1}}</script>
</div>

仮想ノード情報を html と混合せず、すべてを qwik.vnode 内に配置しました。すべては UI の迅速なレンダリングのために。

では、補助情報を失うことになりますが、どうやって 123 が更新されることを知るのでしょうか?特に注意すべきは、123 の前後は純粋な文字列です!

結論として、補助的なノード情報は依然として存在し、qwik/vnode の内容を見れば、すべてのノード情報は 9 文字に圧縮されています:

!{{{HDB1}}

这 9 个字符怎么来的#

この 9 文字はどのように生成されるのでしょうか?これは技術的な問題です。

ドキュメントはこう述べています:

We use the document.createTreeWalker API to retrieve all DOM nodes in depth in the first order.

我々は document.createTreeWalker API を使用して、すべての DOM ノードを 深さ優先順序 で取得します。

キーワード:

  • document.createTreeWalker は不明ですが、すべての DOM ノードを遍歴するための API です。後で補足します。
  • depth-first はシリアル番号を使用して任意のノードを識別します。これにより、ID を割り当てる必要がなくなります。

では、この神秘的な 9 文字は何を意味するのでしょうか? !{{HDB1}} を分解して、レンダリング結果を再度見てみましょう。

// source
<main>
<>
Count: {count.value}!
<button onClick$={() => count.value++}>+1</button>
</>
</main>


// output
<main>
Count: 123!
<button on:click="...">+1</button>
</main>

  • ! エンコードプロセスで <main> に到達するためにスキップする要素の数。
  • { 最初の波括弧は、<main> 要素が仮想要素の props を持っていることを示します。
  • { 2 番目の波括弧は、コンポーネントが <></> の仮想ノードを含んでいることを示します。
  • H はインデックス index=7 の文字 abcd efg h を示し、実際にレンダリングされる Count: 123 の 7 番目の文字位置を示します。つまり、空白の後、123 が始まる前の範囲は Count: (コロンの後の空白) で、合計 7 文字です。この位置が最初のノードです。
  • D はインデックス index=3 を示し、123 の後のカーソル位置は新しいテキストノードです。
  • B はインデックス index=1 に対応し、! を示します。
  • 1 は消費される要素の数を示し、represents the number of elements to consume は 1、つまり <button> です。

素晴らしい、少し理解できたような気がしますが、実際には大文字の文字を使用して、26 の長さの範囲を超えない内容をマークし、小文字の文字と組み合わせることで無限に拡張できるということです。

詳細については後で考えますが、要するに、任意のノードの開始位置をマークできるということです。確かに仮想ノードを省くことができますが、これは極限を追求しているようです!

优势#

仮想ノードのエンコーディングが最後に行われるため、デコード実行時にコンポーネントのすべての詳細を安全に知ることができます。

これにより、送信される HTML の量がさらに減少します。新しいエンコーディングは深さ優先インデックスを使用しているため、余分な不要な ID が残りません。

额外优化#

この記事は主にエンコーディングに関する最適化を紹介していますが、実行時にも最適化があり、主に説明として、データをオブジェクトではなく配列で保存することを使用しています。配列は自由にデータを増やすことができ、単一の配列でデータを保存することでメモリの負担を軽減します。その他の利点は、アクセスが O1 で高速です。

他の点はあまり理解できませんが、しばらく置いておきます。

感受#

qwik は常に極限のレンダリング性能で知られており、今回の 2.0 バージョンは後方互換性を約束しており、ユーザーにとっての影響は少ないため、内部の詳細に期待しています。この文章はシリーズ記事の最初のもので、主に極限圧縮エンコーディングの詳細を紹介しています。多くの部分で理解と消化にもっと時間が必要ですが、次のバージョンに対して非常に信頼を持っています。本当に面白いフレームワークです。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。