我是第一次使用 NextJS,它构建网站的速度给我留下了难以置信的印象……直到我添加了 Stripe Elements。
在添加 Stripe 之前,我的 pagespeed 移动得分范围为 93-99。后来就50左右了。:(
我试过了,按照this dev.to article ,使用 /pure
导入 Stripe导入路径:
import { loadStripe } from '@stripe/stripe-js/pure';
我不确定它的作用,因为它仍然将外部 Stripe 脚本的链接放在 <head>
中。 ,并且不添加任何附加标签:
<script src="https://js.stripe.com/v3"></script>
尽管如此,它似乎确实起到了一些作用,因为页面速度略有提高,达到 58-63 范围 - 但这仍然是 Not Acceptable 。
这一切都是通过 Stripe React 库完成的,所以它的实现看起来像这样:
import React, { useEffect, useState } from 'react';
import { Elements } from '@stripe/react-stripe-js';
import ElementForm from './ElementForm';
import getStripe from '../lib/get-stripejs';
const PurchaseSection = () => {
const [ stripePromise, setStripePromise ] = useState(null);
const [ clientSecret, setClientSecret ] = useState('');
useEffect(() => {
fetch('api/keys', {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
})
.then((res) => res.json())
.then((data) => {
setStripePromise(getStripe(data.publishableKey));
});
}, []);
useEffect(() => {
fetch('api/create-payment-intent', {
method: 'POST',
header: { 'Content-Type': 'applcation/json' },
body: JSON.stringify({
productId: '34032255',
productType: 'seminar'
})
})
.then(async (res) => {
const { clientSecret } = await res.json();
setClientSecret(clientSecret);
});
}, [])
return (
<section>
{stripePromise && clientSecret && (
<Elements stripe={stripePromise} options={{ clientSecret }}>
<ElementForm />
</Elements>
)}
</section>
)
}
lib/get-stripejs
import { loadStripe } from '@stripe/stripe-js/pure';
let stripePromise;
const getStripe = (publishableKey) => {
if (!stripePromise) {
stripePromise = loadStripe(publishableKey);
}
return stripePromise;
}
export default getStripe
元素表单
import React, { useState } from "react";
import { PaymentElement, useStripe, useElements } from '@stripe/react-stripe-js';
function ElementForm({ paymentIntent = null }) {
const stripe = useStripe();
const elements = useElements();
const [ isProcessing, setIsProcessing ] = useState(false);
async function handleSubmit(e) {
// do stuff
}
return (
<form id='payment-form' onSubmit={handleSubmit}>
<div id='payment-element'>
<PaymentElement />
</div>
<button disabled={isProcessing} type='submit'>
{isProcessing ? 'Processing...' : 'Submit'}
</button>
</form>
);
}
export default ElementForm
我不确定问题是否出在 <PaymentElement>
或其他loadStripe
确实如此,或两者兼而有之,但我的想法是我想做类似 next/script
的事情提供,至少尝试一下 the various strategies 。问题是,这似乎只适用于来自 src
的项目。 (我猜这最终是这样,但这不是代码中的实现方式,因为使用了各种 Stripe 包)。
那么,a) 有什么方法可以让我应用 next/script
策略直接针对组件,而不是远程脚本?
或者,b) 更广泛地说,延迟加载所有这些 Stripe 内容的“nextjs”神奇方法是什么? (注意: <PurchaseSection>
组件直到远低于折叠才会出现,因此没有理由提前加载或以任何形式的阻塞方式加载。)
最佳答案
根据 @juliomalves 的评论,我去玩了 next/dynamic
和 IntersectionObserver
。从本质上讲,您必须在此处进行用户体验权衡。
推迟 Elements
的加载,直到它进入视口(viewport),将 PageSpeed 指标提高到 80,这更好,但不是很好。低分主要是由于交互时间为 5 秒造成的。
除此之外,如果我推迟 Stripe 库本身的加载,则 PageSpeed 会跳回到 90 年代中期……但是当用户向下滚动到他们想要输入付款信息的位置时并购买,在表格显示之前会有 5 秒的延迟。
老实说,我不确定哪种体验更糟糕,哪种体验会导致更多的流失,因此您可能需要进行自己的实验。
延迟加载元素
我将 IntersectionObserver
放入自定义 Hook 中,因为我已经沿着这条路走下去了,所以我确信我会在其他地方使用它:
utils/showOnScreen.js
import React, { useEffect, useState} from 'react';
const showOnScreen = (ref) => {
const [ isIntersecting, setIsIntersecting ] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => setIsIntersecting(entry.isIntersecting)
);
if (ref.current) {
observer.observe(ref.current);
}
}, []);
return isIntersecting;
}
export default showOnScreen
组件/PurchaseSection.js
import React, { useEffect, useRef, useState } from 'react';
import dynamic from 'next/dynamic';
import { Elements } from '@stripe/react-stripe-js';
import getStripe from '../lib/get-stripejs';
import showOnScreen from '../utils/showOnScreen';
// We're making the `ElementForm` dynamic, but not `Elements`
// itself, as the latter is not a renderable component, but
// just a wrapper to pass state down to child components.
const ElementForm = dynamic(() => import('./ElementForm'));
const PurchaseSection = () => {
const purchaseSectionRef = useRef();
const purchaseSectionRefValue = showOnScreen(purchaseSectionRef);
// we're keeping track of this state to deal with `IntersectionObserver`
// transitions if the user scrolls (e.g., false, true, false, true).
// An alternative would be to remove the observer once it flips to
// true.
const [ isPurchaseSectionRef, setIsPurchaseSectionRef ] = useState(false);
useEffect(() => {
// if we've ever seen the section before, don't change anything,
// so we don't re-render
if (!isPurchaseSectionRef) {
setIsPurchaseSectionRef(purchaseSectionRefValue);
}, [purchaseSectionRefValue]);
...
return (
<section>
{ isPurchaseSectionRef && <div> { /* only true when we've intersected at least once */ }
{stripePromise && clientSecret && (
<Elements stripe={stripePromise} options={{ clientSecret }}>
<ElementForm />
</Elements>
)}
</div> }
</section>
)
}
延迟 Stripe 库
如果您还想推迟库本身的加载,以便在用户到达付款部分时获得更快的页面加载速度,但较慢的表单加载速度,则需要将 Stripe API 调用拉入新的库中useEffect
,并延迟加载getStripe
:
import React, { useEffect, useRef, useState } from 'react';
import dynamic from 'next/dynamic';
import { Elements } from '@stripe/react-stripe-js';
// import getStripe from '../lib/get-stripejs';
import showOnScreen from '../utils/showOnScreen';
...
useEffect(() => {
// if we've ever seen the section before, don't change anything, so we don't rerender
if (!isPurchaseSectionRef) {
setIsPurchaseSectionRef(purchaseSectionRefValue);
// only do Stripe interactions if we've intersected
// this functionality used to be in `useEffect(() => {...}, [])`
if (purchaseSectionRefValue) {
fetch('api/keys', {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
})
.then((res) => res.json())
.then(async (data) => {
const getStripe = (await import('../lib/get-stripejs')).default; // <- getStripe deferred until here
setStripePromise(getStripe(data.publishableKey));
})
fetch('api/create-payment-intent', {
method: 'POST',
header: { 'Content-Type': 'applcation/json' },
body: JSON.stringify({
productId: '34032255',
productType: 'seminar'
})
})
.then(async (res) => {
const { clientSecret } = await res.json();
setClientSecret(clientSecret);
})
}
}
}, [purchaseSectionRefValue]);
...
关于reactjs - 组件的 NextJS/Script — defer Stripe,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74093819/