如何判断React项目中stripe-pricing-table脚本的加载状态?
解决Next.js中Stripe定价表加载时的白框闪烁问题
针对你在Next.js集成Stripe定价表时遇到的iframe加载闪烁问题,这里提供两种可行的解决方案:
方案一:手动加载脚本并监听加载状态
跳过在_document.tsx的<Head>中添加脚本的步骤,改为在组件内手动加载Stripe定价表脚本,通过监听脚本的onload事件控制组件渲染时机,同时显示自定义加载占位符:
import { useState, useEffect } from 'react'; export default function PricingPage() { const [stripeScriptReady, setStripeScriptReady] = useState(false); useEffect(() => { // 避免重复加载脚本 const existingScript = document.getElementById('stripe-pricing-script'); if (existingScript) { setStripeScriptReady(true); return; } const script = document.createElement('script'); script.id = 'stripe-pricing-script'; script.src = 'https://js.stripe.com/v3/pricing-table.js'; script.async = true; // 脚本加载完成后更新状态 script.onload = () => setStripeScriptReady(true); // 处理加载失败的情况 script.onerror = () => console.error('Stripe定价表脚本加载失败'); document.head.appendChild(script); // 组件卸载时清理脚本 return () => document.head.removeChild(script); }, []); return ( <div className="pricing-wrapper"> {!stripeScriptReady ? ( {/* 自定义加载占位符,比如骨架屏 */} <div className="pricing-skeleton"> <div className="skeleton-card"></div> <div className="skeleton-card"></div> <div className="skeleton-card"></div> </div> ) : ( <stripe-pricing-table pricing-table-id="你的定价表ID" publishable-key="你的可发布密钥" ></stripe-pricing-table> )} </div> ); }
搭配基础骨架屏样式(可根据你的页面风格调整):
.pricing-skeleton { display: flex; gap: 1rem; padding: 2rem; } .skeleton-card { width: 300px; height: 400px; background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 100%; animation: loading 1.5s infinite; border-radius: 8px; } @keyframes loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
方案二:监听生成的iframe加载状态
如果你仍想保留_document.tsx中添加脚本的方式,可以通过MutationObserver监听<stripe-pricing-table>元素的子节点变化,找到生成的iframe后监听其onload事件,通过透明度过渡避免闪烁:
import { useState, useEffect, useRef } from 'react'; export default function PricingPage() { const [isIframeLoading, setIsIframeLoading] = useState(true); const pricingTableRef = useRef<HTMLElement>(null); useEffect(() => { const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { const iframe = pricingTableRef.current?.querySelector('iframe'); if (iframe) { iframe.onload = () => { setIsIframeLoading(false); observer.disconnect(); // 完成后停止监听 }; } }); }); if (pricingTableRef.current) { observer.observe(pricingTableRef.current, { childList: true, subtree: true }); } return () => observer.disconnect(); }, []); return ( <div className="pricing-wrapper"> {isIframeLoading && ( <div className="loading-overlay"> <p>加载定价表中...</p> </div> )} <div ref={pricingTableRef} style={{ opacity: isIframeLoading ? 0 : 1, transition: 'opacity 0.3s ease-in-out', position: 'relative' }} > <stripe-pricing-table pricing-table-id="你的定价表ID" publishable-key="你的可发布密钥" ></stripe-pricing-table> </div> </div> ); }
核心思路
两种方案都是通过延迟渲染定价表/控制显示状态,替换默认的白框加载过程。你可以根据项目需求选择其中一种:
- 方案一更适合需要严格控制脚本加载时机的场景;
- 方案二则兼容原有
_document.tsx的脚本配置方式。
内容的提问来源于stack exchange,提问作者Davis Jones
相关产品推荐
相关产品推荐

