I'm working on moving my react v0.14
+ redux v3.0
+ react-router v1.0
codebase from client-side rendering to server-side rendering using webpack v1.12
to bundle and code-split into chunks to load routes and components on-demand.
Im following and basing my setup on https://github.com/rackt/example-react-router-server-rendering-lazy-routes as I think it provides simplicity and great utilities. All day yesterday I have been working on moving to server-side rendering but I run into a few issues I haven't been able to solve and I'm not completely sure if they are because of webpack
not being setup correctly, if am doing something wrong with react-router
on the server/client or the routes config, or if its something I'm doing wrong with setting up redux
that is causing these issues.
I run into the following issues:
text/javascript
so the styles don't show up unless they are inline.var fs = require('fs')
var path = require('path')
var webpack = require('webpack')
module.exports = {
devtool: 'source-map',
entry: './client/client.jsx',
output: {
path: __dirname + '/__build__',
filename: '[name].js',
chunkFilename: '[id].chunk.js',
publicPath: '/__build__/'
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader'
}
]
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.optimize.DedupePlugin(),
new webpack.optimize.UglifyJsPlugin({
compressor: { warnings: false },
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
})
]
}
import http from 'http';
import React from 'react';
import {renderToString} from 'react-dom/server';
import { match, RoutingContext } from 'react-router';
import {Provider} from 'react-redux';
import configureStore from './../common/store/store.js';
import fs from 'fs';
import { createPage, write, writeError, writeNotFound, redirect } from './server-utils.js';
import routes from './../common/routes/rootRoutes.js';
const PORT = process.env.PORT || 3000;
var store = configureStore();
const initialState = store.getState();
function renderApp(props, res) {
var markup = renderToString(
<Provider store={store}>
<RoutingContext {...props}/>
</Provider>
);
var html = createPage(markup, initialState);
write(html, 'text/html', res);
}
http.createServer((req, res) => {
if (req.url === '/favicon.ico') {
write('haha', 'text/plain', res);
}
// serve JavaScript assets
else if (/__build__/.test(req.url)) {
fs.readFile(`.${req.url}`, (err, data) => {
write(data, 'text/javascript', res);
})
}
// handle all other urls with React Router
else {
match({ routes, location: req.url }, (error, redirectLocation, renderProps) => {
if (error)
writeError('ERROR!', res);
else if (redirectLocation)
redirect(redirectLocation, res);
else if (renderProps)
renderApp(renderProps, res);
else
writeNotFound(res);
});
}
}).listen(PORT)
console.log(`listening on port ${PORT}`)
Is the same as from the repo that I posted above example-react-router-server-rendering-lazy-routes
just navigate to /modules/utils/server-utils.js
in the repo.The only difference is the createPage
function:
export function createPage(html, initialState) {
return( `
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="./../bower_components/Ionicons/css/ionicons.min.css">
<link rel="stylesheet" href="./../dist/main.css">
<title>Sell Your Soles</title>
</head>
<body>
<div id="app">${html}</div>
<script>window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};</script>
<script src="/__build__/main.js"></script>
</body>
</html>
`);
}
// polyfill webpack require.ensure
if (typeof require.ensure !== 'function') require.ensure = (d, c) => c(require)
import App from '../components/App.jsx'
import Landing from '../components/Landing/Landing.jsx'
export default {
path: '/',
component: App,
getChildRoutes(location, cb) {
require.ensure([], (require) => {
cb(null, [
require('./UserProfile/UserProfileRoute.js'),
require('./UserHome/UserHomeRoute.js'),
require('./SneakerPage/SneakerPageRoute.js'),
require('./Reviews/ReviewsRoute.js'),
require('./Listings/ListingsRoute.js'),
require('./Events/EventsRoute.js')
])
})
},
indexRoute: {
component: Landing
}
}
import UserProfile from '../../components/UserProfile/UserProfile.jsx';
export default {
path: 'profile',
component: UserProfile
}
import React from 'react';
import { match, Router } from 'react-router';
import { render } from 'react-dom';
import { createHistory } from 'history';
import routes from './../common/routes/rootRoutes.js';
import {Provider} from 'react-redux';
import configureStore from './../common/store/store.js';
const { pathname, search, hash } = window.location;
const location = `${pathname}${search}${hash}`;
const initialState = window.__INITIAL_STATE__;
const store = configureStore(initialState);
// calling `match` is simply for side effects of
// loading route/component code for the initial location
match({ routes, location }, () => {
render(
<Provider store={store}>
<Router routes={routes} history={createHistory()} />
</Provider>,
document.getElementById('app')
);
});
Code splitting is one of the most compelling features of webpack. This feature allows you to split your code into various bundles which can then be loaded on demand or in parallel.
The react-router-dom library supports inbuilt route-level code splitting. It allows us to download chunks at the route level. Using this feature, we'll code split at the route level, which is tremendously helpful.
import React from "react"; import { lazyLoader } from "./lazyLoader"; const Customer = lazyLoader(() => import("./Customer. js")); const Admin = lazyLoader(() => import("./Admin. js")); //Instead of regular import statements, we will use the above approach for lazy loading export default (props) => { if (props.
Lazy Loading on route level with React Router is a powerful feature. Usually a client-side rendered React applications comes as one bundle from a web server. However, when enabling lazy loading, the bundle is split into smaller bundles.
I helped you out on discord, but I thought I'd post the answer here as well.
If you are using babel6 (instead of babel5) and using export default in your components, then you need to update your routes to the following:
getChildRoutes(location, cb) {
require.ensure([], (require) => {
cb(null, [
require('./UserProfile/UserProfileRoute.js').default,
require('./UserHome/UserHomeRoute.js').default,
require('./SneakerPage/SneakerPageRoute.js').default,
require('./Reviews/ReviewsRoute.js').default,
require('./Listings/ListingsRoute.js').default,
require('./Events/EventsRoute.js').default
])
})
}
See this SO discussion for more details: Babel 6 changes how it exports default
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With