低コード製品のブラウザにおけるJSの評価


Appsmith 開発者が内部のアプリケーションとワークフローを構築するためのオープンソースのローコードプラットフォームです.
AppSmithでは、我々の開発者ユーザーの間に任意のJSコードを書くことによってビジネスロジックを定義します{{ }} ダイナミックなバインディングは、アプリケーションのほぼどこでも.SQLクエリ、API、またはトリガのアクションを作成しながら使用できます.
この機能を使用すると、アプリケーションの構成の最小量で動作する方法を制御することができます.フードの下に、プラットフォームは、アプリケーションがまだ効果的なままであることを確認するために最適化された方法ですべてのこのコードを評価します.
クエリウィジェットをテーブルウィジェットにバインドする例を示します.
すべては、結合ブラケットから始まります{{ }} . プラットフォームがこれらのブラケットとその中のいくつかのコードを見ると、ウィジェットまたはアクションの設定では、フィールドが動的なフィールドとしてフラグを設定し、評価者が後でそれを拾うことができます.この例ではusersqueryをuserstableにバインドしましょう

TableDataフィールドにこのバインディングを追加したので、このフィールドにフラグを付けて、ウィジェットの設定に保存します
// usersTable config
{
  "usersTable": {
        ...
        "tableData": "{{
            usersQuery.data
                .map(row => ({
                    name: row.name,
                    email: row.email
                }))
            }}",
        "dynaminBindingPathList": [
            {"key": "tableData"}
            ...
        ]
    }
}
バックグラウンドでは、我々の評価リスナーは、常に評価を必要とするようなイベントのルックアウトを続けている.私たちの例では、これは間違いなく評価を必要とするシナリオです.
我々はアプリのデータの我々の現在のリストには、我々は何を呼び出すの構築に合格DataTree 評価子スレッドに、辛抱強くそれから戻って聞くのを待つ⏱
// DataTree
{
    "usersQuery": {
        "config": {...},
        "data": [...]
    },
    "usersTable": {
        "tableData": "{{
            usersQuery.data
                .map(row => ({
                    name: row.name,
                    email: row.email
                }))
            }}",
        "dynaminBindingPathList": [{"key": "tableData"}]
    }
}
パフォーマンスの理由で、私たちは別のバックグラウンドスレッドでの評価プロセスを実行しますweb workers . これは、16 msより長く実行されている評価サイクルがユーザー・イベントに常に応えるためにアプリケーション帯域幅を与えている主な糸をハングアップさせないことを確実とします.
スレッド内で、イベントリスナーはモーニングコールを取得し、動作するようになります.

  • 違いを得る:最初にそれはDataTree 前回から.これにより、ツリー全体ではなく変更を処理することができます.
    我々の例では、我々はusersTable.tableData 変更usersTable.dynamicBindingPathList 新しいエントリがあります.
    これは、それぞれの違いを取り、任意の重要な変更をフィルタリングし、残りのプロセス.

  • 依存性マップで評価順序を取得するDependencyMap さまざまなエンティティプロパティ間.任意のバインディングが変更され、それに応じて並べ替え順序を再現した場合、評価子が通知されます.
    我々の例については、我々は推測するusersTable.tableData 今によるusersQuery.data . これは、テーブルのデータを評価する前にクエリ応答を常に評価する必要があることを意味します.クエリ応答の変更を見るたびに、テーブルデータを再評価する必要があります
    // DependencyMap
    {
        ...
        "usersTable.tableData": ["usersQuery.data"]
    }
    
    // Evaluation order
    [
        "usersQuery.data",
        "usersTable.tableData"
    ]
    

  • 評価:最適化された評価順序を作成した後、ツリーの更新を評価します.評価は閉じて起こるeval 機能全体DataTree グローバルスコープとしての役割これが直接我々のオブジェクトを参照できる理由ですDataTree 我々のコードで.
    // Evaluator
    
    const code = `
      usersQuery.data.map(row => ({
        name: row.name,
        email: row.email
      }))
    `;
    
    const scriptToEvaluate = `
      function closedFunction () {
        const result = ${code};
        return result
      }
      closedFunction()
    `;
    
    const result = eval(scriptToEvaluate);
    
  • Validate and Parse :評価後に返された値が、ウィジェットが期待する正しいデータ型にあることを確認します.あなたのコードが何らかのエラーを返したとしても、ウィジェットは常に予測可能なデータを取得します.これは、評価順序の行の下の任意の関数にも必要です.
  • そして、それはそれを完了します.この終わりに、我々は完全に評価されますDataTree その後、メインスレッドに戻ることができますし、任意の新しいイベントのためのリスニングを開始することができますこのプロセス全体を再度行う.
    // Evaluated DataTree
    {
        "usersQuery": {
            "data": [...] 
        }
        "usersTable": {
            "tableData": [...]
        }
    }
    
    我々のメインスレッドは、評価が完了したというイベントを取得しますDataTree それはApp redux状態に格納されます.ここから、ウィジェットはデータを取り出してレンダリングします.

    哲学概論


  • プル対プッシュ:様々な開発者のためのローコードアプリケーションビルダーを構築しながら、我々はハードコードの残りのプラットフォームとどのように動作する方法について考えた.我々はそれが必要なときにまだ強力な起動するように設定をしたかった.この理由から、我々はプッシュではなくプルベースのアーキテクチャで行った.
    これは、ほとんどの場所では、データがどのようにフィールドに到達するかについて考える必要はありません.あなたは世界からすべてを引き出すコードを書きますDataTree そして、あなたがそれを書くフィールドにそれをセットしてください.この方法では、基になるデータが変更された時点で、それに依存するすべてのフィールドに移動し、開発者がUIの変更を調整する必要はありません.

  • つの方法のデータフロー:我々は上に構築されて以来、反応します.JSとRedux、我々は強く一方向データフローモデルを受け入れる.
    これは、アプリケーションのいくつかの他の部分から直接そのフィールドにテーブルのデータを設定することはできませんことを意味します.テーブルを更新する必要がある場合は、実行するクエリをトリガする必要があります.これはあなたが簡単に見つけるために簡単に書くと、バグを見つけるコードを見つけることができます.それも、懸念の良い分離のために、各々の装置の、そして、行動のロジックをカプセル化します.