【发布时间】:2020-06-06 18:30:48
【问题描述】:
我一直在尝试让 SSR 与 react-i18next 一起工作很长一段时间,但文档有些缺乏,所以我从其他一些 repos 和他们的 razzle-ssr example 中拼凑了我能做到的。
我在服务器端工作,我:
- 设置 express,调用适当的中间件,获取适当的语言环境:
const app = express();
await i18n
.use(Backend)
.use(i18nextMiddleware.LanguageDetector)
.init(options)
app.use(i18nextMiddleware.handle(i18n));
app.use('/locales', express.static(`${appDirectory}/locales`));
- 在给定请求的情况下获取应用程序的 DOM 表示:
app.get('/*', req => {
//...
const html = ReactDOMServer.renderToString(
<I18nextProvider i18n={req.i18n}>>
<App />
</I18nextProvider>
)
// ...
})
- 将
initialI18nStore附加到请求内容中:
const initialI18nStore = {};
req.i18n.languages.forEach(l => {
initialI18nStore[l] = req.i18n.services.resourceStore.data[l];
});
const initialLanguage = req.i18n.language;
content = content.replace(
/<head>/,
`<head>
<script>
window.initialI18nStore = "${JSON.stringify(initialI18nStore)}";
window.initialLanguage = "${initialLanguage.slice(0, 2)}";
</script>`,
);
当我 curl http://localhost:3000/ 我得到正确的 DOM 和加载/替换的翻译时,这很好用
我遇到的问题是补水。
我尝试将useSSR 与Suspense 一起使用,但似乎无法正常工作。但我觉得这将有基本相同的问题:i18n 需要在我们应该为应用程序补充水分之前使用语言进行初始化。正确(?)
我试图通过等待client i18n 实例在应用程序水合之前被初始化来模拟与useSSR 相同的东西:
// client i18n.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
i18n
.use(initReactI18next)
.use(Backend)
.use(LanguageDetector);
const i18nInit = () => {
return new Promise(resolve => {
// @todo: shim in moment locale here
i18n.init(options, () => resolve(i18n));
});
};
export default i18nInit;
// client index.js
const renderApp = async () => {
let i18n = await i18ninit();
if (window.initialI18nStore) {
i18n.services.resourceStore.data = window.initialI18nStore;
}
hydrate(<BaseApp />, document.getElementById('root'));
};
renderApp();
这样做的问题是:应用程序从服务器提供的 DOM 表示中渲染得很好。然后,当我等待客户端 i18n 实例初始化然后水合应用程序时,我得到一个巨大的无样式闪烁,然后它返回与 DOM 表示相同的视图。
我还尝试在功能组件内部进行延迟渲染:
const BaseApp = () => {
const [render, setRender] = useState(false);
useEffect( () => {
await initI18();
i18n.services.resourceStore.data = INITIALI18NSTORE;
setRender(true);
}, [])
if(!render) return null;
return <App />
}
但这会导致类似的,但不是样式少的闪烁,而是由于返回null而导致的白屏。
这里有没有我遗漏的概念?还是我做错了什么?如何从服务器提供的 DOM+样式无缝过渡到我的客户端提供的包含翻译的样式?
【问题讨论】:
标签: reactjs server-side-rendering react-i18next