如何在SSR模式下使用CRA的npm build生成的静态媒体资源?
How to use static media assets generated by CRA’s npm build in SSR mode?
我有一个使用 CRA 创建的标准 TypeScript React 应用程序。
它使用 Helmet 作为元数据,使用 styled-components 作为样式。
我目前正在使用 Babel 和 express 来为应用程序提供服务。大多数情况下工作正常,但导入的图像资源在 SSR 模式下不工作。
例如,import sun from "../images/sun.jpg"
呈现为 [object Object]
而不是 data:image/jpeg;base64,...
,import backgroundS from "../images/background-1280x720.jpg"
呈现为 [object Object]
而不是 /static/media/background-1280x720.702f9ac6.jpg
。
import sun from "../images/sun.jpg"
const ImageComponent = () => (
<img src={sun} /> // In SSR mode, this is rendered as <img src="[object Object]">
)
我刚刚开始使用 React SSR,所以我可能缺少一些关键知识来解决这个问题。我花了两个多小时试图找到解决方案。
我错过了什么?
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
"use strict"
const dotenv = require("dotenv").config()
const express = require("express")
const path = require("path")
const fs = require("fs")
const renderToString = require("react-dom/server").renderToString
const React = require("react")
const Helmet = require("react-helmet").default
const ServerStyleSheet = require("styled-components").ServerStyleSheet
const StaticRouter = require("./src/routers/Static").default
const indexPath = path.join(__dirname, "build/index.html")
const server = express()
server.disable("x-powered-by")
const middleware = async (req, res, next) => {
const sheet = new ServerStyleSheet()
let context = {}
let html = renderToString(
sheet.collectStyles(
React.createElement(StaticRouter, {
location: req.url,
context: context,
})
)
)
const helmet = Helmet.renderStatic()
const styleTags = sheet.getStyleTags()
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>`)
.replace("</head>", `${styleTags}</head>`)
)
}
}
// This line is required by SSR
server.get("/", middleware)
server.use(express.static(path.join(__dirname, "build")))
server.get("*", middleware)
server.listen(process.env.PORT, function() {
console.log(`Server listening on port ${process.env.PORT}`)
})
如果你知道如何解决这个问题,请提交答案!
话虽这么说,但它可能无法解决,所以这里有一个解决方法。
我最终将我的图像资产移动到 public/static/media
并使用绝对 src
路径导入和 SSR 一旦我 运行 npm build
!
const ImageComponent = () => (
<img src="/static/media/sun.jpg" />
)
我有一个使用 CRA 创建的标准 TypeScript React 应用程序。
它使用 Helmet 作为元数据,使用 styled-components 作为样式。
我目前正在使用 Babel 和 express 来为应用程序提供服务。大多数情况下工作正常,但导入的图像资源在 SSR 模式下不工作。
例如,import sun from "../images/sun.jpg"
呈现为 [object Object]
而不是 data:image/jpeg;base64,...
,import backgroundS from "../images/background-1280x720.jpg"
呈现为 [object Object]
而不是 /static/media/background-1280x720.702f9ac6.jpg
。
import sun from "../images/sun.jpg"
const ImageComponent = () => (
<img src={sun} /> // In SSR mode, this is rendered as <img src="[object Object]">
)
我刚刚开始使用 React SSR,所以我可能缺少一些关键知识来解决这个问题。我花了两个多小时试图找到解决方案。
我错过了什么?
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
"use strict"
const dotenv = require("dotenv").config()
const express = require("express")
const path = require("path")
const fs = require("fs")
const renderToString = require("react-dom/server").renderToString
const React = require("react")
const Helmet = require("react-helmet").default
const ServerStyleSheet = require("styled-components").ServerStyleSheet
const StaticRouter = require("./src/routers/Static").default
const indexPath = path.join(__dirname, "build/index.html")
const server = express()
server.disable("x-powered-by")
const middleware = async (req, res, next) => {
const sheet = new ServerStyleSheet()
let context = {}
let html = renderToString(
sheet.collectStyles(
React.createElement(StaticRouter, {
location: req.url,
context: context,
})
)
)
const helmet = Helmet.renderStatic()
const styleTags = sheet.getStyleTags()
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>`)
.replace("</head>", `${styleTags}</head>`)
)
}
}
// This line is required by SSR
server.get("/", middleware)
server.use(express.static(path.join(__dirname, "build")))
server.get("*", middleware)
server.listen(process.env.PORT, function() {
console.log(`Server listening on port ${process.env.PORT}`)
})
如果你知道如何解决这个问题,请提交答案!
话虽这么说,但它可能无法解决,所以这里有一个解决方法。
我最终将我的图像资产移动到 public/static/media
并使用绝对 src
路径导入和 SSR 一旦我 运行 npm build
!
const ImageComponent = () => (
<img src="/static/media/sun.jpg" />
)