对于遇到此问题的任何人,我刚刚解决了它,这是我们的解决方案,但事实很少:
-
[Web.config] Node Context,我的意思是进程工作目录,在iisnode中工作方式不同,PWD是目标文件路径,这意味着如果你的main.js在dist/server/main中。 js 那么相对于浏览器的路径不会是 dist/browser/ 而是 ../browser/
-
考虑到在部署期间,您必须根据这个新结构生成 Web.config
-Handler iisnode -NodeStartFile dist/server/main.js -appType 节点
[server.ts] - 考虑到这一点,还要考虑根据您的运行时环境设置浏览器路径,这样如果您在生产环境中,它应该是 ../browser
[server.ts] - server.ts 中的订单事项。 如果您遇到浏览器 API 问题,这是因为“import { AppServerModule } from './main.server';” 必须放置在 多米诺声明之后。
这是一个 server.ts 上的工作示例,它也根据带有区域设置字符串的 url 请求使用 i18n 重定向(现在我也解决了这个 i18n 问题,我可以告诉你阅读文档是值得的)。
/***************************************************************************************************
* Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
*/
import { APP_BASE_HREF } from '@angular/common';
import '@angular/localize/init';
import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { existsSync } from 'fs';
import { join } from 'path';
import 'zone.js/dist/zone-node';
import { environment } from './environments/environment';
// THIS FIX MOST OF THE COMMON ISSUES WITH SSR:
// FIRST SET THE BROWSER PATH ACCORDING TO RUNTIME ENVIRONMENT
let browserPath;
if (environment.production) {
browserPath = '../browser';
} else {
browserPath = 'dist/browser';
}
const enDistFolder = join(process.cwd(), browserPath + '/en');
// Emulate browser APIs
const domino = require('domino');
const fs = require('fs');
const templateA = fs.readFileSync(join(enDistFolder, 'index.html')).toString();
const win = domino.createWindow(templateA);
console.log('win');
win.Object = Object;
console.log('Object');
win.Math = Math;
console.log('Math');
global['window'] = win;
global['document'] = win.document;
global['Event'] = win.Event;
console.log('declared Global Vars....');
/****************************************************/
/** NOTE THIS: I need to avoid sorting this line */
// USE CTRL+P -> SAVE WITHOUT FORMATTING
import { AppServerModule } from './main.server';
/****************************************************/
// The Express app is exported so that it can be used by serverless Functions.
export function app() {
const server = express();
const indexHtml = existsSync(join(browserPath, 'index.original.html')) ? 'index.original.html' : 'index.html';
// Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
server.engine('html', ngExpressEngine({
bootstrap: AppServerModule,
}));
server.set('view engine', 'html');
server.set('views', browserPath);
// Example Express Rest API endpoints
// server.get('/api/**', (req, res) => { });
// Serve static files from /browser
server.get('*.*', express.static(browserPath, {
maxAge: '1y'
}));
server.use('/robots.txt', express.static('/en/robots.txt'));
server.use('/ads.txt', express.static('/en/ads.txt'));
// THE ORIGINAL Universal Requests handler
// // // All regular routes use the Universal engine
// // server.get('*', (req, res) => {
// // res.render(indexHtml, { req, providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }] });
// // });
// OUR i18n REQUESTS HANDLER
// All regular routes use the Universal engine
server.get('*', (req, res) => {
// this is for i18n
const supportedLocales = ['en', 'es'];
const defaultLocale = 'es';
const matches = req.url.match(/^\/([a-z]{2}(?:-[A-Z]{2})?)\//);
// check if the requested url has a correct format '/locale' and matches any of the supportedLocales
const locale = (matches && supportedLocales.indexOf(matches[1]) !== -1) ? matches[1] : defaultLocale;
res.render(`${locale}/index.html`, { req });
});
return server;
}
function run() {
const port = process.env.PORT || 4000;
// Start up the Node server
const server = app();
server.listen(port, () => {
console.log(`Node Express server listening on http://localhost:${port}`);
});
}
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from './main.server';
我仍然需要处理这段代码和我们的应用程序(SSR 和 oauth 问题,另一个有趣的话题),但我想分享它,因为我们花了将近 20 次部署来解决这些问题。
最后的话:如果您在 angular 8 迁移后来到这里,我很乐意为您提供帮助并给您很好的提示,但老实说,请按照指南并仔细阅读文档。此外,如果您使用的是 Azure DevOps 管道,则应考虑使用 npm 缓存。我们的 as 很大,现在我们在每个构建过程中节省了超过 12 分钟(这是一个巨大的时间,不是吗?)请随时与我联系。
胡安