mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-22 17:47:53 +00:00
Add server-side prerendering for Angular 2 template
This commit is contained in:
@@ -31,7 +31,7 @@ function loadViaTypeScript(module, filename) {
|
|||||||
// First perform a minimal transpilation from TS code to ES2015. This is very fast (doesn't involve type checking)
|
// First perform a minimal transpilation from TS code to ES2015. This is very fast (doesn't involve type checking)
|
||||||
// and is unlikely to need any special compiler options
|
// and is unlikely to need any special compiler options
|
||||||
var src = fs.readFileSync(filename, 'utf8');
|
var src = fs.readFileSync(filename, 'utf8');
|
||||||
var compilerOptions = { jsx: ts.JsxEmit.Preserve, module: ts.ModuleKind.ES2015, target: ts.ScriptTarget.ES6 };
|
var compilerOptions = { jsx: ts.JsxEmit.Preserve, module: ts.ModuleKind.ES2015, target: ts.ScriptTarget.ES6, emitDecoratorMetadata: true };
|
||||||
var es6Code = ts.transpile(src, compilerOptions, 'test.tsx', /* diagnostics */ []);
|
var es6Code = ts.transpile(src, compilerOptions, 'test.tsx', /* diagnostics */ []);
|
||||||
|
|
||||||
// Second, process the ES2015 via Babel. We have to do this (instead of going directly from TS to ES5) because
|
// Second, process the ES2015 via Babel. We have to do this (instead of going directly from TS to ES5) because
|
||||||
@@ -116,6 +116,7 @@ function renderToString(callback, bootModulePath, bootModuleExport, absoluteRequ
|
|||||||
var params = {
|
var params = {
|
||||||
location: url.parse(requestPathAndQuery),
|
location: url.parse(requestPathAndQuery),
|
||||||
url: requestPathAndQuery,
|
url: requestPathAndQuery,
|
||||||
|
absoluteUrl: absoluteRequestUrl,
|
||||||
domainTasks: domainTaskCompletionPromise
|
domainTasks: domainTaskCompletionPromise
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
32
templates/Angular2Spa/ClientApp/boot-server.ts
Normal file
32
templates/Angular2Spa/ClientApp/boot-server.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import 'angular2-universal-preview/dist/server/universal-polyfill.js';
|
||||||
|
import * as ngCore from 'angular2/core';
|
||||||
|
import * as ngRouter from 'angular2/router';
|
||||||
|
import * as ngUniversal from 'angular2-universal-preview';
|
||||||
|
import { BASE_URL } from 'angular2-universal-preview/dist/server/src/http/node_http';
|
||||||
|
import * as ngUniversalRender from 'angular2-universal-preview/dist/server/src/render';
|
||||||
|
|
||||||
|
// TODO: Make this ugly code go away, e.g., by somehow loading via Webpack
|
||||||
|
function loadAsString(module, filename) {
|
||||||
|
module.exports = require('fs').readFileSync(filename, 'utf8');
|
||||||
|
}
|
||||||
|
(require as any).extensions['.html'] = loadAsString;
|
||||||
|
(require as any).extensions['.css'] = loadAsString;
|
||||||
|
let App: any = require('./components/app/app').App;
|
||||||
|
|
||||||
|
export default function (params: any): Promise<{ html: string }> {
|
||||||
|
return new Promise<{ html: string, globals: { [key: string]: any } }>((resolve, reject) => {
|
||||||
|
const serverBindings = [
|
||||||
|
ngRouter.ROUTER_BINDINGS,
|
||||||
|
ngUniversal.HTTP_PROVIDERS,
|
||||||
|
ngCore.provide(BASE_URL, { useValue: params.absoluteUrl }),
|
||||||
|
ngCore.provide(ngUniversal.REQUEST_URL, { useValue: params.url }),
|
||||||
|
ngCore.provide(ngRouter.APP_BASE_HREF, { useValue: '/' }),
|
||||||
|
ngUniversal.SERVER_LOCATION_PROVIDERS
|
||||||
|
];
|
||||||
|
|
||||||
|
ngUniversalRender.renderToString(App, serverBindings).then(
|
||||||
|
html => resolve({ html, globals: {} }),
|
||||||
|
reject // Also propagate any errors back into the host application
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as ng from 'angular2/core';
|
import * as ng from 'angular2/core';
|
||||||
import fetch from 'isomorphic-fetch';
|
import { Http } from 'angular2/http';
|
||||||
|
|
||||||
@ng.Component({
|
@ng.Component({
|
||||||
selector: 'fetch-data'
|
selector: 'fetch-data'
|
||||||
@@ -10,11 +10,9 @@ import fetch from 'isomorphic-fetch';
|
|||||||
export class FetchData {
|
export class FetchData {
|
||||||
public forecasts: WeatherForecast[];
|
public forecasts: WeatherForecast[];
|
||||||
|
|
||||||
constructor() {
|
constructor(http: Http) {
|
||||||
fetch('/api/SampleData/WeatherForecasts')
|
http.get('/api/SampleData/WeatherForecasts').subscribe(result => {
|
||||||
.then(response => response.json())
|
this.forecasts = result.json();
|
||||||
.then((data: WeatherForecast[]) => {
|
|
||||||
this.forecasts = data;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
<p>To help you get started, we've also set up:</p>
|
<p>To help you get started, we've also set up:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><strong>Client-side navigation</strong>. For example, click <em>Counter</em> then <em>Back</em> to return here.</li>
|
<li><strong>Client-side navigation</strong>. For example, click <em>Counter</em> then <em>Back</em> to return here.</li>
|
||||||
|
<li><strong>Server-side prerendering</strong>. For faster initial loading and improved SEO, your Angular 2 app is prerendered on the server. The resulting HTML is then transferred to the browser where a client-side copy of the app takes over.</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>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, your Angular 2 app will be rebuilt and a new instance injected is into the page.</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, your Angular 2 app will be rebuilt and a new instance injected is into the page.</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>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
ViewData["Title"] = "Home Page";
|
ViewData["Title"] = "Home Page";
|
||||||
}
|
}
|
||||||
|
|
||||||
<app>Loading...</app>
|
<app asp-prerender-module="ClientApp/boot-server">Loading...</app>
|
||||||
|
|
||||||
@section scripts {
|
@section scripts {
|
||||||
<script src="~/dist/main.js" asp-append-version="true"></script>
|
<script src="~/dist/main.js" asp-append-version="true"></script>
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
@using WebApplicationBasic
|
@using WebApplicationBasic
|
||||||
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
|
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
|
||||||
|
@addTagHelper "*, Microsoft.AspNet.SpaServices"
|
||||||
|
|||||||
@@ -25,10 +25,15 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"angular2": "^2.0.0-beta.7",
|
"angular2": "^2.0.0-beta.7",
|
||||||
|
"angular2-universal-preview": "^0.55.4",
|
||||||
"babel-core": "^6.5.2",
|
"babel-core": "^6.5.2",
|
||||||
|
"css": "^2.2.1",
|
||||||
|
"domain-task": "^1.0.0",
|
||||||
"es6-shim": "^0.33.13",
|
"es6-shim": "^0.33.13",
|
||||||
"es7-reflect-metadata": "^1.5.5",
|
"es7-reflect-metadata": "^1.5.5",
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^2.2.1",
|
||||||
|
"parse5": "^1.5.1",
|
||||||
|
"preboot": "^1.1.3",
|
||||||
"reflect-metadata": "^0.1.2",
|
"reflect-metadata": "^0.1.2",
|
||||||
"rxjs": "^5.0.0-beta.2",
|
"rxjs": "^5.0.0-beta.2",
|
||||||
"zone.js": "^0.5.15"
|
"zone.js": "^0.5.15"
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"target": "es6",
|
"target": "es6",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"experimentalDecorators": true
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules"
|
"node_modules"
|
||||||
|
|||||||
@@ -7,9 +7,6 @@
|
|||||||
"installed": {
|
"installed": {
|
||||||
"requirejs/require.d.ts": {
|
"requirejs/require.d.ts": {
|
||||||
"commit": "dade4414712ce84e3c63393f1aae407e9e7e6af7"
|
"commit": "dade4414712ce84e3c63393f1aae407e9e7e6af7"
|
||||||
},
|
|
||||||
"isomorphic-fetch/isomorphic-fetch.d.ts": {
|
|
||||||
"commit": "4d2d0653003a4d1df4d7c68054cce4f4ace58bdf"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,119 +0,0 @@
|
|||||||
// Type definitions for isomorphic-fetch
|
|
||||||
// Project: https://github.com/matthew-andrews/isomorphic-fetch
|
|
||||||
// Definitions by: Todd Lucas <https://github.com/toddlucas>
|
|
||||||
// Definitions: https://github.com/borisyankov/DefinitelyTyped
|
|
||||||
|
|
||||||
declare enum RequestContext {
|
|
||||||
"audio", "beacon", "cspreport", "download", "embed", "eventsource",
|
|
||||||
"favicon", "fetch", "font", "form", "frame", "hyperlink", "iframe",
|
|
||||||
"image", "imageset", "import", "internal", "location", "manifest",
|
|
||||||
"object", "ping", "plugin", "prefetch", "script", "serviceworker",
|
|
||||||
"sharedworker", "subresource", "style", "track", "video", "worker",
|
|
||||||
"xmlhttprequest", "xslt"
|
|
||||||
}
|
|
||||||
declare enum RequestMode { "same-origin", "no-cors", "cors" }
|
|
||||||
declare enum RequestCredentials { "omit", "same-origin", "include" }
|
|
||||||
declare enum RequestCache {
|
|
||||||
"default", "no-store", "reload", "no-cache", "force-cache",
|
|
||||||
"only-if-cached"
|
|
||||||
}
|
|
||||||
declare enum ResponseType { "basic", "cors", "default", "error", "opaque" }
|
|
||||||
|
|
||||||
declare type HeaderInit = Headers | Array<string>;
|
|
||||||
declare type BodyInit = Blob | FormData | string;
|
|
||||||
declare type RequestInfo = Request | string;
|
|
||||||
|
|
||||||
interface RequestInit {
|
|
||||||
method?: string;
|
|
||||||
headers?: HeaderInit | { [index: string]: string };
|
|
||||||
body?: BodyInit;
|
|
||||||
mode?: string | RequestMode;
|
|
||||||
credentials?: string | RequestCredentials;
|
|
||||||
cache?: string | RequestCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IHeaders {
|
|
||||||
get(name: string): string;
|
|
||||||
getAll(name: string): Array<string>;
|
|
||||||
has(name: string): boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class Headers implements IHeaders {
|
|
||||||
append(name: string, value: string): void;
|
|
||||||
delete(name: string):void;
|
|
||||||
get(name: string): string;
|
|
||||||
getAll(name: string): Array<string>;
|
|
||||||
has(name: string): boolean;
|
|
||||||
set(name: string, value: string): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IBody {
|
|
||||||
bodyUsed: boolean;
|
|
||||||
arrayBuffer(): Promise<ArrayBuffer>;
|
|
||||||
blob(): Promise<Blob>;
|
|
||||||
formData(): Promise<FormData>;
|
|
||||||
json(): Promise<any>;
|
|
||||||
json<T>(): Promise<T>;
|
|
||||||
text(): Promise<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class Body implements IBody {
|
|
||||||
bodyUsed: boolean;
|
|
||||||
arrayBuffer(): Promise<ArrayBuffer>;
|
|
||||||
blob(): Promise<Blob>;
|
|
||||||
formData(): Promise<FormData>;
|
|
||||||
json(): Promise<any>;
|
|
||||||
json<T>(): Promise<T>;
|
|
||||||
text(): Promise<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IRequest extends IBody {
|
|
||||||
method: string;
|
|
||||||
url: string;
|
|
||||||
headers: Headers;
|
|
||||||
context: string | RequestContext;
|
|
||||||
referrer: string;
|
|
||||||
mode: string | RequestMode;
|
|
||||||
credentials: string | RequestCredentials;
|
|
||||||
cache: string | RequestCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare class Request extends Body implements IRequest {
|
|
||||||
constructor(input: string | Request, init?: RequestInit);
|
|
||||||
method: string;
|
|
||||||
url: string;
|
|
||||||
headers: Headers;
|
|
||||||
context: string | RequestContext;
|
|
||||||
referrer: string;
|
|
||||||
mode: string | RequestMode;
|
|
||||||
credentials: string | RequestCredentials;
|
|
||||||
cache: string | RequestCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IResponse extends IBody {
|
|
||||||
url: string;
|
|
||||||
status: number;
|
|
||||||
statusText: string;
|
|
||||||
ok: boolean;
|
|
||||||
headers: IHeaders;
|
|
||||||
type: string | ResponseType;
|
|
||||||
size: number;
|
|
||||||
timeout: number;
|
|
||||||
redirect(url: string, status: number): IResponse;
|
|
||||||
error(): IResponse;
|
|
||||||
clone(): IResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IFetchStatic {
|
|
||||||
Promise: any;
|
|
||||||
Headers: IHeaders
|
|
||||||
Request: IRequest;
|
|
||||||
Response: IResponse;
|
|
||||||
(url: string | IRequest, init?: RequestInit): Promise<IResponse>;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module "isomorphic-fetch" {
|
|
||||||
export default IFetchStatic;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare var fetch: IFetchStatic;
|
|
||||||
1
templates/Angular2Spa/typings/tsd.d.ts
vendored
1
templates/Angular2Spa/typings/tsd.d.ts
vendored
@@ -1,3 +1,2 @@
|
|||||||
|
|
||||||
/// <reference path="requirejs/require.d.ts" />
|
/// <reference path="requirejs/require.d.ts" />
|
||||||
/// <reference path="isomorphic-fetch/isomorphic-fetch.d.ts" />
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ module.exports = merge({
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
entry: {
|
entry: {
|
||||||
main: ['./ClientApp/boot.ts'],
|
main: ['./ClientApp/boot-client.ts'],
|
||||||
vendor: ['angular2/bundles/angular2-polyfills.js', 'bootstrap', 'bootstrap/dist/css/bootstrap.css', 'style-loader', 'jquery', 'angular2/core', 'angular2/common', 'angular2/http', 'angular2/router', 'angular2/platform/browser']
|
vendor: ['angular2/bundles/angular2-polyfills.js', 'bootstrap', 'bootstrap/dist/css/bootstrap.css', 'style-loader', 'jquery', 'angular2/core', 'angular2/common', 'angular2/http', 'angular2/router', 'angular2/platform/browser']
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
|
|||||||
Reference in New Issue
Block a user