【问题标题】:React emotion is not working properly in Server Side RenderingReact 情绪在服务器端渲染中无法正常工作
【发布时间】:2023-01-30 04:11:25
【问题描述】:

我正在使用 React 情感库进行样式设置,当应用程序未在服务器端呈现时,样式可以正常工作,但在服务器端呈现时会出现样式问题。我是否需要对情绪进行额外的配置以使其在服务器端呈现时与反应一起正常工作?

这就是我用 React 实现情感的方式,有什么实现问题吗?

import * as React from 'react';
import * as ReactDOMServer from 'react-dom/server';
import { Provider as ReduxProvider } from 'react-redux';
import Helmet from 'react-helmet';
import { StaticRouter } from 'react-router';
import { ApolloProvider } from '@apollo/client/react/react.cjs';
import { getDataFromTree } from '@apollo/client/react/ssr/ssr.cjs';
import { Html } from './ssr/html';
import path from 'path';
import fs from 'fs';
import { createMemoryHistory } from 'history';
import { logger } from '@cdm-logger/server';
import { ChunkExtractor } from '@loadable/server';
import { CacheProvider } from '@emotion/react';
import createCache from '@emotion/cache';
import createEmotionServer from '@emotion/server/create-instance'
import { createClientContainer } from '../config/client.service';
import { createReduxStore } from '../config/redux-config';
import publicEnv from '../config/public-config';
import clientModules, { MainRoute } from '../modules';

let assetMap;
const key = 'custom';
const cache = createCache({ key });
const { extractCritical, extractCriticalToChunks, constructStyleTagsFromChunks } = createEmotionServer(cache);
async function renderServerSide(req, res) {
    try {
        const { apolloClient: client } = createClientContainer();

        let context: { pageNotFound?: boolean; url?: string } = { pageNotFound: false };
        const history = createMemoryHistory({ initialEntries: [req.url] });
        const { store } = createReduxStore(history);
        const App = () =>
            clientModules.getWrappedRoot(
                // tslint:disable-next-line:jsx-wrap-multiline
                <ReduxProvider store={store}>
                    <ApolloProvider client={client}>
                        <CacheProvider value={cache}>
                            <StaticRouter location={req.url} context={context}>
                                <MainRoute />
                            </StaticRouter>
                        </CacheProvider>
                    </ApolloProvider>
                </ReduxProvider>,
                req,
            );

        await getDataFromTree(App);
        if (context.pageNotFound === true) {
            res.status(404);
        } else {
            res.status(200);
        }
        const extractor = new ChunkExtractor({
            statsFile: path.resolve(__FRONTEND_BUILD_DIR__, 'loadable-stats.json'),
            entrypoints: ['index'],
            publicPath: !__DEV__ && __CDN_URL__ ? __CDN_URL__ : '/',
        });
        // Wrap your application using "collectChunks"
        const JSX = extractor.collectChunks(App);
        const content = extractCritical(ReactDOMServer.renderToString(JSX));


        const chunks = extractCriticalToChunks(JSX)

        const appStyles = constructStyleTagsFromChunks(chunks)

        // We need to tell Helmet to compute the right meta tags, title, and such.
        const helmet = Helmet.renderStatic(); // Avoid memory leak while tracking mounted instances
        if (context.url) {
            res.writeHead(301, { Location: context.url });
            res.end();
        } else {
            if (__DEV__ || !assetMap) {
                assetMap = JSON.parse(fs.readFileSync(path.join(__FRONTEND_BUILD_DIR__, 'assets.json')).toString());
            }
            const apolloState = Object.assign({}, client.extract());
            const reduxState = Object.assign({}, store.getState());
            const env = {
                ...publicEnv,
            };
            const page = (
                <Html
                    content={content}
                    headElements={[
                        ...extractor.getScriptElements(),
                        ...extractor.getLinkElements(),
                        ...extractor.getStyleElements(),
                    ]}
                    state={apolloState}
                    assetMap={assetMap}
                    helmet={helmet}
                    styleSheet={appStyles}
                    env={env}
                    reduxState={reduxState}
                    scriptsInserts={clientModules.scriptsInserts}
                    stylesInserts={clientModules.stylesInserts}
                />
            );
            res.send(`<!doctype html>\n${ReactDOMServer.renderToStaticMarkup(page)}`);
            res.end();
        }
    } catch (err) {
        logger.error('SERVER SIDE RENDER failed due to (%j) ', err.message);
        logger.debug(err);
    }
}
export const websiteMiddleware = async (req, res, next) => {
    try {
        if (req.path.indexOf('.') < 0 && __SSR__) {
            return await renderServerSide(req, res);
        } else if (req.path.indexOf('.') < 0 && !__SSR__ && req.method === 'GET' && !__DEV__) {
            logger.debug('FRONEND_BUILD_DIR with index.html');
            res.sendFile(path.resolve(__FRONTEND_BUILD_DIR__, 'index.html'));
        } else {
            next();
        }
    } catch (e) {
        logger.error('RENDERING ERROR:', e);
        return next(e);
    }
};

【问题讨论】:

    标签: reactjs server-side-rendering emotion pro-components


    【解决方案1】:

    我不能在实施中出现错误。但是服务器端渲染CSS-in-JS 库像 Emotion 可能很棘手,因为样式是在运行时生成的,而不是在构建时生成的。

    一个问题可能是情绪的缓存会干扰服务器渲染过程,导致生成不正确的样式。要解决此问题,您可以为每个请求创建一个新的缓存实例并将其传递给 CacheProvider 组件,如您的实现所示。

    这是一个例子:

    import * as React from 'react';
    import * as ReactDOMServer from 'react-dom/server';
    import { CacheProvider } from '@emotion/react';
    import createCache from '@emotion/cache';
    import createEmotionServer from '@emotion/server/create-instance';
    
    const cache = createCache({ key: 'custom' });
    const { extractCritical } = createEmotionServer(cache);
    
    const Component = () => (
      <div
        css={{
          backgroundColor: 'red',
          padding: 20,
        }}
      >
        Hello World!
      </div>
    );
    
    const App = () => (
      <CacheProvider value={cache}>
        <Component />
      </CacheProvider>
    );
    
    const content = extractCritical(ReactDOMServer.renderToString(<App />));
    
    console.log(content.css);
    

    这里extractCritical 用于提取组件的关键 CSS。 extractCritical 返回的 content 对象包含 css 属性中的关键 CSS。

    【讨论】:

    • 请分享任何情绪的 ssr 代码示例
    • 这个示例代码怎么样?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-02-16
    • 1970-01-01
    • 2015-05-07
    • 2017-11-21
    • 1970-01-01
    • 2015-08-26
    • 1970-01-01
    相关资源
    最近更新 更多