从 “卡顿” 到 “秒开”:外投首屏性能优化的 6 个实战锦囊|得物技术

一、背景

在互联网时代,网站性能的好坏直接影响用户体验和转化率。对投放的广告页面而言,如何在保证视觉效果和功能的同时提升加载速度,成为了开发者必须面对的挑战。

本文将探讨几种有效的外投页面性能优化策略,包括构建方式的优化、非首屏组件的处理、关键大图的预载、动效方面的升级,以及针对弱网环境下的降级策略、外投流渲染的技术升级等相关内容。

二、难点 & 收益

首屏秒开率口径严格

首屏秒开率通常指用户从触发页面加载(如点击广告链接)开始,到首屏内容完全渲染完成,并可进行交互所花费的时间在 1 秒以内的比例。其中,“首屏内容” 指用户设备屏幕可见区域内的全部内容,包括文字、图片、按钮等关键信息元素。

“完全渲染完成” 不仅要求视觉上显示完整,还需保证页面元素的布局稳定,不存在闪烁、错位等情况;“可交互” 则意味着用户能够对首屏内的交互元素(如按钮、输入框)进行有效操作,不会出现点击无响应的情况。

投放环境复杂

外投页面的投放环境极为复杂。用户可能分布在不同网络环境中,包括 4G、5G、Wi-Fi 甚至弱网或不稳定网络环境,在 2G、3G 等低速网络或信号波动较大的区域,数据传输速率受限,资源加载容易出现延迟。而且,用户使用的设备性能差异悬殊,从高端旗舰机到中低端老旧设备,硬件配置的不同会导致页面解析、渲染能力参差不齐,部分低端设备难以快速处理复杂的页面代码和资源。

页面收益

经过长达两个季度的性能优化专项工作,外投页面取得了显著的收益:

  1. 外投页面曝光率提高了10%左右

  2. 首屏秒开率从15%提高到65%左右

三、外投页面的渲染策略

在构建现代 web 应用时,SSR(Server-Side Rendering)是常用的渲染方式,但我们外投页面大部分页面采用的是ISR,字面上理解,可以理解为SSR+SSG。可以参考下面这张图。

在初次构建时,ISR 会像 SSG 一样预生成静态页面,并将其部署到服务器或 CDN。

当用户访问页面时,若页面已经过期,ISR 会触发一个后台再生成过程。这个过程在服务器端完成,类似于 SSR,但与 SSR 不同的是,再生成的页面会被存储为静态文件,而非每次请求都进行实时渲染。

具体再生流程

  1. 用户请求过期的页面时,ISR 会启动一个后台再生成过程。

  2. 服务器重新生成页面,并将新的 HTML 文件更新到缓存中。

  3. 在生成过程完成后,下一个用户请求将会获得更新后的页面。

举个例子,假设一个 Next.js 页面设置了 revalidate 选项为 10 秒,这意味着页面在构建时将会生成静态 HTML 文件,并在 10 秒内保持不变。

具体流程

  1. 0-10 秒:在初始构建完成后的前 10 秒内,用户请求的 HTML 文件都是之前生成的静态页面。页面不会重新生成,所有请求都直接返回缓存的静态页面。

  2. 10 秒后:当第一个请求到达并发现页面已经过期时(超过了 10 秒),ISR 会启动一个后台再生成过程。此时,服务器仍然会返回旧的缓存页面给用户,同时在后台生成新的页面。

  3. 生成完成后:当后台生成的页面完成后,下一个用户请求将会得到新的页面。新生成的页面会被存储为缓存静态文件,并在后续的 10 秒内继续使用,直到再次触发再生成过程。

这种机制保证了页面在大部分时间内快速响应,同时能够在后台静默地更新内容,确保用户能尽快看到最新的数据。

外投主要是几个固定页面,此处作如下分类。

分类

  1. 交互性交强的页面, 如盲盒这类页面的特点,放量占比流量大,首屏秒开受动效影响较大,一般配置完运营就短期内不会进行更新,适合ISR。页面静态内容为主。

  2. 容器页面用于进行效果AB测试的页面,不太适合用ISR,在构建时获取AB的结果, 实时性要求较高,AB的结果因人而异,采用SSR策略。

  3. 变更比较频繁的页面,不接受短时间的差异的,这种一般采取SSR策略。

不足:使用ISG构建的页面, 在于客户端和客户端建联的时候,需要等待HTML文档全部下载完成,浏览器才开始渲染,所以ISG构建的页面 TTFB 都会比较大。若一个网页的TTFB 较慢,后续的FMP、LCP、FCP 等所有指标都会很难提高, 所以在此基础上,我们在Q1启动了外投落地页的流渲染的技术架构升级,具体会在本篇文章的第四段详细介绍。

四、组件懒加载

在优化页面加载时,非首屏组件的渲染方式至关重要。比如页面的挽留弹窗,页面的二级弹窗,这些都属于非首屏的内容。 可通过组件的封装和抽取,区分首屏组件和首屏组件,哪些需要直出dom,哪些无需直出dom,可以在客户端在进行渲染,这里 通过使用 Next.js的 next/dynamic 实现组件的动态加载,可以将这些非首屏组件的 SSR 设置为 false ,在用户点击到了到相应位置时再进行加载。这种懒加载策略最直接影响的首屏的bundle(js、css),追求极致的首屏体积。

const DyamicMarqueeTop = dynamic(() => import('./marqueTop'), {
  ssr: false,
  loading: () => <div></div>,
})


const DyamicDialog = dynamic(() => import('./dialog'), {
  ssr: false,
  loading: () => <div></div>,
})

下面是优化前后的外投H5页面对比, 大图模板 预计减少5.3kB

商品流模板 

五、图片preload策略

对于外投页面而言,影响首屏秒开的有的是一些关键性大图。为了提升大图的加载效率,可以在 HTML 的 <head> 中使用 <link rel="preload"> 标签提前加载关键大图。这种做法能够在用户访问页面时,优先加载重要的视觉元素,从而减少用户等待时间。

import Head from 'next/head'


/** 单品图模板自定义的头的一些 业务逻辑 */
export const ScratchHead = () => {
  const defaultUrl = 'xx'
  
  const scratchInfo = global?.componentList?.find((item: { name: string }) => item.name === 'scratchCard')
  
  const bg = scratchInfo?.props?.bgImg || defaultUrl
  
  return (
    <>
      <Head>
        <link rel="preload" href={`${bg}?x-oss-process=image/format,webp/resize,w_750`} as="image" />
      </Head>
    </>
  )
}

六、外投动画升级

现状

由于盲盒玩法,流量波动比较大,优化盲盒玩法的首屏秒开优先级较高。

结合apm上报的路径,这是一个canvas容器。

html.support-webp > body > div#__next > div > div > div > canvas

初步定位为,需要页面的动画渲染影响了整体秒开,于是我们推动产品和设计同学 进行动画升级从现有的apng升级到lottie

75分位的首屏秒开 从 4.8s 多下降到 1.8s 左右, 首屏秒开提升16pp左右。

为什么升级一个动效,就对页面的秒开影响之大?

apng

在没全量应用lottie之前,外投针对复杂动画的处理一直都是apng,可以说是苦apng久已。

  1. apng的缺点在于资源体积非常大,一般的动画都得达到2-4M, 光资源加载就要好几秒了,何谈首屏秒开。

  2. 由于apng有浏览器兼容性问题,对于apng资源的播放我们都是将apng解码,然后每一帧的图片用canvas 画布进行渲染。

  3. 由于动画的容器是canvas,无法首屏直出dom,只能等到客户端场景下载JS,加载资源后再进行渲染, 这就是含有动画模板秒开比较低的本质原因。

(下面的流程图均是cursor结合excalidraw自动生成出的)

染apng具体流程

 lottie

为了解决上诉问题, 我们第一时间想到了lottie 动画, 对于首屏关键的动画从apng 直接转到lottie。

lottie的文件体积通常较小,因为它是以 JSON 数据的形式存储动画信息,而非实际的图像帧。对于复杂的动画,尤其是包含大量图形元素和动画效果的情况,Lottie 的文件大小优势更为明显。可以看下面对比图:

同样一个动画播放, apng 需要将近2M多,但是lottie需要的资源仅仅需要几百k,资源体积下降十分明显。 

设计师通常给到的lottie资源是这样的:

开发往往需要对设计师给到的lottie文件进行cdn 化处理:

第一步: 需要压缩图片资源,然后上传到OSS。

第二步:  然后替换json 中的 assests 路径。

第三步: 上传lottie.json,生成一个url,供我们前端进行渲染。

对于这些复杂的操作,我们已经内置为一个npm 包@dw/lottie-cli,专门用于处理设计师给的lottie资源,同时封装了基于react的lottie组件。

  1. lottie-cli支持 tinify图片压缩

  2. 支持webp生成

  3. lottie-player 支持 webp 渲染

lottie的缺点,在于依赖一个lottie-web的运行时,以及lottie.json 资源的大小,至于方案如何选择,还是要具体业务具体分析。没有完美的方案,找到适合当前页面的动效才是最好的。

减少runtime体积

lottie动画依赖的lottie-web没法做到tree-shaking,因此修改外投引入lottie的方式。将lottie js 体积包拆分成 svg、html、canvas. 三种渲染依赖JS ,将运行时体积从80kb降低到40kb,首屏秒开预计提升100ms。

优化前

优化后

针对lottie动画SSR场景下没法直出dom的情况,新增lottie动画预渲染骨架。在SSR场景,用动画的第一帧dom,作为lottie动画的骨架(只适用于lottie渲染模式 是用svg 或者是html,canvas模式不支持 ),首屏秒开提升100ms。

优化前

优化后

七、低端机和弱网优化

为保障所有用户都能收获良好使用体验,在这个过程中,尤其需要关注那些使用低端机器的用户群体。针对这部分用户,我们可制定相应的降级策略,比如跳过动画展示环节。具体实施时,通过对用户设备性能进行检测,以此来决定是否加载动画元素,进而确保页面在性能相对较低的设备上也能够保持流畅运行。

弱网指标判断

弱网的初步定义

当网络的往返时间(RT)大于 150ms 时,便认定其处于弱网状态。

网络性能检测方法

针对同一张小体积图片,在每次访问时都对其进行访问操作,并计算相应的往返时间(RT)。我们将该 RT 值作为此次访问网络性能的代表指标,通常情况下,RT 值越短,则意味着网络性能越佳。

低端机指标判断

核心判定逻辑

综合考量去navigator上的 deviceMemory、hardwareConcurrency、saveData 以及显示屏幕、触摸屏幕等方面的信息,以此构成判断低端机的初步标准。

具体判定细则

  • 内存方面:若 deviceMemory 的值小于等于 2GB,那么可直接判定该设备为低端设备。

  • CPU 方面:当 navigator.hardwareConcurrency 的数值小于 4 时,同样能够直接判定此设备属于低端设备范畴。

  • 设备像素方面:倘若设备像素低于 600,那么该设备可被判定为极端设备。

卡顿指标判断

卡顿指标的检测逻辑

针对卡顿指标的检测,我们采取如下方式:通过创建一批div元素,并为其施加动画效果,以此来测试设备的性能表现,随后给当前设备进行打分,最终依据分数的高低来区分不同性能的机器。具体的卡顿判定标准如下:

  • 若渲染时间大于 60ms,这种情况可被视作存在发生卡顿的可能性。

  • 而一旦渲染时间超过 200ms,那么就认定该设备出现了严重卡顿的情况。

收集到上述指标后,最终落地到页面就是动画跳过,或者是动画降级,从动画变成一张静态图,或者是在不阻塞主流程的情况下, 走下一步跳过动画。

 伪代码如下:

// 是低端机或者是弱网设备
if (isLowEndDevice || isLowNetWork) {
  // 跳过动画
} else {
  // 加载动画
}

降级策略

移动端设备,针对我们外投页面而言,影响比较大的两个因素:

  1. 弱网

  2. 设备性能

安卓设备性能小于IOS 设备,安卓设备解析JS的时间长,在弱网的情况下,用户加载JS的时间比较长。

 Lotttie动画降级

a. 主要是针对lottie 文件的assets 图资源进行降级。

b. 针对低端机型直接跳过动画,直接使用lottie预构建的骨架, 保证SSR的dom直出。

动画降级方案

目前外投播放apng 动画的流程涉及到如:

一个apng的动画的播放生命周期,涉及到资源拉取时间 + 解析apng 的时间,但是这对于性能较差的低端机来说,可能是在灾难。这里直接如果识别到是低端机,直接跳过动画。如果在3秒内没有加载完成,可以直接进行兜底超时逻辑,展示兜底静态图。

轮播图降级方案

针对低端机和弱网首屏引入的swiper.js,这里直接去掉swiper,使用css 手写无限轮播动画。

通过上面的优化动作,针对低端机或者是弱网设备,我们的页面也能够快速的打开。

八、流渲染技术架构升级

经历过上面一些常规性的优化,想要首屏秒开进一步提高,需要整体框架层面进行架构升级。 我们针对外投大流量页面进行了流渲染的架构迁移。

流渲染的优势

  • 渲染将页面拆解为多个可独立渲染的 “片段”(Fragment),服务端逐段生成 HTML 并流式传输给客户端。例如,先返回头部和主体框架,再逐步填充内容模块(如导航栏、列表项等)。

  • 用户体验优化:用户无需等待整个页面生成即可看到部分内容,尤其是关键信息(如标题、首屏图文)的展示时间显著缩短,提升 “感知速度”。

  • 性能数据对比:在复杂页面中,流渲染可使首屏渲染时间(TTFB)减少 30%~50%,尤其适用于数据分批次加载的场景(如瀑布流、分页内容)。

  • 流渲染的分阶段交互激活:流渲染可将页面拆解为多个 “可交互单元”,服务端在传输片段时,同步或异步注入对应的客户端逻辑。

  • 例如:先渲染商品列表的静态结构,同时加载列表项的点击事件逻辑,使首屏内容在 hydration 完成前即可部分交互。结合增量 hydration(Incremental Hydration),仅对动态更新的片段进行交互激活,减少整体脚本执行时间。提升页面的 “可交互时间”(TTI,Time to Interactive),使用户更早能与页面互动。

历史流量迁移

在流渲染迁移过程中,外投链接完成从非流渲染到流渲染的升级。

具体实施策略

  1. 新增页面:直接使用新链接,原生支持流渲染技术,享受性能优化红利。

  2. 存量广告计划兼容:

  • 已发布且无法修改链接的计划:通过代理转发机制,将原链接(cdn 域名)的请求回源到流渲染服务,实现 “链接不变、内容由流渲染承载” 的无感知迁移。

  • 未发布的非流渲染链接计划:联合后端与产品团队,通过数据批量处理(刷数),将原链接批量替换为流渲染链接,确保新计划直接使用高性能架构。

具体的架构图参考如下

apm性能脚本内嵌

在流渲染页面外投场景中,虽已实现部分页面流渲染,但首屏秒开指标提升效果不佳。通过对大盘数据深入剖析,发现接口响应时间(RT)75 分位值约为 50ms,首字节时间(TTFB)75 分位值约为 500ms,由此可判断服务端性能并非瓶颈所在。将分析方向转向客户端后,确认首屏秒开的关键制约因素并非框架本身,而是监控 SDK 脚本加载耗时。

具体而言,页面首次有效绘制(FMP)的计算依赖于外部脚本 xxx.js。当前机制下,页面渲染时异步加载 xx.js,需等待该脚本加载完成后才能进行 FMP 标记计算,这导致 xx.js 加载耗时直接计入 FMP 统计。考虑到 xx.js 体积达 35KB,在首屏阶段异步加载此脚本,对网络条件较差的外投页面性能影响显著。

针对外投弱网场景,和APM团队提出首屏先采集后上报的需求,最终APM 团队提供了 7KB 的性能内联版本 SDK。在流渲染的的白屏阶段同步加载 性能脚本JS,经实践验证,优化效果显著:在 4G 网络环境下,FMP 指标从优化前的 1000ms 降至 500ms 左右,首屏秒开率提升约 25 个百分点,提升十分显著。

九、总结

在页面的性能优化过程中,结合流渲染的技术架构升级、ISR、动态加载非首屏组件、关键大图预加载、Lottie 动画,以及针对低端设备的降级策略,可以形成一个多层次的优化方案,为用户提供流畅而快速的访问体验。

通过这些优化措施,外投页面不仅能够更好地满足用户需求,还能有效提高转化率,为业务带来更多机会。希望本文能够帮助你在实际项目中更好地应用这些性能优化策略。

往期回顾

1.从Rust模块化探索到DLB 2.0实践|得物技术

2.eBPF 助力 NAS 分钟级别 Pod 实例溯源|得物技术

3.正品库拍照PWA应用的实现与性能优化|得物技术

4.汇金资损防控体系建设及实践 | 得物技术

5.Redis 是单线程模型?|得物技术

文 / 正飞

关注得物技术,每周更新技术干货

要是觉得文章对你有帮助的话,欢迎评论转发点赞~

未经得物技术许可严禁转载,否则依法追究法律责任。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值