大規模で複雑なキャンバス アプリを構築する

ドキュメントのこのセクションのほとんどの記事は、アプリを使用するユーザーが体験するアプリのランタイム パフォーマンスをカバーしています。 この記事では、アプリの作成者が経験するアプリのパフォーマンスについて説明します。

アプリが大規模で複雑になるにつれ、Power Apps Studio は多数のコントロール、数式、データソースを読み込んで管理する必要があり、これらはすべて指数関数的に増加する相互依存関係があります。 Power Apps Studio の読み込みに時間がかかり、IntelliSense や色分けなどの機能が遅くなる可能性があります。 以下の推奨事項を使用することで、Power Apps Studio の大規模で複雑なアプリをより適切に使用できます。 また、アプリのランタイム パフォーマンスの向上にも役立ちます。

この記事の例では、病院緊急時対応サンプル ソリューション を使用しています。

App.OnStart の代わりに App.Formulas を使用します

ヒント

名前付き式の代わりに、 With 関数とキャンバス コンポーネントのカスタム出力プロパティを使用することができます。

Power Apps Studio とアプリの両方の読み込み時間を短縮する最善の方法は、App.OnStart の変数とコレクションの初期化を、App.Formulas の名前付き数式 に置き換えることです。

App.OnStart を使用する、次の例を見てみましょう。

// Get the color of text on a dark background.
Set(varColorOnDark,RGBA(0, 0, 0, 1));

// Get the color of the menu icons.
Set(varColorMenuIcon,"#0070a9");

// Get the styles for a form.
Set(varFormStyle,
    {
        DataCard: { Height: 50 },
        Title: { Height: 50, Size: 21, Color: varColorOnDark },
        Control: { Height: 50, Size: 18 },
        Label: { Size: 18, Color: varColorOnDark }
    }
);

ClearCollect(
    FacilitiesList,
    ForAll(
        Facilities,
        { Name: 'Facility Name', Id: Facility }
    )
);
If(
    Not IsBlank(Param("FacilityID")),
    Set(ParamFacility,
        LookUp(
            FacilitiesList,
            Id = GUID(Param("FacilityID"))
        ).Name
    );
);

これらは一連のステートメントであるため、アプリは最初の画面を表示する前に、これらの SetCollect の呼び出しを順番に評価する必要があります。そのためアプリの読み込みが遅くなります。 また、最終結果を返す前に、App.OnStart 全体を全体として考慮し、順序を維持し、エラーを集約する必要があるため、Power Apps Studio が分析する数式は複雑です。

これには最適な方法があります。 代わりに App.Formulas を使用し、次の例のように、これらの変数とコレクションを名前付き数式として定義します。

// Get the color of text on a dark background.
varColorOnDark = RGBA(0, 0, 0, 1);

// Get the color of the menu icons.
varColorMenuIcon = "#0070a9";

// Get the styles for a form.
varFormStyle = 
    {
        DataCard: { Height: 50 },
        Title: { Height: 50, Size: 21, Color: varColorOnDark },
        Control: { Height: 50, Size: 18 },
        Label: { Size: 18, Color: varColorOnDark }
    };

FacilitiesList =
    ForAll(
        Facilities,
        { Name: 'Facility Name', Id: Facility }
    );

ParamFacility = 
    If( Not IsBlank(Param("FacilityID")),
        LookUp(
            FacilitiesList,
            Id = GUID(Param("FacilityID"))
        ).Name,
        Blank()
    );

この変化は小さく見えるかもしれませんが、大きな影響を与える可能性があります。 名前付きの各数式は他の数式より独立しているため、Power Apps Studio はそれらを個別に分析でき、大きな App.OnStart を効率的に細かく分割することができます。 この変更だけで Power Apps Studio の読み込み時間が 80% も短縮されます。

また、結果が必要になるまでこれらの数式を評価する必要がないため、アプリの読み込みも速くなります。 すぐにアプリの最初の画面が表示されます。

名前付き数式は、変更したり Set で使用したりできないため、すべての状況で使用できるわけではありません。 状況によっては、変更可能な状態変数を使用する必要があります。 Set はこのような場合に最適であり、引き続き使用する必要があります。 しかし、多くの場合、OnStart でグローバル変数を使用して、変更されない静的な値を設定しています。 そのような場合は、名前付き数式が適しています。

名前付き数式は不変であるため、名前付け規則としての接頭辞 var ("variable" の略) は適切ではなくなります。 この例では名前を変更しませんでした。これは、一致するようにアプリの残りの部分を変更する必要があるためです。

App.OnStart に名前付き数式を配置したくなりますが、配置しないでください。 そこに属するものではありません。 App.OnStart は、On 動作プロパティとして、各ステートメントを順番に評価し、グローバル変数を作成し、アプリが読み込まれるときに一度だけ データベースと通信します。 名前付き数式は、必要なときにいつでも 何かを計算する方法を定義する数式で、常に true です。 この数式性があるため、自立して、評価される前にアプリの読み込みを完了することができます。

長い式を分割する

App.OnStart は、長い数式を構成する大きな原因の一つであり、間違いなくそこから始めるべきですが、唯一のケースというわけではありません。

当社の調査によると、Power Apps Studio の読み込み時間の長いアプリのほぼ全てに、256,000 文字を超える数式が少なくとも 1 つあることが分かっています。 読み込み時間が最も長い一部のアプリには、100 万文字を超える数式が含まれています。 長い数式は、大きな負担を Power Apps Studio にかけます。

さらに悪いことに、長い数式を持つコントロールをコピーして貼り付けると、気づかないうちにコントロールのプロパティに数式を複製してしまいます。 Power Apps は、数式の複数コピーが一般的である Excel をモデルにしています。 ただし、Excel では数式は 1 つの式に制限されており、8,000 文字に制限されています。 Power Apps 式は、命令論理とチェーン演算子の導入により、大幅に長くなる可能性があります (;;; など、ロケールによる)。

一般的な解決策は、前のセクションで App.OnStartSet/Collect ステートメントを App.Formulas の名前付き数式に変更したときのように、長い数式を小さな部分に分割し、その部分を再利用することです。 他のプログラミング言語では、再利用可能な部分はサブルーチン、またはユーザー定義関数と呼ばれることがよくあります。 名前付き数式は、パラメーターや副作用のない単純な形式のユーザー定義関数と考えることができます。

名前付き数式をあらゆる場所で使用する

前の例では、App.OnStart の代わりに名前付き数式を使用しました。 ただし、それらを使用して、アプリ内の任意の場所で計算を置き換えることができます。

たとえば、病院緊急時対応サンプル ソリューションの画面の 1 つには、Screen.OnVisible の次のロジックが含まれています:

ClearCollect(
    MySplashSelectionsCollection,
    {
        MySystemCol: First(
            Filter(
                Regions,
                Region = MyParamRegion
            )
        ).System.'System Name',
        MyRegionCol: First(
            Filter(
                Regions,
                Region = MyParamRegion
            )
        ).'Region Name',
        MyFacilityCol: ParamFacility,
          MyFacilityColID:  LookUp(
            FacilitiesList,
            Id = GUID(Param("FacilityID"))
        ).Id
    }
); 

この数式は、名前付き数式のセットに分割できます。 また、数式が読みやすくなります。

MyRegion = LookUp(
                    Regions,
                    Region = MyParamRegion
           );

MyFacility = LookUp(
                    FacilitiesList,
                    Id = GUID(Param("FacilityID")
            );

MySplashSelectionsCollection = 
    {
        MySystemCol: MyRegion.System.'System Name',
        MyRegionCol: MyRegion.'Region Name',
        MyFacilityCol: ParamFacility,
        MyFacilityColID:  MyFacility.Id
    };

ParamFacility は、以前 Set 呼び出しのほとんどを App.OnStart から App.Formulas の名前付き数式に移動したときに、名前付き数式として抽出しました。

名前付き式は、その値が必要な場合にのみ評価されます。 Screen.OnVisible を使用する本来の目的が、画面が表示されるまで作業を延期することであれば、その作業は App.Formulas のグローバルな名前付き数式として延期されたままです。

With 関数を使用する

数式で With 関数を使用してロジックを分割するすることもできます。 最初のパラメーターでフィールドとして使用する値を含むレコードを作成し、2 番目のパラメーターでそれらのフィールドを使用して With からの戻り値を計算します。 たとえば、前の例は、1 つの名前付き数式として記述できます:

MySplashSelectionsCollection = 
    With( { MyRegion: LookUp(
                            Regions,
                            Region = MyParamRegion
                      ),
            MyFacility: LookUp(
                            FacilitiesList,
                            Id = GUID(Param("FacilityID")
                      ) 
           },
           {
                MySystemCol: MyRegion.System.'System Name',
                MyRegionCol: MyRegion.'Region Name',
                MyFacilityCol: ParamFacility,
                MyFacilityColID:  MyFacility.Id
           }
    )

このように With を使用することの欠点の 1 つは、MyFacility が同じ With 関数で定義されているため MyRegion を使用できないということであり、この問題は名前付き数式にはありません。 1 つの解決策は、With 関数をネストし、As キーワードを使用してそれぞれのレコードに名前を付け、 With 変数のすべてに簡単にアクセスできるようにすることです。

キャンバス コンポーネントの使用

キャンバス コンポーネントは、ほとんどの場合、コントロールのようにキャンバスに配置できる UI コントロールを作成するために使用されます。 また、名前付き数式の代わりにカスタム出力プロパティを使用して計算を実行するために、UI に配置せずに使用することもできます。 キャンバス コンポーネントは、コンポーネント ライブラリでアプリ間で共有しやすく、名前付き数式とは異なり、完全にサポートされています。 ただし、名前付き数式よりも構成と使用が難しくなります。

分割のロジック:

  1. Power Apps Studio で ツリー ビューコンポーネント タブに切り替えます。
  2. 新しいコンポーネントの作成。
  3. プロパティペインで、 アプリ スコープへのアクセス をオンにします。
  4. カスタム プロパティを追加します。
  5. 必要に応じてプロパティの種類出力、およびデータ型に選択します。
  6. 作成 を選択します。
  7. 画面上部の数式バー横にあるプロパティ ピッカーで、新しいプロパティを選択します。
  8. 分割して再利用するロジックの数式を記述します。

ロジックの使用方法:

  1. ツリー ビュー画面 タブに切り替えます。
  2. 挿入 ペインで カスタム を展開し、コンポーネントを挿入します。
  3. プロパティで値を計算するには、ComponentName.PropertyName を使用します。

命令型ロジックの非表示コントロールで Select を使用する

SetCollect で状態を変更し、Notify でユーザーに通知し、NavigateLaunch で別の画面やアプリに移動し、PatchSubmitForm、または RemoveIf で値をデータベースに書き込むために、命令型ロジックが使用されています。

名前付き数式とキャンバス コンポーネントのカスタム出力プロパティは命令型ロジックをサポートしていません。 命令型ロジックを分割する一般的な方法は、非表示コントロールの OnSelect プロパティを使用することです。

  1. 画面にボタン コントロールを追加します。
  2. OnSelect プロパティを、実行する命令型ロジックに設定します。
  3. Visible プロパティを false に設定します。これは、ユーザーが表示または操作する必要がないためです。
  4. 命令型ロジックを実行する場合は、Select( Button ) を呼び出します。

たとえば、サンプルの画面の 1 つには、ボタン コントロールに次の OnSelect プロパティがあります。 (この簡単な例は、説明のみを目的としています。 通常、この方法は長い数式にのみ使用します。)

btnAction_17.OnSelect = 
    Trace("Feedback Screen: Submit Button",TraceSeverity.Information);
    If(
        // Proceed if all forms are validated.
        And(
            FormFeedback.Valid
        ),
    
        // Set the updates to static variables.
        Set(updatesFeedback,Patch(Defaults('App Feedbacks'), FormFeedback.Updates));
        // Submit the first form. Subsequent actions can be found in the OnSuccess.
        SubmitForm(FormFeedback);
        ,
    
        Notify("Please complete all fields before proceeding",
               NotificationType.Warning,2000)
    );

このロジックを部分に分割するには、部分を個別の ボタン コントロールに配置し、元のコントロールから選択します。

btnTrace.OnSelect = 
    Trace("Feedback Screen: Submit Button",TraceSeverity.Information);

btnSubmit.OnSelect = 
    If(
        // Proceed if all forms are validated.
        And(
            FormFeedback.Valid
        ),
    
        // Set the updates to static variables.
        Set(updatesFeedback,Patch(Defaults('App Feedbacks'), FormFeedback.Updates));
        // Submit the first form. Subsequent actions can be found in OnSuccess.
        SubmitForm(FormFeedback);
        ,
    
        Notify("Please complete all fields before proceeding",
               NotificationType.Warning,2000)
    );

btnAction_17.OnSelect = 
    Select( btnTrace );
    Select( btnSubmit );

この方法は、同じ画面でのみ機能します。 少し複雑な他の方法は、トグル コントロールを使用し、OnCheck を実行するロジックに設定し、Default をグローバル変数に設定し、ロジックを実行するポイントでグローバル変数を Set( global, true ); Set( global, false ) に切り替えるなど、画面間で動作します。

この例では、いくつかのロジック分割が既に行われています。 「後続のアクションは OnSuccess で見つけることができる」というコメントが記載されています。 このイベントは、レコードが正常に送信された後に命令型ロジックを実行します。これは、SubmitForm 関数に固有のソリューションです。

アプリを分割する

一部のアプリは数千のコントロールと数百のデータ ソースにまで拡大するため、Power Apps Studio が遅くなります。 長い数式と同様に、大きなアプリは小さなセクションに分割でき、それらが連携して 1 つのユーザー エクスペリエンスを作成します。

キャンバス アプリの分割

1 つの方法は、別々のキャンバス アプリにセクションを実装し、Launch 関数を使用して別々のアプリ間を移動し、必要なコンテキストを渡すことです。

このアプローチは、病院の緊急対応時のサンプル ソリューション で使用されました。 個別のアプリが、アプリ全体の主要な領域をそれぞれ管理します。 アプリは、各アプリが起動画面に表示するコンポーネント ライブラリで、共通のスイッチボード コンポーネントを共有します:

スイッチボード キャンバス コンポーネントを示す、携帯電話で動作する病院緊急時対応サンプル ソリューション キャンバス アプリのスクリーンショット。

ユーザーが領域を選択すると、コンポーネントは利用可能なアプリと、どのアプリがコンポーネントをホストしているかについてのメタデータを使用します。 目的の画面がこのアプリ内にある (つまり、ThisItem.Screen が空白でない) 場合、Navigate の呼び出しが行われます。 ただし、目的の画面が別のアプリにある (つまり、ThisItem.PowerAppID が空白でない) 場合、Launch 関数はターゲットのアプリ ID と FacilityID コンテキストで使用されます:

If(
    IsBlank(ThisItem.Screen),
    If(IsBlank(ThisItem.PowerAppID), 
        Launch(ThisItem.URL),           
        Launch("/providers/Microsoft.PowerApps/apps/" & ThisItem.PowerAppID, 
               "FacilityID", Home_Facility_DD.Selected.Id)
    ),
    Navigate(
        ThisItem.Screen,
        Fade
    )
);

別のアプリを起動すると、元のアプリの状態は失われます。 Launch 関数を呼び出す前に、必ず状態を保存してください。 データベースに書き込むか、SaveData を呼び出すか、状態を Param 関数で読み取られるパラメーターを使用してターゲット アプリに渡します。

カスタム ページを含むモデル駆動型アプリ

セクションは カスタム ページ として実装することもできます。 カスタム ページは、ナビゲーション用のモデル駆動型アプリ コンテナーで、ミニ キャンバス アプリとして機能します。

注意

ドキュメントの言語設定についてお聞かせください。 簡単な調査を行います。 (この調査は英語です)

この調査には約 7 分かかります。 個人データは収集されません (プライバシー ステートメント)。