我正在尝试将第 3 方评论脚本嵌入到我的 next.js 中,例如。 (disqus,remark42,hyvor)但不幸的是,它仅在第一次加载时加载,我必须再次重新加载页面才能显示嵌入的第3方脚本,但它是反应/下一步的反模式。因为当您通过链接组件导航到另一个页面时,next.js 不会重新加载,所以我正在寻找一种解决方案,仅重新加载脚本组件本身,以便我的评论小部件出现在网站的每个文章页面上。
代码:
export const getStaticPaths: GetStaticPaths = async () => {
const data = await getArticles();
const paths = data.map((article) => ({
params: {
slug: article?.slug,
},
}));
return {
paths,
fallback: true,
};
};
export const getStaticProps = async ({
params,
}: GetStaticPropsContext<{ slug: string }>) => {
const article = await getArticleByProp("slug", params!.slug);
return {
props: {
article: article[0],
},
notFound: article.length === 0,
revalidate: 60,
};
};
const ArticlePage = ({
article,
}: InferGetStaticPropsType<typeof getStaticProps>) => {
const router = useRouter();
const articleDate = useFormattedDate(
article?.createdAt ? new Date(article.createdAt) : new Date(),
"distance"
);
if (router.isFallback) {
return <div>Loading...</div>;
}
return (
<Container>
<SEOHeader
title={article?.title}
author={article?.author}
description={article?.excerpt}
ogImage={article?.featuredImage}
canonical={article?.slug}
/>
<Wrapper>
<ArticleWrapper>
<ArticleHeader>
<small className="category">{article?.category}</small>
<h1 className="title">{article?.title}</h1>
<p className="contributor">
<span>
By <strong> {article?.author}</strong> <br />
</span>
</p>
<div className="date">
<Clock size={18} />{" "}
<span>{article?.createdAt ? articleDate : "N/A"}</span>
</div>
<button className="share">
<Share size={24} />
</button>
</ArticleHeader>
<ArticleBody>
{!!article?.featuredImage && (
<Featured>
<Image
src={article.featuredImage}
layout="responsive"
width={1920}
height={1080}
alt="Featured article image"
/>
</Featured>
)}
<ArticleExcerpt>{article?.excerpt}</ArticleExcerpt>
<ArticleMdx>{article?.body}</ArticleMdx>
</ArticleBody>
<div id="remark42">{""}</div>
{/* <Script id="remark42-script" strategy="afterInteractive">
{`
var remark_config = {
host: "http://localhost:5010",
site_id: "mysite",
show_email_subscription: false,
url: "${process.env.BASE_URL + router.asPath}"
}
!(function (e, n) {
for (var o = 0; o < e.length; o++) {
var r = n.createElement("script"),
c = ".js",
d = n.head || n.body;
"noModule" in r ? ((r.type = "module"), (c = ".mjs")) : (r.async = !0),
(r.defer = !0),
(r.src = remark_config.host + "/web/" + e[o] + c),
d.appendChild(r);
}
})(remark_config.components || ["embed"], document);
`}
</Script> */}
{/* <div id="hyvor-talk-view"></div>
<Script id="hyvor-script">
{`
var HYVOR_TALK_WEBSITE = 7527;
var HYVOR_TALK_CONFIG = {
url: "${process.env.BASE_URL + router.asPath}",
id: "${article?.id}",
};
`}
</Script>
<Script
async
src="//talk.hyvor.com/web-api/embed.js"
strategy="lazyOnload"
onLoad={() =>
console.log(
`script loaded correctly, window.FB has been populated`
)
}
/> */}
{/* <Discussion id={article?.id} title={article?.title} /> */}
<Script id="remark42-test">{`
const remark_config = {
host: 'https://demo.remark42.com',
site_id: 'remark',
};
window.remark_config = remark_config;
!function(e,n){for(var o=0;o<e.length;o++){var r=n.createElement("script"),c=".js",d=n.head||n.body;"noModule"in r?(r.type="module",c=".mjs"):r.async=!0,r.defer=!0,r.src=remark_config.host+"/web/"+e[o]+c,d.appendChild(r)}}(remark_config.components||["embed"],document);
`}</Script>
</ArticleWrapper>
<Recommended>
<h2>Recommended</h2>
<ArticleCard card={article} variant="slim" />
<ArticleCard card={article} variant="slim" />
<ArticleCard card={article} variant="slim" />
<ArticleCard card={article} variant="slim" />
</Recommended>
</Wrapper>
</Container>
);
};
export default ArticlePage;
也许这有助于解决类似的问题:
next/script 提供了更新的
onReady
属性,对于处理第三方地图、小部件等非常有用。
您可以在脚本首次加载时的加载事件之后执行代码,然后使用
属性重新挂载每个后续组件后执行代码。onReady
文档中的代码示例:
<Script
id="google-maps"
src="https://maps.googleapis.com/maps/api/js"
onReady={() => {
new google.maps.Map(mapRef.current, {
center: { lat: -34.397, lng: 150.644 },
zoom: 8,
})
}}
/>
“next/script”中的脚本组件的工作方式与您在问题中提到的方式相同,即它只会在您登陆页面或重新加载脚本时加载脚本,之后脚本将被缓存并且不会在页面之间导航时重新加载。
因此,为了实现在来回导航时重新加载脚本的要求,我们需要使用 Vanilla JS 和上下文引用进行一些自定义调整。
请参考以下代码,并根据要求在您的代码中进行相应调整 -
export default function LoyaltyRewards(): JSX.Element | null {
const socialAnnexRef = useRef<HTMLDivElement>(null)
const script = `
// Whatever script you want to inject
<script id="social-annex">
var siteID = '${siteId}';
var sa_emailid = '${sAEmailId}';
var token = '${token}';
var sa_uni = sa_uni || []; sa_uni.push(['sa_pg', '5']);
(function () {
function sa_async_load () {
var sa = document.createElement('script');
sa.type = 'text/javascript';
sa.async = true; sa.src="https://cdn.socialannexuat.com/partner/${siteId}/universal.js";
var sax = document.getElementsByTagName('script')[0]; sax.parentNode.insertBefore(sa, sax);
}
sa_async_load();
})();
</script>
`
useEffect(() => {
/* Clear out the loaded script's on component's un-mount */
return () => {
document.getElementById('social-annex')?.remove()
document.getElementById('sa_load_loader')?.remove()
document.getElementById('socialannex-s15dasboard')?.remove()
document.getElementById('social-annex-universal')?.remove()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
useEffect(() => {
if (siteId) {
// creates a document range (grouping of nodes in the document). In this case, we instantiate it as empty, on purpose
const range = document.createRange()
// creates a mini-document (lightweight version), in our range with our script in it
const documentFragment = range.createContextualFragment(script)
// appends it on the same level of annex div - so that it renders in the correct location
socialAnnexRef.current?.appendChild(documentFragment)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [siteId])
return (
<div className="mt-8" ref={socialAnnexRef}>
<div id="socialannex_dashboard" />
</div>
)
}
希望这对您或其他人将来有所帮助。谢谢!
快乐编码:-)