我正在使用Express运行Apollo / React,我正试图让服务器端渲染工作。 Apollo docs建议使用以下初始化代码连接到API服务器:
app.use((req, res) => {
match({ routes, location: req.originalUrl }, (error, redirectLocation, renderProps) => {
const client = new ApolloClient({
ssrMode: true,
networkInterface: createNetworkInterface({
uri: 'http://localhost:3000', // Instead of 3010
opts: {
credentials: 'same-origin',
headers: {
cookie: req.header('Cookie'),
},
},
}),
});
const app = (
<ApolloProvider client={client}>
<RouterContext {...renderProps} />
</ApolloProvider>
);
getDataFromTree(app).then(() => {
const content = ReactDOM.renderToString(app);
const initialState = {[client.reduxRootKey]: client.getInitialState() };
const html = <Html content={content} state={initialState} />;
res.status(200);
res.send(`<!doctype html>\n${ReactDOM.renderToStaticMarkup(html)}`);
res.end();
});
});
});
使用React Router v3中的match()
函数(由文档链接的“GitHunt”示例中的package.json证明)。我正在使用缺少match()
的React Router v4,因此我使用react-router-config
package中的renderRoutes()
重构了以下代码。
app.use((req, res) => {
const client = new ApolloClient(/* Same as above */)
const context = {}
const app = (
<ApolloProvider client={client}>
<StaticRouter context={context} location={req.originalUrl}>
{ renderRoutes(routes) }
</StaticRouter>
</ApolloProvider>
)
getDataFromTree(app).then(/* Same as above */)
})
我的理解是<StaticRouter>
不使用match()
。但是react-router-config
提供了matchRoutes()
函数,如果需要,它似乎提供了类似的功能(尽管没有回调)。
当我访问http://localhost:3000时,页面按预期加载,我可以关注指向子目录的链接(例如http://localhost:3000/folder)。 当我尝试通过在地址栏中输入名称直接加载子目录时,我的浏览器一直在等待服务器响应。大约六秒钟后,终端显示以下错误之一(不确定是什么原因导致错误在后续尝试中更改):
(node:1938)UnhandledPromiseRejectionWarning:未处理的承诺 rejection(拒绝ID:1):错误:网络错误:请求 http://localhost:3000失败,原因: getaddrinfo ENOTFOUND localhost 本地主机:3000
或
(node:8691)UnhandledPromiseRejectionWarning:未处理的承诺 rejection(拒绝ID:1):错误:网络错误:请求 http://localhost:3000失败,原因:套接字挂断
我几个小时以来一直在努力解决这个问题,但似乎无法弄明白。 similar problem的解决方案似乎与此案无关。任何帮助将不胜感激!
更多信息
如果我没有杀死nodemon服务器,一段时间后我会收到以下数千个错误:
POST / - - ms - -
(node:1938)UnhandledPromiseRejectionWarning: 未处理的承诺拒绝(拒绝ID:4443):错误:网络 错误:对http://localhost:3000的请求失败,原因:套接字挂断
但是,如果我确实杀了服务器,我会立即收到此错误:
/用户/.../ node_modules /双工器/ index.js:31
writer.on("drain", function() { ^
TypeError:无法读取未定义
的属性'on' 双工(/ Users /.../ node_modules / duplexer / index.js:31:11)
at Object.module.exports(/ Users /.../ node_modules / stream-combiner / index.js:8:17)
at childrenOfPid(/ Users /.../ node_modules / ps-tree / index.js:50:6)
at kill(/ Users /.../ node_modules / nodemon / lib / monitor / run.js:271:7)
在Bus.onQuit(/ Users /.../ node_modules / nodemon / lib / monitor / run.js:327:5)
在emitNone(events.js:91:20)
在Bus.emit(events.js:188:7)
正在处理中。 (/用户/.../ node_modules / nodemon / LIB /监视器/ run.js:349:9)
at Object.onceWrapper(events.js:293:19)
在emitNone(events.js:86:13)
此外,端口3000是正确的。如果我改变号码,我会得到一个不同的错误:
(node:2056)UnhandledPromiseRejectionWarning:未处理的承诺 rejection(拒绝ID:1):错误:网络错误:请求 http://localhost:3010失败,原因是:连接ECONNREFUSED 127.0.0.1:3010
答案 0 :(得分:0)
您是在容器/容器内运行服务器/项目吗?我有同样的问题,最后做了以下修复。
const networkInterface = createNetworkInterface({
uri: process.browser
? 'http://0.0.0.0:1111/graphql'
: 'http://my-docker-container-name:8080/graphql'
})
我在docker-compose.yml中为我的容器创建了一个内部docker网络,它允许容器相互通信,但是浏览器在不同的URL上与GQL服务器通信,导致你在上面描述的问题SSR getaddrinfo ENOTFOUND
,所以虽然它在客户端工作,但在SSR上它会失败。
我正在使用nextJS框架,它能够检测浏览器或SSR,我相信你可以在nextJS之外做同样的事情。
答案 1 :(得分:0)
我终于发现错误是由于导致renderToString
导致renderToStaticMarkup
和ReactDOM
无效。
导入语句import ReactDOM from 'react-dom'
必须由import ReactDOMServer from 'react-dom/server'
替换。
此外,uri
必须指向http://localhost:3000/graphql
而不是http://localhost:3000
。