本文将为您提供关于极致性能优化:前端SSR渲染利器Qwik.js的详细介绍,我们还将为您解释前端的渲染是什么意思的相关知识,同时,我们还将为您提供关于.NET8极致性能优化CHRL、3倍+提升,高德地
本文将为您提供关于极致性能优化:前端 SSR 渲染利器 Qwik.js的详细介绍,我们还将为您解释前端的渲染是什么意思的相关知识,同时,我们还将为您提供关于.NET8 极致性能优化 CHRL、3 倍 + 提升,高德地图极致性能优化之路、3倍+提升,高德地图极致性能优化之路、Flutter 性能优化:打造高性能 widget的实用信息。
本文目录一览:- 极致性能优化:前端 SSR 渲染利器 Qwik.js(前端的渲染是什么意思)
- .NET8 极致性能优化 CHRL
- 3 倍 + 提升,高德地图极致性能优化之路
- 3倍+提升,高德地图极致性能优化之路
- Flutter 性能优化:打造高性能 widget
极致性能优化:前端 SSR 渲染利器 Qwik.js(前端的渲染是什么意思)

Qwik.js 不仅是一个前端框架,更是一种前端性能的终极解决方案。它不仅提供了卓越的性能,还以其独特的特点和优势脱颖而出。让我们一起深入探索 Qwik.js,发现它如何超越传统,成为前端性能优化的新标杆。
.NET8 极致性能优化 CHRL

前言
.NET8 在.NET7 的基础上进行了进一步的优化,比如 CHRL (全称:CORINFO_HELP_RNGCHKFAIL) 优化技术,CORINFO_HELP_RNGCHKFAIL 是边界检查,在.NET7 里面它已经进行了部分优化,但是.NET8 里面它继续优化,类似人工智能,.NET8 能意识到某些性能问题,从而进行优化。本篇来看下
原文地址:.NET8 极致性能优化 CHRL
概述
JIT 会对数组,字符串的范围边界进行检查。比如数组的索引是否在数组长度范围内,不能超过。所以 JIT 就会产生边界检查的步骤。
public class Tests
{
private byte[] _array = new byte[8];
private int _index = 4;
public void Get() => Get(_array, _index);
[MethodImpl(MethodImplOptions.NoInlining)]
private static byte Get(byte[] array, int index) => array[index];
}
Get 函数.NET7 的 ASM 如下:
; Tests.Get(Byte[], Int32)
sub rsp,28
cmp edx,[rcx+8]
jae short M01_L00
mov eax,edx
movzx eax,byte ptr [rcx+rax+10]
add rsp,28
ret
M01_L00:
call CORINFO_HELP_RNGCHKFAIL
int 3
cmp 指令把数组的 MT (方法表) 偏移 8 位置的数组长度与当前的数组索引对比,两者如果索引大于 (后者) 或等于 (jae) 数组长度 (前者) 的时候。就会跳转到 CORINFO_HELP_RNGCHKFAIL 进行边界检查,可能会引发超出引范围的异常 IndexOutOfRangeException。但是实际上这段这段代码的访问只需要两个 mov,一个是数组的索引,一个是 (MT (方法表)+0x10 + 索引)取其值返回即可。所以这个地方有清晰可见的优化的地方。
.NET8 学习了一些范围边界的智能化优化,也就说,有的地方不需要边界检查,从而把边界检查优化掉,用以提高代码的性能。下面例子:
private readonly int[] _array = new int[7];
public int GetBucket() => GetBucket(_array, 42);
private static int GetBucket(int[] buckets, int hashcode) =>
buckets[(uint)hashcode % buckets.Length];
.NET7 它的 ASM 如下:
; Tests.GetBucket()
sub rsp,28
mov rcx,[rcx+8]
mov eax,2A
mov edx,[rcx+8]
mov r8d,edx
xor edx,edx
idiv r8
cmp rdx,r8
jae short M00_L00
mov eax,[rcx+rdx*4+10]
add rsp,28
ret
M00_L00:
call CORINFO_HELP_RNGCHKFAIL
int 3
它依然进行了边界检查,然.NET8 的 JIT 能自动识别到 (uint) hashcode% buckets.Length 这个索引不可能超过数组的长度也就是 buckets.Length。所以.NET8 可以省略掉边界检查,如下.NET8 ASM
; Tests.GetBucket()
mov rcx,[rcx+8]
mov eax,2A
mov r8d,[rcx+8]
xor edx,edx
div r8
mov eax,[rcx+rdx*4+10]
ret
再看下另外一个例子:
public class Tests
{
private readonly string _s = "\"Hello, World!\"";
public bool IsQuoted() => IsQuoted(_s);
private static bool IsQuoted(string s) =>
s.Length >= 2 && s[0] == ''"'' && s[^1] == ''"'';
}
IsQuoted 检查字符串是否至少有两个字符,并且字符串开头和结尾均以引号结束,s [^1] 表示 s [s.Length - 1] 也就是字符串的长度。.NET7 ASM 如下:
; Tests.IsQuoted(System.String)
sub rsp,28
mov eax,[rcx+8]
cmp eax,2
jl short M01_L00
cmp word ptr [rcx+0C],22
jne short M01_L00
lea edx,[rax-1]
cmp edx,eax
jae short M01_L01
mov eax,edx
cmp word ptr [rcx+rax*2+0C],22
sete al
movzx eax,al
add rsp,28
ret
M01_L00:
xor eax,eax
add rsp,28
ret
M01_L01:
call CORINFO_HELP_RNGCHKFAIL
int 3
注意看.NET7 的骚操,它实际上进行了边界检查,但是只检查了一个,因为它只有一个 jae 指令跳转。这是为什么呢?JIT 已经知道不需要对 s [0] 进行边界检查,因为 s.Length >= 2 已经检查过了,只要是小于 2 的索引 (因为索引是无符号,没有负数) 都不需要检查。但是依然对 s [s.Length - 1] 进行了边界检查,所以.NET7 虽然也是骚操,但是它这个骚操不够彻底。
我们来看下彻底骚操的.NET8
; Tests.IsQuoted(System.String)
mov eax,[rcx+8]
cmp eax,2
jl short M01_L00
cmp word ptr [rcx+0C],22
jne short M01_L00
dec eax
cmp word ptr [rcx+rax*2+0C],22
sete al
movzx eax,al
ret
M01_L00:
xor eax,eax
ret
完全没有了边界检查,JIT 不仅意识到 s [0] 是安全的,因为检查过了 s.Length >= 2。因为检查过了 s.Length >= 2,还意识到 s.length> s.Length-1 >=1。所以不需要边界检查,全给它优化掉了。
可以看到.NET8 的性能优化的极致有多厉害,它基本上榨干了 JIT 的引擎,让其进行最大智能化程度的优化。
点击下加入技术讨论群:
欢迎加入.NET 技术交流群
3 倍 + 提升,高德地图极致性能优化之路


-
启动攻坚:启动耗时降低 70%+,实现 2s 地图元素完成展示,并管控保持在稳定低水位,呈下降趋势。
-
核心链路交互优化:在搜索、路线等链路上实现中高端机型秒开、低端机型 2s 内打开,整体提升用户流畅交互体验 。
-
行中内存优化:全机型优化了 30% 左右,提高稳定性。
-
包大小攻坚:双端体积降低 20%,提高安装转换率。


-
硬件设备上,一方面通过集团实验室对已知设备进行评测跑分确定高中低端机型,另一方面在用户设备上本地对硬件进行实时算力评估。
-
业务场景上,将业务分为前台展示、后台运行、交互操作等几类,一般情况下前台正在进行交互操作的业务场景优先级最高,后台数据预处理业务场景优先级最低。对于同类别业务场景,根据业务 UV、交易量、资源消耗等维度进行 PK,确定细分优先级。
-
用户行为上,结合服务用户画像和本地实时推算,确定用户功能偏好和操作习惯,为下一步针对用户的精准优化决策做准备。
-
系统状态上,一方面通过系统提供接口获取诸如内存警告、温度警告及省电模式等来获取系统极端状态,另一方面通过对内存、线程、CPU 和电量进行监控,来实时确定系统性能资源情况。
-
降级规则:在低端设备上或者系统资源紧张告警(如内存、温度告警)时,关闭高耗能功能或者低优先级功能。
-
避让规则:高优先级功能运行时,低优先级功能进行避让,如用户点击搜索框时到搜索结果完全展示到时间段内,后台低优任务进行暂停避让,保证用户交互体验。
-
预处理规则:依据用户操作及习惯进行预处理,如某用户通常在启动 3s 后,点击搜索,则在 3s 之前对该用户搜索结果进行预加载,从而在用户点击时呈现极致的交互体验效果。
-
拥塞控制规则:在设备资源紧张时,主动降低资源申请量,如 CPU 繁忙时,主动降低线程并发量;这样在高优任务到来时,避免出现资源紧缺申请不到资源性能体验问题。



3倍+提升,高德地图极致性能优化之路
1.导读
随着移动互联网的成熟发展,移动应用技术上呈现出多样化的趋势,业务上倾向打造平台及超级入口,超级应用应运而生。但业务快速扩张与有限的系统资源必然是冲突的,如何实现多(能力服务的高增长)、快(体验流畅)、好(兼容稳定)、省(资源成本低),让大象也能跳舞,成为摆在超级应用面前必须解决的问题。
伴随着高德地图APP近几年的高速发展,也面临到这些问题,从2019年开始,我们开启了一系列性能优化专项,对高德地图APP进行了深入性能分析和极致优化,取得比较显著的效果。在这个过程中总结了一系列优化思路和技术方案,希望对同样面临超级应用性能问题的你有所帮助。
经过一系列优化动作,我们在保证业务需求正常迭代新增的基础上,启动、核心链路交互、行中内存、包大小等多方面均实现了性能的成倍提升,尤其是低端机上达到了3倍+的提升,从多个维度改善了用户性能体验。
- 启动攻坚:启动耗时降低70%+,实现2s地图元素完成展示,并管控保持在稳定低水位,呈下降趋势。
- 核心链路交互优化:在搜索、路线等链路上实现中高端机型秒开、低端机型2s内打开,整体提升用户流畅交互体验 。
- 行中内存优化:全机型优化了30%左右,提高稳定性。
- 包大小攻坚:双端体积降低20%,提高安装转换率。
2.性能优化业务背景
某段时间,高德地图APP面临着性能恶化、管控困难的问题。以启动耗时为例,双端启动等待体感明显,并且历史上治理后出现反复,整体呈上升趋势,我们思考问题背后的问题,主要有以下几个方面:
业务庞大
超级应用一般都经历这样的发展过程。首先,应用提供服务给用户,用户开始增长,增长的用户会产生更多的需求。应用为满足新增需求不断迭代,提供新的服务。新的服务推动用户进一步增长,进入下一个循环。正是在这个正循环发展中,应用像滚雪球一样越滚越大,终于成为超级应用。
然而,随着业务需求的不断增长,业务量和复杂度也随着上升,系统资源会越占越多。但机器资源是有限的,资源的争夺不可避免地会导致性能问题,从而影响用户体验和业务扩展,成为超级应用正循环发展的拦路虎。
高德地图也同样经历了这样的过程,随着这几年的快速发展,应用从手机扩展到了车机,平台从iOS、Android扩展了Windows和Liunx,覆盖10多种出行方式的同时,还在不断提供组队、视频、语音、AR等新服务。与此相应的是单端代码行超百万行,线程上百,任务上千,造成了持续的性能压力。
环境复杂
性能问题面临的另一个主要挑战是超级应用的环境复杂。一方面,随着移动设备的长线发展,系统碎片化情况越来越严重,Android系统横跨11个主版本,iOS横跨14个主版本,加之设备厂商对系统进行各种各样的改造,进一步增加了系统的碎片化;另一方面,用户移动设备的环境是非常不稳定的,电量、温度的变化以及其他应用的抢占都会造成内存、CPU、GPU等资源波动。复杂不可控的环境为一致的性能体验的保持增加了很大的难度。
但作为大用户体量的超级应用,任何环境的体验都要保证。特别是地图领域,用户习惯对不同产品直接对比,任何环境下性能体验问题,都会直接影响产品的整体口碑,导致用户流失。所以需要兼顾所有环境,不只是主流机型系统和场景,在长尾场景与机型系统上也必须流畅运行,这就要求超级应用这头大象不但要在舞台上跳舞,在凳子上、甚至在水里也能跳舞。
技术链路长
为了满足研发效率提升、产品动态化等多样需求,移动应用技术上除支持原生开发外,也要支持小程序、Web H5、C基础库等跨平台、容器化、动态化开发。从高德APP来看,最顶层业务除了OC、Java外,还支持JS开发。支撑层提供了AJX、小程序、原生、C等多种容器框架,同时还涉及JS、JNI等桥接层。最下面则用C++提供地图各个引擎能力,这里包括OpenGL、定位传感器融合等多种底层能力。技术链路自上而下开始变得长且复杂,链路上任何一环都可能导致性能问题,原有的单技术语言的排查工具已经无法定位明确性能卡点模块,为性能排查和管控带来挑战。
3.解法:低成本优化迁移,长线管控
基于上面的问题,原有传统的一招鲜的优化方案,显然解决不了需求日益增长和复杂环境下的性能一致体验。所以,我们在专项实践过程中,沉淀了一套自适应资源调度框架,解决历史性能问题的同时,能够在不影响现有的研发效率的情况下,低成本优化迁移,实现新业务高性能的开发。此外,从系统底层进行全维度资源监控,自动定位分发问题,来实现长线管控,避免先治理后反弹的情况。
自适应资源调度框架
自适应资源调度框架在应用运行过程中,感知采集运行环境。然后对不同环境状态进行不同的调度决策,生成相应的性能优化策略,最终根据优化策略执行对应优化功能。与此同时,监测调度上下文以及调度策略执行效果,并将其反馈给调度决策系统,从而为进一步的决策调优提供信息输入。这样,可以做到在不同的运行环境下都能达到可预期的极致性能体验。并且,整个过程,对业务无需额外开发,做到无感接入,避免影响业务开发效率。
• 环境感知
感知环境分为硬件设备、业务场景、用户行为和系统状态四个维度:
- 硬件设备上,一方面通过集团实验室对已知设备进行评测跑分确定高中低端机型,另一方面在用户设备上本地对硬件进行实时算力评估。
- 业务场景上,将业务分为前台展示、后台运行、交互操作等几类,一般情况下前台正在进行交互操作的业务场景优先级最高,后台数据预处理业务场景优先级最低。对于同类别业务场景,根据业务UV、交易量、资源消耗等维度进行PK,确定细分优先级。
- 用户行为上,结合服务用户画像和本地实时推算,确定用户功能偏好和操作习惯,为下一步针对用户的精准优化决策做准备。
- 系统状态上,一方面通过系统提供接口获取诸如内存警告、温度警告及省电模式等来获取系统极端状态,另一方面通过对内存、线程、CPU和电量进行监控,来实时确定系统性能资源情况。
• 调度决策
感知到环境状态之后,调度系统将结合各种状态与调度规则,进行业务以及资源调配决策:
- 降级规则:在低端设备上或者系统资源紧张告警(如内存、温度告警)时,关闭高耗能功能或者低优先级功能。
- 避让规则:高优先级功能运行时,低优先级功能进行避让,如用户点击搜索框时到搜索结果完全展示到时间段内,后台低优任务进行暂停避让,保证用户交互体验。
- 预处理规则:依据用户操作及习惯进行预处理,如某用户通常在启动3s后,点击搜索,则在3s之前对该用户搜索结果进行预加载,从而在用户点击时呈现极致的交互体验效果。
- 拥塞控制规则:在设备资源紧张时,主动降低资源申请量,如CPU繁忙时,主动降低线程并发量;这样在高优任务到来时,避免出现资源紧缺申请不到资源性能体验问题。
• 策略执行
策略执行分为任务执行和硬件调优:其中任务执行,主要是通过内存缓存、数据库、线程池和网络库对相应任务的进行运行控制,来间接实现对各类资源的调度控制。而硬件调优,则是通过与系统厂商合作,直接对硬件资源进行控制,如CPU密集的高优业务开始运行时,将提高CPU频率,并将其运行线程绑定到大核上,避免线程来回切换损耗性能,最大化地调度系统资源来提升性能。
• 效果监测
在资源调度过程中对各个模块进行监测,并将环境状态、调度策略、执行记录、业务效果、资源消耗等情况反馈给调度系统,调度系则统以此来评判本次调度策略的优劣,以做进一步的调优。
全维度资源监控
由于技术链路长、关联模块复杂,原来出现性能问题时,需要所有相关方集中排查,check所有的改动代码,依赖个人经验判断代码的成本来定位问题,协作和排查成本都很高,导致性能管控有效落地阻力很大。所以我们就思考,性能问题的根本是硬件资源的竞争,那能不能逆向解题,反过来对资源成本进行监控,如果发现异常再回溯产生成本的代码,以及分发给相应owner.
那基于这个思路,在构建的时候,首先通过代码扫描建立代码模块关联库。然后,进行成本和调用栈采集。采集完成后,对基线版本和当前版本的成本进行对比,如果发现异常,则通过符号反解异常成本的调用栈直接定位到问题代码。另外,基于问题代码查找代码模块关联库,来定位问题模块,最后将问题准确分发给模块相应的owner,最终实现问题的自动定位和分发,支持团队并行解题。
管控流程体系
性能的有效长线管控,除了上面的资源监控平台,还需要配套的流程体系及组织保障。所以在APP的生命周期每个阶段都建立了从测试分析到修复验证的闭环管控。前置监控在迭代开发阶段,早发现早解决。在集成阶段监控每一个改动,保证及时处理。线上通过实时监控和动态下发,实现快速修复。
4.总结与思考
决心大于方案
超级应用的性能问题往往关联多方业务,需要多方团队协作,所以自上而下对性能的重视程度和优化决心是决定成败的关键,打通任督二脉,才能事半功倍,把优化方案顺利落地。
攻城难,守城更难
业务与技术都在快速迭代,要想保证优化成果防止反弹,管控是必须的,而管控就会有束缚和效率影响,管控过程中就难免会遇到各种各样的阻力。所以一方面技术上,建立标准规则,配合提效工具和优化流程,尽量避免影响业务开发。另一方面,团队需要具有共同认知,性能体验与功能体验同等重要,用户对比心智很强,性能体验往往与产品口碑直接挂钩。
性能优化永远在路上
目前,我们很多优化策略以及数据参数还是从实验室调校而来。未来,我们会进一步探索云端一体、端智能等技术,做到更懂用户,贴合业务和用户特点,实现性能体验的个性化提升。
Flutter 性能优化:打造高性能 widget
本文是 Flutter 性能优化系列文章之一,记录了 Flutter 团队优化 Flutter Gallery(https://gallery.flutter.dev/#/) 的实践。本文主要介绍了如何打造高性能的 widget。原文链接:https://medium.com/flutter/bu...
所有无状态和有状态 widget 都会实现 build()
方法,这个方法决定了它们是如何渲染的。app 中的一屏就可能有成百上千个部件,这些部件可能只会构建一次,或者在有动画或者某种特定的交互情况下,也有可能构建多次。如果想构建快速的 widget,你一定要很谨慎地选择构建哪些 widget,以及在什么时候构建。
这篇文章主要讨论只构建必要的和只在必要时构建,然后会分享我们是如何使用这个办法来显著提高 Flutter Gallery 的性能。我们还会分享一些高级技巧用于诊断你的 web app 中类似的问题。
只在必要时构建
一个重要的优化方法是,只在绝对必要时才构建 widget。
谨慎地调用 setState()
调用 setState
方法会引起 build()
方法调用。如果调用太多次,会使性能变慢。
看一下下面的动画,显示在前面的黑色 widget 向下滑动,露出后面类似棋盘的面板,类似于 bottom sheet 的行为。前面黑色 widget 很简单,但是后面的 widget 很忙碌。
Stack(
children: [
Back(),
PositionedTransition(
rect: RelativeRectTween(
begin: RelativeRect.fromLTRB(0, 0, 0, 0),
end: RelativeRect.fromLTRB(0, MediaQuery.of(context).size.height, 0, 0),
).animate(_animationController),
child: Front(),
)
],
),
你可能会像以下这样写父 widget,但在这个场景下,这样是错误的:
// BAD CODE
@override
void initState() {
super.initState();
_animationController = AnimationController(
duration: Duration(seconds: 3),
vsync: this,
);
_animationController.addListener(() {
setState(() {
// Rebuild when animation ticks
});
});
}
这样性能并不好。为什么?因为动画在做不必要的工作。
以下是有问题的代码:
// BAD CODE
_animationController.addListener(() {
setState(() {
// Rebuild when animation ticks.
});
});
- 这种类型的动画只在你需要让整个 widget 动起来时才推荐使用,但这并不是我们在这种布局中需要的。
- 在动画监听器中调用
setState()
会引起整个Stack
重新构建,这是完全没必要的 PositionedTransition
部件已经一个AnimatedWidget
了,所以它会在动画开始的时候自动重新构建- 不需要在这里调用
setState()
即使后面的组件是很忙碌的,前面的组件动画也可以达到 60 FPS。更多有关合理地调用 setState
方法的内容,请看 Flutter 卡顿的动画:你不该这样 setState
只构建必要的部分
除了只在必要的时候进行构建,你还需要只构建 UI 中变化的部分。接下来的章节主要关注如何创建一个高性能的 list。
优先使用 ListView.builder()
首先,让我们简单地看看显示 list 的基础:
- 竖 list 使用
Column
- 如果 list 需要滚动,使用
ListView
- 如果 list 有很多 item,使用
ListView.builder
,这个方法会在 item 滚动进入屏幕的时候才创建 item,而不是一次性创建所有的 item。这在 list 很复杂和 widget 嵌套很深的情况下,有明显的性能优势。
为了解释多 item 情况下 ListView.builder
相较于 ListView
的优势,我们来看几个例子。
在这个 DartPad 例子中运行以下 ListView
。你可以看到 8 个 item 都创建好了。(点击左下角的 Console 按钮,然后点击Run按钮。右边的输出面板没有滚动条,但是你可以滚动内容,然后通过控制台看到什么被创建了以及什么时候进行构建)
ListView(
children: [
_ListItem(index: 0),
_ListItem(index: 1),
_ListItem(index: 2),
_ListItem(index: 3),
_ListItem(index: 4),
_ListItem(index: 5),
_ListItem(index: 6),
_ListItem(index: 7),
],
);
接下来,在这个 DartPad 例子中运行 ListView.builder
。你可以看只有可见的 item 被创建了,当你滚动时,新的 item 才被创建。
ListView.builder(
itemBuilder: (context, index) {
return _ListItem(index: index);
},
itemCount: 8,
);
现在,运行这个例子。在这里例子中,ListView
的孩子都是提前一次性创建好的。在这种场景下,使用 ListView
的效率更高。
final listItems = [
_ListItem(index: 0),
_ListItem(index: 1),
_ListItem(index: 2),
_ListItem(index: 3),
_ListItem(index: 4),
_ListItem(index: 5),
_ListItem(index: 6),
_ListItem(index: 7),
];
@override
Widget build(BuildContext context) {
// 这种情况下 ListView.builder 并不会有性能上的好处
return ListView.builder(
itemBuilder: (context, index) {
return listItems[index];
},
itemCount: 8,
);
}
更多有关延迟构建 list 的内容,请看 Slivers, Demystified。
怎样通过一行代码,提升超过两倍的性能
Flutter Gallery 支持超过 100 个地区;这些地区,可能你也猜到了,是通过 ListView.builder()
来展示的。通过查看 widget 重新构建的次数,我们注意到这些 item 会在启动时进行不必要的构建。这个情况有点难发现,因为这些 item 藏在折叠了两层的菜单下:设置面板和地区列表。(后来我们发现,因为使用了 ScaleTransitioin
,设置面板在不可见状态下也会进行渲染,意味着它会不断地被构建)。
通过简单地将 ListView.builder
的 itemCount
在未展开状态下设置为 0
,我们确保了 item 只会在展开的、可见的设置面板中才进行构建。这一行改动提高了在 web 环境下渲染时间将近两倍,其中的关键是定位过度的 widget 构建。
如何查看 widget 的构建次数
虽然 Flutter 的构建是很高效的,但是也会出现过度构建导致性能问题的情况。有几种方法可以帮助定位过度的 widget 构建:
使用 Android Studio/IntelliJ
Android Studio 和 IntelliJ 开发者可以使用自带的工具来查看 widget 重新构建信息。
修改 Flutter 框架本身
如果使用的不是以上的编辑器,或者希望可以知道 web 环境下 widget 的重新构建次数,你可以在 Flutter 框架中加入几行简单的代码。
先看一下输出效果:
RaisedButton 1
RawMaterialButton 2
ExpensiveWidget 538
Header 5
先定位到文件:<Flutter path>/packages/flutter/lib/src/widgets/framework.dart
,然后加入以下代码。这些代码会在启动时统计 widget 的构建次数,并在一段时间(这里设置的是 10 秒)后输出结果。
bool _outputScheduled = false;
Map<String, int> _outputMap = <String, int>{};
void _output(Widget widget) {
final String typeName = widget.runtimeType.toString();
if (_outputMap.containsKey(typeName)) {
_outputMap[typeName] = _outputMap[typeName] + 1;
} else {
_outputMap[typeName] = 1;
}
if (_outputScheduled) {
return;
}
_outputScheduled = true;
Timer(const Duration(seconds: 10), () {
_outputMap.forEach((String key, int value) {
switch (widget.runtimeType.toString()) {
// Filter out widgets whose build counts we don''t care about
case ''InkWell'':
case ''RawGestureDetector'':
case ''FocusScope'':
break;
default:
print(''$key $value'');
}
});
});
}
然后,修改 StatelessElement
和 StatelessElement
的 build
方法来调用 _output(widget)
。
class StatelessElement extends ComponentElement {
...
@override
Widget build() {
final Widget w = widget.build(this);
_output(w);
return w;
}
class StatefulElement extends ComponentElement {
...
@override
Widget build() {
final Widget w = _state.build(this);
_output(w);
return w;
}
你可以在这里查看修改后的 framework.dart 文件。
需要注意的是,几次重新构建不一定会引起问题,但是这个办法可以通过验证不可见的 widget 是否在构建来帮你 debug 性能问题。
web 专用 tips:你可以添加一个 resetOutput
函数(可以在浏览器的控制台中调用)来获取随时获取 widget 的构建次数。
import ''dart:js'' as js;
void resetOutput() {
_outputScheduled = false;
_outputMap = <String, int>{};
}
void _output(Widget widget) {
// Add this line
js.context[''resetOutput''] = resetOutput;
...
查看修改后的 framework.dart 文件。
结语
高效的性能调优需要我们明白底层的工作原理。文章里的 tips 可以帮助你决定什么时候构建 widget 来使你的 app 在所有场景都保持高性能。
这篇文章是我们在提高 Flutter Gallery 性能中学习到的系列内容之一。希望对你有所帮助,能让你学到可以在你的 Flutter app 中用上的内容。系列文章如下:
- Flutter 性能优化系列之 tree shaking 和延迟加载
- Flutter 性能优化系列之图片占位符、预缓存和禁用导航过渡动画
- Flutter 性能优化系列之打造高性能 widget(本文)
你还可以查看适用所有水平开发者的 Flutter UI 性能文档。
欢迎关注凹凸实验室博客:aotu.io
或者关注凹凸实验室公众号(AOTULabs),不定时推送文章。
今天关于极致性能优化:前端 SSR 渲染利器 Qwik.js和前端的渲染是什么意思的讲解已经结束,谢谢您的阅读,如果想了解更多关于.NET8 极致性能优化 CHRL、3 倍 + 提升,高德地图极致性能优化之路、3倍+提升,高德地图极致性能优化之路、Flutter 性能优化:打造高性能 widget的相关知识,请在本站搜索。
本文标签: