gpt4 book ai didi

c# - 关于 .NET Core 3.1 和 Angular SSR spa.UseS Prerendering 替代方案的问题

转载 作者:行者123 更新时间:2023-12-03 14:48:05 27 4
gpt4 key购买 nike

我创建这篇文章是为了从社区中获得一些见解。
不久前,随着 .NET Core 3.0 的发布,广为人知且广泛使用的用法 spa.UseSpaPrerendering已被标记为过时。

大约在 2019 年初,我在一个使用 Angular 但需要 SEO 和更好的加载性能的项目中使用 .NET Core 实现了 SSR。

1 年后(现在,2020 年初),他们希望在不同的项目中也有同样的情况。但它已经使用了 Core 3.1。我们立即注意到了 Depricated 标志,所以我开始寻找自己做的方法。

根据过去的经验,SSR 问题有两部分,第一部分是让您的 Angular 应用程序能够在服务器端实际运行。因此,摆脱或解决所有无法在服务器端执行的东西(解决窗口 API 的使用,通过在 Angular 中使用 isPlatform 东西)。第二部分是实际让 .NET Core 启动 Angular CLI 以开始实际的预渲染。这是使用 UseSpaPrerendering 完成的.

分析告诉我们自己解决问题的文档并检查我过去的代码,事情实际上开始变得有意义了。

我查看了 package.json 文件中的命令,执行的 2 个主要命令是 build:ssr实际预编译整个 server/main.js 旁边的 browser/... 文件。第二个命令是 serve:ssr ,这将由 UseSpaPrerendering 执行代码(至少这是我的假设)。

在实践中,我们的 CI/CD 会去执行 build:ssr并将所有文件发布到运行 .NET Core 运行时的应用服务器。并使用 UseSpaPrerendering然后它会执行 serve:ssr 的代码.

现在跳到我需要找到解决方案的现在。我想我也可以自己运行必要的命令。所以在我的 Angular 中排除了一些非 SSR 兼容的代码后,我运行了 build:ssr命令我自己,然后是 serve:ssr命令。效果很好,我的 Angular 应用程序是 SSR 渲染的,由节点本身而不是 .NET Core 提供。

下一步是我在我的 .NET Core 启动文件中尝试过。现在我做了 build:ssr我自己(因为在生产中它将由 CI/CD 完成)并且我重新编写了 start脚本在我的 package.json 中运行命令 npm run serve:ssr .我使用 spa.UseAngularCliServer(npmScript: "start"); 启动了该命令代码在我的 startup.cs 中,我有它,我的 .NET Core 运行时启动我的 API 和我的 SSR Angular 应用程序。

到目前为止一切顺利,但现在只有 1 个问题。我的 SSR Angular 默认托管在端口 4000 上,并且还监听该端口(我可以在输出中看到),而我的 API 监听端口 5000(http) 和 5001(https)。

所以现在我有几个关于这个的问题:

  • 这是现在这样做的正确方法吗?
  • 我如何确保在生产中,当人们进入我的应用程序时,节点监听器将启动?
  • 如果我将 .NET Core API 和 Angular SSR 应用程序完全分开会更好吗?因此也分别部署它们?
  • 最佳答案

    对于任何面临这个问题的人,我刚刚解决了它,这是我们的解决方案,但事实很少:

  • [Web.config] 节点上下文,我的意思是进程工作目录,在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 分钟(这是一个巨大的时间,不是吗?)请随时与我联系。

    胡安

    关于c# - 关于 .NET Core 3.1 和 Angular SSR spa.UseS Prerendering 替代方案的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59747892/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com