所以我开发了一个简单的 gulp 脚本来创建一个很好的 node.js 应用程序来在 Azure 上运行 Angular Universal,但它并不完全有效。
我正在使用这个: github angular universal
当我正常运行 Angular 通用时,它可以完美运行,但我需要让它在 Azure 上运行。所以 gulp 文件是将所有内容组合在一个构建文件夹中,如果有更好的方法让它在 azure 上运行,请告诉我。
我用 gulp 创建了一个构建文件夹,如下所示:
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 31.01.2017 15.21 assets
d----- 31.01.2017 15.01 node_modules
-a---- 31.01.2017 15.24 1661564 app.js
-a---- 31.01.2017 15.24 6460064 app.js.map
-a---- 31.01.2017 15.21 656 index.html
-a---- 31.01.2017 15.21 616568 main.bundle.js
-a---- 31.01.2017 15.21 4688762 main.bundle.js.map
-a---- 31.01.2017 15.27 60 morgan.log
-a---- 31.01.2017 15.21 4188 package.json
它运行得很好,但是当我连接到页面时,我得到一个
错误:无法在视图目录“src”中查找视图“索引”
更多细节在文章末尾。
这是我的 gulp 文件:
"use strict";
var gulp = require("gulp");
var del = require("del");
var sourcemaps = require('gulp-sourcemaps');
var rename = require('gulp-rename');
var clean = require('gulp-clean');
/**
* Remove build directory.
*/
gulp.task('clean', function (cb) {
return del(["build"], cb);
});
/**
* Copy all resources that are not TypeScript files into build directory.
*/
gulp.task("resources", ["server", "app", "assets", "client", "rename1", "rename2", "move", "removetemp"], function () {
console.log("Building resources...");
});
/* copy the app core files to the build folder */
gulp.task("app", ['index'], function(){
return gulp.src(["app/**", "!app/**/*.ts"])
.pipe(gulp.dest("build/app"));
});
/* get the index file to the root of the build */
// TODO: Add web.config
gulp.task("index", function(){
return gulp.src(["src/index.html", "package.json"])
.pipe(gulp.dest("build"));
});
/* copy node server to build folder */
gulp.task("server", function () {
return gulp.src(["index.js", "package.json"], { cwd: "server/**" })
.pipe(gulp.dest("build"));
});
/* styles and other assets */
gulp.task("assets", function(){
return gulp.src(["src/assets/**"])
.pipe(gulp.dest("build/assets"));
});
/* client folder */
gulp.task("client", function(){
return gulp.src(["dist/client/main.bundle.js", "dist/client/main.bundle.js.map"])
.pipe(gulp.dest("build"));
});
/* rename /server/index.js to app.js and move to build */
gulp.task('rename1', function() {
return gulp.src(["dist/server/index.js"])
.pipe(rename('app.js'))
.pipe(gulp.dest("build/temp"));
});
gulp.task('rename2', function() {
return gulp.src(["dist/server/index.js.map"])
.pipe(rename('app.js.map'))
.pipe(gulp.dest("build/temp"));
});
gulp.task('move', function() {
return gulp.src(["build/temp/**"])
.pipe(gulp.dest("build"));
});
gulp.task('removetemp', function () {
return del(["build/temp"]);
});
/*
gulp.task('temp', function() {
return gulp.src(["build/temp"])
.pipe(clean({force: true}))
});
*/
/**
* Copy all required libraries into build directory.
*
gulp.task("libs", function () {
return gulp.src([
'core-js/client/shim.min.js',
'zone.js/dist/zone.js',
'systemjs/dist/system.src.js',
'systemjs.config.js',
// 'es6-shim/es6-shim.min.js',
'systemjs/dist/system-polyfills.js',
'angular2/bundles/angular2-polyfills.js',
'angular2/es6/dev/src/testing/shims_for_IE.js',
'systemjs/dist/system.src.js',
'rxjs/bundles/Rx.js',
'angular2/bundles/angular2.dev.js',
'angular2/bundles/router.dev.js'
], { cwd: "node_modules/**" }) /* Glob required here.
.pipe(gulp.dest("build/node_modules"));
});
/**
* Build the project.
*/
gulp.task("default", ['resources'], function () {
console.log("Building the project ...");
});
当我在构建文件夹中运行“node app.js”时出现完整的错误消息:
Listening on: http://localhost:3000
Error: Failed to lookup view "index" in views directory "src"
at EventEmitter.render (C:\front-end\universal\node_modules\express\lib\application.js:579:17)
at ServerResponse.render (C:\front-end\universal\node_modules\express\lib\response.js:960:7)
at C:\front-end\universal\build\app.js:52:12237
at ZoneDelegate.invoke (C:\front-end\universal\node_modules\zone.js\dist\zone-node.js:232:26)
at Zone.run (C:\front-end\universal\node_modules\zone.js\dist\zone-node.js:114:43)
at ngApp (C:\front-end\universal\build\app.js:52:12218)
at Layer.handle [as handle_request] (C:\front-end\universal\node_modules\express\lib\router\layer.js:95:5)
at next (C:\front-end\universal\node_modules\express\lib\router\route.js:131:13)
at Route.dispatch (C:\front-end\universal\node_modules\express\lib\router\route.js:112:3)
at Layer.handle [as handle_request] (C:\front-end\universal\node_modules\express\lib\router\layer.js:95:5)
服务器.ts
// the polyfills must be one of the first things imported in node.js.
// The only modules to be imported higher - node modules with es6-promise 3.x or other Promise polyfill dependency
// (rule of thumb: do it if you have zone.js exception that it has been overwritten)
// if you are including modules that modify Promise, such as NewRelic,, you must include them before polyfills
import 'angular2-universal-polyfills';
import 'ts-helpers';
import './__workaround.node'; // temporary until 2.1.1 things are patched in Core
import * as path from 'path';
import * as express from 'express';
import * as bodyParser from 'body-parser';
import * as cookieParser from 'cookie-parser';
import * as morgan from 'morgan';
import * as compression from 'compression';
// Angular 2
import { enableProdMode } from '@angular/core';
// Angular 2 Universal
import { createEngine } from 'angular2-express-engine';
// App
import { MainModule } from './node.module';
// Routes
import { routes } from './server.routes';
// enable prod for faster renders
enableProdMode();
const app = express();
const ROOT = path.join(path.resolve(__dirname, '..'));
// Express View
app.engine('.html', createEngine({
ngModule: MainModule,
providers: [
// use only if you have shared state between users
// { provide: 'LRU', useFactory: () => new LRU(10) }
// stateless providers only since it's shared
]
}));
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname);
app.set('view engine', 'html');
app.set('json spaces', 2);
app.use(cookieParser('Angular 2 Universal'));
app.use(bodyParser.json());
app.use(compression());
app.use(morgan('dev'));
function cacheControl(req, res, next) {
// instruct browser to revalidate in 60 seconds
res.header('Cache-Control', 'max-age=60');
next();
}
// Serve static files
app.use('/assets', cacheControl, express.static(path.join(__dirname, 'assets'), {maxAge: 30}));
app.use(cacheControl, express.static(path.join(ROOT, 'dist/client'), {index: false}));
//
/////////////////////////
// ** Example API
// Notice API should be in aseparate process
import { serverApi, createTodoApi } from './backend/api';
// Our API for demos only
app.get('/data.json', serverApi);
app.use('/api', createTodoApi());
process.on('uncaughtException', function (err) {
console.error('Catching uncaught errors to avoid process crash', err);
});
function ngApp(req, res) {
function onHandleError(parentZoneDelegate, currentZone, targetZone, error) {
console.warn('Error in SSR, serving for direct CSR');
res.sendFile('index.html', {root: './src'});
return false;
}
Zone.current.fork({ name: 'CSR fallback', onHandleError }).run(() => {
res.render('index', {
req,
res,
// time: true, // use this to determine what part of your app is slow only in development
preboot: false,
baseUrl: '/',
requestUrl: req.originalUrl,
originUrl: `http://localhost:${ app.get('port') }`
});
});
}
/**
* use universal for specific routes
*/
app.get('/', ngApp);
routes.forEach(route => {
app.get(`/${route}`, ngApp);
app.get(`/${route}/*`, ngApp);
});
app.get('*', function(req, res) {
res.setHeader('Content-Type', 'application/json');
var pojo = { status: 404, message: 'No Content' };
var json = JSON.stringify(pojo, null, 2);
res.status(404).send(json);
});
// Server
let server = app.listen(app.get('port'), () => {
console.log(`Listening on: http://localhost:${server.address().port}`);
});