• 开发教程与代码示例
  • 入门
  • 小程序框架
  • 小程序运行时
  • 自定义组件
  • 基础教程
  • 能力教程
  • 性能优化
  • 概述
  • 性能优化辅助平台/工具
  • 性能优化方法
  • 启动性能
  • 小程序启动流程
  • 小程序启动性能优化
  • 概述
  • 运行时性能
  • 小程序白屏&渲染异常问题优化
  • 安全
  • 小程序启动流程
    收藏
    我的收藏

    小程序运行原理

    在优化小程序之前,先介绍下小程序的运行原理及启动过程。小程序的运行环境分成渲染层和逻辑层,其中 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.onLaunchApp.onShow 生命周期,如果依赖了插件或其他JS库,也会在启动过程中同步加载。
    为降低小程序代码加载执行耗时,框架层通过 CodeCaching 缓存代码编译结果,降低非首次加载执行耗时。
    对启动耗时影响
    小程序代码加载执行直接影响小程序启动耗时。耗时长短与文件本身及其依赖的代码复杂度,App.onLaunchApp.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,上报后也可以在「控制台-开发-性能分析」看板中关注该指标数据,详细见《性能分析平台使用说明》。
    为避免用户在主要内容渲染完成前看到白屏状态,可以使用骨架屏能力优化启动体验,在确认页面有渲染数据时移除骨架屏。
    对启动耗时的影响
    页面依赖的数据获取耗时直接影响页面最大元素渲染耗时,最有效的方式是接入数据预取,可参考《小程序启动性能优化-更早的展示首屏数据》。