我正在尝试使用@loadable/component
.
我目前的情况:这就是我在我的网站上为机器人实现 SSR 的方式。由于它仅适用于机器人,因此我不使用hydrate
. 基本上,我index.html
为普通用户发送带有 JS 包的脚本标签的 ,或者我为机器人发送一个完全呈现的 HTML 页面,而无需hydrate
.
我的目标:我想@loadable/component
总是从我的服务器返回 SSR 页面,并hydrate
用来附加我的 JS 包。并以此实现代码拆分。
这是我目前构建应用程序的方式(伪代码):
1. webpack BUILD FOR entry { app: "src/index.tsx" } AND OUTPUT BUNDLES TO MY /public FOLDER
2. babel TRANSPILE WHOLE `/src` FOLDER AND OUTPUT FILES TO MY /dist_app FOLDER
它基本上是 2 个构建,其中一个是使用 webpack 进行捆绑,另一个基本上是将文件src
从distApp
.
这就是我的服务器所做的(伪代码)
1. CHECK IF USER IS ROBOT (FROM THE USER AGENT STRING)
2. IF REGULAR USER
res.send("public/index.html"); // SEND REGULAR index.html WITH JS BUNDLES GENERATED BY WEBPACK
IF ROBOT
const App = require("./dist_app/App"); // THIS IS THE src/App COMPONENT TRANSPILED BY BABEL
const ssrHtml = ReactDOM.renderToString(<App/>);
// ADD <head> <helmet> <styles> ETC
res.send(ssrHtml);
上述步骤适用于我的初始要求(ssr 仅适用于机器人)。
但是我加@loadable/component
了实现分码+SSR之后,上面的设置就不行了。
因为现在我有dynamic imports
一些路线。例如:
const AsyncPage = loadable(() => import("@app/pages/PageContainer"));
所以我的renderToString(<App/>)
电话大部分都是空的,因为它没有加载那些AsyncPages
.
在可加载组件的文档上:服务器端渲染他们有一个关于如何实现这一点的示例存储库。
但是他们的例子有点复杂,似乎他们webpack
在服务器内部使用。我将在下面发布他们在他们的服务器上所做的事情。
问题
我真的必须使用webpack
捆绑我的应用程序的服务器代码才能@loadable/component
像他们在示例中显示的那样用于 SSR 吗?我不能只使用某种babel
插件将其转换dynamic imports
为常规require
调用吗?这样我就可以像以前那样渲染它了?
很奇怪,他们使用webpack-dev-middleware
. 就像这个例子应该只用于开发。有人知道带有生产示例的回购吗?
import path from 'path'
import express from 'express'
import React from 'react'
import { renderToString } from 'react-dom/server'
import { ChunkExtractor } from '@loadable/server'
const app = express()
app.use(express.static(path.join(__dirname, '../../public')))
if (process.env.NODE_ENV !== 'production') {
/* eslint-disable global-require, import/no-extraneous-dependencies */
const { default: webpackConfig } = require('../../webpack.config.babel')
const webpackDevMiddleware = require('webpack-dev-middleware')
const webpack = require('webpack')
/* eslint-enable global-require, import/no-extraneous-dependencies */
const compiler = webpack(webpackConfig)
app.use(
webpackDevMiddleware(compiler, {
logLevel: 'silent',
publicPath: '/dist/web',
writeToDisk(filePath) {
return /dist\/node\//.test(filePath) || /loadable-stats/.test(filePath)
},
}),
)
}
const nodeStats = path.resolve(
__dirname,
'../../public/dist/node/loadable-stats.json',
)
const webStats = path.resolve(
__dirname,
'../../public/dist/web/loadable-stats.json',
)
app.get('*', (req, res) => {
const nodeExtractor = new ChunkExtractor({ statsFile: nodeStats })
const { default: App } = nodeExtractor.requireEntrypoint()
const webExtractor = new ChunkExtractor({ statsFile: webStats })
const jsx = webExtractor.collectChunks(<App />)
const html = renderToString(jsx)
res.set('content-type', 'text/html')
res.send(`
<!DOCTYPE html>
<html>
<head>
${webExtractor.getLinkTags()}
${webExtractor.getStyleTags()}
</head>
<body>
<div id="main">${html}</div>
${webExtractor.getScriptTags()}
</body>
</html>
`)
})
// eslint-disable-next-line no-console
app.listen(9000, () => console.log('Server started http://localhost:9000'))