</nav><div data-moniker="aspnetcore-3.1 aspnetcore-5.0 aspnetcore-6.0 aspnetcore-7.0">
讯享网
Blazor 已针对最真实的应用程序 UI 方案中的高性能进行了优化。 但是,**性能取决于开发人员是否采用正确的模式和功能。
优化呈现速度以最大程度地减少呈现工作负载并提高 UI 响应能力,这可以使 UI 呈现速度提高十倍或更高。
发生事件时,通过跳过子组件子树的重新呈现,可以消除父组件的大部分呈现成本。 只应考虑跳过呈现子树,这些子树的呈现费用非常昂贵并且会导致 UI 延迟。
在运行时,组件存在于层次结构中。 根组件(加载的第一个组件)具有子组件。 反过来,根的子项也有其自己的子组件,依此类推。 发生事件(例如用户选择某个按钮)时,以下过程确定要重新呈现哪些组件:
- 事件将被调度到呈现事件处理程序的组件。 执行事件处理程序后,将重新呈现该组件。
- 每当重新呈现组件时,都会向其每个子组件提供参数值的新副本。
- 接收一组新的参数值后,每个组件都会决定是否要重新呈现。 如果参数值可能已更改(例如,如果它们是可变对象),则组件重新呈现。
前面序列的最后两个步骤以递归方式沿着组件层次结构继续向下。 在许多情况下,将重新呈现整个子树。 针对高级组件的事件可能会导致成本高昂的重新呈现,因为必须重新呈现高级组件下的所有组件。
若要防止将递归呈现到特定子树中,请使用以下方法之一:
- 确保子组件参数为基元不可变类型,例如 、、、 和其他类似类型。 如果这些基元不可变参数值未更改,则用于检测更改的内置逻辑会自动跳过重新呈现。 如果使用 呈现子组件(其中 为 类型),则除非 更改,否则不会重新呈现 组件。
- 重写 :
- 接受非基元参数值,例如复杂的自定义模型类型、事件回调或 值。
- 如果创作了一个仅限 UI 的组件,且该组件在最初呈现后不会更改,无论使用哪个参数值。
下面的航空公司搜索工具示例使用专用字段跟踪必要的信息以检测更改。 上一个入站航班标识符 () 和上一个出站航班标识符 () 会跟踪下一次可能的组件更新的信息。 如果在 中设置组件的参数,并且任一航班标识符发生更改,则会重新呈现组件,因为 设置为了 。 如果在检查航班标识符后 的计算结果为 ,则可以避免成本高昂的重新呈现过程:
讯享网
事件处理程序还可以将 设置为 。 对于大多数组件,通常不需要在单个事件处理程序级别确定重新呈现。
有关更多信息,请参见以下资源:
- ASP.NET Core Razor 组件生命周期
- ASP.NET Core Razor 组件呈现
在循环中呈现大量 UI(例如,具有数千个条目的列表或网格)时,呈现操作的极大数量可能会导致 UI 呈现延迟。 假设用户只能在不滚动的情况下同时查看少量元素,则花费时间呈现当前不可见的元素通常太浪费了。
Blazor 提供了 组件,用于创建任意规模的列表的外观和滚动行为,但只会呈现当前滚动视区内的列表项。 例如,组件可以呈现一个包含 100,000 个条目的列表,但只需支付 20 个可见项的费用。
有关详细信息,请参阅 ASP.NET Core Razor 组件虚拟化。
大多数 Razor 组件不需要主动的优化工作,因为大多数组件并不会在 UI 中重复,也不会高频率重新呈现。 例如,带有 指令的可路由组件和用于呈现高级 UI 块(如对话框或窗体)的组件在大多数情况下一次仅显示一个,并且仅在响应用户手势时重新呈现。 这些组件通常不会产生较高的呈现工作负载,因此你可以自由地使用任意框架功能组合,而不必太担心呈现性能。
但是,在一些常见场景中,组件会大规模重复,并且经常会导致 UI 性能不佳:
- 包含数百个单独元素(例如输入或标签)的大型嵌套窗体。
- 包含数百行或数千个单元格的网格。
- 包含数百万个数据点的散点图。
如果将每个元素、单元格或数据点建模为单独的组件实例,则通常会有很多呈现性能变得至关重要的实例。 本部分提供了有关使此类组件变得轻量,以便 UI 保持快速且响应迅速的建议。
避免上千个组件实例
每个组件都是单独的,可以独立于其父组件和子组件进行呈现。 通过选择如何将 UI 拆分为组件的层次结构,你将控制 UI 呈现的粒度。 这可能会导致性能良好或性能不佳。
将 UI 拆分为单独的组件后,当发生事件时,可以有较小部分的 UI 重新呈现。 在包含许多行且每行都有一个按钮的表格中,你可以通过使用子组件来仅重新呈现单行,而不是整页或整个表。 但是,每个组件都需要额外的内存和 CPU 开销,用于处理其独立状态和呈现生命周期。
由 ASP.NET Core 产品单元工程师执行的测试显示,Blazor WebAssembly 应用中每个组件实例的呈现开销约为 0.06 毫秒。 测试应用呈现了一个接受三个参数的简单组件。 在内部,开销很大程度上取决于从字典中检索每个组件的状态以及传递和接收参数。 通过成倍增加,你可以看到添加 2,000 个额外的组件实例会使呈现时间增加 0.12 秒,并且用户会觉得 UI 开始变得缓慢。
可以使组件变得更轻量,以便你可以拥有更多组件。 但是,更强大技巧通常是要避免有太多要呈现的组件。 以下部分介绍了可以采用的两种方法。
有关内存管理的详细信息,请参阅托管和部署 ASP.NET Core 服务器端Blazor应用。
将子组件嵌入到其父组件中
请考虑在循环中呈现子组件的父组件的以下部分:
:
讯享网
只要不一次显示数千条消息,前面的示例就可以表现良好。 若要一次显示数千条消息,请考虑不分解出单独的 组件。 而改为将子组件内联到父组件中。 以下方法可避免呈现的子组件过多时的每组件开销,但代价是无法单独重新呈现每个子组件的标记:
在代码中定义可重用的
你可能会分解出子组件,纯粹作为重复使用呈现逻辑的方法。 如果是这种情况,可以创建可重用的呈现逻辑,而无需实现其他组件。 在任何组件的 块中,定义 。 根据需要多次从任意位置呈现片段:
讯享网
若要使 代码可跨多个组件重用,请声明 和 :
可以从不相关的组件调用前面示例中的 。 此方法可用于生成无需每组件开销即可呈现的可重用标记段库。
委托可以接受参数。 以下组件将消息 () 传递给 委托:
讯享网
前面的方法无需每组件开销即可重复使用呈现逻辑。 但是,该方法不允许单独刷新 UI 的子树,也无法在呈现 UI 的父树时跳过呈现其子树,因为没有组件边界。 只有 Razor 组件文件 () 中支持分配给 委托,并且不支持事件回叫。
对于无法由字段初始化表达式引用的非静态字段、方法或属性(如以下示例中的 ),请使用属性,而不是 的字段:
不接收太多参数
如果某个组件极频繁地重复(例如,数百或数千次),会产生传递和接收每个参数的开销。
很少有过多参数严格地限制性能,但这可能是一个因素。 对于在网格内呈现 4,000 次的 组件,传递给该组件的每个参数可能会使总呈现开销增加大约 15 毫秒。 传递十个参数需要大约 150 毫秒,并导致 UI 呈现滞后。
若要减少参数负载,请捆绑自定义类中的多个参数。 例如,表单元格组件可能接受一个公共对象。 在下面的示例中,每个单元格的 都是不同的,但 在所有单元格实例中都是通用的:
讯享网
但是,考虑到不需要表单元格组件(如前面的示例所示),而是将其逻辑内联到父组件中可能是一种改进。
如需详细了解泛型类型参数 (),请参阅以下资源:
- ASP.NET Core 的 Razor 语法参考
- ASP.NET Core Razor 组件
- ASP.NET Core Blazor 模板化组件
确保级联参数是固定的
组件具有可选的 参数:
- 如果 为 (默认值),则级联值的每个接收方都会将订阅设置为接收更改通知。 由于订阅跟踪,每个 的开销大体上都要比常规 昂贵。
- 如果 为 (例如,),则接收方会接收初始值,但不会将订阅设置为接收更新。 每个 都是轻型的,并不比常规 昂贵。
如果有大量其他组件接收级联值,则将 设置为 可提高性能。 只要有可能,就应将级联值的 设置为 。 当提供的值不会随时间而改变时,可以将 设置为 。
在组件将 作为级联值传递时,也可以将 设置为 :
有关详细信息,请参阅 ASP.NET Core Blazor 级联值和参数。
使用 避免特性展开
组件可以选择使用 标志接收“不匹配”参数值:
讯享网
此方法允许将任意附加特性传递到元素。 但是,此方法很昂贵,因为呈现器必须:
- 将所有提供的参数与用于生成字典的已知参数集匹配。
- 跟踪同一特性的多个副本彼此覆盖的方式。
在组件呈现性能不重要的情况下使用 ,例如不经常重复的组件。 对于大规模呈现的组件(如大型列表或网格单元格中的每个项),请尝试避免特性展开。
有关详细信息,请参阅 ASP.NET Core Blazor 属性展开和任意参数。
手动实现
每组件呈现开销的一个重要来源是将传入参数值写入 属性。 呈现器使用反射来写入参数值,这可能会导致大规模性能下降。
在某些极端情况下,你可能希望避免反射并手动实现自己的参数设置逻辑。 这可能适用于以下情况:
- 例如,当 UI 中有数百或数千个组件副本时,组件会非常频繁地呈现。
- 一个组件接受多个参数。
- 你会发现接收参数的开销对 UI 响应能力有明显的影响。
在极端情况下,可以重写组件的虚拟 方法,并实现自己的特定于组件的逻辑。 下面的示例特意避免了字典查找:
在上面的代码中,返回基类 会运行正常的生命周期方法,而不会再次分配参数。
正如你在前面的代码中所看到的,重写 和提供自定义逻辑比较复杂且繁琐,因此,我们通常不建议采用此方法。 在极端情况下,它可以提高 20-25% 的呈现性能,但只应在本部分前面列出的极端方案中考虑使用此方法。
某些浏览器事件极频繁地触发。 例如, 和 每秒可以触发数十或数百次。 在大多数情况下,不需要经常执行 UI 更新。 如果事件触发速度过快,可能会损害 UI 响应能力或消耗过多的 CPU 时间。
请考虑使用 JS 互操作来注册不太频繁触发的回调,而不是使用快速触发的本机事件。 例如,以下组件显示鼠标的位置,但每 500 毫秒最多只能更新一次:
讯享网
相应的 JavaScript 代码会注册用于鼠标移动的 DOM 事件侦听器。 在此示例中,事件侦听器使用 Lodash 的 函数来限制调用速率:
组件继承自 ,这会在 调用组件的事件处理程序后自动调用。 在某些情况下,在调用事件处理程序后,可能不需要或不希望触发这类重新呈现。 例如,事件处理程序可能不会修改组件状态。 在这些情况下,应用程序可以利用 接口来控制 Blazor 事件处理的行为。
若要防止重新呈现用于组件的所有事件处理程序,请实现 并提供调用事件处理程序的 任务,而无需调用 。
在以下示例中,没有向组件添加任何事件处理程序会触发这类重新呈现,因此在调用时 不会导致这种重新呈现。
:
讯享网
除了在组件中以全局方式触发事件处理程序之后阻止重新呈现,还可以通过使用以下实用工具方法,阻止在单个事件处理程序后重新呈现。
将以下 类添加到 Blazor 应用程序。 类顶部的静态操作和函数提供处理程序,这些处理程序涵盖了处理事件时 Blazor 使用的参数和返回类型之若干组合。
:
通过调用 ,调用在调用时不触发呈现的事件处理程序。
如下示例中:
- 选择会调用 的第一个按钮,会触发重新呈现。
- 选择会调用 的第二个按钮,则不会触发重新呈现。
- 选择会调用 的第三个按钮,则不会触发重新呈现并使用事件参数 ()。
:
讯享网
除了实现 接口外,利用本文所述的其他**做法还有助于减少处理事件后出现的不必要呈现。 例如,替代目标组件子组件中的 以控制重新呈现。
Blazor 在循环中为元素或组件重新创建 lambda 表达式委托可能会导致性能不佳。
事件处理一文中所示的以下组件呈现一组按钮。 每个按钮都为其 事件分配一个委托,如果要呈现的按钮不多,这样就可以了。
:
讯享网
如果使用上面的方法呈现大量按钮,则会对呈现速度产生不利影响,进而导致用户体验不佳。 下面的示例使用一个将每个按钮的 委托分配给 的按钮对象集合,来呈现大量带有单击事件回调的按钮。 以下方法不需要 Blazor 在每次呈现按钮时重新生成所有按钮委托:
:
.NET 和 JavaScript 之间的调用需要额外的开销,因为:
- 调用是异步的。
- 参数和返回值已进行 JSON 序列化,以便在 .NET 和 JavaScript 类型之间提供一种易于理解的转换机制。
此外,对于服务器端 Blazor 应用,这些调用在网络中传递。
由于每个调用都涉及一些开销,因此减少调用次数可能会有用。 请考虑以下代码,该代码在浏览器的 中存储项的集合:
讯享网
前面的示例对每个项进行单独的 JS 互操作调用。 而以下方法则会将 JS 互操作减少为单个调用:
相应的 JavaScript 函数将整个项集合存储在客户端上:
讯享网
对于 Blazor WebAssembly 应用,如果组件进行大量的 JS 互操作调用,则将单独的 JS 互操作调用滚动到单个调用中通常会显著提高性能。
从 .NET 调用 JavaScript
本部分仅适用于客户端组件。
JS 互操作调用是异步的,无论调用的代码是同步还是异步。 调用是异步的,以确保组件在服务器端和客户端呈现模式之间都兼容。 在服务器上,所有 JS 互操作调用都必须是异步的,因为它们通过网络连接发送。
如果你确定组件只在 WebAssembly 上运行,则可以选择执行同步 JS 互操作调用。 这比进行异步调用的开销略少,并且可能会导致呈现周期更少,因为在等待结果时没有中间状态。
若要在客户端组件中进行从 .NET 到 JavaScript 的同步调用,请将 强制转换为 以进行 JS 互操作调用:
在 ASP.NET Core 5.0 或更高版本的客户端组件中使用 时,可以改为同步使用 。 实现 / 并应释放以进行垃圾回收,防止内存泄漏,如以下示例所示:
讯享网
从 JavaScript 调用 .NET
本部分仅适用于客户端组件。
JS 互操作调用是异步的,无论调用的代码是同步还是异步。 调用是异步的,以确保组件在服务器端和客户端呈现模式之间都兼容。 在服务器上,所有 JS 互操作调用都必须是异步的,因为它们通过网络连接发送。
如果你确定组件只在 WebAssembly 上运行,则可以选择执行同步 JS 互操作调用。 这比进行异步调用的开销略少,并且可能会导致呈现周期更少,因为在等待结果时没有中间状态。
若要在客户端组件中进行从 JavaScript 到 .NET 的同步调用,请使用 而不是 。
同步调用在以下情况下起作用:
- 该组件仅在 WebAssembly 上执行时才呈现。
- 调用的函数以同步方式返回值。 该函数不是 方法,不会返回 .NET 或 JavaScript 。
本部分仅适用于客户端组件。
JS 互操作调用是异步的,无论调用的代码是同步还是异步。 调用是异步的,以确保组件在服务器端和客户端呈现模式之间都兼容。 在服务器上,所有 JS 互操作调用都必须是异步的,因为它们通过网络连接发送。
如果你确定组件只在 WebAssembly 上运行,则可以选择执行同步 JS 互操作调用。 这比进行异步调用的开销略少,并且可能会导致呈现周期更少,因为在等待结果时没有中间状态。
若要在客户端组件中进行从 .NET 到 JavaScript 的同步调用,请将 强制转换为 以进行 JS 互操作调用:
在 ASP.NET Core 5.0 或更高版本的客户端组件中使用 时,可以改为同步使用 。 实现 / 并应释放以进行垃圾回收,防止内存泄漏,如以下示例所示:
讯享网
本部分仅适用于 Blazor WebAssembly应用。
在 Blazor WebAssembly 上运行时,可以进行从 .NET 到 JavaScript 的已打乱调用。 这些是不执行参数或返回值的 JSON 序列化的同步调用。 内存管理以及 .NET 和 JavaScript 表示形式之间的转换的所有方面均留给开发人员处理。
讯享网
与 .NET 7 的 ASP.NET Core 之前的框架版本中的 JS 互操作 API 相比,用于 Blazor WebAssembly 应用的 JavaScript / 互操作提供了改进的性能和稳定性。
有关详细信息,请参阅 JavaScript JSImport/JSExport 与 ASP.NET Core Blazor 互操作。
预先 (AOT) 编译将 Blazor 应用的 .NET 代码直接编译到原生 WebAssembly 中,供浏览器直接执行。 AOT 编译的应用往往较大,下载用时更长,但 AOT 编译的应用通常可提供更佳的运行时性能,尤其是对于执行 CPU 密集型任务的应用。 有关详细信息,请参阅 ASP.NET Core Blazor WebAssembly 生成工具和预先 (AOT) 编译。
若要了解运行时重新链接如何最大限度地减少应用的下载大小,请参阅 ASP.NET CoreBlazor WebAssembly 生成工具和预先 (AOT) 编译。
Blazor 的 JS 互操作实现依赖于 - 这是一个性能高但内存分配较低的 JSON 序列化库。 与添加一个或多个备用 JSON 库相比,使用 不应增加应用有效负载的大小。
有关迁移指南,请参阅如何从 迁移到 。
本部分仅适用于客户端 Blazor 场景。
从 Blazor WebAssembly 应用修剪未使用的程序集会通过删除应用的二进制文件中的未使用代码来减小应用的大小。 有关详细信息,请参阅配置适用于 ASP.NET Core Blazor 的裁边器。
通过链接 Blazor WebAssembly 应用,可剪裁应用二进制文件中未使用的代码来减小应用的大小。 中间语言 (IL) 链接器仅在 配置中生成时才启用。 要从此中受益,请使用 命令发布应用用于部署,并将 -c|--configuration 选项设置为 :
本部分仅适用于客户端 Blazor 场景。
当路由需要程序集时,在运行时加载程序集。 有关详细信息,请参阅 ASP.NET Core Blazor WebAssembly 中的延迟加载程序集。
本部分仅适用于 Blazor WebAssembly应用。
发布 Blazor WebAssembly 应用时,将在发布过程中对输出内容进行静态压缩,从而减小应用的大小,并免去运行时压缩的开销。 Blazor 依赖服务器来执行内容协商和提供静态压缩的文件。
部署应用后,请验证该应用是否提供压缩的文件。 检查浏览器开发人员工具中的“网络”选项卡,并验证文件是否具有 (Brotli 压缩)或 (Gzip 压缩)。 如果主机未提供压缩的文件,请按照托管和部署 ASP.NET Core Blazor WebAssembly 中的说明操作。
本部分仅适用于客户端 Blazor 场景。
Blazor WebAssembly 的运行时包含以下 .NET 功能,可以为较小的有效负载大小禁用这些功能:
- Blazor WebAssembly 携带在用户区域性中显示值(如日期和货币)所需的全球化资源。 如果应用不需要本地化,你可以将应用配置为支持固定区域性,这基于 区域性。
- 采用固定全球化只会导致使用非本地化的时区名称。 要从应用删除时区代码和数据,请在应用的项目文件中应用值为 的 MSBuild 属性:
讯享网
- 包含数据文件来确保时区信息正确。 如果应用不需要此功能,请考虑通过将应用项目文件中的 MSBuild 属性设置为 来禁用它:
- 包括排序规则信息来确保 之类的 API 正常工作。 如果确定应用不需要排序规则数据,请考虑通过将应用项目文件中的 MSBuild 属性设置为 来禁用它:
讯享网

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/200920.html