带有React Helmet的头盔令人眼花:乱:当使用Axios中的动态值时,搜寻器中的meta标签显示错误

时间:2019-07-16 05:29:30

标签: reactjs react-helmet razzle

我正在使用RazzleReact Helmet一起使用React和Server Side Rendering。当您使用React Helmet设置具有动态值的元标记时,出现了这个问题,它无法正确显示。但是,如果您将meta标签设置为静态值,则可以使用。

请查看一些代码。

SEO.js组件

import React, { Component } from 'react';
import { Helmet } from "react-helmet-async";

class SEO extends Component {
    constructor(props) {
        super(props);
        this.state = {
            title: this.props.title,
            description: this.props.description,
            image: this.props.image
        }
    }

    shouldComponentUpdate(nextProps) {
        if(this.props != nextProps) {
            this.setState({
                title: nextProps.title,
                description: this.props.description,
                image: nextProps.image
            })
            return true;
        } else {
            return false;
        }
    }

    render() { 
        return (
            <div>
                <Helmet>
                    <title>{this.state.title ? this.state.title : "Volunteer Hub by Indorelawan"}</title>
                    <meta name="title" content={this.state.title ? this.state.title : "Volunteer Hub by Indorelawan"} />
                    <meta
                    name="description"
                    content={this.state.description ? this.state.description : "Volunteer Hub by Indorelawan adalah tempat kolaborasi antara relawan dan komunitas sosial yang memiliki semangat kerelawanan dan gotong royong untuk Indonesia."}
                    />
                    <meta
                    property="og:title"
                    content={this.state.title ? this.state.title : "Volunteer Hub by Indorelawan"}
                    />
                    <meta
                    property="og:description"
                    content={this.state.description ? this.state.description : "Volunteer Hub by Indorelawan adalah tempat kolaborasi antara relawan dan komunitas sosial yang memiliki semangat kerelawanan dan gotong royong untuk Indonesia."}
                    />
                    <meta
                    property="og:image"
                    content={this.state.image ? this.state.image : "https://volunteerhub.id/assets/logo/seo.jpg"}
                    />
                    <meta property="og:url" content="https://volunteerhub.id" />
                    <meta
                    name="twitter:title"
                    content={this.state.title ? this.state.title : "Volunteer Hub by Indorelawan"}
                    />
                    <meta
                    name="twitter:description"
                    content={this.state.description ? this.state.description : "Volunteer Hub by Indorelawan adalah tempat kolaborasi antara relawan dan komunitas sosial yang memiliki semangat kerelawanan dan gotong royong untuk Indonesia."}
                    />
                    <meta
                    name="twitter:image"
                    content={this.state.image ? this.state.image : "https://volunteerhub.id/assets/logo/seo.jpg"}
                    />
                    <meta name="twitter:card" content="summary_large_image" />
                </Helmet>
            </div>
        );
    }
}

export default SEO;

以下是设置静态元标记的示例:

import React, {Component} from "react";
import SEO from "../../components/SEO";

class ScheduleContainer extends Component {
    constructor(props) { super(props); }
    render() {
        return(
            <div>
                <SEO 
                    title="Cek Jadwal | Volunteer Hub by Indorelawan"
                    description="Cek jadwal kegiatan di Volunteer Hub! Volunteer Hub by Indorelawan adalah tempat kolaborasi antara relawan dan komunitas sosial yang memiliki semangat kerelawanan dan gotong royong untuk Indonesia." />
            </div>);
    }
}

以下是设置动态元标记的示例:

import React, {Component} from "react";
import axios from "axios";
import SEO from "../../components/SEO";

class EventContainer extends Component {
    constructor(props) {
        super(props);
        this.state = {
            event: {}
        }
    }

    componentDidMount() {
        axios.get('API_URL')
        .then(response => {
            this.setState({ event: response.data.result })
        });
    }

    render() {
        return(
            <div>
                <SEO 
                    title={this.state.event.title}
                    description={this.state.event.description} />
            </div>);
    }
}

Server.js

import RootContainer from "./containers/RootContainer";
import React from "react";
import { StaticRouter } from "react-router-dom";
import express from "express";
import { renderToString } from "react-dom/server";
import { Helmet, HelmetProvider } from "react-helmet-async";

const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);

const server = express();
server
  .disable("x-powered-by")
  .use(express.static(process.env.RAZZLE_PUBLIC_DIR))
  .get("/*", (req, res) => {
    const context = {};
    const helmetContext = {};
    const markup = renderToString(
      <HelmetProvider context={helmetContext}>
        <StaticRouter context={context} location={req.url}>
          <RootContainer />
        </StaticRouter>
      </HelmetProvider>
    );

    const { helmet } = helmetContext;

    if (context.url) {
      res.redirect(context.url);
    } else {
      res.status(200).send(
        `<!doctype html>
        <html lang="">
        <head>
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <meta http-equiv="X-UA-Compatible" content="ie=edge" />
            <meta
            name="keywords"
            content="volunteer, hub, by, indorelawan, volunteer hub, volunteer hub by indorelawan, kolaborasi, dimulai, dari, sini, ubah, niat, baik, jadi, aksi, baik, hari, ini"
            />
            <meta name="robots" content="index, follow" />
            <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
            <meta name="language" content="Indonesia" />
            <meta name="author" content="Indorelawan" />
            <meta name="msapplication-TileColor" content="#ffffff">
            <meta name="theme-color" content="#222222" />
            ${helmet.title.toString()}
            ${helmet.meta.toString()}
            ${
        assets.client.css
          ? `<link rel="stylesheet" href="${assets.client.css}">`
          : ""
        }
            ${
        process.env.NODE_ENV === "production"
          ? `<script src="${assets.client.js}" defer></script>`
          : `<script src="${
          assets.client.js
          }" defer crossorigin></script>`
        }
        ...
        </head>
        <body>
            <div id="root">${markup}</div>
            <script>
            if ("serviceWorker" in navigator) {
              if (navigator.serviceWorker.controller) {
                console.log("[PWA Builder] active service worker found, no need to register");
              } else {
                // Register the service worker
                navigator.serviceWorker
                  .register("pwabuilder-sw.js", {
                    scope: "./"
                  })
                  .then(function (reg) {
                    console.log("[PWA Builder] Service worker has been registered for scope: " + reg.scope);
                  });
              }
            }
            </script>
        </body>
    </html>`
      );
    }
  });

export default server;

现在您已经看到了代码,这是我复制粘贴到Google SERP Simulator和WhatsApp时的结果:

静态元标记来自Schedule Page

Google SERP模拟器 Static meta tags - Google SERP Simulator WhatsApp的 Static meta tags - WhatsApp

动态元标记来自Event Page

Google SERP模拟器 Dynamic meta tags - Google SERP Simulator WhatsApp的 Dynamic meta tags - Whatsapp

从结果中,它始终返回默认标题和描述标签,而不是从axios传递的标题和描述。是正常行为还是我做错了什么?

2 个答案:

答案 0 :(得分:0)

Razzle IS服务器端渲染,动态元标记方案的问题在于,您依赖于在componentDidMount中获取的数据,而作为提交阶段的生命周期方法的componentDidMount在服务器上未被调用,因为服务器端没有实际安装。

NextJS通过在服务器和客户端上调用的getInitialProps为您解决了此问题。

答案 1 :(得分:-1)

结果证明Razzle不是服务器端渲染。您必须使用自定义快递服务器定义SEO标签,或者仅对React使用SSR。

我正在使用NextJS,这没问题。