应用启动性能的最佳实践Best practices for your app's startup performance

通过用户改进处理启动和激活的方式,创建启动时间得到优化的通用 Windows 平台 (UWP) 应用。Create Universal Windows Platform (UWP) apps with optimal startup times by improving the way you handle launch and activation.

应用启动性能的最佳实践Best practices for your app's startup performance

用户认为你的应用是快还是慢,在某种程度上取决于启动应用所需的时间。In part, users perceive whether your app is fast or slow based on how long it takes to start up. 就本主题而言,应用的启动时间从用户启动应用起算,并在用户以某些有意义的方式与应用交互时截止。For the purposes of this topic, an app's startup time begins when the user starts the app, and ends when the user can interact with the app in some meaningful way. 本部分提供了关于如何在应用启动时获取更好的性能的建议。This section provides suggestions on how to get better performance out of your app when it starts.

评估应用的启动时间Measuring your app's startup time

在你实际评估应用的启动时间前,请确保启动你的应用几次。Be sure to start your app a few times before you actually measure its startup time. 这将为你的评估提供一个基准,并确保在尽可能短的启动时间内完成评估。This gives you a baseline for your measurement and ensures that you're measuring as reasonably short a startup time as possible.

当你的 UWP 应用送达客户的计算机时,你的应用已使用 .NET Native 工具链进行编译。By the time your UWP app arrives on your customers' computers, your app has been compiled with the .NET Native toolchain. .NET Native 是一种先进的编译技术,可将 MSIL 转换为可本机运行的计算机代码。.NET Native is an ahead-of-time compilation technology that converts MSIL into natively-runnable machine code. .NET Native 应用启动速度更快、使用的内存更少,并且比其对应的 MSIL 更省电。.NET Native apps start faster, use less memory, and use less battery than their MSIL counterparts. 使用 .NET Native 生成的应用程序可静态链接到自定义运行时,而使用新的聚合 .NET Core 生成的应用程序可以在所有设备运行,因此它们不依赖于内置 .NET 实现。Applications built with .NET Native statically link in a custom runtime and the new converged .NET Core that can run on all devices, so they don’t depend on the in-box .NET implementation. 在你的开发计算机上,如果你在“发布”模式下生成你的应用,则应用默认使用 .NET Native;如果你在“调试”模式下生成你的应用,则应用默认使用 CoreCLR。On your development machine, by default your app uses .NET Native if you’re building it in “Release” mode, and it uses CoreCLR if you’re building it in “Debug” mode. 在 Visual Studio 中,你可以从“生成”页面的“属性”(C#) 或“我的项目”(VB) 中的“编译”->“高级”来配置它。You can configure this in Visual Studio from the Build page in “Properties” (C#) or Compile->Advanced in "My Project" (VB). 查找一个显示“使用 .NET Native 工具链编译”的复选框。Look for a checkbox that says “Compile with .NET Native Toolchain”.

当然,你应该获取能代表最终用户所体验的衡量基准。Of course, you should take measurements that are representative of what the end user will experience. 因此,如果你不确定是否要在你的开发计算机上将你的应用编译为本机代码,则可以在衡量应用的启动时间前,运行本机映像生成器 (Ngen.exe) 工具来预编译应用。So, if you're not sure you're compiling your app to native code on your development machine, you could run the Native Image Generator (Ngen.exe) tool to precompile your app before you measure its startup time.

以下过程描述了如何运行 Ngen.exe 来编译你的应用。The following procedure describes how to run Ngen.exe to precompile your app.

运行 Ngen.exeTo run Ngen.exe

  1. 至少运行你的应用一次,以确保 Ngen.exe 能检测到它。Run your app at least one time to ensure that Ngen.exe detects it.

  2. 通过执行以下操作之一,打开“任务计划程序” :Open the Task Scheduler by doing one of the following:

    • 从开始屏幕中搜索“任务计划程序”。Search for "Task Scheduler" from the start screen.
    • 运行“taskschd.msc”。Run "taskschd.msc."
  3. 在 “任务计划程序”的左侧窗格,展开 "任务计划程序库"。In the left-hand pane of Task Scheduler, expand Task Scheduler Library.

  4. 展开 "Microsoft."Expand Microsoft.

  5. 展开 "Windows."Expand Windows.

  6. 选择 ".NET Framework"。Select .NET Framework.

  7. 从任务列表中选择 ".NET Framework NGEN 4.x"。Select .NET Framework NGEN 4.x from the task list.

    如果你使用的是 64 位计算机,还有一个 .NET Framework NGEN v4.x 64If you are using a 64-bit computer, there is also a .NET Framework NGEN v4.x 64. 如果你要构建 64 位应用,选择 ".NET Framework NGEN v4.x 64"。If you are building a 64-bit app, select .NET Framework NGEN v4.x 64.

  8. “操作” 菜单上,单击 “运行”From the Action menu, click Run.

Ngen.exe 编译计算机上所有已被使用和不拥有本机映像的应用。Ngen.exe precompiles all the apps on the machine that have been used and do not have native images. 如果存在许多需要编译的应用,这会花费较长时间,但后续的运行会更快。If there are a lot of apps that need to be precompiled, this can take a long time, but subsequent runs are much faster.

编译你的应用时,不再使用本机映像。When you recompile your app, the native image is no longer used. 应用反而正好被编译,也即是指应用在运行时被编译。Instead, the app is just-in-time compiled, which means that it is compiled as the app runs. 你必须返回 Ngen.exe 来获取新的本机映像。You must rerun Ngen.exe to get a new native image.

尽量推迟工作Defer work as long as possible

要改善应用的启动时间,请仅处理必须要完成的工作,以让用户开始与应用交互。To improve your app's startup time, do only the work that absolutely needs to be done to let the user start interacting with the app. 如果你可延迟加载其他程序集,这样会十分有利。This can be especially beneficial if you can delay loading additional assemblies. 常见语言运行时间加载首次使用的程序集。The common language runtime loads an assembly the first time it is used. 如果你能将所加载的程序集数目降至最低,则应该能够改善应用的启动时间及其内存消耗。If you can minimize the number of assemblies that are loaded, you might be able to improve your app's startup time and its memory consumption.

独立执行长时间的运行工作Do long-running work independently

即使应用的部分功能不全,应用也可交互。Your app can be interactive even though there are parts of the app that aren't fully functional. 例如,如果你的应用显示需要些时间检索的数据,你可通过异步检索数据来确保独立于应用的启动代码的代码执行。For example, if your app displays data that takes a while to retrieve, you can make that code execute independently of the app's startup code by retrieving the data asynchronously. 数据可用时,用数据填充应用的用户界面。When the data is available, populate the app's user interface with the data.

多数用于检索数据的通用 Windows 平台 (UWP) API 都是异步的,因此无论如何你都可以异步检索数据。Many of the Universal Windows Platform (UWP) APIs that retrieve data are asynchronous, so you will probably be retrieving data asynchronously anyway. 有关异步 API 的详细信息,请参阅使用 C# 或 Visual Basic 调用异步 APIFor more info about asynchronous APIs, see Call asynchronous APIs in C# or Visual Basic. 如果处理不使用异步 API 的工作,可以使用 Task 类来处理长时间运行的工作,以便不会阻止用户与应用交互。If you do work that doesn't use asynchronous APIs, you can use the Task class to do long running work so that you don't block the user from interacting with the app. 这将使你的应用能够在加载数据时对用户作出响应。This will keep your app responsive to the user while the data loads.

如果你的应用花费很长时间来加载其部分 UI,则可以考虑在该区域添加一个字符串(如“获取最新数据”之类的提示信息),以便你的用户知道应用仍在运行。If your app takes an especially long time to load part of its UI, consider adding a string in that area that says something like, "Getting latest data," so that your users know that the app is still processing.

最小化启动时间Minimize startup time

除了最简单应用之外的所有应用都需要一段长到可以察觉的时间来在激活时加载资源、分析 XAML、设置数据结构以及运行逻辑。All but the simplest apps require a perceivable amount of time to load resources, parse XAML, set up data structures, and run logic at activation. 下面我们通过将激活过程分为三个阶段来对其进行分析。Here, we analyze the process of activation by breaking it into three phases. 我们还提供关于减少在每个阶段所花时间的提示,以及关于让应用启动的每个阶段更适合用户的技巧。We also provide tips for reducing the time spent in each phase, and techniques for making each phase of your app's startup more palatable to the user.

激活时段是指用户启动应用和该应用开始正常运行之间的那段时间。The activation period is the time between the moment a user starts the app and the moment the app is functional. 这是一段很关键的时段,因为这是用户对你的应用的第一印象。This is a critical time because it’s a user’s first impression of your app. 他们期望来自系统和应用的即时而连续的反馈。They expect instant and continuous feedback from the system and apps. 应用不能快速启动时,用户会觉得系统和应用有问题或设计得很差。The system and the app are perceived to be broken or poorly designed when apps don't start quickly. 更糟的是,如果应用激活耗时过长,进程生命期管理器 (PLM) 可能会终止它,或者用户可能会卸载它。Even worse, if an app takes too long to activate, the Process Lifetime Manager (PLM) might kill it, or the user might uninstall it.

启动阶段简介Introduction to the stages of startup

启动涉及到大量的事件操控,并且它们都需要进行正确协调,以便提供最佳用户体验。Startup involves a number of moving pieces, and all of them need to be correctly coordinated for the best user experience. 在用户单击你的应用磁贴和应用程序内容显示之间的这段时间,将发生以下事件。The following steps occur between your user clicking on your app tile and the application content being shown.

  • Windows shell 启动进程,Main 将进行调用。The Windows shell starts the process and Main is called.
  • 创建 Application 对象。The Application object is created.
    • (ProjectTemplate) 构造函数将调用 InitializeComponent,从而导致对 App.xaml 进行分析以及创建对象。(Project template) Constructor calls InitializeComponent, which causes App.xaml to be parsed and objects created.
  • 引发 Application.OnLaunched 事件。Application.OnLaunched event is raised.
    • (ProjectTemplate) 应用代码将创建一个帧,并导航到 MainPage。(ProjectTemplate) App code creates a Frame and navigates to MainPage.
    • (ProjectTemplate) Mainpage 构造函数将调用 InitializeComponent,从而导致对 MainPage.xaml 进行分析以及创建对象。(ProjectTemplate) Mainpage constructor calls InitializeComponent which causes MainPage.xaml to be parsed and objects created.
    • 调用 (ProjectTemplate) Window.Current.Activate()。ProjectTemplate) Window.Current.Activate() is called.
  • XAML 平台运行布局过程,包括 Measure 和 Arrange。XAML Platform runs the Layout pass including Measure & Arrange.
    • ApplyTemplate 将导致针对每个控件创建控件模板内容,这通常是启动的布局时间内主要执行的操作。ApplyTemplate will cause control template content to be created for each control, which is typically the bulk of Layout time for startup.
  • 将调用呈现器来为所有窗口内容创建视觉效果。Render is called to create visuals for all the window contents.
  • 框架将呈现在桌面窗口管理器 (DWM) 中。Frame is presented to the Desktop Windows Manager (DWM).

精简启动路径Do less in your Startup path

不要让你的启动代码路径包含第一帧不需要的任何内容。Keep your startup code path free from anything that is not needed for your first frame.

  • 如果你的用户 dll 中包含第一帧呈现期间不需要的任何控件,请考虑延迟加载它们。If you have user dlls containing controls that are not needed during first frame, consider delay loading them.
  • 如果你的 UI 中有一部分依赖于云中的数据,请拆分该 UI。If you have a portion of your UI dependent on data from the cloud, then split that UI. 先运行不依赖于云数据的 UI,然后异步运行依赖于云数据的 UI。First, bring up the UI that is not dependent on cloud data and asynchronously bring up the cloud-dependent UI. 你还应考虑本地缓存数据,以便应用程序可以脱机工作,或不受较慢网络连接的影响。You should also consider caching data locally so that the application will work offline or not be affected by slow network connectivity.
  • 如果你的 UI 正在等待数据,将显示进度 UI。Show progress UI if your UI is waiting for data.
  • 请留意涉及很多配置文件分析的应用设计或由代码动态生成的 UI。Be cautious of app designs that involve a lot of parsing of configuration files, or UI that is dynamically generated by code.

减少元素计数Reduce element count

XAML 应用中的启动性能与启动期间创建的元素数直接关联。Startup performance in a XAML app is directly correlated to the number of elements you create during startup. 创建的元素越少,启动应用所需的时间就越短。The fewer elements you create, the less time your app will take to start up. 作为粗略的基准,设定每个元素创建所需的时间为 1 毫秒。As a rough benchmark, consider each element to take 1ms to create.

  • 在项目控件中使用的模板具有最大的影响力,因为它们会重复使用多次。Templates used in items controls can have the biggest impact, as they are repeated multiple times. 请参阅 ListView 和 GridView UI 优化See ListView and GridView UI optimization.
  • 用户控件和控件模板将进行扩展,所以应将这些内容考虑在内。UserControls and control templates will be expanded, so those should also be taken into account.
  • 如果你创建了不会在屏幕上显示的任意 XAML,则应判断 XAML 的这些部分是否应在启动期间创建。If you create any XAML that does not appear on the screen, then you should justify whether those pieces of XAML should be created during your startup.

Visual Studio 实时可视化树窗口会显示树中每个节点的子元素计数。The Visual Studio Live Visual Tree window shows the child element counts for each node in the tree.


使用延迟Use deferral. 无法通过折叠某个元素或将其不透明度设置为 0 来阻止该元素创建。Collapsing an element, or setting its opacity to 0, will not prevent the element from being created. 可使用 x:Load 或 x:DeferLoadStrategy 来延迟部分 UI 的加载,并在需要时加载它。Using x:Load or x:DeferLoadStrategy, you can delay the loading of a piece of UI, and load it when needed. 最好延迟处理在启动屏幕期间不可见的 UI,这样你便可以在需要时加载它,或作为一组延迟逻辑的一部分加载它。This is good way to delay processing UI that is not visible during the startup screen, so that you can load it when needed, or as part of a set of delayed logic. 若要触发加载,只需针对元素调用 FindName 即可。To trigger the loading, you need only call FindName for the element. 有关示例和详细信息,请参阅 x:Load 属性x:DeferLoadStrategy 属性For an example and more information, see x:Load attribute and x:DeferLoadStrategy attribute.

虚拟化Virtualization. 如果你的 UI 中有列表或 repeater 内容,强烈建议你使用 UI 虚拟化。If you have list or repeater content in your UI then it’s highly advised that you use UI virtualization. 如果未虚拟化列表 UI,则在创建所有元素前需要花费一些开销,而这样可能会减慢启动速度。If list UI is not virtualized then you are paying the cost of creating all the elements up front, and that can slow down your startup. 请参阅 ListView 和 GridView UI 优化See ListView and GridView UI optimization.

应用程序性能不仅仅是原始性能,还包括感知方面。Application performance is not only about raw performance, it’s also about perception. 更改操作顺序以便先出现视觉方面的内容,通常会让用户觉得应用程序的启动速度更快。Changing the order of operations so that visual aspects occur first will commonly make the user feel like the application is faster. 当内容显示在屏幕上时,用户会认为应用程序已加载。Users will consider the application loaded when the content is on the screen. 通常情况下,应用程序需要执行多项操作作为启动的一部分,但并非所有这些操作都是显示 UI 所需的操作,因此应当延迟那些不必要的操作或使它们的优先级低于 UI。Commonly, applications need to do multiple things as part of the startup, and not all of that is required to bring up the UI, so those should be delayed or prioritized lower than the UI.

本主题将讨论“第一帧”,它来源于动画/电视节目,并且是内容呈现给最终用户所需时长的测量方式。This topic talks about the “first frame” which comes from animation/TV, and is a measure of how long until content is seen by the end user.

改善启动感知Improve startup perception

让我们使用一个简单的在线游戏示例来识别一下启动的每个阶段以及在整个过程中为用户提供反馈的不同技巧。Let’s use the example of a simple online game to identify each phase of startup and different techniques to give the user feedback throughout the process. 在此示例中,激活的第一个阶段是指用户点击游戏磁贴和游戏开始运行其代码之间的这段时间。For this example, the first phase of activation is the time between the user tapping the game’s tile and the game starting to run its code. 在这段时间内,系统不向用户显示任何内容来指示正确的游戏已启动。During this time, the system doesn’t have any content to display to the user to even indicate that the correct game has started. 但提供一个初始屏幕来为系统提供该内容。But providing a splash screen gives that content to the system. 接下来,当游戏开始运行代码时,游戏会通过将静态初始屏幕替换为它自己的 UI 来通知用户激活的第一个阶段已完成。The game then informs the user that the first phase of activation has completed by replacing the static splash screen with its own UI when it begins running code.

激活的第二个阶段包括创建和初始化对于游戏至关重要的结构。The second phase of activation encompasses creating and initializing structures critical for the game. 如果应用可以在激活的第一个阶段之后使用可用的数据快速创建其初始 UI,那么第二个阶段是微不足道的,你可以立即显示 UI。If an app can quickly create its initial UI with the data available after the first phase of activation, then the second phase is trivial and you can display the UI immediately. 否则,我们建议应用在进行初始化时显示一个加载页面。Otherwise we recommend that the app display a loading page while it is initialized.

加载页面的外观由你决定,并且可以使其外观像显示一个进度条或进度环一样简单。What the loading page looks like is up to you and it can be as simple as displaying a progress bar or a progress ring. 关键点是应用指示它正在执行任务,直到做出响应的那一刻。The key point is that the app indicates that it is performing tasks before becoming responsive. 就该游戏来说,它想显示其初始屏幕,但 UI 要求将某些图像和声音从磁盘加载到内存中。In the case of the game, it would like to display its initial screen but that UI requires that some images and sounds be loaded from disk into memory. 这些任务会花费几秒钟,因此应用通过将初始屏幕替换为加载页面来通知用户,该加载页面显示与游戏的主题相关的一个简单动画。These tasks take a couple of seconds, so the app keeps the user informed by replacing the splash screen with a loading page, which shows a simple animation related to the theme of the game.

在游戏有一个最小的信息集来创建交互式 UI(该 UI 将替换加载页面)之后,第三个阶段将开始。The third stage begins after the game has a minimal set of info to create an interactive UI, which replaces the loading page. 此时,在线游戏唯一可以使用的信息是应用从磁盘加载的内容。At this point the only info available to the online game is the content that the app loaded from disk. 该游戏可以提供足够的内容来创建交互式 UI,但是因为它是一个在线游戏,所以在其连接到 Internet 并下载某些附加信息之后,它才能正常运行。The game can ship with enough content to create an interactive UI; but because it’s an online game it won’t be functional until it connects to the internet and downloads some additional info. 在游戏获得正常运行所需的所有信息之后,用户才可以与 UI 进行交互,但是,那些需要来自 Web 的附加数据的功能应该提供关于内容仍在加载中的反馈。Until it has all the info it needs to be functional, the user can interact with the UI, but features that need additional data from the web should give feedback that content is still loading. 应用进入完全正常运行的状态可能需要一些时间,因此尽快使功能可用很重要。It may take some time for an app to become fully functional, so it’s important that functionality be made available as soon as possible.

既然我们已确定了该在线游戏中激活的三个阶段,让我们将它们与实际代码联系起来吧。Now that we have identified the three stages of activation in the online game, let’s tie them to actual code.

第 1 阶段Phase 1

在应用启动之前,它需要告诉系统它希望显示为初始屏幕的内容。Before an app starts, it needs to tell the system what it wants to display as the splash screen. 如示例中所示,它是通过向应用部件清单中的 SplashScreen 元素提供图像颜色和背景颜色来完成此任务的。It does so by providing an image and background color to the SplashScreen element in an app’s manifest, as in the example. 在应用开始激活之后,Windows 显示此内容。Windows displays this after the app begins activation.

<Package ...>
    <Application ...>
      <VisualElements ...>
        <SplashScreen Image="Images\splashscreen.png" BackgroundColor="#000000" />

有关详细信息,请参阅添加初始屏幕For more info, see Add a splash screen.

使用应用的构造函数仅初始化对于应用至关重要的数据结构。Use the app’s constructor only to initialize data structures that are critical to the app. 仅第一次运行应用时会调用该构造函数,而不必在每次激活应用时调用它。The constructor is called only the first time the app is run and not necessarily each time the app is activated. 例如,对于已经运行过并置于后台中、然后通过搜索合约激活的应用,不会调用构造函数。For example, the constructor isn't called for an app that has been run, placed in the background, and then activated via the search contract.

第 2 阶段Phase 2

激活应用存在许多原因,你可能希望以不同的方式处理每个原因。There are a number of reasons for an app to be activated, each of which you may want to handle differently. 你可以替代 OnActivatedOnCachedFileUpdaterActivatedOnFileActivatedOnFileOpenPickerActivatedOnFileSavePickerActivatedOnLaunchedOnSearchActivatedOnShareTargetActivated 方法来处理每个激活原因。You can override OnActivated, OnCachedFileUpdaterActivated, OnFileActivated, OnFileOpenPickerActivated, OnFileSavePickerActivated, OnLaunched, OnSearchActivated, and OnShareTargetActivated methods to handle each reason of activation. 在这些方法中,应用必须完成的一个事项是创建 UI,将其分配给 Window.Content,然后调用 Window.ActivateOne of the things that an app must do in these methods is create a UI, assign it to Window.Content, and then call Window.Activate. 此时会将初始屏幕替换为应用创建的 UI。At this point the splash screen is replaced by the UI that the app created. 如果在激活时有足够的信息可供创建此视觉对象,那么此视觉对象可以是加载屏幕或应用的实际 UI。This visual could either be loading screen or the app's actual UI if enough info is available at activation to create it.

public partial class App : Application
    // A handler for regular activation.
    async protected override void OnLaunched(LaunchActivatedEventArgs args)

        // Asynchronously restore state based on generic launch.

        // Create the ExtendedSplash screen which serves as a loading page while the
        // reader downloads the section information.
        ExtendedSplash eSplash = new ExtendedSplash();

        // Set the content of the window to the extended splash screen.
        Window.Current.Content = eSplash;

        // Notify the Window that the process of activation is completed

    // a different handler for activation via the search contract
    async protected override void OnSearchActivated(SearchActivatedEventArgs args)

        // Do an asynchronous restore based on Search activation

        // the rest of the code is the same as the OnLaunched method

partial class ExtendedSplash : Page
    // This is the UIELement that's the game's home page.
    private GameHomePage homePage;

    public ExtendedSplash()
        homePage = new GameHomePage();

    // Shown for demonstration purposes only.
    // This is typically autogenerated by Visual Studio.
    private void InitializeComponent()
    Partial Public Class App
    Inherits Application

    ' A handler for regular activation.
    Protected Overrides Async Sub OnLaunched(ByVal args As LaunchActivatedEventArgs)

        ' Asynchronously restore state based on generic launch.

        ' Create the ExtendedSplash screen which serves as a loading page while the
        ' reader downloads the section information.
        Dim eSplash As New ExtendedSplash()

        ' Set the content of the window to the extended splash screen.
        Window.Current.Content = eSplash

        ' Notify the Window that the process of activation is completed
    End Sub

    ' a different handler for activation via the search contract
    Protected Overrides Async Sub OnSearchActivated(ByVal args As SearchActivatedEventArgs)

        ' Do an asynchronous restore based on Search activation

        ' the rest of the code is the same as the OnLaunched method
    End Sub
End Class

Partial Friend Class ExtendedSplash
    Inherits Page

    Public Sub New()

        ' Downloading the data necessary for 
        ' initial UI on a background thread.
        Task.Run(Sub() DownloadData())
    End Sub

    Private Sub DownloadData()
        ' Download data to populate the initial UI.

        ' Create the first page. 
        Dim firstPage As New MainPage()

        ' Add the data just downloaded to the first page

        ' Replace the loading page, which is currently 
        ' set as the window's content, with the initial UI for the app
        Window.Current.Content = firstPage
    End Sub

    ' Shown for demonstration purposes only.
    ' This is typically autogenerated by Visual Studio.
    Private Sub InitializeComponent()
    End Sub
End Class 

在激活处理程序中显示加载页面的应用开始工作,以在后台中创建 UI。Apps that display a loading page in the activation handler begin work to create the UI in the background. 在已创建该元素之后,其 FrameworkElement.Loaded 事件发生。After that element has been created, its FrameworkElement.Loaded event occurs. 在事件处理程序中,你将窗口的内容(当前为加载屏幕)替换为新创建的主页。In the event handler you replace the window's content, which is currently the loading screen, with the newly created home page.

对于初始化时段比较长的应用,显示加载页面至关重要。It’s critical that an app with an extended initialization period show a loading page. 除了提供关于激活过程的用户反馈之外,如果在激活过程开始之后 15 秒内未调用 Window.Activate,将终止激活过程。Aside from providing the user feedback about the activation process, the process will be terminated if Window.Activate is not called within 15 seconds of the start of the activation process.

partial class GameHomePage : Page
    public GameHomePage()

        // add a handler to be called when the home page has been loaded
        this.Loaded += ReaderHomePageLoaded;

        // load the minimal amount of image and sound data from disk necessary to create the home page.        
    void ReaderHomePageLoaded(object sender, RoutedEventArgs e)
        // set the content of the window to the home page now that it's ready to be displayed.
        Window.Current.Content = this;

    // Shown for demonstration purposes only.
    // This is typically autogenerated by Visual Studio.
    private void InitializeComponent()
    Partial Friend Class GameHomePage
    Inherits Page

    Public Sub New()

        ' add a handler to be called when the home page has been loaded
        AddHandler Me.Loaded, AddressOf ReaderHomePageLoaded

        ' load the minimal amount of image and sound data from disk necessary to create the home page.        
    End Sub

    Private Sub ReaderHomePageLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
        ' set the content of the window to the home page now that it's ready to be displayed.
        Window.Current.Content = Me
    End Sub

    ' Shown for demonstration purposes only.
    ' This is typically autogenerated by Visual Studio.
    Private Sub InitializeComponent()
    End Sub
End Class

有关使用延长的初始屏幕的示例,请参阅初始屏幕示例For an example of using extended splash screens, see Splash screen sample.

第 3 阶段Phase 3

不能仅仅因为应用显示了 UI 就认为它已完全可以使用了。Just because the app displayed the UI doesn't mean it is completely ready for use. 在我们的游戏示例中,对于需要来自 Internet 的数据的功能,UI 会显示占位符。In the case of our game, the UI is displayed with placeholders for features that require data from the internet. 此时游戏将下载使应用完全可以正常运行所需的附加数据,并随着数据的获取逐步启用功能。At this point the game downloads the additional data needed to make the app fully functional and progressively enables features as data is acquired.

有时激活所需的许多内容可以与应用打包到一起。Sometimes much of the content needed for activation can be packaged with the app. 对于简单的游戏,就是如此。Such is the case with a simple game. 这样,激活过程会很简单。This makes the activation process quite simple. 但许多程序(例如新闻阅读器和照片查看器)必须从 Web 拉信息才能进入正常运行状态。But many programs (such as news readers and photo viewers) must pull information from the web to become functional. 此数据可能会很大,并会花费相当长的时间来下载。This data can be large and take a fair amount of time to download. 应用在激活过程中如何获取此数据可能会对用户对该应用的性能感知有巨大影响。How the app gets this data during the activation process can have a huge impact on the perceived performance of an app.

如果应用已尝试下载整个数据集(该应用需要该数据集才能实现激活的第 1 阶段或第 2 阶段中的功能),你可能在数分钟内显示一个加载页面,或者更糟一点,在数分钟内显示一个初始屏幕。You could display a loading page, or worse, a splash screen, for minutes if an app tried to download an entire data set it needs for functionality in phase one or two of activation. 这使应用看起来就像是已挂起,或者会导致系统终止应用。This makes an app look like it’s hung or cause it to be terminated by the system. 我们建议应用在第 2 阶段中下载最少数量的数据,以使用占位符元素显示交互式 UI,然后在第 3 阶段中逐步加载数据来替换占位符元素。We recommend that an app download the minimal amount of data to show an interactive UI with placeholder elements in phase 2 and then progressively load data, which replaces the placeholder elements, in phase 3. 有关处理数据的详细信息,请参阅优化 ListView 和 GridViewFor more info on dealing with data, see Optimize ListView and GridView.

应用对启动的每个阶段到底做出怎样的反应完全取决于你,但是,为用户提供尽可能多的反馈(初始屏幕、加载屏幕、数据加载时的 UI)会使用户感觉应用和系统作为一个整体而言速度是很快的。How exactly an app reacts to each phase of startup is completely up to you, but providing the user as much feedback as possible (splash screen, loading screen, UI while data loads) makes the user feel as though an app, and the system as a whole, are fast.

最小化启动路径中的托管程序集Minimize managed assemblies in the startup path

可重用的代码经常以在一个项目中包含的多个模块 (DLL) 的形式出现。Reusable code often comes in the form of modules (DLLs) included in a project. 加载这些模块要求访问磁盘,你可以想象得出来,这样做会增加开销。Loading these modules requires accessing the disk, and as you can imagine, the cost of doing so can add up. 虽然这对冷启动的影响最大,但对热启动同样有影响。This has the greatest impact on cold startup, but it can have an impact on warm startup, too. 对于 C# 和 Visual Basic,CLR 将通过按需加载程序集尽可能力求延迟该开销。In the case of C# and Visual Basic, the CLR tries to delay that cost as much as possible by loading assemblies on demand. 即,在已执行的方法引用某个模块之前,CLR 不会加载该模块。That is, the CLR doesn’t load a module until an executed method references it. 因此,请在启动代码中仅引用启动你的应用所必需的程序集,这样 CLR 就不会加载不必要的模块。So, reference only assemblies that are necessary to the launch of your app in startup code so that the CLR doesn’t load unnecessary modules. 如果包含不必要的引用的启动路径中有未使用的代码路径,那么你可以将这些代码路径移动到其他方法,以避免不必要的负载。If you have unused code paths in your startup path that have unnecessary references, you can move these code paths to other methods to avoid the unnecessary loads.

减少模块负载的另一个方法是组合你的应用模块。Another way to reduce module loads is to combine your app modules. 加载一个大型程序集花费的时间通常比加载两个小型程序集的时间要少。Loading one large assembly typically takes less time than loading two small ones. 该方法并非始终可用。并且,仅当组合模块不会对开发人员生产效率或代码可重用性造成实质性影响时,你才应组合模块。This is not always possible, and you should combine modules only if it doesn't make a material difference to developer productivity or code reusability. 你可以使用 PerfViewWindows 性能分析器 (WPA) 等工具来查明在启动时加载了哪些模块。You can use tools such as PerfView or the Windows Performance Analyzer (WPA) to find out what modules are loaded on startup.

发出智能 Web 请求Make smart web requests

通过以本地方式将应用的内容(包括 XAML、图像和对应用非常重要的任何其他文件)打包,可极大地缩短应用的加载时间。You can dramatically improve the loading time of an app by packaging its contents locally, including XAML, images, and any other files important to the app. 磁盘操作的速度快于网络操作。Disk operations are faster than network operations. 如果应用在初始化时需要某个特定文件,你可以通过从磁盘加载该文件(而不是从远程服务器检索该文件)来缩短总启动时间。If an app needs a particular file at initialization, you can reduce the overall startup time by loading it from disk instead of retrieving it from a remote server.

对页面进行高效日记记录和缓存Journal and Cache Pages Efficiently

帧控件提供导航功能。The Frame control provides navigation features. 该功能提供到 Page 的导航(Navigate 方法)、导航日记记录(BackStack/ForwardStack 属性、GoForward/GoBack 方法)、页面缓存 (Page.NavigationCacheMode) 以及序列化支持(GetNavigationState 方法)。It offers navigation to a Page (Navigate method), navigation journaling (BackStack/ForwardStack properties, GoForward/GoBack method), Page caching (Page.NavigationCacheMode), and serialization support (GetNavigationState method).

需要注意的帧性能主要围绕日记记录和页面缓存展开。The performance to be aware of with Frame is primarily around the journaling and page caching.

帧日记记录Frame journaling. 当导航到带有 Frame.Navigate() 的页面时,当前页面的 PageStackEntry 将添加到 Frame.BackStack 集合中。When you navigate to a page with Frame.Navigate(), a PageStackEntry for the current page is added to Frame.BackStack collection. PageStackEntry 相对较小,但并未针对 BackStack 集合的大小内置任何限制。PageStackEntry is relatively small, but there’s no built-in limit to the size of the BackStack collection. 用户可以循环导航,并且可以无限增大该集合。Potentially, a user could navigate in a loop and grow this collection indefinitely.

PageStackEntry 还包括已传递给 Frame.Navigate() 方法的参数。The PageStackEntry also includes the parameter that was passed to the Frame.Navigate() method. 建议将该参数作为原始可序列化类型(如整数或字符串),以便 Frame.GetNavigationState() 方法可以正常运行。It’s recommended that that parameter be a primitive serializable type (such as an int or string), in order to allow the Frame.GetNavigationState() method to work. 不过,该参数可能会引用占用大量工作集或其他资源的对象,从而使 BackStack 中每个项所需的开销变得更大。But that parameter could potentially reference an object that accounts for more significant amounts of working set or other resources, making each entry in the BackStack that much more expensive. 例如,你可能会将 StorageFile 用作参数,而使得 BackStack 将若干个文件保持打开。For example, you could potentially use a StorageFile as a parameter, and consequently the BackStack is keeping an indefinite number of files open.

因此,建议使导航参数保持较小,并限制 BackStack 的大小。Therefore it’s recommended to keep the navigation parameters small, and to limit the size of the BackStack. BackStack 是一个标准矢量(在 C# 中为 IList,而在 C++/CX 中则为 Platform::Vector),因此可以仅通过删除项来对其进行剪裁。The BackStack is a standard vector (IList in C#, Platform::Vector in C++/CX), and so can be trimmed simply by removing entries.

页面缓存Page caching. 默认情况下,当使用 Frame.Navigate 方法导航到页面时,将为该页面实例化新的实例。By default, when you navigate to a page with the Frame.Navigate method, a new instance of the page is instantiated. 同样,如果你使用 Frame.GoBack 导航回之前的页面,将为该页面分配新的实例。Similarly, if you then navigate back to the previous page with Frame.GoBack, a new instance of the previous page is allocated.

而帧将提供一个可选的页面缓存来避免这些实例化。Frame, though, offers an optional page cache that can avoid these instantiations. 若要将某一页面放入缓存,请使用 Page.NavigationCacheMode 属性。To get a page put into the cache, use the Page.NavigationCacheMode property. 如果将该模式设置为“Required”,将强制缓存页面;如果将该模式设置为“Enabled”,则允许缓存页面。Setting that mode to Required will force the page to be cached, setting it to Enabled will allow it to be cached. 默认情况下,缓存大小为 10 个页面,不过可以使用 Frame.CacheSize 属性进行重写。By default the cache size is 10 pages, but this can be overridden with the Frame.CacheSize property. 将缓存所有 Required 页面,如果该缓存的页面数少于 CacheSize Required 页面数,还将缓存 Enabled 页面。All Required pages will be cached, and if there are fewer than CacheSize Required pages, Enabled pages can be cached as well.

页面缓存通过避免实例化来改善性能,进而提高导航性能。Page caching can help performance by avoiding instantiations, and therefore improving navigation performance. 如果过度缓存,页面缓存可能会降低性能,进而会对工作集造成影响。Page caching can hurt performance by over-caching and therefore impacting working set.

因此,建议根据你的应用程序使用页面缓存。Therefore it’s recommended to use page caching as appropriate for your application. 例如,假设你有一个显示帧中的项目列表的应用,则当你点击某一项目时,它会将该帧定位到该项目的详细信息页面。For example, say you have an app that shows a list of items in a Frame, and when you tap on an item, it navigates the frame to a detail page for that item. 列表页面应该可以设置为缓存。The list page should probably be set to cache. 如果详细信息页面对所有项目都是相同的,它应该也可以缓存。If the detail page is the same for all items, it should probably be cached as well. 但是,如果详细信息页面较为异类,最好关闭缓存。But if the detail page is more heterogeneous, it might be better to leave caching off.