如何在服务器端渲染中使用react-helmet?

5

我一直在尝试使用服务器端渲染设置react-helmet。我按照文档和一些博客文章上的说明来设置react-helmet与SSR,但是一直无法获得所需的结果。这里是我正在呈现应用程序的代码片段:

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>
  `
}

当我运行上面的代码时,出现了一个错误,显示“无法在模块外部使用import语句”。是否可以同时使用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"
  ]
}

请查看这个示例 - JMadelaine
我尝试过了。在我导入React组件的那一行仍然出现错误。[SyntaxError: Cannot use import statement outside a module] - Ghouse Mohamed
你是如何打包应用程序的?你使用Webpack吗?你的配置文件是什么样子的? - JMadelaine
我正在使用babel webpack进行打包。我已经包含了.babelrc配置文件。 - Ghouse Mohamed
@GhouseMohamed 我刚刚更新了我的答案,并加入了最新的研究成果。我现在正在用渲染后的 HTML 替换 <div id="root"></div>,这可以加快首次内容绘制速度,从而提高 SEO 性能。 - sunknudsen
1个回答

1
你需要使用@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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接