共用方式為


設計具良好效能的模型驅動應用程式表單

建立可以快速有效地完成任務的體驗對於使用者滿意度至關重要。 模型導向應用程式可以高度自訂,以建立符合使用者需求的體驗,但請務必了解如何有效地撰寫程式碼、建置和執行模型導向應用程式,當使用者在處理日常工作時,在您的應用程式中開啟和導覽時,這些應用程式會快速載入。 當應用程式未針對效能進行最佳化時,效能已被證明是應用程式不滿意的關鍵驅動因素。

智慧型自訂和高效能表單是建置高效率且生產力表單的重要層面。 確保您使用使用者介面設計和版面配置的最佳實務來建置高生產力的表單也很重要。 如需有關設計表單以提高效率和生產力的資訊,請參閱在 模型導向應用程式中設計生產性主要表單

確保用戶使用推薦和支持的設備以及最低要求的規格也很重要。 其他資訊: 支援的網頁瀏覽器和行動裝置

處理資料與標籤頁

本節說明顯示資料和索引標籤的控制項如何影響表單效能。

預設標籤的重要性

預設索引標籤是表單上第一個展開的索引標籤。 它在表單頁面的載入中扮演特殊角色。 根據設計,預設索引標籤的控制項一律會在開啟記錄時轉譯。 具體而言,會針對索引標籤上的每個控制項叫用控制項初始化邏輯,例如資料擷取。

相反地,次要索引標籤在最初載入表單時,不會在其控制項上執行此初始化。 相反地,控制項初始化會在透過使用者互動或呼叫 setFocus 用戶端 API 方法開啟次要索引標籤時發生。 這可讓您將某些控制項放在次要索引標籤中,而不是預設索引標籤中,以保護初始表單負載免受過度控制處理的影響。因此,控制放置策略可以對初始表單負載的響應能力產生顯著影響。 回應速度更快的預設索引標籤為修改重要欄位、與命令列互動以及探索其他索引標籤和區段提供了更好的整體體驗。

一律將最常使用的控制項放在預設索引標籤的頂端。版面配置和資訊架構不僅對效能很重要,而且對於提高使用者與表單上的資料互動時的生產力也很重要。 其他資訊: 在模型導向應用程式中設計有生產力的主要表單

數據驅動的控制項

需要主要記錄以外的額外資料的控制項對表單回應性和載入速度造成最大的壓力。 這些控制項透過網路擷取資料,通常涉及等待期 (視為進度指標),因為傳輸資料可能需要一些時間。

一些數據驅動的控制包括:

僅將這些控制項中最常用的控制項保留在預設索引標籤上。其餘資料驅動控制項應該分散到次要索引標籤中,以允許預設索引標籤快速載入。 此外,這種佈局策略減少了獲取最終未使用的數據的機會。

還有其他控制項的影響較資料驅動控制項小,但仍可以參與上述版面配置策略,以達到最佳效能。 這些控制包括:

Web 瀏覽器

本節涵蓋與網頁瀏覽器搭配使用的良好作法。

不要開啟新視窗

openForm用戶端 API 方法允許參數選項在新視窗中顯示表單。 請勿使用此參數或將其設定為 false。 將它設定為 false 可確保 openForm 方法執行使用現有視窗顯示表單的預設行為。 也可以直接 window.open 從自訂腳本或其他應用程式呼叫 JavaScript 函數;不過,也應該避免這種情況。 開啟新視窗表示所有頁面資源都需要從頭開始擷取和載入,因為頁面無法利用先前載入的表單與新視窗中的表單之間的記憶體內資料快取功能。 除了打開新視窗之外,請考慮使用多工作階段體驗,讓記錄可以在多個索引標籤中打開,同時仍最大化用戶端快取的效能優勢。

使用現代瀏覽器

使用最新版本的網頁瀏覽器是確保您的模型導向應用程式能以最快速度運行的關鍵。 原因是許多性能改進只能在較新的現代瀏覽器中使用。

例如,如果您的組織有舊版 Firefox、非 Chromium 型瀏覽器等,則模型導向應用程式內建的許多效能提升在舊版瀏覽器中將無法使用,因為它們不支援應用程式所依賴的功能來快速流暢地執行。

在大多數情況下,您只需切換到 Microsoft Edge、從舊版本更新到最新的當前瀏覽器版本,或遷移到基於 Chromium 的新式瀏覽器,就可以期待看到頁面加載改進。

JavaScript 自訂

本節說明如何在使用 JavaScript 時進行智慧型自訂,以協助您在模型導向應用程式中建立高效能的表單和頁面。

將 JavaScript 與表單搭配使用

JavaScript 自訂表單的能力為專業開發人員提供了表單外觀和行為的極大靈活性。 這種靈活性的不當使用可能會對表單效能產生負面影響。 開發人員在實作JavaScript自訂時,應使用下列策略來最大化表單效能。

請求資料時使用非同步網路請求

當需要額外的資料來進行自訂時,請以非同步方式要求資料,而不是同步。 對於支援等候非同步程式碼的事件,例如表單 OnLoad 和表單 OnSave 事件,事件處理常式應該傳回 a Promise ,讓平台等候 ,直到 解決 Promise 為止。 當使用者等待事件完成時,平台將顯示適當的UI。

對於不支援等候非同步程式碼的事件,例如表單 OnChange 事件,您可以使用因應措施,透過 showProgressIndicator 暫停與表單的互動,當程式碼正在執行非同步要求時。 這比使用同步請求更好,因為用戶仍然能夠在顯示進度指示器時與應用程序的其他部分進行交互。

以下是在同步擴充點中使用非同步程式碼的範例。

//Only do this if an extension point does not yet support asynchronous code
try {
	await Xrm.WebApi.retrieveRecord("settings_entity", "7333e80e-9b0f-49b5-92c8-9b48d621c37c");
	//do other logic with data here
} catch (error) {
	//do other logic with error here
} finally {
	Xrm.Utility.closeProgressIndicator();
}

// Or using .then/.finally
Xrm.Utility.showProgressIndicator("Checking settings...");
Xrm.WebApi.retrieveRecord("settings_entity", "7333e80e-9b0f-49b5-92c8-9b48d621c37c")
	.then(
		(data) => {
			//do other logic with data here
		},
		(error) => {
			//do other logic with error here
		}
	)
	.finally(Xrm.Utility.closeProgressIndicator);

在不支援等候非同步程式碼的事件處理常式中使用非同步程式碼時,您應該小心。 對於需要在非同步程式碼解析時採取或處理動作的程式碼尤其如此。 如果解析處理常式預期應用程式內容與啟動非同步程式碼時相同,則非同步程式碼可能會導致問題。 您的程式碼應該檢查使用者在每個非同步續點之後是否處於相同的環境中。

例如,事件處理常式中可能有程式碼來提出網路要求,並根據回應資料變更要停用的控制項。 在收到要求的回應之前,使用者可能已與控制項互動或流覽至不同的頁面。 由於使用者位於不同的頁面上,因此表單內容可能無法使用,這可能會導致錯誤,或可能存在其他不想要的行為。

表單 OnLoad 和表單 OnSave 事件中的非同步支援

OnLoad 表單和 OnSave 事件支援傳回承諾的處理常式。 事件將等待處理常式所傳回的任何承諾解析,直到逾時期限。 可以透過應用程式設定啟用此支援。

其他資訊:

限制表單載入期間要求的資料量

只要求在表單上執行商務邏輯所需的最小資料量。 盡力快取要求的資料,尤其是對於不常變更或不需要重新整理的資料。 例如,假設有一個表單從 設定 表請求資料。 根據設定表中的資料,表單可能會選擇隱藏表單的某個部分。 在這種情況下,JavaScript 可以緩存資料在 sessionStorage 以便在每個會話只請求一次資料 (onLoad1)。 當 JavaScript 使用 sessionStorage 的資料,同時要求下一次瀏覽到表單 (onLoad2) 的資料時,也可以使用 stale-while-revalidate 策略。 最後,如果連續多次呼叫處理常式 (onLoad3),則可以使用重複資料刪除策略。

const SETTING_ENTITY_NAME = "settings_entity";
const SETTING_FIELD_NAME = "settingField1";
const SETTING_VALUE_SESSION_STORAGE_KEY = `${SETTING_ENTITY_NAME}_${SETTING_FIELD_NAME}`;

// Retrieve setting value once per session
async function onLoad1(executionContext) {
	let settingValue = sessionStorage.getItem(SETTING_VALUE_SESSION_STORAGE_KEY);

	// Ensure there is a stored setting value to use
	if (settingValue === null || settingValue === undefined) {
		settingValue = await requestSettingValue();
	}

	// Do logic with setting value here
}

// Retrieve setting value with stale-while-revalidate strategy
async function onLoad2(executionContext) {
	let settingValue = sessionStorage.getItem(SETTING_VALUE_SESSION_STORAGE_KEY);

    // Revalidate, but only await if session storage value is not present
	const requestPromise = requestSettingValue();

	// Ensure there is a stored setting value to use the first time in a session
	if (settingValue === null || settingValue === undefined) {
		settingValue = await requestPromise;
	}
	
	// Do logic with setting value here
}

// Retrieve setting value with stale-while-revalidate and deduplication strategy
let requestPromise;
async function onLoad3(executionContext) {
	let settingValue = sessionStorage.getItem(SETTING_VALUE_SESSION_STORAGE_KEY);

	// Request setting value again but don't wait on it
	// In case this handler fires twice, don’t make the same request again if it is already in flight
	// Additional logic can be added so that this is done less than once per page
	if (!requestPromise) {
		requestPromise = requestSettingValue().finally(() => {
			requestPromise = undefined;
		});
	}

	// Ensure there is a stored setting value to use the first time in a session
	if (settingValue === null || settingValue === undefined) {
		settingValue = await requestPromise;
	}
	
	// Do logic with setting value here
}

async function requestSettingValue() {
	try {
		const data = await Xrm.WebApi.retrieveRecord(
			SETTING_ENTITY_NAME,
			"7333e80e-9b0f-49b5-92c8-9b48d621c37c",
			`?$select=${SETTING_FIELD_NAME}`);
		try {
			sessionStorage.setItem(SETTING_VALUE_SESSION_STORAGE_KEY, data[SETTING_FIELD_NAME]);
		} catch (error) {
			// Handle sessionStorage error
		} finally {
			return data[SETTING_FIELD_NAME];
		}
	} catch (error) {
		// Handle retrieveRecord error   
	}
}

使用用戶端 API 中可用的資訊,而不是提出要求。 例如,與其在表單載入時查詢使用者的安全角色,您可以使用 getGlobalContext.userSettings.roles

僅在需要時載入程式碼

載入特定表單事件所需的全部程式碼。 如果您有僅適用於 表單 A表單 B 的程式碼,則不應將其包含在 針對表單 C 載入的程式庫中。它應該在自己的庫中。

避免在 OnLoad 事件中載入程序庫,如果它們僅用於 OnChangeOnSave 事件。 而是將程式庫載入到這些事件中。 如此一來,平台就可以將載入它們推遲到表單載入之後。 其他資訊:最佳化表單效能

移除正式環境程式碼中控制台 API 的使用

請勿使用 主控台 API 方法 ,例如 console.log 在生產程式碼中。 將資料記錄到主控台可能會大幅增加記憶體需求,並可能阻止在記憶體中清除資料。 這可能會導致應用程式隨著時間的推移變慢並最終崩潰。

避免記憶體洩漏

程式碼中的記憶體洩漏可能會導致效能隨時間推移而變慢,並最終導致應用程式當機。 當應用程式在不再需要時無法釋放記憶體時,就會發生記憶體洩漏。 透過表單上的所有自訂和程式碼元件,您應該:

  • 徹底考慮並仔細測試任何負責清理記憶體的情境,例如負責管理物件生命週期的類別。
  • 清理所有事件接聽程式和訂閱,尤其是當它位於 window 物件上時。
  • 清除所有計時器,例如 setInterval.
  • 避免、限制及清除對全域或靜態物件的參照。

對於自訂控制元件,可以在 destroy 方法中完成清理。

有關修復內存問題的更多信息,請訪問 此 Edge 開發人員文檔

可用來協助提高應用程式效能的工具

本節說明可協助您瞭解效能問題的工具,並提供有關如何在模型導向應用程式中最佳化自訂的建議。

效能洞察

效能深入解析是企業應用程式製作者的自助式工具,可分析執行階段遙測資料,並提供優先順序的建議清單,以協助改善模型導向應用程式的效能。 此功能每日提供與 Power Apps 模型導向應用程式或客戶互動應用程式 (例如 Dynamics 365 Sales 或 Dynamics 365 Service) 效能相關的分析見解,以及建議與可執行項目。 企業應用程式製作者可以在 Power Apps 中檢視應用程式層級的詳細效能深入解析。 其他資訊:什麼是效能見解?(預覽)

解決方案檢查器

解決方案檢查器是一個強大的工具,可以分析客戶端和伺服器自訂的效能或可靠性問題。 它可以解析用戶端 JavaScript、表單 XML 和 .NET 伺服器端外掛程式,並提供有針對性的見解,了解可能減慢最終使用者速度的因素。 建議您每次在開發環境中發佈變更時執行解決方案檢查器,以便在到達終端使用者之前顯示任何效能問題。 其他資訊: 使用解決方案檢查器驗證 Power Apps 中的模型導向應用程式

在解決方案檢查器中發現的效能相關問題的一些範例:

物件檢查器

物件檢查程式會對解決方案中的元件物件執行即時診斷。 如果偵測到問題,則會傳回說明如何解決問題的建議。 其他資訊: 使用物件檢查器診斷解決方案元件 (預覽版)

後續步驟

在模型導向應用程式中設計有生產力的主要表單