小程序启动流程收藏我的收藏
收藏
我的收藏小程序运行原理
在优化小程序之前,先介绍下小程序的运行原理及启动过程。小程序的运行环境分成渲染层和逻辑层,其中 TTML 模板和 TTSS 样式工作在渲染层,JS 脚本工作在逻辑层。小程序的渲染层和逻辑层分别由 2 个线程管理:
- •渲染层的界面使用了 WebView 进行渲染;
- •逻辑层采用 JSC 线程运行 JS 脚本。
一个小程序存在多个界面,所以渲染层存在多个 WebView 线程,这两个线程的通信会经由客户端做中转,逻辑层发送网络请求也经由客户端转发,两个线程间的通信是异步的,这意味着当我们调用
setData
更新数据时,不会立即渲染,而是需要从 JSC 异步传输到 WebView。小程序的通信模型下图所示:
小程序启动流程
小程序启动过程主要包括以下几个环节:
小程序环境、资源准备
小程序信息准备
完全由抖音客户端控制,开发者目前无法直接进行优化。
在用户访问小程序时,抖音客户端需要从后台获取小程序meta信息(如头像、昵称、版本、配置、权限等),以对小程序进行版本管理、权限控制和校验等。该信息会缓存到本地,并通过一定的机制进行更新。
信息的获取和更新需要发起网络请求。请求分为两种情况:
(1)同步请求:会阻塞小程序的启动流程,影响小程序的启动耗时。有以下情况需要进行同步请求:
首次访问:用户首次访问该小程序(或小程序被清理)时,客户端没有缓存,需要同步请求小程序相关信息并缓存到本地。
强制更新:抖音冷启动 时会检查本地缓存的小程序信息,并进行更新。
(2)异步请求:与启动流程并行,不影响启动耗时。在启动时候本地有小程序包,优先使用本地缓存的信息完成启动, 异步获取小程序信息,如有变更则更新本地缓存。
对启动耗时影响
在用户首次访问小程序、小程序版本更新或使用长期未使用的小程序时,信息的获取和更新会影响小程序的启动耗时,具体和网络环境有关。小程序版本发布时,会导致启动时需要同步请求的比例上升,进而导致平均启动耗时的上涨。因此,建议合理规划版本发布。
小程序运行环境准备
完全由抖音客户端控制,开发者目前无法直接进行优化。
小程序的运行环境包括小程序容器相关,有小程序进程、客户端原生系统组件和 UI 元素、渲染页面使用的 WebView 容器、开发者 JavaScript 代码的运行环境、小程序基础库等。
部分环境(如 JavaScript 引擎、小程序基础库)需要在执行小程序代码之前准备完成,其他的会在启动过程中并行进行。运行环境的准备时间相对较长(尤其是在低端设备上),为提升启动性能,启动时会对本环节做预加载优化。
预加载
为提升小程序启动体验,抖音客户端会按照一定策略在小程序启动前预加载部分小程序资源。
预加载策略与抖音宿主环境、入口、设备性能等因素有关,为保证整体体验,不能够保证小程序每次启动都能命中预加载。
对启动耗时的影响
小程序运行环境和资源准备耗时较长,如果没有命中预加载,小程序启动耗时将明显劣化,受影响的比例与平台策略、设备等有关。通常来说,预加载命中率越高,启动性能越好。该部分逻辑由抖音宿主控制,开发者无法控制预加载命中率,但可以通过以下手段降低在非预取场景的耗时:
- •降低包大小
- •降低 TTML 层级和复杂度,降低 TTSS 文件大小
小程序代码包准备
开发者可直接进行优化。
小程序启动时,会根据用户访问的小程序版本获取代码包地址,从 CDN 下载小程序代码包,并对代码包进行校验。根据小程序启动页面所在分包和使用的插件不同,一次启 动可能需要下载多个代码包或插件包。代码包下载场景有:
- •启动时:本地无小程序meta或不是最新、本地有小程序meta但包不是最新
- •页面跳转:跳转到其他包的页面,且该包不存在
- •使用分包异步化:异步加载其他分包的JS或组件,且该包不存在
代码包下载过程又包含分为同步、异步下载
- •同步下载:会阻塞小程序的启动流程,影响小程序的启动耗时。发生在首次访问小程序或包被清理时。
- •异步下载:不会阻塞小程序的启动流程。发生在本地有包时启动,且小程序信息有更新,优先读取本地包,异步更新小程序包在下次启动时生效。
对启动耗时影响
在无包场景下(受新用户占比及框架预加载率影响),包下载耗时对启动性能影响较大,具体影响与网络环境、包大小、是否增量更新等有关。为避免包过大对资源和体验的影响,抖音平台限制小程序主代码包上限为 4M,整个小程序所有包大小上限为 16M。
小程序代码加载-逻辑层
与视图层并行
小程序启动时会从代码包中读取并注入配置信息和小程序代码,逻辑层包含 App.js 及页面 JS 文件的加载执行。App.js 执行过程中会同步触发
App.onLaunch
和 App.onShow
生命周期,如果依赖了插件或其他JS库,也会在启动过程中同步加载。为降低小程序代码加载执行耗时,框架层通过 CodeCaching 缓存代码编译结果,降低非首次加载执行耗时。
对启动耗时影响
小程序代码加载执行直接影响小程序启动耗时。耗时长短与文件本身及其依赖的代码复杂度,
App.onLaunch
和 App.onShow
生命周期的执行耗时有关。可通过减少以上阶段耗时优化启动性能,具体见《小程序启动性能优化-减少同步逻辑》。小程序代码加载-视图层
与逻辑层并行
小程序代码包(主包或独立分包)中的 TTML 和 TTSS 会被编译成 JavaScript 代码注入到视图层,启动时会加载所在包的页面结构和样式信息,然后创建启动页面的样式块。
对启动耗时影响
小程序视图层的代码加载会直接影响小程序的启动耗时,小程序双线 程的代码加载完成后才会进行双线程通信,随后逻辑层发送到视图层渲染数据,视图层接收后启动渲染。
该阶段的耗时与页面结构复杂度、使用的自定义组件数量有关,可以通过降低 TTML, TTSS 复杂度,页面配置
usingComponents
中避免含有不参与页面渲染的自定义组件等方式降低这部分耗时。页面首帧渲染完成
逻辑层在小程序代码加载完成后,开始处理框架层的小程序路由事件,依次加载小程序插件,小程序启动页面的 JS 文件,小程序依赖的自定义组件,生成页面渲染初始数据并发送到视图层(webview 侧),然后触发页面的
Page.onLoad
, 当视图层页面树构建后通知逻辑层触发 Page.onShow
生命周期。页面首次渲染完成后通知逻辑层触发 Page.onReady
事件,相当于页面的 FP 触发。页面的初始渲染数据来源为 Page 实例的 data 对象。
对启动耗时的影响
页面首帧渲染完成是页面首次出现像素点,该阶段耗时过长直接影响启动耗时,耗时长短与页面 JS 依赖逻辑复杂度、页面结构复杂度、参与渲染的自定义组件数量、页面初始数据大小有关,可参考《小程序启动性能优化-按需渲染》。
页面最大元素渲染完成
如果小程序首页的主要内容依赖网络请求(例如
tt.request
)等异步来源,用户并不一定能在页面首帧渲染完成后立刻看到有意义的界面,可能看到的仍然是白屏状态。需要等待主要数据依赖的网络请求成功后,调用 setData 进行页面更新,才能呈现真正的页面。截止到用户交互,小程序框架层会统计并上报页面的冷启动耗时指标即 LCP(largest contentful paint)。如果小程序侧有其他比较关注的渲染完成时机,可以在该数据对应的 setData callback 中自行调用 tt.performance.mark 上报首次有意义的渲染耗时 FMP,上报后也可以在「控制台-开发-性能分析」看板中关注该指标数据,详细见《性能分析平台使用说明》。对启动耗时的影响