我在这个问题上困扰了很多天。我正在使用Next.js,共有3页。
categories/[slug].js
使用Next.js获取方法名称getServerSideProps,该方法名称在每个请求上运行,并在运行时用于构建动态页面。 categories/[slug].js
在页面上呈现动态内容,该动态内容来自CMS,是API端点的响应。动态内容不过是包含带有<script />
元素的HTML的字符串。
注意:要从CMS中获取内容,我们必须发送一个POST
凭据,并带有CMS
凭据,例如用户名,密码和该内容的页面标记。我正在使用axios库发送发布请求,并且该方法位于post.js
文件中。
post.js :
import axios from 'axios';
const postCMS = async (slug) => {
const url = `${process.env.CMS_API_URL}/render-page/`;
let pageSlug = slug;
// If the pageSlug is not start with `/`, then create the slug with `/`
if (!pageSlug.startsWith('/')) {
pageSlug = `/${pageSlug}`;
}
const head = {
Accept: 'application/json',
'Content-Type': 'application/json'
};
const data = JSON.stringify({
username: process.env.CMS_API_USERNAME,
password: process.env.CMS_API_PASSWORD,
slug: pageSlug
});
try {
const response = await axios.post(url, data, {
headers: head
});
return response.data;
} catch (e) {
return e;
}
};
export default postCMS;
但是对于categories/[slug].js
页面上的呈现内容,我使用Reactjs道具名称dangerouslySetInnerHTML来呈现所有在JSON中也包含<script />
元素的HTML字符串。
页面/类别/[slug].js:
<div dangerouslySetInnerHTML={{ __html: result.html }} />
根据每个段的内容,内容加载正常。但是当我导航到该类别页面时,即pages/categories/index.js
。
<Link href="/categories/[slug]" as="/categories/online-cloud-storage">
<a>Online Cloud Storage</a>
</Link>
它有一个<Link />
元素,当我单击它时。
动态内容可以很好地加载,但是该动态内容包含accordion
和slider
元素,它们不能工作。我认为这些元素中的<script />
无效。但是,当我刷新页面时,它们可以正常工作。看到这个。
当我设置“链接”时,它们也可以正常工作。
<Link href="/categories/online-cloud-storage" as="/categories/online-cloud-storage">
<a>Online Cloud Storage</a>
</Link>
但是在像上述方法一样设置链接之后,会导致单击以重新加载页面。但是我不要这个。一切都应该工作。当用户单击类别链接时。
是否可以解决此问题?
为什么当您从categories/index.js
页单击时内容元素不起作用?
代码:
pages / index.js :
import React from 'react';
import Link from 'next/link';
const IndexPage = () => {
return (
<div>
<Link href="/categories">
<a>Categories</a>
</Link>
</div>
);
};
export default IndexPage;
页面/类别/index.js:
import React from 'react';
import Link from 'next/link';
const Categories = () => {
return (
<div>
<Link href="/categories/[slug]" as="/categories/online-cloud-storage">
<a>Online Cloud Storage</a>
</Link>
</div>
);
};
export default Categories;
页面/类别/[slug].js:
import React from 'react';
import Head from 'next/head';
import postCMS from '../../post';
const CategoryPage = ({ result }) => {
return (
<>
<Head>
{result && <link href={result.assets.stylesheets} rel="stylesheet" />}
</Head>
<div>
<h1>Category page CMS Content</h1>
<div dangerouslySetInnerHTML={{ __html: result.html }} />
</div>
</>
);
};
export const getServerSideProps = async (context) => {
const categorySlug = context.query.slug;
const result = await postCMS(categorySlug);
return {
props: {
result
}
};
};
export default CategoryPage;
答案 0 :(得分:1)
这里的问题是,用 script元素在插入时不会执行。 如果要在页面最初呈现后插入新的 更改路由后脚本不起作用,但是刷新页面后它们起作用的原因与Next.js应用程序生命周期过程有关。 / p>
<script>
或dangerouslySetInnerHTML
动态插入的innerHTML
标签没有按HTML 5 specification状态执行:< / p>
使用innerHTML插入的
<script>
标签,则需要通过JavaScript的document.createElement('script')
接口进行操作,并使用element.appendChild()
附加到DOM上以确保它们被执行。
。
<script>
标签
page
组件,并且将其动态插入到现有布局中,以替换先前的page
。因此,不执行<script>
标签。让一些现有的库通过解析HTML字符串并重新创建DOM树结构为您处理艰苦的工作。这是它在jQuery中的外观:
import { useEffect, useRef } from 'react';
import $ from 'jquery';
const CategoryPage = ({ result }) => {
const element = useRef();
useEffect(() => {
$(element.current).html($(result.html));
}, []);
return (
<>
<Head>
{result && <link href={result.assets.stylesheets} rel="stylesheet" />}
</Head>
<div>
<h1>Category page CMS Content</h1>
<div ref={element}></div>
</div>
</>
);
};
export const getServerSideProps = async (context) => {
/* ... */
return { props: { result } };
}
您将必须找到一种从HTML字符串中提取所有<script>
标签并将其分别添加到页面的方法。最干净的方法是修改API响应,以在两个单独的字符串中提供静态HTML和动态脚本。然后,您可以使用dangerouslySetInnerHTML
插入HTML并在JS中添加脚本:
const scripts = extractScriptTags(result.html); // The hard part
scripts.forEach((script) => {
const scriptTag = document.createElement('script');
scriptTag.innerText = script;
element.current.appendChild(scriptTag);
});
答案 1 :(得分:0)
恕我直言,我认为脚本加载不正确是由于在客户端渲染(CSR)和服务器端渲染(SSR)上错误导入了脚本。在此处阅读more,但也可以看看this interesting article。这也可以解释您的链接组件的问题行为。
在您的情况下,我了解此行为是由于CSR期间对页面及其组件的生命周期的错误处理所致,因为许多脚本可能需要在页面导航中正确共享(可能在SSR中)。我没有全面了解问题所在或在NextJS
上拥有丰富的专业知识,但是我认为这些脚本应该在一个位置导入并可能在服务器上呈现,而不是在每个页面上错误地导入让CSR以非NextJS
优化的方式完成工作。
建议的方法是为应用程序使用自定义Document
实现(仅SSR),您可以在其中定义脚本。有关更多详细信息,请参见here。同样,我想您已经为您的App设置了custom App file,将在文档中使用它来实现所有页面通用的CSR和SSR渲染(有关更多信息,请参见this SO question)。 / p>
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
// ...
}
render() {
return (
<Html>
<Head>
{/*Your head scripts here*/}
</Head>
<body>
<Main />
{/*Your body scripts here*/}
<NextScript />
</body>
</Html>
)
}
}
Head
本机组件在后台做了很多工作,以便为脚本,标记等设置东西。我建议您采用这种方式,而不是直接将脚本添加到每个页面中。 / p>