branch-name:
Click To Copy
Let’s clean up the code a bit and move the html code from ./ssr-server.js to a separate component called html.js.
./html.js
import React from 'react'; const Html = ({ content, cssBundles, jsBundles, apolloClient }) => ( <html lang="en"> <head> <meta charSet="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <title>Server Side Rendering and Bundle Splitting</title> <link href="/dist/main.css" rel="stylesheet" as="style" media="screen, projection" type="text/css" charSet="UTF-8" /> { cssBundles.map( (bundle) => (<link href={`${bundle.publicPath}`} rel="stylesheet" as="style" media="screen, projection" type="text/css" charSet="UTF-8" />)) } {jsBundles.map( ( {file}) => (<script src={`/dist/${file}`}>{file}</script>) )} </head> <body cz-shortcut-listen="true"> <div id="root" dangerouslySetInnerHTML={{ __html: content }} /> <script dangerouslySetInnerHTML={{ __html: `window.__APOLLO_STATE__=${JSON.stringify(apolloClient.cache.extract())}`}} /> <script src="/dist/main-bundle.js"></script> </body> </html> ); export default Html;
what we just did:
– we created a plain react component that will accept 3 parameters:
– content [string]: rendered components returned from react-apollo
renderToStringWithData(…)
– cssBundles [array of CSS bundle objects]:
– jsBundles [array of js bundle objects]:
– apolloClient [Object]: An instance of the Apollo client.
And now, let’s remove the HTML coder from ./server.js and add the new component instead, passing all necessary parameters.
./server-js
import React from 'react'; import express from 'express'; import App from './src/components/App/ssr-index'; import Loadable from 'react-loadable'; import manifest from './dist/loadable-manifest.json'; import { getDataFromTree } from "react-apollo"; import { ApolloClient } from 'apollo-client'; import { InMemoryCache } from 'apollo-cache-inmemory'; import { renderToStringWithData } from "react-apollo" import { createHttpLink } from 'apollo-link-http'; import { getBundles } from 'react-loadable/webpack'; import ReactDOMServer from 'react-dom/server'; import Html from './html.js'; const PORT = process.env.PROD_SERVER_PORT; const app = express(); app.use('/server-build', express.static('./server-build')); app.use('/dist', express.static('dist')); // to serve frontent prod static files app.use('/favicon.ico', express.static('./src/images/favicon.ico')); app.get('/*', (req, res) => { const GRAPHQL_URL = process.env.GRAPHQL_URL; const client = new ApolloClient({ ssrMode: true, link: createHttpLink({ uri: GRAPHQL_URL, fetch: fetch, credentials: 'same-origin', headers: { cookie: req.header('Cookie'), }, }), cache: new InMemoryCache() }); // Prepare to get list of all modules that have to be loaded for this route const modules = []; const mainApp = ( <Loadable.Capture report={moduleName => modules.push(moduleName)}> <App req={req} client={client} /> </Loadable.Capture> ); // Execute all queries and fetch the results before continue getDataFromTree(mainApp).then(() => { // Once we have the data back, this will render the components with the appropriate GraphQL data. renderToStringWithData(<App req={req} client={client} />).then( (HTML_content) => { // Extract CSS and JS bundles const bundles = getBundles(manifest, modules); const cssBundles = bundles.filter(bundle => bundle && bundle.file.split('.').pop() === 'css'); const jsBundles = bundles.filter(bundle => bundle && bundle.file.split('.').pop() === 'js'); const html = <Html content={HTML_content} cssBundles={cssBundles} jsBundles={jsBundles} apolloClient={client} />; res.status(200); res.send(`<!doctype html>\n${ReactDOMServer.renderToStaticMarkup(html)}`); res.end(); }); }).catch( (error) => { console.log("ERROR !!!!", error); }); }); Loadable.preloadAll().then(() => { app.listen(PORT, () => { console.log(`? Server is listening on port ${PORT}`); }); });
branch-name:
Click To Copy
where to go from here ?