隨著畫布應用程式不斷發展以滿足不同的業務需求,保持最佳效能至關重要。 資料處理、使用者介面設計和應用程式功能都需要謹慎的程式碼最佳化方法。
當畫布應用程式變得更加複雜時,您可能會遇到資料擷取、公式複雜性和渲染速度的問題。 平衡強大的功能和回應式使用者介面代表您需要有系統地進行程式碼最佳化。
Power Fx 公式優化
With 函式
With 函式評估單一記錄的公式。 公式可以計算值或執行動作,例如修改資料或處理連線。 使用 With 將複雜公式劃分為更小的命名子公式,使其更易於閱讀。 這些命名值的作用類似於僅限於 With 範圍的簡單局部變數。 使用 With 比內容或全域變數更好,因為它是獨立的、易於理解的,並且可以在任何聲明性公式內容中起作用。
深入了解With 函式
Concurrent 函式
如果相同屬性中的多個公式具有連接器或 Dataverse 叫用,則 Concurrent 函數允許同時評估它們。 通常,當您使用 ; (分號) 運算子連結多個公式時,會同時對它們進行評估。 使用 Concurrent,即使在使用 ; 運算子之後,應用程式也會同時評估屬性中的所有公式。 這種並行代表使用者等待結果的時間更少。 當資料呼叫在前一個呼叫完成後才開始時,應用程式會等待所有請求時間的總和。 如果資料呼叫同時開始,應用程式僅等待最長的請求時間。
深入了解Concurrent 函式
Concurrent(
ClearCollect(colAccounts1, Accounts),
ClearCollect(colUsers1, Users),
ClearCollect(colEnvDef1, 'Environment Variable Definitions'),
ClearCollect(colEnvVal1, 'Environment Variable Values')
);
合併功能
Coalesce 函數依序評估其參數並傳回第一個非空或非空字串的值。 使用此函數可以將空白值或空字串替換為不同的值,但保持非空白值和非空字串值不變。 如果所有參數都是空白或空字串,則函數傳回空白。
Coalesce 是將空字串轉換為空值的好方法。
例如:
If(Not IsBlank(value1), value1, Not IsBlank(value2), value2)
需要對值 1 和值 2 進行兩次評估。 此函式可以簡化為:
Coalesce(value1, value2)
IsMatch 函式
IsMatch 函數測試文字字串是否與由普通字元、預定義模式或規則運算式組成的模式相符。
深入了解IsMatch 函式
例如,此公式符合美國社會安全號碼:
IsMatch(TextInput1.Text, "\d{3}-\d{2}-\d{4}")
正規表示式的解釋:
\\d 匹配任何數位(0-9)。
{3} 指定前面的數位模式 (\d) 應恰好出現三次。
- 匹配連字符。
{2} 指定前面的數位模式 (\d) 應恰好出現兩次。
{4} 指定前面的數位模式 (\d) 應恰好出現四次。
IsMatch 的更多範例:
IsMatch(TextInput1.Text, "Hello World")
IsMatch(TextInput1\_2.Text, "(?!^\[0-9\]\\\*$)(?!^\[a-zA-Z\]\\\*$)(\[a-zA-Z0-9\]{8,10})")
優化應用啟動
cavas 應用程式的 OnStart 屬性在定義應用程式啟動時發生的動作中扮演至關重要的作用。 此屬性允許應用開發人員執行全域初始化任務、設置變數以及執行在應用啟動過程中只應發生一次的操作。 理解並有效利用 OnStart 屬性對於建立回應迅速且高效的畫布應用程式至關重要。
推薦的方法是通過將變數設置遷移到命名公式來簡化 App.OnStart 函數。 命名公式(尤其是在應用生命週期早期配置的公式)被證明是有利的。 這些公式根據數據調用處理變數的初始化,為您的代碼提供更清晰、更有條理的結構。 更多詳細資訊建立大型且複雜的畫布應用程式 - Power Apps | Microsoft Learn。
注意
OnStart 屬性是強制的。 這是一個工作的排序清單,需要在顯示第一個畫面之前完成。 因為它不僅具體指出需要做什麼,而且也明確指出何時必須根據順序完成該工作,因此它限制了本來可能會以其他方式完成的重新排序和延遲最佳化。
開始畫面
如果 App.OnStart 包含一個 Navigate 函式呼叫,則即使它在一個 If 函式中且很少被呼叫,我們也必須完成應用程式的執行。在我們顯示應用程式的第一個畫面之前的 OnStart。
App.StartScreen是一個新的宣告方式,會指出應先顯示哪個畫面,這不會阻止最佳化。
在 StartScreen 完成之前,設定 App.OnStart 屬性將顯示第一個螢幕。
App.StartScreen declares 首先顯示哪個屏幕物件,而無需任何預處理。
而不是編寫這樣的代碼:
App.OnStart = Collect(OrdersCache, Orders);
If(Param("AdminMode") = "1", Navigate(AdminScreen), Navigate(HomeScreen))
將程式碼更改為:
App.OnStart = Collect(OrdersCache, Orders);
App.StartScreen = If(Param("AdminMode") = "1", AdminScreen, HomeScreen)
如需詳細資訊,請參閱 <https://Power Apps.microsoft.com/en-us/blog/app-startscreen-a-new-declarative-alternative-to-navigate-in-app-onstart/>。
警告
避免 StartScreen 和 OnStart之間的依賴關係。 參考命名公式,而公式參考全域變數,可能會導致競爭條件,從而導致 StartScreen 無法正確應用程式。
注意:StartScreen 和 OnStart 之間不應該存在相依性。 我們阻止在 StartScreen 中參考全域變數,但我們可以參考命名公式,該公式又參考全域變數,這可能會導致 StartScreen 無法正確應用程式的競爭條件。
具名公式
命名公式是靜態公式或常量,可以在 App.Formulas 部分定義。 在 App.Formulas 中聲明後,就可以在應用程式中的任何位置使用,並且它們的值始終保持最新。 在平台名稱 Power Apps 中的命名公式允許定義由平台自動管理和更新的值或值集。 此功能將價值運算和維護的責任從開發人員轉移到 Power Apps,從而簡化了開發程序。 中的 Power Apps 命名公式是一項強大的功能,可以顯著提高應用性能和可維護性。
命名公式還可以解決聲明應用程式主題的問題。 在構建企業應用的許多情況下,我們希望應用具有共同的主題,以提供一致的外觀和用戶體驗。 若要建立主題,需要在 App OnStart 上宣告數十到數百個變數。 這增加了應用的代碼長度和初始化時間。
新式控制項還可以顯著幫助主題化,並有助於減少客戶編寫的處理主題的邏輯。 現代控制目前處於預覽狀態。
例如,可以將 App.OnStart 上的以下代碼移動到 App.Formulas,從而減少全域變數聲明的啟動時間。
Set(BoardDark, RGBA(181,136,99, 1));
Set(BoardSelect, RGBA(34,177,76,1));
Set(BoardRowWidth, 10); // expected 8 plus two guard characters for regular expressions.
Set(BoardMetadata, 8 \* BoardRowWidth + 1); // which player is next, have pieces moved for castling rules, etc.
Set(BoardBlank, "----------------------------------------------------------------\_00000000000000");
Set(BoardClassic, "RNBQKBNR\_\_PPPPPPPP------------------------\_--------\_\_pppppppp\_\_rnbqkbnr\_\_0000000000");
代碼可以移動到 App.Formulas,如下所示:
BoardSize = 70;
BoardLight = RGBA(240,217,181, 1);
BoardDark = RGBA(181,136,99, 1);
BoardSelect = RGBA(34,177,76,1);
BoardRowWidth = 10; // expected 8 plus two guard characters for regular expressions
BoardMetadata = 8 \* BoardRowWidth + 1; // which player is next, have pieces moved for castling rules, etc.
BoardBlank = "----------------------------------------------------------------\_00000000000000";
BoardClassic = "RNBQKBNR\_\_PPPPPPPP------------------------\_--------\_\_pppppppp\_\_rnbqkbnr\_\_0000000000";
另一個例子是旅館設定 Lookups。 在這裡,需要在 Lookup 公式中進行更改才能從 Office 365中獲取用戶資訊,而不是 Dataverse。 只有一個地方需要更改,而無需在任何地方更改代碼。
UserEmail = User().Email;
UserInfo = LookUp(Users, 'Primary Email' = User().Email);
UserTitle = UserInfo.Title;
UserPhone = Switch(UserInfo.'Preferred Phone', 'Preferred Phone (Users)'.'Mobile Phone', UserInfo.'Mobile Phone',
UserInfo.'Main Phone');
這些公式體現了計算的本質。 它們闡明了根據其他值確定 UserEmail、UserInfo、UserTitle 和 UserPhone 的過程。 此邏輯已封裝,可在整個應用中廣泛使用,並且可以在單個位置進行修改。 這種適應性擴展到從 Dataverse 使用者表切換到 Office 365 連接器,而無需更改分散在應用程式中的公式。
另一種方法是優化 countRows。
varListItems = CountRows(SampleList)
使用 Set 函式,變數 varListItems 必須使用範例清單中的初始行數進行初始化,並在新增或刪除清單項目後再次設定。 使用命名公式,當數據更改時,varListitems 變數會自動更新。
App.Formulas 屬性中的命名公式提供了一種更靈活和聲明性的方法來管理整個應用程式中的值和計算,與僅依賴 App.OnStart 相比,在時間獨立性、自動更新、可維護性和不可變定義方面提供了優勢。
| 方面 | 命名公式(App.Formulas) | App.OnStart |
|---|---|---|
| 時序獨立性 | 公式立即生效。可以按任何順序計算。 | 變數可能會引入計時依賴性,從而影響可用性。 |
| 自動更新 | 當依賴項更改時,公式會自動更新。 | 變數在啟動時設定一次;可能需要手動更新。 |
| 可維護性 | 將公式集中在一個位置可提高可維護性。 | 分散的變數可能需要在多個位置查找和更新。 |
| 不可變的定義 | App.Formulas 中的公式定義是不可變的。 | 變數值可能容易受到意外更改的影響。 |
使用者定義的函式
Power Apps Authoring Studio 中的使用者定義函數使使用者能夠建立自己的自訂函數。
若要使用此功能,請在預覽設定下開啟使用者定義函數 (UDF)。 預覽功能不應在生產中使用,這就是它預設被停用的原因,但很快就會普遍可用。
定義公式如下 App.Formulas :
FunctionName(Parameter1:DataType1, Parameter2:DataType2):OutputDataType = Formula
代碼的工作方式如下:
FunctionName用於調用函數Parameter是輸入的名稱。 允許一個或多個輸入傳遞給函式的
DataType參數必須與此資料類型相符。 可用的資料類型包括布林值、顏色、日期、日期時間、動態、GUID、超連結、文字和時間OutputDataType是函式輸出的資料類型Formula是函式的輸出
// Function to calculate the area of a circle based on the radius
calcAreaOfCircle(radius: Number): Number =
IfError(Pi() * radius * radius, 0);
使用 IfError 在定義的函式中實現錯誤處理。
從文本/標籤控制項調用定義的函數。
calcAreaOfCircle(Int(*TextInput1*.Text))
注意
這是一項實驗性功能,且將隨時變更。 某些數據類型(如記錄和篩選器)尚不受支援。
優化變數
變數定義並設定您在整個應用程式中使用的本機值和全域值。 雖然很方便,但使用太多變數會降低應用程式的效率。
下面的範例示範如何為物件的每個屬性設定一個變數,這需要對每個屬性使用 Set。
Set(varEmpName, Office365Users.MyProfile().DisplayName);
Set(varEmpCity, Office365Users.MyProfile().City);
Set(varEmpPhone, Office365Users.MyProfile().BusinessPhones);
Set(varEmpUPN, Office365Users.MyProfile().UserPrincipalName);
Set(varEmpMgrName, Office365Users.ManagerV2(varEmpUPN).DisplayName);
更有效的方法是僅在需要時使用該屬性:
Set(varEmployee, Office365Users.MyProfile())
"Welcome " & varEmployee.DisplayName
明智地使用上下文變數和全域變數。 如果變數的範圍超出單一螢幕,請使用全域變數而不是內容變數。
太多未使用的變數會增加記憶體使用量並會減慢應用程式初始化速度。 即使您不使用這些變數,也會為它們分配資源。 未使用的變數也會增加應用程式邏輯的複雜性。 雖然影響可能並不嚴重,但保持 Power App 整潔有序是一個好習慣,可以獲得更好的效能並更輕鬆地進行開發。
優化集合
集合是用於在 Power Apps 應用程式中儲存和動作資料的暫存資料儲存結構。 但是如果過度使用集合,可能會導致效能負荷。 限制您對收藏品的使用,並且僅在必要時使用它們。
// Use this pattern
ClearCollect(colErrors, {Text: gblErrorText, Code: gblErrorCode});
// Do not use this pattern
Clear(colErrors);
Collect(colErrors, {Text: gblErrorText, Code: gblErrorCode});
若要計算本機集合中的記錄數,請使用 CountIf 而非 Count(Filter())。
使用集合時,請考慮以下指南:
限制集合的大小和數量。 由於集合對於應用程式來說是本地的,因此它們儲存在行動裝置記憶體中。 資料集合儲存的越多,或使用的集合越多,效能就越差。 使用 ShowColumns 函數僅取得特定列。 新增 Filter 功能以僅取得相關資料。
下面的示例函數返回整個資料集。
ClearCollect(colDemoAccount, Accounts);
將其與以下程式碼進行比較,該程式碼僅傳回特定的記錄和列:
ClearCollect(colAcc,
ShowColumns(
Filter(Accounts, !IsBlank('Address 1: City')),
"name","address1_city"))
此範例傳回以下資料集:
設置資料來源刷新頻率。 如果您在集合中新增記錄,請重新整理它或收集它以取得新的或變更的記錄。 如果多個使用者更新您的資料來源,請重新整理集合以取得新的或變更的記錄。 更多的重新整理呼叫意味著與伺服器的更多互動。
在集合和變數中緩存數據
集合是一個表變數,用於儲存資料的行和列,而不僅僅是單一資料項。 集合有兩個主要用途:在將資料傳送到資料來源之前對其進行彙總,以及快取資訊以避免頻繁查詢。 由於集合與資料來源和 Power Apps 的資料表結構相匹配,因此即使您處於離線狀態,它們也能讓您有效率地與資料互動。
// Clear the contents of EmployeeCollection, it already contains data
ClearCollect(
colEmployee,
{
Id: "1",
Name: "John",
Department: "IT"
},
{
Id: "2",
Name: "Nestor",
Department: "IT"
}
)
刪除未使用的變數和介質
雖然未使用的媒體和變數可能不會對應用程式效能產生重大影響,但透過刪除任何未使用的媒體或變數來清理應用程式非常重要。
未使用的媒體檔案會增加應用程式的大小,從而減慢應用程式的載入時間。
未使用的變數會增加記憶體使用量,並會稍微減慢應用程式初始化的速度。 即使未使用,也會為這些變數分配資源。 太多未使用的變數也會使應用程式的邏輯更加複雜。
使用應用檢查器查看未使用的媒體和變數。
優化螢幕和控制件
避免交叉引用控制件
引用其他螢幕上的控制項的控制項可能會減慢應用程式載入和導航的速度。 這樣做可以強制應用程式立即載入其他螢幕,而不是等到使用者前往該螢幕。 若要解決此問題,請使用變數、集合和導覽內容跨螢幕共用狀態。
Power Apps Studio 中的應用程式檢查器顯示交叉參考的控制項。 定期檢查應用程式檢查器以解決此問題。
這是交叉參考控制項的一個例子。 在下圖中,資源庫 1 控制項在螢幕 2、索引標籤 2 控制項中交叉參考。
如果您在第二個畫面中參考應用程式第一個畫面的控制項,則不會對效能造成影響,因為第一個畫面已載入。 這實際上是一件好事,因為應用程式是聲明性的,而不是使用變數。
如果參考尚未載入的控制項,例如第一個畫面參考畫面 3 中名為 Label 3 的控制項,則應用程式會將該畫面載入記憶體。
為文字控制項啟用 DelayOutput
當 DelayOutput 設定設為 true 時,會在半秒延遲後記錄使用者輸入。 這對於延遲昂貴的動作直到使用者完成輸入文字很有用,例如在其他公式中使用輸入時進行篩選。
例如,對於一個畫廊,其項目會根據使用者在 TextInput 控制項中輸入的內容進行篩選:
當 DelayOutput 設定為 false (預設值) 時,只要輸入任何文字,資源庫就會被篩選。 如果您的畫廊中有很多物品,則立即重新載入有變更的畫廊會降低效能。 最好再等一會兒。 當您使用 TextInput 作為搜尋字串時,這很實用 (請參閱搜尋或新的 StartsWith 函數)。
將 DelayOutput 設為 true 後,偵測到變更之前會有短暫的延遲。 這使您有時間完成打字。 延遲與 TextInput.OnChange 屬性配合良好。 如果您有與變更相關的動作,則您不希望在完成欄位輸入之前觸發它們。
委派和伺服器端處理
委派
Power Apps 中的委派是一個概念,指應用程式將某些作業卸載到底層資料來源的能力,而不是在 Power Apps 本身內處理作業。 通過使用委派 Power Apps,開發人員可以建立更高效且可縮放的應用程式,即使在涉及大型數據集的方案中也能很好地運行。 重要的是要了解特定資料來源和操作的委派限制,並相應地設計應用程式以實現最佳效能。
![注]並非所有函數都是可委派的。 請參閱 瞭解委派 以瞭解有關委派的更多資訊。
委派具有多種優點,例如查詢最佳化並增加了對大型資料集的支援。 此外,如果源數據頻繁更改,委派有助於使數據保持最新。
減少對資料來源的 API 調用
有時,透過在畫布應用程式中執行連接來建立集合似乎很方便。 以下是範例:
在這個例子中,有兩個表:司機表和卡車表。 該代碼建立了司機和卡車詳細資訊的集合,並且對於每輛卡車,它會呼叫擁有該卡車的司機。
// Bad code
ClearCollect(vartruckdata, AddColumns('Truck Details',
"CITY",LookUp(Drivers, 'Truck Details'\[@'Dummy ID'\] = Drivers\[@'Truck Details'\],City),
"FIRSTNAME",LookUp(Drivers, 'Truck Details'\[@'Dummy ID'\] = Drivers\[@'Truck Details'\],'Driver First Name'),
"LASTNAME",LookUp(Drivers, 'Truck Details'\[@'Dummy ID'\] = Drivers\[@'Truck Details'\],'Driver Last Name'),
"STATE",LookUp(Drivers, 'Truck Details'\[@'Dummy ID'\] = Drivers\[@'Truck Details'\],State)));
在畫布應用程式中執行此類連接會產生對資料來源的多次叫用,從而導致載入時間變慢。
更好的方法是:
// Good code
Set(
varTruckData,
LookUp(
Drivers,
'Dummy ID' = ThisRecord.'Dummy ID',
'Driver First Name'
) & LookUp(
Drivers,
'Dummy ID' = ThisRecord.'Dummy ID',
'Driver Last Name'
)
);
Set(
varTruckData,
With(
{
vDriver: LookUp(
Drivers,
'Dummy ID' = ThisRecord.'Dummy ID'
)
},
vDriver.'Driver First Name' & vDriver.'Driver Last Name'
)
)
在即時情境中,您可以透過在來源修復資料將載入時間從五分鐘縮短到十秒以內。
伺服器端處理
不同的資料來源,如 SQL 和 Dataverse,可讓您將資料處理 (如篩選器和查閱) 委託給資料來源。 在 SQL Server 中,您可以建立由查詢定義的檢視。 在 Dataverse 中,您可以建立低程式碼外掛程式來處理伺服器上的資料,並僅將最終結果傳回您的畫布應用程式。
將資料處理委託給伺服器可以提高效能,減少用戶端程式碼,並使您的應用程式更易於維護。
深入了解 Dataverse 增益集。
最佳化的查詢資料模式
使用明確資料行選項
所有新應用程式均預設啟用明確列選擇 (ECS) 功能。 如果您的應用程式尚未啟用該功能,請將其啟用。 ECS 會自動將擷取到的列數減少為僅應用程式中使用的列數。 如果 ECS 未打開,您可能會獲得超出所需數量的資料,從而影響效能。 有時,當應用程式透過集合獲取資料時,列的原始來源可能會遺失。 如果 ECS 無法判斷某一列是否已使用,則它會刪除該列。 若要強制 ECS 保留缺少的列,請在集合參考後或控制項中使用 PowerFx 運算式 ShowColumns。
避免調用 Power Automate 以填充集合
常見做法是使用 Power Automate 取得並填入 Power Apps 中的集合。 雖然這種方法是有效的,但在某些情況下它可能不是最有效的選擇。 呼叫 Power Automate 會產生網路延遲開銷,且實例化 Power Automate 流程會增加 0.6 秒的效能成本。
過度使用 Power Automate 流程也會導致執行限制和限制。 因此,請始終評估網路延遲和性能成本之間的權衡。
消除 N+1 問題
N+1 問題是資料庫查詢中的常見問題,其中不是在單個查詢中獲取所有必需的數據,而是進行多個額外查詢來檢索相關數據。 這可能會導致性能問題,因為每個額外的查詢都會產生開銷。
像這樣載入集合的簡單調用可以生成 N+1 個對資料來源的調用。
ClearCollect(MyCollection, OrdersList,
{
LookUp(CustomersList,CustomerID = OrdersList[@CustomerID])
}
)
在畫布應用程式和資源庫的上下文中,在使用顯示相關記錄的資料來源和資源庫時可能會出現 N+1 問題。 當對資源庫中顯示的每個項目進行更多查詢時,通常會出現此問題,從而導致效能瓶頸。
使用 SQL Server 中的檢視物件來避免 N+1 查詢問題,或變更使用者介面以避免觸發 N+1 場景。
Dataverse 自動獲取相關表的所需數據,您可以從相關表中選擇列。
ThisItem.Account.'Account Name'
如果 RelatedDataSource` 的尺寸較小 (< 500 筆記錄),您可以在集合中快取它,並使用該集合來驅動 Lookup (N+1) 查詢情境。
限制套件大小
儘管 Power Apps 在最佳化應用程式載入方面做了很多工作,但您可以採取措施來減少應用程式的佔用空間。 對於舊設備的使用者或延遲較高或頻寬減少的區域設置中的使用者,減少佔用空間尤為重要。
評估嵌入在應用中的媒體。 如果某些內容未使用,請將其刪除。
嵌入的圖像可能太大。 查看是否可以使用 SVG 影像而不是 PNG 檔案。 但是,在 SVG 圖像中使用文字時要小心,因為使用的字體必須安裝在用戶端上。 當您需要顯示文本時,一個很好的解決方法是在圖像上疊加文本標籤。
評估解析度是否適合外形規格。 移動應用的解析度不需要像桌面應用的解析度那樣高。 嘗試在圖像品質和大小之間取得適當的平衡。
如果您有未使用的螢幕,請將其刪除。 請注意不要刪除只有應用製作者或管理員使用的任何隱藏螢幕。
評估是否嘗試將過多的工作流放入一個應用中。 例如,您是否在同一應用中同時具有管理螢幕和客戶端螢幕? 如果是這樣,請考慮將它們分解為單獨的應用。 這種方法還將使多人更容易同時處理應用程式,並且當應用程式更改需要完整的測試通過時,它限制了「爆炸半徑」(測試量)。
最佳化 ForAll
Power Apps 中的 ForAll 函式用於迭代記錄表並對每個記錄套用一個或一組公式。 雖然函數本身是通用的,但不正確地使用 ForAll 函數可能會很快使應用的性能降低。
ForAll 函式是單一順序函式而非同時函式。 因此,它一次只查看一條記錄,獲取結果,然後繼續查看下一條記錄,直到遍歷其範圍內的所有記錄。
不惜一切代價避免嵌套 ForAll。 這可能會導致指數級反覆運算並顯著影響性能。
ClearCollect(FollowUpMeetingAttendees.ForAll(ForAll(Distinct(AttendeesList.EmailAddress.Address).Lookup(Attendees))))
批量更新到資料庫
ForAll + Patch 可以是批次更新資料庫的一種方法。 但是,在使用 For All 和 Patch 的順序時要小心。
以下函式:
Patch(SampleFoodSalesData, ForAll(colSampleFoodSales,
{
demoName:"fromCanvas2"
})
);
效能優於:
ForAll(colSampleFoodSales, Patch(SampleFoodSalesData,
{
demoName:"test"
})
);