本文档本部分中的大部分文章涵盖了使用应用的用户所体验的应用的运行时性能。 本文介绍由制作应用的人员所经历的应用性能。
随着应用变得越来越复杂,Power Apps Studio 需要加载和管理大量控件、公式和数据源,所有这些都具有指数级增长的相互依赖性。 Power Apps Studio 加载所需的时间可能更长,IntelliSense 和颜色编码等功能可能会滞后。 使用后续建议更好地处理 Power Apps Studio 中的大型和复杂应用。 它们还有助于提高应用的运行时性能。
本文中的示例使用 医院紧急响应示例解决方案。
使用 App.Formulas 而不是 App.OnStart
小窍门
可以使用 With 函数和画布组件自定义输出属性作为命名公式的替代方法。
减少 Power Apps Studio 和应用的加载时间的最佳方式是在 App.OnStart 中将 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
);
);
由于它们是一系列语句,因此应用必须按顺序评估这些 Set 和 Collect 调用,然后才能显示第一个屏幕,从而使应用加载速度更慢。 由于整个 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 中放置命名公式很诱人,但不要。 他们不属于那里。 作为 On 行为属性, App.OnStart 按顺序评估其每个语句,创建全局变量,并在 加载应用时仅与数据库通信一次。 命名公式是定义如何在需要时进行某些计算的公式,始终为 true。 正是这种公式性质允许它们独立,并允许应用在评估之前完成加载。
将长公式拆分开
App.OnStart 是导致公式冗长的最严重问题之一,绝对是您应优先处理的对象,但并非唯一情况。
我们的研究表明,Power Apps Studio 的几乎所有加载时间较长的应用至少有一个公式超过 256,000 个字符。 某些加载时间最长的应用的公式超过 100 万个字符。 过长的公式会给 Power Apps Studio 带来巨大压力。
更糟糕的是,将包含长公式的控件复制粘贴时,会在控件的属性中重复该公式,但这一点并不明显。 Power Apps 在 Excel 之后建模,其中公式的多个副本很常见。 但是,在 Excel 公式中,限制为一个表达式,上限为 8,000 个字符。 通过引入命令式逻辑和链式运算符(; 或 ;;,具体取决于区域设置),Power Apps 公式可以变得更加复杂。
一般解决方案是将长公式拆分为较小的部分,并重复使用这些部分,就像在 App.OnStart 中将 /Collect 语句更改为 App.Formulas 中的命名公式时所做的那样。 在其他编程语言中,可重用部件通常称为子例程或用户定义的函数。 可以将命名公式视为没有参数或副作用的用户定义函数的简单形式。
在所有地方使用命名公式
在前面的示例中,我们使用命名公式替换 App.OnStart。 但是,可以使用它们替换应用中的任意位置的计算。
例如,医院紧急响应示例解决方案中的其中一个屏幕在 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
};
之前,在我们将 App.OnStart 中的大部分 Set 调用移动到 App.Formulas 中的命名公式时,我们将 ParamFacility 提取为命名公式。
仅在需要其值时才计算命名公式。 如果使用 Screen.OnVisible 的原始意图是延迟执行直到屏幕显示,则该任务仍以 App.Formulas 中的全局命名公式的形式延迟。
使用 With 函数
还可以在公式中使用 With 函数来拆分逻辑。 在第一个参数中创建包含要用作字段的值的记录,然后使用第二个参数中的这些字段计算 With 的返回值。 例如,可以只将前面的示例编写为一个命名公式:
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 的一个缺点是 MyFacility 无法使用 MyRegion ,因为它们在同一 With 函数中定义,这是命名公式不存在的问题。 一种解决方案是嵌套 With 函数,并使用 As 关键字为每个函数命名记录,以便轻松访问所有 With 变量。
使用画布组件
画布组件通常用于创建可以像控件一样放置在画布上的 UI 控件。 还可以使用它们而不将它们放在 UI 中,以使用自定义输出属性执行计算,作为命名公式的替代方法。 画布组件可以通过组件库在应用程序之间轻松共享,且与命名公式不同的是,它们得到了完全支持。 但是,它们比命名公式更难配置和使用。
要拆分逻辑:
- 在 Power Apps Studio 中,切换到树视图中的“组件”选项卡。
- 创建新组件。
- 在 “属性 ”窗格中,打开 Access 应用范围。
- 添加自定义属性。
- 根据需要将 属性类型 设置为 “输出 ”和 “数据类型 ”。
- 选择 创建。
- 在屏幕顶部编辑栏旁边的属性选取器中,选择新属性。
- 编写用于拆分和重用逻辑的公式。
若要使用逻辑,请使用:
- 切换到树视图中的“屏幕”选项卡。
- 在 “插入 ”窗格中,展开 “自定义 ”并插入组件。
- 若要使用属性计算值,请使用 ComponentName.PropertyName。
对命令性逻辑使用带有隐藏控件的 Select
命令性逻辑用于使用 Set 和 Collect 修改状态,使用 Notify 通知用户,使用 Navigate 和 Launch 导航到另一个屏幕或应用,并使用 Patch、 SubmitForm 或 RemoveIf 将值写入数据库。
命名公式和画布组件自定义输出属性不支持命令性逻辑。 拆分命令性逻辑的一种常见方法是使用隐藏控件的 OnSelect 属性。
- 向屏幕添加 按钮 控件。
- 将 OnSelect 属性设置为要执行的命令性逻辑。
- 将 Visible 属性设置为 false,因为用户无需查看或与之交互。
- 如果要执行命令性逻辑,请调用
Select( Button )。
例如,示例中的一个屏幕在 Button 控件上具有以下 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 );
此方法仅适用于同一屏幕。 其他稍微复杂一些的技术可在屏幕上工作,例如使用 Toggle 控件、将 OnCheck 设置为要运行的逻辑,并将 “默认值 ”设置为全局变量,然后在要运行逻辑时切换全局变量 Set( global, true ); Set( global, false ) 。
在此示例中,已完成一些逻辑拆分。 评论提到“可以在 OnSuccess 中找到后续作”。成功提交记录后,此事件将运行命令性逻辑,这是 特定于 SubmitForm 函数的解决方案。
对应用进行分区
某些应用增长到数千个控件和数百个数据源,这会减慢 Power Apps Studio。 与长公式一样,大型应用可以拆分为较小的部分,共同创建一个用户体验。
独立的画布应用
一种方法是在画布应用中将各个部分作为独立的应用来实现,然后使用 Launch 函数在这些应用之间进行导航,并传递所需的上下文。
此方法在 医院紧急响应示例解决方案中使用。 单独的应用管理整个应用的每个主要区域。 应用通过每个应用在其启动屏幕上显示的组件库共享通用交换机组件:
当用户选择某个区域时,该组件将使用有关可用应用以及托管该组件的应用的元数据。 如果所需的屏幕位于此应用中(即 ThisItem.Screen 不为空),则进行 Navigate 调用。 但是,如果所需的屏幕位于其他应用中(即 ThisItem.PowerAppID 不为空),则使用 启动 函数,并将应用 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函数读取参数,将状态传递给目标应用。
具有自定义页面的模型驱动应用
部分也可以作为 自定义页面实现。 自定义页面充当具有模型驱动应用容器的微型画布应用,用于导航。