mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-23 01:58:29 +00:00
Enable server-side prerendering in React+Redux template
This commit is contained in:
38
templates/ReactReduxSpa/ClientApp/boot-server.tsx
Normal file
38
templates/ReactReduxSpa/ClientApp/boot-server.tsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { renderToString } from 'react-dom/server';
|
||||||
|
import { match, RouterContext } from 'react-router';
|
||||||
|
import createMemoryHistory from 'history/lib/createMemoryHistory';
|
||||||
|
import routes from './routes';
|
||||||
|
import configureStore from './configureStore';
|
||||||
|
|
||||||
|
export default function (params: any): Promise<{ html: string }> {
|
||||||
|
return new Promise<{ html: string, globals: { [key: string]: any } }>((resolve, reject) => {
|
||||||
|
// Match the incoming request against the list of client-side routes
|
||||||
|
match({ routes, location: params.location }, (error, redirectLocation, renderProps: any) => {
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build an instance of the application
|
||||||
|
const store = configureStore();
|
||||||
|
const app = (
|
||||||
|
<Provider store={ store }>
|
||||||
|
<RouterContext {...renderProps} />
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
// Perform an initial render that will cause any async tasks (e.g., data access) to begin
|
||||||
|
renderToString(app);
|
||||||
|
|
||||||
|
// Once the tasks are done, we can perform the final render
|
||||||
|
// We also send the redux store state, so the client can continue execution where the server left off
|
||||||
|
params.domainTasks.then(() => {
|
||||||
|
resolve({
|
||||||
|
html: renderToString(app),
|
||||||
|
globals: { initialReduxState: store.getState() }
|
||||||
|
});
|
||||||
|
}, reject); // Also propagate any errors back into the host application
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@ export default class Home extends React.Component<any, void> {
|
|||||||
<li><strong>Webpack dev middleware</strong>. In development mode, there's no need to run the <code>webpack</code> build tool. Your client-side resources are dynamically built on demand. Updates are available as soon as you modify any file.</li>
|
<li><strong>Webpack dev middleware</strong>. In development mode, there's no need to run the <code>webpack</code> build tool. Your client-side resources are dynamically built on demand. Updates are available as soon as you modify any file.</li>
|
||||||
<li><strong>Hot module replacement</strong>. In development mode, you don't even need to reload the page after making most changes. Within seconds of saving changes to files, rebuilt CSS and React components will be injected directly into your running application, preserving its live state.</li>
|
<li><strong>Hot module replacement</strong>. In development mode, you don't even need to reload the page after making most changes. Within seconds of saving changes to files, rebuilt CSS and React components will be injected directly into your running application, preserving its live state.</li>
|
||||||
<li><strong>Efficient production builds</strong>. In production mode, development-time features are disabled, and the <code>webpack</code> build tool produces minified static CSS and JavaScript files.</li>
|
<li><strong>Efficient production builds</strong>. In production mode, development-time features are disabled, and the <code>webpack</code> build tool produces minified static CSS and JavaScript files.</li>
|
||||||
|
<li><strong>Server-side prerendering</strong>. To optimize startup time, your React application is first rendered on the server. The initial HTML and state is then transferred to the browser, where client-side code picks up where the server left off.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import { typedToPlain } from 'redux-typed';
|
|||||||
export default function configureStore(initialState?: Store.ApplicationState) {
|
export default function configureStore(initialState?: Store.ApplicationState) {
|
||||||
// Build middleware. These are functions that can process the actions before they reach the store.
|
// Build middleware. These are functions that can process the actions before they reach the store.
|
||||||
const thunk = (thunkModule as any).default; // Workaround for TypeScript not importing thunk module as expected
|
const thunk = (thunkModule as any).default; // Workaround for TypeScript not importing thunk module as expected
|
||||||
const devToolsExtension = (window as any).devToolsExtension; // If devTools is installed, connect to it
|
const windowIfDefined = typeof window === 'undefined' ? null : window as any;
|
||||||
|
const devToolsExtension = windowIfDefined && windowIfDefined.devToolsExtension; // If devTools is installed, connect to it
|
||||||
const createStoreWithMiddleware = compose(
|
const createStoreWithMiddleware = compose(
|
||||||
applyMiddleware(thunk, typedToPlain),
|
applyMiddleware(thunk, typedToPlain),
|
||||||
devToolsExtension ? devToolsExtension() : f => f
|
devToolsExtension ? devToolsExtension() : f => f
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
ViewData["Title"] = "Home Page";
|
ViewData["Title"] = "Home Page";
|
||||||
}
|
}
|
||||||
|
|
||||||
<div id="react-app">Loading...</div>
|
<div id="react-app" asp-prerender-module="ClientApp/boot-server"
|
||||||
|
asp-prerender-webpack-config="webpack.config.js">Loading...</div>
|
||||||
|
|
||||||
@section scripts {
|
@section scripts {
|
||||||
<script src="~/dist/main.js" asp-append-version="true"></script>
|
<script src="~/dist/main.js" asp-append-version="true"></script>
|
||||||
|
|||||||
@@ -6,9 +6,7 @@
|
|||||||
<title>@ViewData["Title"] - WebApplicationBasic</title>
|
<title>@ViewData["Title"] - WebApplicationBasic</title>
|
||||||
|
|
||||||
<link rel="stylesheet" href="~/dist/vendor.css" asp-append-version="true" />
|
<link rel="stylesheet" href="~/dist/vendor.css" asp-append-version="true" />
|
||||||
<environment names="Staging,Production">
|
<link rel="stylesheet" href="~/dist/site.css" asp-append-version="true" />
|
||||||
<link rel="stylesheet" href="~/dist/site.css" asp-append-version="true" />
|
|
||||||
</environment>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@RenderBody()
|
@RenderBody()
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
"react-router-redux": "^4.0.0",
|
"react-router-redux": "^4.0.0",
|
||||||
"redux": "^3.3.1",
|
"redux": "^3.3.1",
|
||||||
"redux-thunk": "^2.0.1",
|
"redux-thunk": "^2.0.1",
|
||||||
"redux-typed": "^1.0.0"
|
"redux-typed": "^1.0.0",
|
||||||
|
"require-from-string": "^1.1.0",
|
||||||
|
"webpack-externals-plugin": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
devtool: 'inline-source-map',
|
devtool: 'inline-source-map'
|
||||||
module: {
|
|
||||||
loaders: [
|
|
||||||
{ test: /\.css/, loader: 'style!css' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
var path = require('path');
|
var path = require('path');
|
||||||
var webpack = require('webpack');
|
var webpack = require('webpack');
|
||||||
|
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
var merge = require('extendify')({ isDeep: true, arrays: 'concat' });
|
var merge = require('extendify')({ isDeep: true, arrays: 'concat' });
|
||||||
var devConfig = require('./webpack.config.dev');
|
var devConfig = require('./webpack.config.dev');
|
||||||
var prodConfig = require('./webpack.config.prod');
|
var prodConfig = require('./webpack.config.prod');
|
||||||
var isDevelopment = process.env.ASPNET_ENV === 'Development';
|
var isDevelopment = process.env.ASPNET_ENV === 'Development';
|
||||||
|
var extractCSS = new ExtractTextPlugin('site.css');
|
||||||
|
|
||||||
module.exports = merge({
|
module.exports = merge({
|
||||||
resolve: {
|
resolve: {
|
||||||
@@ -12,11 +14,12 @@ module.exports = merge({
|
|||||||
module: {
|
module: {
|
||||||
loaders: [
|
loaders: [
|
||||||
{ test: /\.ts(x?)$/, include: /ClientApp/, loader: 'babel-loader' },
|
{ test: /\.ts(x?)$/, include: /ClientApp/, loader: 'babel-loader' },
|
||||||
{ test: /\.ts(x?)$/, include: /ClientApp/, loader: 'ts-loader' }
|
{ test: /\.ts(x?)$/, include: /ClientApp/, loader: 'ts-loader' },
|
||||||
|
{ test: /\.css/, loader: extractCSS.extract(['css']) }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
entry: {
|
entry: {
|
||||||
main: ['./ClientApp/boot.tsx'],
|
main: ['./ClientApp/boot-client.tsx'],
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: path.join(__dirname, 'wwwroot', 'dist'),
|
path: path.join(__dirname, 'wwwroot', 'dist'),
|
||||||
@@ -24,6 +27,7 @@ module.exports = merge({
|
|||||||
publicPath: '/dist/'
|
publicPath: '/dist/'
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
extractCSS,
|
||||||
new webpack.DllReferencePlugin({
|
new webpack.DllReferencePlugin({
|
||||||
context: __dirname,
|
context: __dirname,
|
||||||
manifest: require('./wwwroot/dist/vendor-manifest.json')
|
manifest: require('./wwwroot/dist/vendor-manifest.json')
|
||||||
|
|||||||
@@ -1,15 +1,7 @@
|
|||||||
var webpack = require('webpack');
|
var webpack = require('webpack');
|
||||||
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
|
||||||
var extractCSS = new ExtractTextPlugin('site.css');
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
module: {
|
|
||||||
loaders: [
|
|
||||||
{ test: /\.css/, loader: extractCSS.extract(['css']) },
|
|
||||||
]
|
|
||||||
},
|
|
||||||
plugins: [
|
plugins: [
|
||||||
extractCSS,
|
|
||||||
new webpack.optimize.OccurenceOrderPlugin(),
|
new webpack.optimize.OccurenceOrderPlugin(),
|
||||||
new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }),
|
new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }),
|
||||||
new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' })
|
new webpack.DefinePlugin({ 'process.env.NODE_ENV': '"production"' })
|
||||||
|
|||||||
Reference in New Issue
Block a user