草庐IT

node.js - 在 renderStatic 期间 react Helm 混合场

coder 2023-05-30 原文

我正在运行 React 应用程序的服务器端渲染。为此,我使用 express。整个服务器端渲染代码如下所示:

import * as React from "react"
import * as ReactDOMServer from "react-dom/server"
import * as express from "express"
import { StaticRouter } from "react-router-dom"
import walker = require("react-tree-walker")
import { useStaticRendering } from "mobx-react"

import Helmet from "react-helmet"
import Provider from "../src/Provider"
import { StaticRouterContext } from "react-router"
import version = require("../version")

var _template: string = require(`../dist/${version.v()}/index.html`)

interface IRenderResponse {
    statusCode: number,
    template: string,
    redirect?: string
}

const run = (url: string, locale?: string): Promise<IRenderResponse> => {
    var template: string
    var html: string = ""
    var head: object
    var context: StaticRouterContext = {}

    useStaticRendering(true)

    var routing = (
        <StaticRouter 
            location={url} 
            context={context}
        >
            <Provider defaultLocale={locale} />
        </StaticRouter>
    )

    return new Promise((resolve) => {
        walker(routing, (element, instance) => {
            if (instance && typeof instance._prepare == typeof (() => {}))
                return instance._prepare()
        }).then(() => {
            html = ReactDOMServer.renderToString(routing)
            head = Helmet.renderStatic()
            template = _template.replace(/\${__rh-([a-z]+)}/gi, (match, group) => {
                return head[group].toString()
            })
            template = template.replace(/\${__body}/gi, (match) => {
                return html
            })
            if (context.url)
                context["statusCode"] = 301

            resolve({
                statusCode: context["statusCode"] || 200, 
                template, 
                redirect: context.url
            })
        }).catch((error) => {
            template = _template.replace(/\${__rh-([a-z]+)}/gi, "")
            template = template.replace(/\${__body}/gi, error.stack || error.toString())
            resolve({
                statusCode: 500, 
                template
            })
        })
    })
}

var app = express()

app.get("*", (req, res) => {
    var accepted = req.acceptsLanguages()
    var locale = accepted ? (accepted[0] || "ru").split("-")[0] : "ru"
    run(req.originalUrl, locale).then((data) => {
        if (data.redirect)
            res.redirect(data.redirect)
        else
            res.status(data.statusCode).send(data.template)
    })
})

app.listen(1239)

可以看到这里用到了react-tree-walker。但是无论我使用服务器端渲染,都会出现这个问题。

问题在于,如果我的 node-js 服务器在一个线程中运行,那么如果同时完成两个不同的请求,那么 react-helmet 会混合字段。例如,如果有两个 View :

class FirstView extends React.Component {
    render() {
        return (
            <Helmet>
                <title>This is first title</title>
                <meta name="description" content="My first view description" />
            </Helmet>
        )
    }
}

class SecondView extends React.Component {
    render() {
        return (
            <Helmet>
                <title>This is second title</title>
                <meta name="description" content="My second view description" />
            </Helmet>
        )
    }
}

然后我可以收到类似这样的头部:

<title>This is first title</title>
<meta name="description" content="My second view description" />

这显然是因为 react-helmet 使用静态字段,我想。所以,如果两个请求被并行处理,这个字段就会被乱七八糟地改变。

我怎样才能打败它?这个问题经常出现在高负载的项目中,这会导致 SEO 崩溃,因为爬虫可能会接收到错误的数据。


webpack.config.js:

var webpack = require("webpack")

var config = {
    mode: "production",
    target: "node",
    entry: __dirname + "/index.tsx",
    output: {
        path: __dirname,
        filename: `index.js`
    },
    module: {
        rules: [
            {
                test: require.resolve("phoenix"),
                use: "imports-loader?window=>global"
            },
            {
                test: /\.tsx?$/,
                loader: "awesome-typescript-loader?configFileName=tsconfig.server.json",
                exclude: /node_modules/
            },
            {
                test: /\.js$/,
                enforce: "pre",
                loader: "source-map-loader"
            },
            {
                test: /\.html$/,
                loader: "html-loader"
            },
            {
                test: /\.(svg|woff|woff2|ttf|otf|png|jpg)$/,
                loader: "url-loader"
            },
            {
                test: /\.(css|sass)$/,
                loader: "ignore-loader"
            }
        ]
    },
    resolve: {
        modules: [
            "node_modules",
            `${__dirname}/../src`
        ],
        extensions: [".js", ".jsx", ".sass", ".json", ".css", ".ts", ".tsx"]
    },
    parallelism: 2,
    plugins: [
        new webpack.DefinePlugin({
            "ENV": JSON.stringify("server"),
            "process.env.ENV": JSON.stringify("server"),
            "process.env.VERSION": JSON.stringify("n/a"),
            "process.env.NODE_ENV": JSON.stringify("production"),
            "global.GENTLY": false
        })
    ]
}

module.exports = config

编辑

似乎,根据 this issuereact-helmetthread-unsafe .是否有可能根据这些知识创建一些解决方法?

最佳答案

我觉得 require(../dist/${version.v()}/index.html); 只导入了一个模板实例。

通过在所有网络调用中重复要求模板,您可能会一次又一次地更新相同的字符串引用。

所以。您可以执行以下操作:

  1. import(require) template 仅一次位于文件顶部。
  2. 将其分配给run函数返回的promise中的字符串类型的新变量,并对其执行替换操作。

基本代码片段:

...
import version = require("../version");

import appTemplate = require(`../dist/${version.v()}/index.html`)


interface IRenderResponse {
    statusCode: number,
    template: string,
    redirect?: string
}

const run = (url: string, locale?: string): Promise<IRenderResponse> => {
    var template: string = "";
    var html: string = ""
    var head: object
    var context: StaticRouterContext = {}

    useStaticRendering(true)

    var routing = (
        <StaticRouter 
            location={url} 
            context={context}
        >
            <Provider defaultLocale={locale} />
        </StaticRouter>
    )

    return new Promise((resolve) => {
        walker(routing, (element, instance) => {
            if (instance && typeof instance._prepare == typeof (() => {}))
                return instance._prepare()
        }).then(() => {
            html = ReactDOMServer.renderToString(routing)
            head = Helmet.renderStatic()
            template = appTemplate.replace(/\${__rh-([a-z]+)}/gi, (match, group) => {
                return head[group].toString()
            })
            template = template.replace(/\${__body}/gi, (match) => {
                return html
            })
            if (context.url)
                context["statusCode"] = 301

            resolve({
                statusCode: context["statusCode"] || 200, 
                template, 
                redirect: context.url
            })
        })....

关于node.js - 在 renderStatic 期间 react Helm 混合场,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54722073/

有关node.js - 在 renderStatic 期间 react Helm 混合场的更多相关文章

  1. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  2. ruby - 如何测试正在使用 RSpec 和 Mocha 调用的混合类方法? - 2

    我有一个模块:moduleMyModuledefdo_something#...endend由类使用如下:classMyCommandextendMyModuledefself.execute#...do_somethingendend如何验证MyCommand.execute调用了do_something?我已经尝试使用mocha进行部分模拟,但是当未调用do_something时它不会失败:it"callsdo_something"doMyCommand.stubs(:do_something)MyCommand.executeend 最佳答案

  3. ruby-on-rails - 为什么 DataMapper 使用混合与继承? - 2

    所以我只是对此感到好奇:DataMapper为其模型使用混合classPostincludeDataMapper::Resource虽然active-record使用继承classPost有谁知道为什么DataMapper选择这样做(或者为什么AR选择不这样做)? 最佳答案 它允许您从另一个不是DM类的类继承。它还允许动态地将DM功能添加到类中。这是我正在处理的模块中的类方法:defdatamapper_classklass=self.dupklass.send(:include,DataMapper::Resource)klass

  4. 多种方法期间的 Ruby 救援异常 - 2

    我构建了一个简单的银行应用程序,它能够执行通常的操作;充值、提现等我的Controller方法执行这些操作并拯救由帐户或其他实体引发的异常。以下是Controller代码中使用的一些方法:defopen(type,with:)account=createtype,(holders.findwith)addaccountinit_yearly_interest_foraccountboundary.renderAccountSuccessMessage.new(account)rescueItemExistError=>messageboundary.rendermessageendde

  5. ruby-on-rails - 如何在记录更新期间从验证中排除密码字段? ( rails 3.0.4, ruby 1.9.2) - 2

    我有一个允许更新用户记录的表单。它包含:password和:password_confirmation字段,但我不希望在数据库中已存储加密密码时对它们运行验证。View文件中的字段:'ConfirmPassword'%>在互联网上搜索时,我发现了这段代码,我认为它是针对以前版本的Ruby/Rails的。(我会把它放在我的用户模型中。)validates_presence_of:password,:on=>create由于我的用户模型中密码验证的语法不同(如下),我对我需要的语法感到困惑。validates:password,:presence=>true,:confirmation=>

  6. ruby-on-rails - Assets 管道损坏 : Not compiling on the fly css and js files - 2

    我开始了一个新的Rails3.2.5项目,Assets管道不再工作了。CSS和Javascript文件不再编译。这是尝试生成Assets时日志的输出:StartedGET"/assets/application.css?body=1"for127.0.0.1at2012-06-1623:59:11-0700Servedasset/application.css-200OK(0ms)[2012-06-1623:59:11]ERRORNoMethodError:undefinedmethod`each'fornil:NilClass/Users/greg/.rbenv/versions/1

  7. ruby-on-rails - Rails - 理解 application.js 和 application.css - 2

    rails新手。只是想了解\assests目录中的这两个文件。例如,application.js文件有如下行://=requirejquery//=requirejquery_ujs//=require_tree.我理解require_tree。只是将所有JS文件添加到当前目录中。根据上下文,我可以看出requirejquery添加了jQuery库。但是它从哪里得到这些jQuery库呢?我没有在我的Assets文件夹中看到任何jquery.js文件——或者直接在我的整个应用程序中没有看到任何jquery.js文件?同样,我正在按照一些说明安装TwitterBootstrap(http:

  8. ruby-on-rails - 将 Ruby 代码和文字标记与 Haml 混合 - 2

    如何用HAML编写这个ERB:#OR我可以:=some_ruby_code+":"#and=some_ruby_code%br但我不想在这里连接,我想将它写成内联:(=some_ruby_code):#and(=some_ruby_code)%br 最佳答案 =some_ruby_code+":"-#and=some_ruby_code+""编辑1:我不确定您在寻找什么。你想要其中之一吗?==#{some_ruby_code}:-#and==#{some_ruby_code}或==#{some_ruby_code}:-#and=so

  9. ruby-on-rails - 在条件路由期间未在 Rails 3 中设置 request.subdomain - 2

    我正在尝试根据RyanBatesscreencastonsubdomains在Rails3中设置子域.但是它对我不起作用。我有以下设置:#routes.rbconstraints(Subdomain)doget'devices'=>'devices#all'end#lib/subdomain.rbclassSubdomaindefself.matches?(request)#binding.pryrequest.subdomain.present?&&request.subdomain=="admin"endend加载urladmin.localhost:3000/devices应该将

  10. node.js - 如何在 Travis CI 上的一个项目中运行 Node.js 和 Ruby 测试 - 2

    我有一个包含多个组件的存储库,其中大部分是用JavaScript(Node.js)编写的,一个是用Ruby(RubyonRails)编写的。我想要一个.travis.yml文件来触发一个运行每个组件的所有测试的构建。根据thisTravisCIGoogleGroupthread,目前还没有官方支持。我的目录结构是这样的:.├──构建服务器├──核心├──扩展├──网络应用├──流浪文件├──package.json├──.travis.yml└──生成文件我希望能够运行特定版本的Ruby(2.2.2)和Node.js(0.12.2)。我已经有了一个make目标,所以maketest在每

随机推荐