如何解决为什么 React hydration 没有加载?
我正在尝试使用 React、Fastify 和 esbuild 构建我自己的服务器端渲染应用程序。捆绑过程成功运行而无需担心。但是当我尝试运行服务器时,没有加载客户端包脚本。 HTML 预计会显示在浏览器中,并且会请求客户端脚本(通过 chrome 开发工具检查 200 个响应代码)。就像脚本被请求但没有加载一样。我错过了什么吗?
显然影响任何事件监听器(onclick 等)都不会被调用。您可以在 repository
中查看完整版本的代码段src/client/index.tsx
import React from 'react';
import { hydrate } from 'react-dom';
import canUseDOM from '@bconnorwhite/can-use-dom';
import { createbrowserHistory,createMemoryHistory } from 'history';
import App from './App';
const bootstrap = (): void => {
const root = document.getElementById('root');
const history = canUseDOM ? createbrowserHistory() : createMemoryHistory();
hydrate(<App history={history} />,root);
};
export default bootstrap;
src/server/index.ts
import fastify from 'fastify';
import helmet from 'fastify-helmet';
import staticAsset from 'fastify-static';
import debug from 'debug';
import { config } from 'dotenv';
import path from 'path';
import routes from './routes';
import assetParser from './decorators/assetParser';
config();
const log = debug('server:app');
const app = fastify({
logger: true,disableRequestLogging: true,});
app.decorate('assetManifest',assetParser(log));
app.register(staticAsset,{
root: path.join(process.cwd(),'dist'),});
app.register(helmet);
app.register(routes,{ log });
const PORT: number =
process.env.NODE_ENV === 'development' ? Number(process.env.DEV_PORT || '') : Number(process.env.PROD_PORT || '');
const HOST: string = process.env.NODE_ENV === 'development' ? process.env.DEV_HOST || '' : process.env.PROD_HOST || '';
app.listen(PORT || 3000,HOST,(err,address) => {
if (err) {
log('Error bootstraping server:',err);
app.log.error(JSON.stringify(err));
process.exit(1);
} else {
log('Server listening to',address);
}
});
builder/index.js
const path = require('path');
const debug = require('debug')('builder');
const { startService } = require('esbuild');
const { DEFAULT_CONfig } = require('./constant');
/**
* Build function performs bundle and transpilation operation using esbuild. The function works
* by reaching the configuration file (usually named esbuild.config.js) and parse the configuration
* to the startService parameter.
*
* The function also calculates the process time using process.hrtime() and translate the result
* into milisecond. The timer log is displayed for debugging purpose only.
* @function build
* @returns {void}
*/
const build = async () => {
let config = {};
try {
config = await require(path.resolve(process.cwd(),'esbuild.config.js'));
if (!config) {
throw new Error('Config file is not defined');
}
} catch (error) {
debug('Failed to read config file: ',error);
process.exit(1);
}
const service = await startService();
const startTimer = process.hrtime();
try {
await Promise.all(
config.map(async (currentConfig) => {
await service.build({
...DEFAULT_CONfig,...currentConfig,});
}),);
} catch (error) {
debug('Error happened during build process:',error);
} finally {
service.stop();
const endTimer = process.hrtime(startTimer);
const timeInMs = (endTimer[0] * 1e9 + endTimer[1]) / 1e3;
debug('Bundle process is finished at',timeInMs,'ms');
}
};
(async () => {
debug('Starting bundle process...');
try {
await build();
} catch (error) {
debug('Error happend during bundle process:',error);
}
})();
esbuild.config.js
const path = require('path');
const { nodeExternalsPlugin } = require('esbuild-node-externals');
module.exports = [
{
entryPoints: [path.resolve(__dirname,'src/client/index.tsx')],outfile: './dist/index.browser.js',platform: 'browser',loader: {
'.svg': 'file','.jpg': 'file','.png': 'file','.gif': 'file',},tsconfig: 'tsconfig.json',banner: '/* client dist */',Metafile: 'dist/asset-client-manifest.json',{
entryPoints: [path.resolve(__dirname,'src/server/index.ts')],outfile: './dist/index.js',platform: 'node',minify: false,banner: '/* server dist */',format: 'cjs',plugins: [nodeExternalsPlugin()],];
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。