Add links and syntax highlighting to SpaServices README.md

This commit is contained in:
SteveSandersonMS
2016-06-13 14:57:13 +01:00
parent 266ea8815b
commit b4fd30dee7

View File

@@ -4,7 +4,7 @@ If you're building an ASP.NET Core application, and want to use Angular 2, React
This package enables: This package enables:
* [**Server-side prerendering**](#server-sideprerendering) for *universal* (a.k.a. *isomorphic*) applications, where your Angular 2 / React / etc. components are first rendered on the server, and then transferred to the client where execution continues * [**Server-side prerendering**](#server-side-prerendering) for *universal* (a.k.a. *isomorphic*) applications, where your Angular 2 / React / etc. components are first rendered on the server, and then transferred to the client where execution continues
* [**Webpack middleware**](#webpack-dev-middleware) so that, during development, any webpack-build resources will be generated on demand, without you having to run webpack manually or compile files to disk * [**Webpack middleware**](#webpack-dev-middleware) so that, during development, any webpack-build resources will be generated on demand, without you having to run webpack manually or compile files to disk
* [**Hot module replacement**](#webpack-hot-module-replacement) so that, during development, your code and markup changes will be sent to your browser and updated in the running application, without even needing to reload the page * [**Hot module replacement**](#webpack-hot-module-replacement) so that, during development, your code and markup changes will be sent to your browser and updated in the running application, without even needing to reload the page
* [**Routing helpers**](#routing-helper-mapspafallbackroute) for integrating server-side routing with client-side routing * [**Routing helpers**](#routing-helper-mapspafallbackroute) for integrating server-side routing with client-side routing
@@ -59,7 +59,7 @@ If you run your application now, and browse to whatever page renders the view yo
Create a JavaScript file at the path matching the `asp-prerender-module` value you specified above. In this example, that means creating a folder called `ClientApp` at the root of your project, and creating a file inside it called `boot-server.js`. Try putting the following into it: Create a JavaScript file at the path matching the `asp-prerender-module` value you specified above. In this example, that means creating a folder called `ClientApp` at the root of your project, and creating a file inside it called `boot-server.js`. Try putting the following into it:
``` ```javascript
module.exports = function(params) { module.exports = function(params) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
var result = '<h1>Hello world!</h1>' var result = '<h1>Hello world!</h1>'
@@ -80,13 +80,15 @@ As you can see, your JavaScript code receives context information (such as the U
If you want to pass some contextual data from your server-side code to your client-side code (for example, to avoid having to load the same data you just loaded on the server again on the client), you can supply a `globals` object alongside the initial `html`, e.g.: If you want to pass some contextual data from your server-side code to your client-side code (for example, to avoid having to load the same data you just loaded on the server again on the client), you can supply a `globals` object alongside the initial `html`, e.g.:
resolve({ ```javascript
resolve({
html: result, html: result,
globals: { globals: {
albumsList: someDataHere, albumsList: someDataHere,
userData: someMoreDataHere userData: someMoreDataHere
} }
}); });
```
When the `aspnet-prerender-*` tag helper emits this result into the document, as well as injecting the `html` string, it will also emit code that populates `window.albumsList` and `window.userData` with JSON-serialized copies of the objects you passed. When the `aspnet-prerender-*` tag helper emits this result into the document, as well as injecting the `html` string, it will also emit code that populates `window.albumsList` and `window.userData` with JSON-serialized copies of the objects you passed.
@@ -118,7 +120,7 @@ Let's say you want to write your boot module and SPA code in TypeScript. First e
Next, create a file `webpack.config.js` at the root of your project, containing: Next, create a file `webpack.config.js` at the root of your project, containing:
``` ```javascript
module.exports = { module.exports = {
resolve: { extensions: [ '', '.js', '.ts' ] }, resolve: { extensions: [ '', '.js', '.ts' ] },
module: { module: {
@@ -133,7 +135,7 @@ This tells webpack that it should compile `.ts` files using TypeScript, and that
Now you can delete `ClientApp/boot-server.js`, and in its place, create `ClientApp/boot-server.ts`, containing the TypeScript equivalent of what you had before: Now you can delete `ClientApp/boot-server.js`, and in its place, create `ClientApp/boot-server.ts`, containing the TypeScript equivalent of what you had before:
``` ```javascript
export default function (params: any): Promise<{ html: string}> { export default function (params: any): Promise<{ html: string}> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const html = ` const html = `
@@ -181,7 +183,7 @@ React components can be executed synchronously on the server quite easily, altho
Let's say you want to write a React component in ES2015 code. You might install the NPM modules `react react-dom babel-loader babel-preset-react babel-preset-es2015`, and then prepare Webpack to build `.jsx` files by creating `webpack.config.js` in your project root, containing: Let's say you want to write a React component in ES2015 code. You might install the NPM modules `react react-dom babel-loader babel-preset-react babel-preset-es2015`, and then prepare Webpack to build `.jsx` files by creating `webpack.config.js` in your project root, containing:
``` ```javascript
var path = require('path'); var path = require('path');
module.exports = { module.exports = {
@@ -203,7 +205,7 @@ module.exports = {
You will also need a `.babelrc` file in your project root, containing: You will also need a `.babelrc` file in your project root, containing:
``` ```javascript
{ {
"presets": ["es2015", "react"] "presets": ["es2015", "react"]
} }
@@ -211,7 +213,7 @@ You will also need a `.babelrc` file in your project root, containing:
This is enough to be able to build ES2015 `.jsx` files via Webpack. Now you could implement a simple React component, for example the following at `ClientApp/react-app.jsx`: This is enough to be able to build ES2015 `.jsx` files via Webpack. Now you could implement a simple React component, for example the following at `ClientApp/react-app.jsx`:
``` ```javascript
import * as React from 'react'; import * as React from 'react';
export class HelloMessage extends React.Component export class HelloMessage extends React.Component
@@ -224,7 +226,7 @@ export class HelloMessage extends React.Component
... and the following code to run it in a browser at `ClientApp/boot-client.jsx`: ... and the following code to run it in a browser at `ClientApp/boot-client.jsx`:
``` ```javascript
import * as React from 'react'; import * as React from 'react';
import * as ReactDOM from 'react-dom'; import * as ReactDOM from 'react-dom';
import { HelloMessage } from './react-app'; import { HelloMessage } from './react-app';
@@ -232,7 +234,7 @@ import { HelloMessage } from './react-app';
ReactDOM.render(<HelloMessage message="World" />, document.getElementById('my-spa')); ReactDOM.render(<HelloMessage message="World" />, document.getElementById('my-spa'));
``` ```
At this stage, run `webpack` on the command line to build `wwwroot/dist/main.js`. Or, to avoid having to do this manually, you could use the `SpaServices` package to enable Webpack dev middleware. At this stage, run `webpack` on the command line to build `wwwroot/dist/main.js`. Or, to avoid having to do this manually, you could use the `SpaServices` package to [enable Webpack dev middleware](#webpack-dev-middleware).
You can now run your React code on the client by adding the following to one of your MVC views: You can now run your React code on the client by adding the following to one of your MVC views:
@@ -248,7 +250,7 @@ Now you have React code being built using Webpack, you can enable server-side pr
... along with the following boot module at `ClientApp/boot-server.jsx`: ... along with the following boot module at `ClientApp/boot-server.jsx`:
``` ```javascript
import * as React from 'react'; import * as React from 'react';
import { renderToString } from 'react-dom/server'; import { renderToString } from 'react-dom/server';
import { HelloMessage } from './react-app'; import { HelloMessage } from './react-app';
@@ -292,7 +294,7 @@ It lets you work as if the browser natively understands whatever file types you
After installing the `Microsoft.AspNetCore.SpaServices` NuGet package and the `aspnet-webpack` NPM package, go to your `Startup.cs` file, and **before your call to `UseStaticFiles`**, add the following: After installing the `Microsoft.AspNetCore.SpaServices` NuGet package and the `aspnet-webpack` NPM package, go to your `Startup.cs` file, and **before your call to `UseStaticFiles`**, add the following:
``` ```csharp
if (env.IsDevelopment()) { if (env.IsDevelopment()) {
app.UseWebpackDevMiddleware(); app.UseWebpackDevMiddleware();
} }
@@ -302,7 +304,7 @@ if (env.IsDevelopment()) {
You will also need to edit your webpack configuration at `webpack.config.js`. Since `UseWebpackDevMiddleware` needs to know which incoming requests to intercept, specify a `publicPath` value on your `output`, for example: You will also need to edit your webpack configuration at `webpack.config.js`. Since `UseWebpackDevMiddleware` needs to know which incoming requests to intercept, specify a `publicPath` value on your `output`, for example:
``` ```javascript
module.exports = { module.exports = {
// ... rest of your webpack config is here ... // ... rest of your webpack config is here ...
@@ -336,13 +338,13 @@ npm install --save webpack-hot-middleware
At the top of your `Startup.cs` file, add the following namespace reference: At the top of your `Startup.cs` file, add the following namespace reference:
``` ```csharp
using Microsoft.AspNetCore.SpaServices.Webpack; using Microsoft.AspNetCore.SpaServices.Webpack;
``` ```
Now amend your call to `UseWebpackDevMiddleware` as follows: Now amend your call to `UseWebpackDevMiddleware` as follows:
``` ```csharp
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
HotModuleReplacement = true HotModuleReplacement = true
}); });
@@ -350,7 +352,7 @@ app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
Also, to work around a temporary issue in `SpaServices`, you must ensure that your Webpack config includes a `plugins` array, even if it's empty. For example, in `webpack.config.js`: Also, to work around a temporary issue in `SpaServices`, you must ensure that your Webpack config includes a `plugins` array, even if it's empty. For example, in `webpack.config.js`:
``` ```javascript
module.exports = { module.exports = {
// ... rest of your webpack config is here ... // ... rest of your webpack config is here ...
@@ -372,7 +374,7 @@ If you edit any of your source files that get built by webpack, the result will
Webpack has built-in support for updating React components in place. To enable this, amend your `UseWebpackDevMiddleware` call further as follows: Webpack has built-in support for updating React components in place. To enable this, amend your `UseWebpackDevMiddleware` call further as follows:
``` ```csharp
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions { app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions {
HotModuleReplacement = true, HotModuleReplacement = true,
ReactHotModuleReplacement = true ReactHotModuleReplacement = true
@@ -404,7 +406,7 @@ If a request arrives for `/some/page`, and it doesn't match any server-side rout
To help distinguish between these cases, the `Microsoft.AspNetCore.SpaServices` NuGet package includes a routing helper, `MapSpaFallbackRoute`. For example, in your `Startup.cs` file's `Configure` method, you might add: To help distinguish between these cases, the `Microsoft.AspNetCore.SpaServices` NuGet package includes a routing helper, `MapSpaFallbackRoute`. For example, in your `Startup.cs` file's `Configure` method, you might add:
``` ```csharp
app.UseStaticFiles(); app.UseStaticFiles();
app.UseMvc(routes => app.UseMvc(routes =>
@@ -428,4 +430,3 @@ Then, since `MapSpaFallbackRoute` is last, any other requests **that don't appea
Any requests that do appear to be for static files (i.e., those that end with filename extensions), will *not* be handled by `MapSpaFallbackRoute`, and so will end up as 404s. Any requests that do appear to be for static files (i.e., those that end with filename extensions), will *not* be handled by `MapSpaFallbackRoute`, and so will end up as 404s.
This is not a perfect solution to the problem of identifying 404s, because for example `MapSpaFallbackRoute` will not match requests for `/users/albert.einstein`, because it appears to contain a filename extension (`.einstein`). If you need your SPA to handle routes like that, then don't use `MapSpaFallbackRoute` - just use a regular MVC catch-all route. But then beware that requests for unknown static files will result in your client-side app being rendered. This is not a perfect solution to the problem of identifying 404s, because for example `MapSpaFallbackRoute` will not match requests for `/users/albert.einstein`, because it appears to contain a filename extension (`.einstein`). If you need your SPA to handle routes like that, then don't use `MapSpaFallbackRoute` - just use a regular MVC catch-all route. But then beware that requests for unknown static files will result in your client-side app being rendered.