nextjs 动态路由渲染内容不起作用

Posted

技术标签:

【中文标题】nextjs 动态路由渲染内容不起作用【英文标题】:nextjs Dynamic route rendering content not working 【发布时间】:2021-01-11 03:12:18 【问题描述】:

我被这个问题困扰了很多天。我正在使用Next.js 并且有 3 页。

pages/index.js pages/categories.js 页面/类别/[slug].js

categories/[slug].js 使用Next.js 获取方法名称getServerSideProps,该方法在每个请求上运行并用于在运行时构建动态页面。 categories/[slug].js 在页面上呈现动态内容,动态内容来自 CMS,作为 API 端点的响应。动态内容只不过是一个包含带有<script /> 元素的html 的字符串。

注意:要从 CMS 获取内容,我们必须发送带有 CMS 凭据的 POST 请求,例如用户名、密码和内容的页面 slug。我正在使用axios 库发送一个post 请求,方法在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 来渲染所有HTML,其中还包含JSON 字符串中的<script /> 元素。

页面/类别/[slug].js

<div dangerouslySetInnerHTML= __html: result.html  /> 

根据每个 slug,内容加载良好。但是当我导航到该类别页面时,即pages/categories/index.js

<Link href="/categories/[slug]" as="/categories/online-cloud-storage">
      <a>Online Cloud Storage</a>
</Link>

它有一个&lt;Link /&gt; 元素,当我点击它时。

动态内容加载正常,但动态内容包含无法正常工作的 accordionslider 元素。我认为这些元素中的&lt;script /&gt; 不起作用。但是当我刷新页面时,它们工作正常。看这个。

当我设置这样的链接时,它们也可以正常工作。

<Link href="/categories/online-cloud-storage" as="/categories/online-cloud-storage">
          <a>Online Cloud Storage</a>
 </Link>

但是像上面的方法设置链接后,点击导致页面硬重载。但我不想要这个。一切都应该工作。当用户点击类别链接时。

有没有办法解决这个问题

为什么当您从categories/index.js 页面点击时内容元素不起作用

Github repo

代码:

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;

【问题讨论】:

点击手风琴时控制台是否有任何错误? (在不工作的版本中) 您能否提供 CMS 网址?如果可以,能否提供用户名和密码? @dna 不,没有任何控制台错误。我认为 dangerouslySetInnerHTML= __html: result.html 没有运行 javascript &lt;script /&gt;&lt;script /&gt; 元素总是在客户端运行。 @TopTalent 这是机密凭据。 我认为*** question 会对您有所帮助。实际上,script 标签在 dangerouslySetInnerHTML 中不起作用,所以需要在组件挂载后运行脚本。 【参考方案1】:

恕我直言,我认为脚本加载不正确是由于错误地导入了客户端渲染 (CSR) 和服务器端渲染 (s-s-r) 上的脚本。在这里阅读more,也可以看看this interesting article。这也可以解释链接组件的问题行为。

就您而言,我理解这种行为是由于在 CSR 期间对页面及其组件的生命周期进行了错误处理,因为许多脚本可能需要在页面导航中正确共享,可能在 s-s-r 中。我对NextJS 没有全面的问题或扩展专业知识,但我认为这些脚本应该导入一个地方并可能在服务器上呈现,而不是在每个页面上错误导入,错误地让 CSR以非NextJS 优化的方式完成工作。

建议的方法是为您的应用程序使用自定义 Document 实现(仅限 s-s-r),您可以在其中定义脚本。有关这方面的更多详细信息,请参阅here。另外我想您已经为您的应用设置了custom App file,您将在文档中使用它,用于所有页面通用的 CSR 和 s-s-r 渲染(有关更多信息,请参阅this SO question)。

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 原生组件在后台做了很多工作,以便为脚本、标记等设置内容。我建议你这样做,而不是直接将脚本添加到每个页面中。

【讨论】:

【参考方案2】:

这里的问题是&lt;script&gt; 标签是动态 插入dangerouslySetInnerHTMLinnerHTML,不会像HTML 5 specification 状态那样执行:

使用 innerHTML 插入的脚本元素在插入时不会执行。

如果您想在页面初始呈现后插入新的&lt;script&gt; 标签,您需要通过 JavaScript 的 document.createElement('script') 接口执行此操作,并使用 element.appendChild() 附加到 DOM 以确保它们被执行。

脚本在更改路由后不起作用,但在您刷新页面后它们起作用与 Next.js 应用程序生命周期过程相关联。

如果刷新页面,Next.js 会在服务器上预渲染整个网站,并将其作为一个整体发送回客户端。因此,该网站被解析为常规静态页面,&lt;script&gt; 标签按正常方式执行。 如果您更改路由,Next.js 不会刷新整个网站/应用程序,而只会刷新其中已更改的部分。换言之,仅获取page 组件,并将其动态 插入到现有布局中,替换之前的page。因此,&lt;script&gt; 标签被执行。

简单的解决方案

通过解析 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 字符串中提取所有 &lt;script&gt; 标记并将它们分别添加到您的页面中。最简洁的方法是修改 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);
);

【讨论】:

以上是关于nextjs 动态路由渲染内容不起作用的主要内容,如果未能解决你的问题,请参考以下文章

路由问题:我的渲染路由不起作用?

Tailwind CSS 动画在 ReactJs/NextJs 中不起作用

带有 nextjs 的 G2Plot。堆积图不起作用

j渲染视图后Javascript不起作用

动态 TabView primefaces,选项卡渲染属性不起作用

组件中的 NextJS serverSiderProps 不起作用[关闭]