React Helmet仅在索引页面上加载脚本,而不在其他页面上加载脚本

问题描述

我正在使用Gatsby.js作为我的入门模板。

我有一个Layout.js

import React from 'react'
import { Helmet } from 'react-helmet'
import Footer from '../components/Footer'
import Navbar from '../components/Navbar'
import useSiteMetadata from './SiteMetadata'
import { withPrefix } from 'gatsby'
import './all.sass'
import './styles/blogs.sass'

const TemplateWrapper = ({ children }) => {
  const { title,description } = useSiteMetadata()
  return (
    <div>
      <Helmet>
        <html lang="en" />
        <title>{title}</title>
        <Meta name="description" content={description} />

        <link
          rel="icon"
          sizes="180x180"
          href={`${withPrefix('/')}img/wf-logo.png`}
        />
        <link
          rel="icon"
          type="image/png"
          href={`${withPrefix('/')}img/wf-logo-32.png`}
          sizes="32x32"
        />
        <link
          rel="icon"
          type="image/png"
          href={`${withPrefix('/')}img/wf-logo-16.png`}
          sizes="16x16"
        />

        <link
          rel="mask-icon"
          href={`${withPrefix('/')}img/safari-pinned-tab.svg`}
          color="#0288d1"
        />
        <Meta name="theme-color" content="#fff" />

        <Meta property="og:type" content="business.business" />
        <Meta property="og:title" content={title} />
        <Meta property="og:url" content="/" />
        <Meta
          property="og:image"
          content={`${withPrefix('/')}img/3d-desktop-backgrounds.jpg`}
        />
        <script src="../helpers.js" type="text/javascript" />
      </Helmet>
      <Navbar />
      <div>{children}</div>
      <Footer />
    </div>
  )
}

export default TemplateWrapper

也有2页:

const IndexPage = ({ data }) => {
  const { frontmatter } = data.markdownRemark

  return (
    <Layout>
      <IndexPageTemplate
        image={frontmatter.image}
        title={frontmatter.title}
        subheading={frontmatter.subheading}
        mainpitch={frontmatter.mainpitch}
        intro={frontmatter.intro}
      />
    </Layout>
  )
}


const BlogPost = ({ data }) => {
  const { markdownRemark: post } = data;

  return (
    <Layout>
      <BlogPostTemplate
        content={post.html}
        contentComponent={HTMLContent}
        description={post.frontmatter.description}
        helmet={
          <Helmet titleTemplate="%s | Blog">
            <title>{`${post.frontmatter.title}`}</title>
            <Meta
              name="description"
              content={`${post.frontmatter.description}`}
            />
            <script src="../helpers.js" type="text/javascript" />
          </Helmet>
        }
        tags={post.frontmatter.tags}
        title={post.frontmatter.title}
        from={'blog'}
      />
    </Layout>
  );
};

正如您在此处看到的,两个页面都有布局作为包装,我有一个名为:helpers.js的脚本 如果我从主页打开网站,则React Helmet仅将此脚本注入首页(即索引页面),而不注入Blog页面

对于“索引”页面

enter image description here

对于Blog页面

enter image description here

值得一提的是:如果我先打开主页,然后通过汉堡栏内的导航链接导航到博客页面,则helpers.js将保留在那里。直接打开主页以外的页面将不会加载helpers.js

更新

将Helmet从BlogPostTemplate内部移动到Layout和BlogPostTemplate之间之后,整个博客页面都消失了。

代码如下:

import React from "react";
import PropTypes from "prop-types";
import { kebabCase } from "lodash";
import Helmet from "react-helmet";
import { graphql,Link } from "gatsby";
import { discussionEmbed } from "disqus-react";
import Layout from "../components/Layout";
import Content,{ HTMLContent } from "../components/Content";
// import ImportMyScript from "../hooks/importMyScript";

export const BlogPostTemplate = ({
  content,contentComponent,description,tags,title,// helmet,from,}) => {
  const PostContent = contentComponent || Content;
  const disqusConfig = {
    shortname: process.env.GATSBY_disQUS_NAME,config: { identifier: title },};

  return (
    <section className="section">
      {/* {helmet || ""} */}
      <div className="container content">
        <div className="columns">
          <div className="column is-10 is-offset-1">
            <h1 className="title is-size-2 has-text-weight-bold is-bold-light">
              {title}
            </h1>
            <p>{description}</p>
            <PostContent content={content} />
            {tags && tags.length ? (
              <div style={{ marginTop: `4rem` }}>
                <h4>Tags</h4>
                <ul className="taglist">
                  {tags.map((tag) => (
                    <li key={tag + `tag`}>
                      <Link to={`/tags/${kebabCase(tag)}/`}>{tag}</Link>
                    </li>
                  ))}
                </ul>
              </div>
            ) : null}
            {from !== "preview" ? <discussionEmbed {...disqusConfig} /> : null}
          </div>
        </div>
      </div>
    </section>
  );
};

BlogPostTemplate.propTypes = {
  content: PropTypes.node.isrequired,contentComponent: PropTypes.func,description: PropTypes.string,title: PropTypes.string,helmet: PropTypes.object,};

const BlogPost = ({ data }) => {
  const { markdownRemark: post } = data;
  // ImportMyScript("../helpers.js");
  return (
    <Layout>
      <Helmet titleTemplate="%s | Blog">
        <title>{`${post.frontmatter.title}`}</title>
        <Meta name="description" content={`${post.frontmatter.description}`} />

        <BlogPostTemplate
          content={post.html}
          contentComponent={HTMLContent}
          description={post.frontmatter.description}
          tags={post.frontmatter.tags}
          title={post.frontmatter.title}
          from={"blog"}
        />
      </Helmet>
    </Layout>
  );
};

BlogPost.propTypes = {
  data: PropTypes.shape({
    markdownRemark: PropTypes.object,}),};

export default BlogPost;

export const pageQuery = graphql`
  query BlogPostByID($id: String!) {
    markdownRemark(id: { eq: $id }) {
      id
      html
      frontmatter {
        date(formatString: "MMMM DD,YYYY")
        title
        description
        tags
      }
    }
  }
`;

它确实加载了helpers.js,但是我的博客页面现在看起来像这样:

enter image description here

解决方法

发生这种情况是由于@reach/router(响应路由)。它仅加载针对每个页面/组件更改的组件。由于您的<Layout>已为所有页面共享,因此它只会刷新children内的内容,因为<Layout>组件本身(仅是其自己的子项props)没有任何变化。

一种可行的方法是在您的<Helmet>内部和IndexPageBlogPost中使用自定义组件并将其加载。像这样:

const BlogPost = ({ data }) => {
  const { markdownRemark: post } = data;

  return (
    <Layout>
      <Helmet titleTemplate="%s | Blog">
        <title>{`${post.frontmatter.title}`}</title>
        <meta
          name="description"
          content={`${post.frontmatter.description}`}
        />
        <script src="../helpers.js" type="text/javascript" />
      </Helmet>

      <BlogPostTemplate
        content={post.html}
        contentComponent={HTMLContent}
        description={post.frontmatter.description}
        tags={post.frontmatter.tags}
        title={post.frontmatter.title}
        from={'blog'}
      />
    </Layout>
  );
};

const IndexPage = ({ data }) => {
  const { frontmatter } = data.markdownRemark

  return (
    <Layout>
          <Helmet>
            <script src="../helpers.js" type="text/javascript" />
          </Helmet>
      <IndexPageTemplate
        image={frontmatter.image}
        title={frontmatter.title}
        subheading={frontmatter.subheading}
        mainpitch={frontmatter.mainpitch}
        intro={frontmatter.intro}
      />
    </Layout>
  )
}

或者,我建议您使用gatsby-browser.js API,但不确定是否符合您的要求。 Gatsby在此处公开了多个API,其中一种方法可让您在所有页面中加载脚本。看起来应该像这样:

import { helpers } from './your/path/to/helpers.js';
import React from 'react';

export const onClientEntry = () => helpers();

从文档中:

onClientEntry功能

(_: emptyArg,pluginOptions: pluginOptions) => undefined

在Gatsby浏览器运行时首次启动时调用。

,

我通过使用Hooks导入JavaScript解决了这个问题。

我也得出了有关何时在静态文件夹下导入脚本以及何时不这样做的结论。

在我的情况下,html组件不是来自Reactjs组件,例如About.component.js,相反,我是通过Netilify CMS提供的Blog Editor快速创建这些html元素的。

因此,这里是提示:当从编辑器创建html元素时,例如Netlify CMS的Markdown / Rich编辑器,那么我必须按原样导入Javascript(因为否则javascript函数将被丑化,从而导致名称不同,因此您无法从在Netlify CMS Rich Editor中创建的html元素中引用它)并且脚本文件需要放在static文件夹下。否则,您可以按照常规方式进行操作,例如在Reactjs组件中定义您的方法,并将其作为属性传递并在内部使用。

这是ImportScript钩子的样子:

import { useEffect } from 'react';
const ImportMyScript = resourceUrl=> {
  useEffect(() => {
    const script = document.createElement('script');
    script.src = resourceUrl;
    script.async = true;
    document.body.appendChild(script);
return () => {
      document.body.removeChild(script);
    }
  },[resourceUrl]);
};
export default ImportMyScript;

使用方法如下:

const BlogPost = ({ data }) => {
  const { markdownRemark: post } = data;
  ImportMyScript("../../bloghelpers.js");
  return (
  ............
  );
}

这对我的情况很有意义,因为我只想在“关于”页面上导入abouthelpers.js,在博客页面上导入bloghelpers.js。