Вопрос или проблема
Я использую маршрутизатор приложений из next.js.
Я хотел бы добавить данные jsonLd для обеих страниц, “/blog” и “/blog/[searchKeyword]”.
Но я столкнулся с некоторыми проблемами в этом процессе.
blog > layout.tsx
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
id="jsonLd-blog"
/>
{children}
</> );
blog/[searchKeyword] > layout.tsx
return ( <>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
id={jsonLd-${params.searchKeyword}}
/>
{children}
</> );
- Во-первых, на странице “/blog/[searchKeyword]” есть не только jsonLd из ее layout.tsx, но и из layout.tsx “/blog”.
- Во-вторых, я попытался удалить родительский код скрипта jsonld, используя метод ‘remove()’ с их идентификатором, таким как ‘jsonLd-blog’, как ниже.
"use client";
import { useEffect } from "react";
const RemoveJsonScript: React.FC<{ elementId: string }> = ({ elementId }) => {
useEffect(() => {
const oldJsonLds = document.getElementById(elementId);
if (oldJsonLds) {
oldJsonLds.remove();
}
}, []);
return <></>;
};
export default RemoveJsonScript;
3. В-третьих, однако это вызывает ошибку ниже, независимо от того, используется ли она в серверном компоненте или клиентском компоненте.
Uncaught NotFoundError: Не удалось выполнить 'removeChild' на 'Node': Узел, который нужно удалить, не является дочерним этого узла.
4. Кроме того, это не работает в теге <Head> из “next/head”, а только в теге <head>. Эта проблема не связана с вышеупомянутыми тремя проблемами, но мне интересно, почему это так.
Я не знаю, как решить эту проблему. Next.js – это самый великое фреймворк для SEO, который я когда-либо использовал, но эта ошибка вызывает у меня разочарование. Мне нужна ваша помощь.
Я хочу избавиться от тега скрипта jsonLd родителя, без каких-либо сообщений об ошибках, таких как ‘Uncaught NotFoundError: Не удалось выполнить ‘removeChild’ на ‘Node’: Узел, который нужно удалить, не является дочерним этого узла.’
Это поведение ожидаемо, так как layout.tsx
будет оборачивать все свои дочерние макеты или страницы. Таким образом, если у вас есть /blog/layout.tsx
и /blog/[searchKeyword]/layout.tsx
, дерево компонентов для /blog/[searchKeyword]/page.tsx
будет выглядеть так:
<BlogLayout>
<BlogSearchKeywordLayout>
<BlogSearchKeywordPage />
</BlogSearchKeywordLayout>
</BlogLayout>
Лучшее решение для вас – разместить свои теги скриптов json-ld в /blog/page.tsx
и /blog/[searchKeyword]/page.tsx
Ответ или решение
Чтобы устранить проблему с повторяющимся тегом <script>
для JSON-LD в компоненте Next.js, есть несколько моментов, которые необходимо учесть. Давайте рассмотрим решение по шагам.
Описание проблемы
Ваша структура маршрутов Next.js включает общий родительский компонент (/blog/layout.tsx
), который создает тег JSON-LD, и дочерний компонент (/blog/[searchKeyword]/layout.tsx
), который тоже создает свой собственный тег JSON-LD. В итоге при загрузке маршрута /blog/[searchKeyword]
у вас оказывается два тега JSON-LD: один от родительского компонента и второй от дочернего.
Ошибка
Вы попытались удалить родительский тег JSON-LD с помощью метода remove()
, но столкнулись с ошибкой Uncaught NotFoundError
. Это связано с тем, что когда вы пытаетесь удалить элемент, он не является дочерним каким-либо узлом, что приводит к этой ошибке.
Решение
1. Перемещение тегов JSON-LD
Наиболее простым и эффективным решением будет переместить создание тегов JSON-LD в компоненты страниц, а не в компоненты макета. Так каждый компонент страницы будет отвечать за свой собственный вывод данных. В результате вы избежите конфликта между родительским и дочерним тегами.
// blog/page.tsx
export default function BlogPage() {
const jsonLd = { /* ваши данные JSON-LD */ };
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
id="jsonLd-blog"
/>
{/* Остальной код страницы */}
</>
);
}
// blog/[searchKeyword]/page.tsx
export default function BlogSearchKeywordPage({ params }) {
const jsonLd = { /* ваши данные JSON-LD для конкретного поиска */ };
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
id={`jsonLd-${params.searchKeyword}`}
/>
{/* Остальной код страницы */}
</>
);
}
2. Удаление тега JSON-LD через эффект
Если по какой-то причине вы все же хотите оставить теги JSON-LD в макетах и только удалять их в дочернем компонентах, то можно использовать следующий подход:
// blog/[searchKeyword]/layout.tsx
"use client";
import { useEffect } from "react";
const BlogSearchKeywordLayout = ({ children }) => {
useEffect(() => {
const scriptId = 'jsonLd-blog';
const oldJsonLd = document.getElementById(scriptId);
if (oldJsonLd) {
oldJsonLd.remove();
}
}, []);
return <>{children}</>;
};
export default BlogSearchKeywordLayout;
Объяснение проблемы с <Head>
Что касается вашего вопроса о том, почему next/head
не работает так, как вы ожидаете, это связано с тем, что next/head
управляет содержимым <head>
в рендеринге и не удаляет элементы, которые были добавлены в предыдущих рендерах. Теги, добавленные напрямую в компонент, будут присутствовать в DOM, пока не будут удалены вручную. Это объясняет, почему вы видите теги <script>
в некомпилированном виде.
Итог
Наилучший способ устранить дублирование тегов JSON-LD – это поместить их в конкретные страницы, как показано выше. Это не только устранит проблему, но и улучшит читаемость и mantenimiento кода.
Если у вас остались вопросы или требуются дополнительные разъяснения, не стесняйтесь обращаться!