Warning

注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。

依存関係の理解

Slate で「なぜ事が起こるのか」を理解する

Slateでは、すべてのウィジェット、関数、変数、クエリがグラフ内のノードとしてモデル化されています。各ノードは JSON 出力に評価され、他のノードはその出力を参照するためにテンプレート化されます。これらの参照はノード間の依存関係を定義し、Slateは何かが変わったとき(つまり、新しいユーザーの入力; クエリが実行される; 変数の値が更新される)にノードを再評価するタイミングを理解するためにこれを使用します。

ユーザーの Slate アプリケーションの「開発」の主要なプロセスは、出力を表示し、入力をキャプチャするためのウィジェットの追加、データの表示とアプリケーションのログイン処理のための軽微なデータ操作を行う関数の追加、データや他の Foundry サービスAPIへの接続のためのクエリを作成するクエリの追加を通じてこれらのノードを定義するプロセスです。そして、その後、Handlebar 構文を通じてその依存関係を設定します。

依存関係グラフ

ユーザーは常に依存関係タブを使用して、この依存関係グラフの表現を表示し、SlateがユーザーのHandlebar参照を通じて設定した関係をどのように解釈しているかを理解することができます。このグラフの表現はユーザーのアプリケーションであり、それが「間違っている」と思われる何かを表している場合、その出発点は、グラフがアプリケーションを誤って表していると仮定するのではなく、_なぜ_そうなるのかを理解することです。

アプリケーションで予期しない事が起こるたびに、どのウィジェットが誤動作しているかを理解するために依存関係タブから始めます。関連性のないクエリやユーザー入力に基づいてノードが再評価される原因となる予期しない関係を探します。

依存関係グラフを「怠惰」と考えることが時々有用です。それは上流の参照が変更したに対してのみノードを再評価することで、不必要な作業を避けます。これは上流のノードが以前と同じ値に更新されたときに混乱を招く可能性があります。下流のクエリは実行されず、下流のウィジェットは再描画されません。これは通常、ユーザー入力をリセットするパターンで遭遇します。Foundryで依存関係グラフとイベントフレームワークの紹介チュートリアルとウィジェット選択のリセット例を検索し、これを行動に移し、依存関係グラフの評価にエントロピーを注入してMath.random()を使用し、ノードが再評価されることを確認します。

イベント

依存関係グラフを使用して、Slateは関数が再計算すべきか、またはクエリが上流の依存関係の状態に基づいて再実行すべきかを決定する複雑さを処理します。ほとんどのワークフローはこの機能を使用して設定でき、これはSlate開発者として常に依存関係グラフに依存することが最初の衝動であるべきです。

しかし、明確なトリガー/アクションのペアで依存関係グラフを補完することが理にかなっている状況もあります。これをSlateは「イベント」と呼んでいます。簡単な例としては、クエリが終了したときにバナーやトーストの表示をトリガーする、ユーザーが何かをクリックして詳細を表示するダイアログを開く、または一連の入力を設定した後にボタンクリックからクエリを実行する、などがあります。

イベントフレームワークと一般的な基本的なイベントパターンの例について詳しく学びましょう。

イベントの乱用

イベントフレームワークの力を認識したら、Slateのすべての開発タスクをイベントベースの解決策の観点から見ることが魅力的に思えるかもしれません。これは、単一のアプリに何百ものイベント/アクションのペアが含まれているという、一般的な失敗モードにつながる可能性があります。これは技術的に可能ですが、この複雑さはアプリケーションの期待される動作を追跡することを難しくし、単純なデバッグさえも防ぐ可能性があります。

一般的なルールとして、イベントの「チェーン」を可能な限り短く保ち、依存関係グラフに依存する解決策に偏るようにします。アプリが>50のイベントに成長していることがわかった場合、特にそれらのイベントがトーストのトリガーやクエリの開始以上のことを行っている場合、アプリケーションのアーキテクチャを慎重に考え直し、開発をリファクタリングして簡素化する機会、またはアプリケーションをサブアプリケーションにさらに分解する機会がないかを探すために一時停止するのが良い時間です。

カスタムイベントトリガー

組み込みのイベントタイプに加えて、ユーザーはHTMLTableタイプのウィジェットでカスタムイベントを作成することができます。これにより、これらのウィジェット内の異なる要素がユーザーのクリックから異なるイベントをブロードキャストできます。

これらのカスタムトリガーの設定は2ステップで行います:

  1. slClickEventプロパティを使用してHTML要素でトリガーを定義します:

  2. ウィジェットの設定にトリガーを登録し、名前を Click Event Names 配列に追加します。

新しいイベントが登録されると、myWidgetName.click.delete のようなイベントトリガーを選択できる場所に新しいオプションが表示されます。クリックイベントを定義する HTML を動的に生成しても、Slate が可能なトリガーを追跡できるように、そのイベントの 登録 はハードコーディングする必要があります。つまり、現時点ではそのボタンが生成されていないような条件でもです。

これらのイベントを使用して、HTML のカスタムボタングループを作成したり、以下で説明するイベント値と組み合わせて、各アイテムが独自のアクションセットを持つリストやテーブルを作成できます。

条件付きイベント

追加の条件(単純なトリガーの発火を超えて)を追加する必要がある場合は、イベント設定の JavaScript 部分を使用して追加のロジックを実行できます。ロジックを評価し、イベントがアクションを 実行しない 場合は、特別な {{slDisableAction}} 値を返します。この値はイベントを永続的に無効にするものではなく、イベントアクションの 特定 のインスタンスを中断します。

ベストプラクティスとして、イベント JavaScript の 内部 で複雑なロジックを開発することは避けてください。リンティングやエラーレポートがないため、ミスや未発見のエッジケースがあると、コードが静かに失敗し、予期しない動作を引き起こす可能性があります。代わりに、イベントの有効性に関するロジックをカプセル化する関数を実装することを考えてください。その後、イベントの JavaScript は次のようにシンプルになるでしょう: ただし、イベントに debugger() ステートメントを入れて、Chrome Developer Tools を使用してその実行をキャッチし、ロジックをステップスルーすることができます。

イベント値の渡し方

slClickEvent プロパティに加えて、特定のHTML要素に slClickEventValue を定義することができます。このイベントがトリガーされるたびに、イベントJavaScriptで特別な {{slEventValue}} 変数を使って関連する値を参照できます。

これにより、動的なインタラクションのパターンがさまざまに展開されます。

  1. テキストテーブル ウィジェットのHTMLを生成し、各要素の slClickEventslClickEventValue を定義する関数を作成します。
  2. 必要なイベントを作成し、そのイベントを使用して、1つまたは複数の 単一 変数の値をイベントからの値に設定します。
  3. variable.changed イベントトリガーをオフにして、変数の値を参照して、クエリなどのさらなるアクションをトリガーします。

たとえば、このパターンを使用して、テーブルの行の横に小さな 'x' を追加し、 slClickEvent が 'delete' で slClickEventValue が行の主キーである要素を作成できます。次に、 myTable.click.delete イベントが発火したときに、 v_idToDelete という名前の変数の値をイベント値に設定します。そして、 v_idToDelete.changed イベントに基づいて q_deleteRow クエリを実行し、この小さなチェーンを完成させます。

ただし、このようなチェーンをこれ以上長くしたり、複雑にしたりしないように注意してください。循環依存関係に関する以下の警告を参照してください。また、大きくてネストされた、あるいは複雑な配置よりも、短くて明確なイベントを優先してください。これらは理解しやすく、デバッグしやすく、予期しない動作につながることがよくあります。

イベントと循環依存関係

Slate の依存関係グラフは技術的には 'Directed Acyclic Graph'(DAG)であり、これはノード関係が指向されていることを意味します(つまり、ルートノードからリーフノードへの階層があり、その方向でノード解決が行われる)し、グラフ全体が非循環的であることを意味します。Slate は、依存関係グラフに “ループ” がないようにしようとします。

ただし、イベント を使用して変数の値を設定したり、クエリをトリガーしたりすることで、アプリケーションにループを追加することができます。これは、非決定的な動作につながります。循環依存関係が必要なワークフローがある場合は、一歩立ち止まって代替パターンを検討してください。ほとんどの場合、望ましいワークフロー機能を実現する方法があります。

クエリ実行の管理

依存関係グラフフレームワークの1つの意味は、デフォルトでは、ページの読み込み時に依存関係グラフ全体が解決されることです。これは、クエリや関数が、グラフ内の各ノードが解決されるために必要な順序で実行されることを意味します。実際には、開発中に注意を払っていない場合、ページの読み込みパフォーマンスが徐々に低下し、タイムアウトが発生し始めることに気付くことがよくあります。根本的な原因は、ほとんどの場合、上流のクエリ依存関係がないため、すべて のクエリが同時に実行されることです。これにより、多くの同時ブラウザ接続からのキューイングやロードシェディングが発生し、同じテーブルへの多くの同時クエリや、アプリケーションに戻る多くのネットワークトラフィックが発生します。最終的な状態は、非決定的なクエリの失敗と、ページの読み込み時に遅い、もしくは完全に壊れたアプリケーションです。

Postgate でのコネクションプールとキューイング

Postgateでは、アプリケーションの各ユーザーに対して、特別に作成されたコネクションプールがあります(または、最近アプリケーションを使用した場合は、キャッシュから復元されます)。 各コネクションプールは、一度に10の接続に制限されており、これにより、Postgate をオーバーロードすることがありません。Postgate は、アプリケーションのすべてのユーザーや同期されたデータを使用する他のアプリケーションによって共有されています。パフォーマンス上、この制限は、一度にすべてのクエリを許可するよりも(10より多い場合)、Postgresが応答する同時クエリが少ないほど、グローバルに速くなります。

10の接続すべてが使われると、残りのクエリ要求がキューに入り、接続が利用可能になると、それぞれが接続を取得します。ただし、接続を永遠に待たせるのではなく、コネクションプールは、5秒間接続を待っているクエリに対してタイムアウトします。つまり、プールが飽和するのに数秒かかるクエリがある場合、高速であった可能性があるクエリ(たとえば、テストボタンを使用して単独で実行する場合)は、長時間かかるか、タイムアウトすることがあります。

ここでの解決策には、いくつかの部分があります。

  1. クエリがスペースアウトされ、ユーザー入力に依存するようにアプリケーションワークフローを開発します。一般的なパターンは、事前集計または DISTINCT テーブルに対していくつかの小さなクエリを実行してフィルターを設定し、ユーザーに選択肢を提供し、実際にデータを取得するクエリを実行するために 送信 ボタンをクリックするようにします。
  2. クエリを慎重に開発し、特にページの読み込み時に実行される “重い”(平均で >5s で返される)クエリの合計数と、各クエリから取得されるデータ量とのバランスを取ります。クエリが不必要な作業を行っていないことを確認してください。上記の インデックスとスキーマ および Postgresチューニング のセクションを参照してください。
  3. クエリを実行するタイミングを制御するために、クエリが実行される前に満たす必要がある追加の条件を指定するか、クエリを manual に設定して、イベント(一般的にはボタンクリック)でクエリをトリガーします。
節約クエリ

すべての開発で、節約コードを目指すべきです。コードは、できるだけ効率的な方法で必要な作業を行うべきです。これは、Slate クエリの頻繁な欠点であり、同じ作業を何度も行って時間を費やしています。次のような簡単なクエリを考えてみてください。ユーザーがカテゴリを選択するためのドロップダウンを表示するために使用されます。 これは、ページの毎回の読み込みにおいて、Postgresが各行のcategory_colの値がnullかどうかを確認し、すべての一意の値のリストを生成するという作業を繰り返さなければならないことを意味します。トランスフォームと新しい派生データセットを使用すれば、この作業は新しいデータが上流に追加されるたびに一度だけ行われ、その後のクエリは単純に次のようになります: SQL表現を完全に排除することができない場合でも、例えばユーザー入力を対応させる必要がある場合など、可能な限り多くの作業を上流の変換に分散させてから、よりシンプルなクエリを作成するように心掛けてください。この「仕事をする」原則を常に念頭に置くことで、より安定したパフォーマンスの高いアプリケーションを実現することができます。

第3部では、2つの一般的なパターンを詳しく見ていきましょう。これらをアプリケーションで利用することで、ページ読み込み時のクエリ数を大幅に削減し、アプリケーションが期待通りに動作するようにすることができます。

条件クエリ

条件クエリは、クエリ実行に最も細かい制御を提供し、「イベントトリガー」または「手動」設定(以下で説明)と組み合わせて、クエリが実行されるための論理条件を追加できます。Runの隣のドロップダウンの下にあるIn addition...ボックスにチェックを入れることで、クエリの実行条件をオンにします。

最もシンプルなケースでは、All dependencies are not nullオプションは、クエリやその部分クエリのハンドルバー式が指定された値を持っていない場合、クエリの実行を防ぎます。これは特に、意味をなす前にユーザー入力が必要なクエリにとって非常に有用で、WHERE文が不完全であるために失敗するクエリに対して、データベースへの不必要な往復を防ぐことができます。

手動クエリ

クエリの実行タイミングを制御するシンプルな戦略として、Runボタンの隣にあるドロップダウンでチェックボックスを使用して、クエリをmanualに設定します。

手動で実行を設定したクエリは、イベントや手動で指定した依存関係によってトリガーされたときだけ実行されます。

これは、ユーザーのアクションによってトリガーされるべきクエリの正しいパターンですが、あまりにも多くのものを「チェイン」すると不必要な複雑さが生じる可能性があります。手動クエリの下流のすべてのノードを依存関係グラフに依存させることができます - クエリがそのトリガーから実行された後、下流の依存関係は自動的に更新されます。q_queryName.successイベントに基づいたイベントを追加して複雑さを増す必要はありません。

JavaScript関数のベストプラクティスと一般的なパターン

関数は、データを軽度に操作したり、ユーザーの選択やアプリケーションの状態に基づいてロジックを実行する能力を提供します。しばしば見落とされるパターンや機能がいくつかあり、関数の作業をよりクリーンでシンプルにすることができます。

複雑なオブジェクトを返すことで重複作業を避ける

Validating User Inputの例を見てみると、検証関数は単一の値を返すのではなく、JavaScript Objectを返していることに気づくでしょう。Slateはこれらのオブジェクトを解析し、ハンドルバーを通じてアプリケーションの他の場所で利用可能にします。

これは、例えば異なるウィジェットで使用する新しい表示行をいくつか派生させる必要がある場合、それぞれに関数を作るのではなく、一つの関数を作って必要なすべての表示値を異なる形式で作成し、それらをすべてオブジェクトで返すことができるということを意味します。この例については後ほど詳しく見ていきましょう。

常にロジックを少数の関数にまとめることと、ロジックを分割することの間にはバランスがあります。前者は重複作業を減らし、類似作業をまとめるのに役立ちますが、後者は個別の操作をカプセル化し、必要なコードだけを見つけるのを容易にするのに役立ちます。このトレードオフを念頭に置き、アプリケーションを洗練させるにつれて、定期的に関数をリファクタリングしてください。

表示列の生成

多くの場合、データをチャートに表示するための少しの加工作業を行う必要があったり、テーブルに表示するためのHTMLを生成する必要があったりします。可能な場合、例えば日付のフォーマットなど、この作業をデータ変換と準備フェーズの一部として行うことを検討してみてください - これは、作業を可能な限り少ない回数で行う(つまり、各ページの読み込みごとに各ユーザーに対して行うのではなく、一度変換する)という原則に従います。

それが不可能な場合、行の各値をマッピングして新しい値を生成するための作業を行い、Slateが期待するデータ構造を保持するためのシンプルな関数を書くことができます。 このスニペットでは、_.map()関数の Lodash ↗ バージョンを使用しています。このケースでは、組み込みの Array.map() を同様に使用することもできます。JavaScript オブジェクトを操作する際に Lodash ヘルパー関数を知っておくことは一般的に役立ちます。これらの関数は、ゼロから再実装するとバグが発生しやすい複雑なアルゴリズムをしばしば実装します。

関数が完成したら、{{q_myQuery}} を直接参照する代わりに、アプリケーションの任意の場所で {{f_deriveDisplayColumns}} を参照できます。これは、追加の「行」が追加されただけで、元のクエリレスポンスオブジェクトと同じ構造を持っています。

カスタムライブラリを使用したコードのファクタリング

JavaScript のかなり複雑な部分を実装する必要がある場合は、それを関数エディターの左下にあるカスタムライブラリに移動することを検討してください。ライブラリは、任意の Slate「関数」で純粋な JavaScript 関数を利用可能にするため、同じヘルパー関数を何度も実装する必要がありません。

ライブラリは純粋な JavaScript なので、Handlebars をその中で使用することはできません。通常の JavaScript のように、ライブラリ関数が Slate「関数」から呼び出されるときに、パラメーターをライブラリ関数に渡すことができます。これらのパラメーターは、Handlebars で埋めることも、動的に生成することもできます。

関数のドキュメンテーション

ライブラリコードと関数の両方で、常にコードをきれいに整理し、潜在的に直感的でないセクションをドキュメンテーションする時間を取ってください。常に「自己文書化」するコードを書くことを目指すことができますが、将来の開発者やメンテナンス担当者に対して、論理をきれいに、そして簡潔にドキュメンテーションする習慣を持つことは常に親切です。