Last active
January 4, 2019 15:04
-
-
Save wwiechorek/2dea042aa0924c606abfe14678e9d9d7 to your computer and use it in GitHub Desktop.
SSR - React com create-react-app
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import bodyParser from 'body-parser'; | |
import compression from 'compression'; | |
import express from 'express'; | |
import cookieParser from 'cookie-parser'; | |
import Loadable from 'react-loadable'; | |
import render from './render'; | |
const app = express(); | |
const PORT = process.env.PORT || 3000; | |
app.use(compression()); | |
app.use(bodyParser.json()); | |
app.use(bodyParser.urlencoded({ extended: false })); | |
app.use(cookieParser()); | |
//bloqueia o acesso ao arquivo de servidor | |
app.use('/server.js', (req, res) => { | |
res.status(404).send(); | |
}); | |
//bloqueia o static renderizar o index.html antes do ssr | |
app.get('/', render) | |
//arquivos estaticos | |
app.use(express.static(__dirname + '/')); | |
//renderiza react para todas outras urls | |
app.use(render) | |
Loadable.preloadAll().then(() => { | |
app.listen(PORT, console.log(`App listening on port ${PORT}!`)); | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[...] | |
//scripts | |
"server-dev": "webpack --w --config ./server/webpack.config.js --mode=development", | |
"server-build": "webpack --config ./server/webpack.config.js --mode=production" | |
//Dev dependences: | |
"@babel/plugin-syntax-dynamic-import": "^7.2.0", | |
"body-parser": "^1.18.3", | |
"compression": "^1.7.3", | |
"cookie-parser": "^1.4.3", | |
"express": "^4.16.4", | |
"ignore-loader": "^0.1.2", | |
"nodemon-webpack-plugin": "^4.0.7", | |
"react-helmet": "^5.2.0", | |
"react-loadable": "^5.5.0", | |
"react-router-dom": "^4.3.1", | |
"webpack-cli": "^3.1.2" | |
[...] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from 'fs' | |
import path from 'path' | |
import React from 'react' | |
import { renderToString } from 'react-dom/server' | |
import { StaticRouter } from 'react-router-dom' | |
import Helmet from 'react-helmet' | |
import Loadable from 'react-loadable' | |
import { Provider } from "react-redux" | |
import { Frontload, frontloadServerRender } from 'react-frontload' | |
import configureStore from "../src/store" | |
import App from '../src/app.js' | |
import manifest from '../build/asset-manifest.json' | |
const injectHTML = (data, { htmlAttrs, bodyAttrs, content, title, meta, link, scripts, styles, state }) => { | |
if(htmlAttrs) | |
data = data.replace(/<html .*?>/, `<html ${htmlAttrs}>`); | |
if(title) | |
data = data.replace(/<title>.*?<\/title>/g, title); | |
if(bodyAttrs) | |
data = data.replace("<body>", `<body ${bodyAttrs}>`); | |
if(meta) | |
data = data.replace('</head>', `${meta}</head>`); | |
if(link) | |
data = data.replace('</head>', `${link}</head>`); | |
if(scripts) | |
data = data.replace('</body>', scripts.join('') + '</body>'); | |
if(styles) | |
data = data.replace('</head>', styles.join('') + `</head>`); | |
data = data.replace( | |
'<div id="root"></div>', | |
`<div id="root">${content}${state ? `<script>window.__initialData__ = ${state}</script>` : ''}</div>` | |
); | |
return data; | |
}; | |
const htmlData = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf8') | |
const extractAssets = (assets, chunks) => | |
Object.keys(assets) | |
.filter(asset => chunks.indexOf(asset.replace('.js', '').replace('.css', '')) > -1) | |
.map(k => assets[k]); | |
export default (req, res) => { | |
const context = {} | |
const modules = [] | |
const store = configureStore() | |
//isomorphic cookie | |
global.document = { | |
cookie: req.headers.cookie | |
} | |
frontloadServerRender(() => | |
renderToString( | |
<Loadable.Capture report={m => modules.push(m)}> | |
<Provider store={store}> | |
<StaticRouter location={req.path} context={context}> | |
<Frontload isServer={true}> | |
<App /> | |
</Frontload> | |
</StaticRouter> | |
</Provider> | |
</Loadable.Capture> | |
) | |
).then(content => { | |
//verifica se tem redirecionamento de url | |
if(context.url) { | |
res.redirect(context.url) | |
return res.end() | |
} | |
const styleChunks = [] | |
const scriptChunks = [] | |
extractAssets(manifest, modules).map( | |
c => { | |
if(/(.*)\.js/.test(c)) | |
scriptChunks.push(`<script type="text/javascript" src="/${c.replace(/^\//, '')}"></script>`) | |
if(/(.*)\.css/.test(c)) | |
styleChunks.push(`<link rel="stylesheet" href="/${c.replace(/^\//, '')}">`) | |
return; | |
} | |
); | |
const helmet = Helmet.renderStatic(); | |
const htmlAttrs = helmet.htmlAttributes.toString(); | |
const bodyAttrs = helmet.bodyAttributes.toString(); | |
const html = injectHTML(htmlData, { | |
htmlAttrs, | |
bodyAttrs, | |
content, | |
title: helmet.title.toString(), | |
meta: helmet.meta.toString(), | |
link: helmet.link.toString(), | |
scripts: scriptChunks, | |
styles: styleChunks, | |
state: JSON.stringify(store.getState()).replace(/</g, '\\u003c') | |
}) | |
res.send(html) | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
process.env.NODE_ENV = 'production' | |
process.env.BABEL_ENV = 'production' | |
const path = require('path') | |
const webpack = require('webpack') | |
const NodemonPlugin = require('nodemon-webpack-plugin') | |
const cssModuleRegex = /\.module\.css$/; | |
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent'); | |
module.exports = { | |
entry: ["@babel/polyfill", __dirname + "/index.js"], | |
target: "node", | |
node: { | |
__filename: false, | |
__dirname: false | |
}, | |
output: { | |
path: path.resolve(__dirname, '../build'), | |
filename: "server.js" | |
}, | |
plugins: [ | |
new NodemonPlugin(), | |
new webpack.optimize.LimitChunkCountPlugin({ | |
maxChunks: 1 | |
}), | |
], | |
module: { | |
rules: [ | |
{ | |
test: /\.js$/, | |
exclude: /node_modules/, | |
use: { | |
loader: "babel-loader", | |
options: { | |
presets: ['@babel/preset-env', '@babel/preset-react'], | |
plugins: [ | |
"@babel/plugin-proposal-class-properties", | |
"@babel/plugin-syntax-dynamic-import", | |
'dynamic-import-node', | |
"react-loadable/babel" | |
], | |
}, | |
} | |
}, | |
{ | |
test: cssModuleRegex, | |
use: [ | |
{ | |
loader: "css-loader/locals", | |
options: { | |
modules: true, | |
getLocalIdent: getCSSModuleLocalIdent, | |
} | |
}, | |
] | |
}, | |
{ test: /\.css$/, exclude: /\.module.css$/, loader: 'ignore-loader' }, | |
{ | |
test: [/\.bmp$/, /\.gif$/, /\.svg$/, /\.jpe?g$/, /\.png$/], | |
loader: 'file-loader', | |
options: { | |
name: '/static/media/[name].[hash:8].[ext]', | |
emitFile: false //não copiar os arquivos, apenas colocar o caminho dos mesmos | |
}, | |
} | |
], | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment