一、背景
在互联网时代,网站性能的好坏直接影响用户体验和转化率。对投放的广告页面而言,如何在保证视觉效果和功能的同时提升加载速度,成为了开发者必须面对的挑战。
本文将探讨几种有效的外投页面性能优化策略,包括构建方式的优化、非首屏组件的处理、关键大图的预载、动效方面的升级,以及针对弱网环境下的降级策略、外投流渲染的技术升级等相关内容。
二、难点 & 收益
首屏秒开率口径严格
首屏秒开率通常指用户从触发页面加载(如点击广告链接)开始,到首屏内容完全渲染完成,并可进行交互所花费的时间在 1 秒以内的比例。其中,"首屏内容" 指用户设备屏幕可见区域内的全部内容,包括文字、图片、按钮等关键信息元素。
"完全渲染完成" 不仅要求视觉上显示完整,还需保证页面元素的布局稳定,不存在闪烁、错位等情况;"可交互" 则意味着用户能够对首屏内的交互元素(如按钮、输入框)进行有效操作,不会出现点击无响应的情况。
投放环境复杂
外投页面的投放环境极为复杂。用户可能分布在不同网络环境中,包括 4G、5G、Wi-Fi 甚至弱网或不稳定网络环境,在 2G、3G 等低速网络或信号波动较大的区域,数据传输速率受限,资源加载容易出现延迟。而且,用户使用的设备性能差异悬殊,从高端旗舰机到中低端老旧设备,硬件配置的不同会导致页面解析、渲染能力参差不齐,部分低端设备难以快速处理复杂的页面代码和资源。
页面收益
经过长达两个季度的性能优化专项工作,外投页面取得了显著的收益:
- 外投页面曝光率提高了10%左右
- 首屏秒开率从15%提高到65%左右
三、外投页面的渲染策略
在构建现代 web 应用时,SSR(Server-Side Rendering)是常用的渲染方式,但我们外投页面大部分页面采用的是ISR**,字面上理解,可以理解为SSR+SSG。可以参考下面这张图。

在初次构建时,ISR 会像 SSG** 一样预生成静态页面,并将其部署到服务器或 CDN。
当用户访问页面时,若页面已经过期,ISR 会触发一个后台再生成过程。这个过程在服务器端完成,类似于 SSR,但与 SSR 不同的是,再生成的页面会被存储为静态文件,而非每次请求都进行实时渲染。
具体再生流程
- 用户请求过期的页面时,ISR 会启动一个后台再生成过程。
- 服务器重新生成页面,并将新的 HTML 文件更新到缓存中。
- 在生成过程完成后,下一个用户请求将会获得更新后的页面。
举个例子,假设一个 Next.js 页面设置了 revalidate 选项为 10 秒,这意味着页面在构建时将会生成静态 HTML 文件,并在 10 秒内保持不变。
具体流程
- 0-10 秒: 在初始构建完成后的前 10 秒内,用户请求的 HTML 文件都是之前生成的静态页面。页面不会重新生成,所有请求都直接返回缓存的静态页面。
- 10 秒后: 当第一个请求到达并发现页面已经过期时(超过了 10 秒),ISR 会启动一个后台再生成过程。此时,服务器仍然会返回旧的缓存页面给用户,同时在后台生成新的页面。
- 生成完成后: 当后台生成的页面完成后,下一个用户请求将会得到新的页面。新生成的页面会被存储为缓存静态文件,并在后续的 10 秒内继续使用,直到再次触发再生成过程。
这种机制保证了页面在大部分时间内快速响应,同时能够在后台静默地更新内容,确保用户能尽快看到最新的数据。
外投主要是几个固定页面,此处作如下分类。
分类
- 交互性交强的页面, 如盲盒这类页面的特点,放量占比流量大,首屏秒开受动效影响较大,一般配置完运营就短期内不会进行更新,适合ISR。页面静态内容为主。
- 容器页面用于进行效果AB测试的页面,不太适合用ISR,在构建时获取AB的结果, 实时性要求较高,AB的结果因人而异,采用SSR策略。
- 变更比较频繁的页面,不接受短时间的差异的,这种一般采取SSR策略。
不足: 使用ISG构建的页面, 在于客户端和客户端建联的时候,需要等待HTML文档全部下载完成,浏览器才开始渲染,所以ISG构建的页面 TTFB** 都会比较大。若一个网页的TTFB 较慢,后续的FMP、LCP、FCP 等所有指标都会很难提高, 所以在此基础上,我们在Q1启动了外投落地页的流渲染的技术架构升级,具体会在本篇文章的第四段详细介绍。
四、组件懒加载
在优化页面加载时,非首屏组件的渲染方式至关重要。比如页面的挽留弹窗,页面的二级弹窗,这些都属于非首屏的内容。 可通过组件的封装和抽取,区分首屏组件和首屏组件,哪些需要直出dom,哪些无需直出dom,可以在客户端在进行渲染,这里 通过使用 Next.js的 next/dynamic 实现组件的动态加载,可以将这些非首屏组件的 SSR 设置为 false ,在用户点击到了到相应位置时再进行加载。这种懒加载策略最直接影响的首屏的bundle**(js、css),追求极致的首屏体积。
javascript
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 的 中使用 标签提前加载关键大图。这种做法能够在用户访问页面时,优先加载重要的视觉元素,从而减少用户等待时间。
typescript
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容器。
less
html.support-webp > body > div#__next > div > div > div > canvas
初步定位为,需要页面的动画渲染影响了整体秒开,于是我们推动产品和设计同学 进行动画升级从现有的apng升级到lottie。
75分位的首屏秒开 从 4.8s 多下降到 1.8s 左右, 首屏秒开提升16pp左右。

为什么升级一个动效,就对页面的秒开影响之大?
apng
在没全量应用lottie之前,外投针对复杂动画的处理一直都是apng,可以说是苦apng久已。
- apng的缺点在于资源体积非常大,一般的动画都得达到2-4M, 光资源加载就要好几秒了,何谈首屏秒开。
- 由于apng有浏览器兼容性问题,对于apng资源的播放我们都是将apng解码,然后每一帧的图片用canvas 画布进行渲染。
- 由于动画的容器是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组件。
- lottie-cli支持 tinify图片压缩
- 支持webp生成
- 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,那么就认定该设备出现了严重卡顿的情况。
收集到上述指标后,最终落地到页面就是动画跳过,或者是动画降级,从动画变成一张静态图,或者是在不阻塞主流程的情况下, 走下一步跳过动画。
伪代码如下:
perl
// 是低端机或者是弱网设备if (isLowEndDevice || isLowNetWork) { // 跳过动画} else { // 加载动画}
降级策略
移动端设备,针对我们外投页面而言,影响比较大的两个因素:
- 弱网
- 设备性能
安卓设备性能小于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),使用户更早能与页面互动。
历史流量迁移
在流渲染迁移过程中,外投链接完成从非流渲染到流渲染的升级。
具体实施策略
- 新增页面:直接使用新链接,原生支持流渲染技术,享受性能优化红利。
- 存量广告计划兼容:
-
已发布且无法修改链接的计划:通过代理转发机制,将原链接(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 动画,以及针对低端设备的降级策略,可以形成一个多层次的优化方案,为用户提供流畅而快速的访问体验。
通过这些优化措施,外投页面不仅能够更好地满足用户需求,还能有效提高转化率,为业务带来更多机会。希望本文能够帮助你在实际项目中更好地应用这些性能优化策略。
往期回顾
2.eBPF 助力 NAS 分钟级别 Pod 实例溯源|得物技术
文 / 正飞
关注得物技术,每周更新技术干货
要是觉得文章对你有帮助的话,欢迎评论转发点赞~
未经得物技术许可严禁转载,否则依法追究法律责任。