如何使用服务器端渲染设置 react-helmet?

How setup react-helmet with Server Side Rendering?

我一直在尝试使用服务器端渲染设置 react-helmet。我遵循了有关如何使用 SSR 设置 react-helmet 的文档和一些博客文章,但无法产生预期的结果。这是我如何呈现应用程序的代码片段:

import React from 'react';
import { renderToString } from 'react-dom/server';
import App from './src/App';
const express = require('express');

const app = express();
app.get('*', (req, res) => {
  const app = renderToString(<App />);
  const helmet = Helmet.renderStatic();

  res.send(formatHTML(app, helmet));
})

function formatHTML(appStr, helmet) {
  return `
    <!DOCTYPE html>
    <html lang="en">
      <head>
        ${helmet.title.toString()}
        ${helmet.meta.toString()}
      </head>
      <body>
        <div id="root">
          ${ appStr }
        </div>
        <script src="./bundle.js"></script>
      </body>
    </html>
  `
}

当我运行上面的代码时,我得到一个错误'cannot use import statement outside a module'。是否可以同时使用 es5 和 es6 语法?或者有没有更好的方法来设置 React-helmet?

这是我的babel配置文件

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "modules": false
      }
    ],
    "@babel/preset-react",
    "@babel/preset-flow"
  ],
  "env": {
    "development": {
      "only": [
        "app",
        "internals/scripts"
      ],
      "plugins": [
        "@babel/plugin-transform-react-jsx-source"
      ]
    },
    "production": {
      "only": [
        "app"
      ],
      "plugins": [
        "transform-react-remove-prop-types",
        "@babel/plugin-transform-react-constant-elements",
        "@babel/plugin-transform-react-inline-elements"
      ]
    },
    "test": {
      "plugins": [
        "@babel/plugin-transform-modules-commonjs",
        "dynamic-import-node"
      ]
    }
  },
  "compact": true,
  "plugins": [
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-import-meta",
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-proposal-json-strings",
    [
      "@babel/plugin-proposal-decorators",
      {
        "legacy": true
      }
    ],
    "@babel/plugin-proposal-function-sent",
    "@babel/plugin-proposal-export-namespace-from",
    "@babel/plugin-proposal-numeric-separator",
    "@babel/plugin-proposal-throw-expressions",
    "@babel/plugin-proposal-export-default-from",
    "@babel/plugin-proposal-logical-assignment-operators",
    "@babel/plugin-proposal-optional-chaining",
    [
      "@babel/plugin-proposal-pipeline-operator",
      {
        "proposal": "minimal"
      }
    ],
    "@babel/plugin-proposal-nullish-coalescing-operator",
    "@babel/plugin-proposal-do-expressions",
    "@babel/plugin-proposal-function-bind",
    "lodash"
  ]
}

您需要使用 @babel/register 包装您的服务器。

这就是我在不弹出的情况下处理我的 TypeScript CRA 项目的方式。

注意: 我使用此方法将元数据注入 index.html 与渲染整个应用程序(我使用的某些组件不能很好地与 SSR 配合使用)。

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="shortcut icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

index.js

"use strict"

require("ignore-styles")

require("@babel/register")({
  ignore: [/(node_modules)/],
  presets: [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-typescript",
  ],
  extensions: [".tsx"],
  cache: false,
})

require("./server")

server.js(节选)

const indexPath = path.join(__dirname, "build/index.html")

const middleware = async (req, res, next) => {
  let context = {}
  let html = renderToString(
    React.createElement(StaticRouter, {
      location: req.url,
      context: context,
    })
  )
  const helmet = Helmet.renderStatic()
  if (context.url) {
    res.redirect(context.url)
  } else if (!fs.existsSync(indexPath)) {
    next("Site is updating... please reload page in a few minutes.")
  } else {
    let index = fs.readFileSync(indexPath, "utf8")
    let status = 200
    if (typeof context.status === "number") {
      status = context.status
    }
    return res.status(status).send(
      index
        .replace('<div id="root"></div>', `<div id="root">${html}</div>`)
        .replace("</head>", `${helmet.meta.toString()}</head>`)
        .replace("</head>", `${helmet.title.toString()}</head>`)
        .replace("</head>", `${helmet.script.toString()}</head>`)
    )
  }
}

server.get("/", middleware)

server.use(express.static(path.join(__dirname, "build")))

server.get("*", middleware)