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

依存関係の理解

Slate における "なぜ事が起こるか" の理解

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

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

依存関係グラフ

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

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

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

イベント

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

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

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

イベントの狂気

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

一般的なルールとして、イベントの「チェーン」をできるだけ短く保ち、依存関係グラフに依存する解決策にバイアスをかけます。アプリケーションが>50のイベントに成長していることに気付き、特にそれらのイベントがトーストをトリガーするかクエリを開始するだけでなく、それが良い時期であると思われる場合は、ユーザーのアプリケーションアーキテクチャを慎重に考え直し、開発をリファクタリングして簡素化するか、アプリケーションをさらにサブアプリケーションに分解する機会を探すべきです。

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

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

これらのカスタムトリガーの設定には2つのステップが必要です:

  1. HTMLエレメントでslClickEventプロパティを使用してトリガーを定義します:
Copied!
1 2 3 <!-- HTMLのdiv要素を作成します。クラス名は'pt-button'で、クリックイベントとして'delete'が設定されています。 --> <!-- 'Delete Me'というテキストが表示されるボタンとなります。 --> <div class='pt-button' slClickEvent='delete'>Delete Me</div>
  1. ウィジェット設定でトリガーを登録するには、Click Event Names 配列に名前を追加します。

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

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

条件付きイベント

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

ベストプラクティスとして、イベント JavaScript の内部で複雑なロジックを開発することは避けてください。リンティングやエラー報告がないため、ミスやカバーされていないエッジケースがあると、コードが静かに失敗し、予期しない動作につながる可能性があります。代わりに、イベントの有効性に関するロジックをカプセル化する関数を実装することを検討してください。これにより、イベント JavaScript は次のように簡単になります。

# もしf_checkValidActionが偽であれば、slDisableActionを返す
unless ({{f_checkValidAction}})
    return {{slDisableAction}}

しかし、イベント内で debugger() ステートメントを使用して、Chrome Developer Tools を使ってそれらの実行をキャッチし、ロジックをステップごとに実行することができます。

イベント値の渡し方

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

これにより、動的なインタラクションのためのさまざまなパターンが利用可能になります。

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

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

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

イベントと循環依存

Slate の依存関係グラフは、技術的には「有向非循環グラフ」(DAG)であり、ノード関係が指向されている(つまり、ルートノードからリーフノードへの階層があり、その方向でノード解決が行われる)ことを意味し、グラフ全体が非循環である必要があります。これは、Slateがグラフの依存関係にどんなに長いループも設定できないようにすることを意味します。

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

クエリ実行の管理

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

Postgate とキューイングの接続プール

Postgate では、アプリケーションの各ユーザーに対して、特別に作成された接続プールがあります(最近アプリケーションを使用した場合は、キャッシュから復元されます)。各接続プールは、一度に 10 の接続に制限されており、これにより、1 つのアプリケーションインスタンスが Postgate を過負荷にすることが防止されます。Postgate は、アプリケーションのすべてのユーザーや同期されたデータを使用する他のアプリケーションによって共有されています。パフォーマンス上、この制限は、一度にすべてのクエリを実行させるよりも(10 件以上ある場合)、PostgreSQL が同時に応答しているクエリが少ないほど、グローバルに速くなるため、より良いパフォーマンスを提供します。

すべての 10 の接続が使い果たされると、残りのクエリリクエストがキューに入り、利用可能になった接続を取得します。ただし、接続が永遠に待機するのではなく、接続プールは 5 秒間接続を待っていたクエリでタイムアウトします。これは、プールが飽和している場合に、いくつかのクエリが数秒かかる場合、テストボタンを使って単独で実行した場合には速かったかもしれないクエリが、長時間かかるか、タイムアウトすることを意味します。

ここでの解決策はいくつかあります:

  1. クエリが間隔を空けて実行され、ユーザー入力に依存するようにアプリケーションワークフローを開発します。一般的なパターンは、事前集計されたまたは DISTINCT テーブルに対していくつかの小さなクエリを実行してフィルターを設定し、ユーザーにデータを取得するクエリを実行するためにいくつかの選択肢をクリックするように促すボタンをクリックします。
  2. クエリを慎重に開発し、ページロード時に実行されるクエリの総数(特に平均で >5 秒かかる「重い」クエリ)と、各クエリから戻されるデータ量をバランスさせます。クエリが不要な作業を行っていないことを確認してください。上記のインデックスとスキーマおよびPostgreSQL チューニングのセクションを参照してください。
  3. クエリが実行される前に満たす必要がある追加の基準を指定するか、クエリを manual に設定してイベントでクエリをトリガーします。これは、ボタンクリックなどのイベントが一般的です。
節度あるクエリ

すべての開発で、節度あるコードを目指すべきです。コードは、できるだけ効率的な方法で必要な作業だけを行うべきです。これは、Slate のクエリによく失敗する点です。クエリは、何度も同じ作業を行って時間を無駄にすることがよくあります。ユーザーがカテゴリを選択するためのドロップダウンを作成するための簡単なクエリを考えてみてください。

Copied!
1 2 3 -- 以下のクエリは "my_table" から重複なしの category_col の値を取得します。 -- COALESCE関数を用いて、null値がある場合はそれを無視します。 SELECT DISTINCT(COALESCE(category_col)) FROM "my_table"

これは、すべてのページ読み込みで Postgres が、各行の category_col 値を調べて null かどうかを確認し、すべての異なる値のリストを生成する作業を繰り返さなければならないことを意味します。トランスフォームと新しい派生データセットを使用することで、この作業は上流で新しいデータが追加されるたびに一度だけ行われ、クエリは単純に次のようになります。

Copied!
1 2 -- "my_table_categories"からすべてのデータを選択する SELECT * FROM "my_table_categories"

たとえば、ユーザーの入力を考慮に入れる必要がある場合など、SQL表現を完全に除去できない場合でも、できるだけ多くの作業を上流の変換に要素分解し、より単純なクエリを作成するように努めるべきです。可能な限り少ない回数で "作業を行う" という原則を常に念頭に置くと、より安定した、高性能なアプリケーションを作成する道筋が見えてきます。

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

条件付きクエリ

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

最も単純なケースでは、All dependencies are not nullオプションは、クエリまたはその部分集合のハンドルバー表現が指定された値を持たない場合、クエリの実行を防ぎます。これは特に、意味をなす前にユーザーの入力が必要なクエリに役立ち、WHERE文が不完全であるために失敗するクエリに対するデータベースへの不必要な往復を防ぎます。

手動クエリ

クエリが実行されるタイミングを制御する簡単な戦略は、Runボタンの隣のドロップダウンでチェックボックスを使用して、それらを manualに設定することです。

手動で実行するように設定されたクエリは、イベントや手動で指定した依存関係によってトリガーされた場合にのみ実行されます。

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

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

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

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

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

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

論理をより少ない関数に統合するというバランスと、論理を分解するというバランスの間には常にバランスがあります。これは重複した作業を減らし、類似した作業を統合するのに役立つ一方で、離散的な操作をカプセル化し、必要なコードだけを見つけるのを容易にするのに役立ちます。このトレードオフを念頭に置き、アプリケーションを洗練させるにつれて定期的に関数をリファクタリングしてください。

表示行の生成

よくあるのは、チャートでの表示のためにデータを少し整形したり、テーブルでの表示のためにHTMLを生成するために少し余分な作業をする必要があるということです。可能な場合、たとえば日付のフォーマットについては、この作業をデータ変換および準備フェーズの一部として行うことを探求するべきです - これは可能な限り少ない回数で作業を行うという原則に従います(つまり、すべてのユーザーに対してページの読み込みごとに行うのではなく、変換で一度行う)。

それが不可能な場合、行の各値をマップして新しい値を生成するための簡単な関数を書くことができます。これはSlateが期待するデータ構造を保持しながら行います。

Copied!
1 2 3 4 5 6 7 8 `// f_deriveDisplayColumnsという関数 var data = {{q_myQuery.results.[0]}} data.newColA = _.map(data.primaryKey, (value,key,index) => { // 新しい列を導出するための作業 return newColValue // 新しい列の値を返す }); return data;` // データを返す

このスニペットでは、_.map()関数の Lodash バージョンを使用していますが、このケースではビルトインの Array.map() を同様に使用することができます。Lodash のヘルパー関数に慣れ親しむことは一般的に有益であり、JavaScript オブジェクトを操作する際に非常に役立つことが多く、最初から再実装することがバグが発生しやすい複雑なアルゴリズムも実装しています。

関数が完成したら、{{q_myQuery}} を直接参照する代わりに、アプリのどこでも {{f_deriveDisplayColumns}} を参照できます。これは、元のクエリ応答オブジェクトと同じ構造を持っているだけでなく、追加の「行」が追加されています。

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

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

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

関数のドキュメント化

ライブラリコードや関数の中で、常にコードをきれいに整理し、直感的でないセクションをドキュメント化する時間を取ってください。常に「自己ドキュメント化」されたコードを書くことを目指すことができますが、将来の開発者やメンテナに親切にするために、ロジックをきれいに、簡潔にドキュメント化する習慣を身につけることが常に良いことです。