Next.js嵌入Wistia视频致Lighthouse移动端TTI升高问题咨询
Next.js 项目零TTI损耗加载Wistia视频方案
问题背景
现有基于MUI、NextAuth.js技术栈的Next.js项目,需求为加载Wistia视频的同时,不影响Lighthouse移动端Time-to-Interactive(可交互时间,简称TTI)指标。
直接在组件中通过Next.js的Script组件写入如下嵌入代码时,TTI会出现大幅上涨:
<div> <Script src={`https://fast.wistia.com/embed/medias/${wistiaId}.jsonp`} strategy="beforeInteractive"/> <Script src={"https://fast.wistia.com/assets/external/E-v1.js"} defer /> <Box className={`wistia_embed wistia_async_${wistiaId}`} sx={{ position: "absolute", top: 0, left: 0, right: 0 }} /> </div>
经Lighthouse检测,未做优化的嵌入逻辑会使TTI增加约3.2秒,检测结果如下:
此前尝试的优化方案为监听scroll、touch等用户输入事件,事件触发后再加载Wistia相关脚本,该方案确实可将TTI影响降到极低水平,检测结果如下:
但该方案存在明显缺陷:必须提前渲染完成视频播放器界面,无法展示加载状态提示。
核心问题
- 是否存在无需等待用户输入即可完成Wistia视频嵌入,且完全不对TTI指标造成负面影响的方案?
- Next.js中是否可以感知页面达到可交互状态的时机,从而无需等待用户输入即可延迟加载视频资源?
方案实现
页面可交互时机感知逻辑
Next.js本身未直接暴露页面达到TTI标准的生命周期钩子,但可通过浏览器原生API实现感知,无需绑定用户输入事件:
- 禁止使用
beforeInteractive、afterInteractive两个Script策略加载Wistia资源:前者会阻塞首屏渲染,后者会在页面hydration阶段立即执行脚本,产生的长任务会直接拖慢TTI。 - 监听浏览器原生
load事件,事件触发后通过requestIdleCallback将脚本加载逻辑注册到浏览器空闲时段执行。该时间点所有首屏关键资源已加载完成,hydration执行完毕,此时加载非关键的视频脚本不会阻塞主线程,完全不会影响TTI统计。 - 逻辑可直接封装在客户端
useEffect中执行,useEffect本身仅会在客户端hydration完成后触发,叠加空闲判断即可保证加载时机安全。
无用户交互零TTI损耗嵌入方案
基于上述时机判断逻辑即可实现需求,无需等待任何用户操作,同时支持自定义加载提示,完整实现代码如下:
import { useState, useEffect } from 'react' import Script from 'next/script' import Box from '@mui/material/Box' const WistiaVideoEmbed = ({ wistiaId }) => { const [loadVideoAssets, setLoadVideoAssets] = useState(false) useEffect(() => { const scheduleLoad = () => { if ('requestIdleCallback' in window) { // 空闲时加载,最长等待2秒兜底 requestIdleCallback(() => setLoadVideoAssets(true), { timeout: 2000 }) } else { // 低版本浏览器兜底延迟 setTimeout(() => setLoadVideoAssets(true), 1500) } } if (document.readyState === 'complete') { scheduleLoad() } else { window.addEventListener('load', scheduleLoad, { once: true }) } return () => window.removeEventListener('load', scheduleLoad) }, []) return ( <div> {loadVideoAssets && ( <> <Script src={`https://fast.wistia.com/embed/medias/${wistiaId}.jsonp`} fetchPriority="low" /> <Script src="https://fast.wistia.com/assets/external/E-v1.js" fetchPriority="low" /> </> )} <Box className={loadVideoAssets ? `wistia_embed wistia_async_${wistiaId}` : ''} sx={{ position: "absolute", top: 0, left: 0, right: 0 }} > {/* 可自由替换为占位图、自定义Loading组件 */} {!loadVideoAssets && <div className="video-placeholder">视频加载中...</div>} </Box> </div> ) } export default WistiaVideoEmbed
额外优化建议
- 若视频不在首屏视口范围内,可叠加Intersection Observer逻辑,等视频滚动到距视口200-300px范围时再触发加载,性能收益更高。
- Wistia脚本默认会预加载部分视频分片资源,可在初始化时配置Wistia播放器的预加载策略为
preload="none",进一步降低非必要资源加载对主线程的占用。
内容的提问来源于stack exchange,提问作者jmarioste
相关产品推荐
相关产品推荐

