如何在初始化之前以编程方式设置 Angular 基本路径?

How can I set the Angular base path programmatically prior to initialization?

背景/目标

⚠️ℹ️ 我们正在执行构建一次,部署多次的策略,因此我无法将 baseHref 作为构建过程或类似过程的一部分进行传递

问题

到目前为止,无论我尝试过什么,cdn.com/dev/index.html 都会尝试从根目录 (cdn.com/script.js) 而不是子文件夹 (cdn.com/dev/script.js)

加载脚本

我尝试过的

❌ 尝试 1:设置 APP_BASE_HREF 并将其用作提供者

我在index.html中设置了一个window变量:

<script>
    window['base-href'] = window.location.pathname;
    console.log(window['base-href']);
</script>

然后我尝试使用该值作为提供程序的一部分传递给 APP_BASE_HREF,在 app.module.ts:

import { APP_BASE_HREF } from '@angular/common';
let baseHref = (window as { [key: string]: any })["base-href"] as string;

if (!baseHref.endsWith("/")) {

  baseHref += "/";

}

// I only updated the providers array below
@NgModule({
  declarations: [
    AppComponent,
    ComingSoonListComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule
  ],
  providers: [
    {
      provide: APP_BASE_HREF,
      useValue: baseHref
    }
  ],
  bootstrap: [AppComponent]
})

这似乎没有用。

❌ 尝试二:稍微不那么幼稚的尝试

我从 index.html 页面上删除了 base 标签,以防它覆盖了某些内容。

在index.html中,我调用脚本捕获路径名:

<script>
    window['base-href'] = window.location.pathname;
    console.log(window['base-href']);
  </script>
</

app.module.ts中,我创建了一个捕获window值的工厂方法,然后使用它:

const baseHrefFactory = () => {
  let baseHref = (window as { [key: string]: any })["base-href"] as string;
  if (!baseHref.endsWith("/")) {

    baseHref += "/";

  }
  console.log('TS: using baseHref of:', baseHref);
  return baseHref;
}

// Note I just use the factory method below

@NgModule({
  declarations: [
    AppComponent,
    ComingSoonListComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
  ],
  providers: [{ provide: APP_BASE_HREF, useFactory: baseHrefFactory }],
  bootstrap: [AppComponent],
})
export class AppModule { }

❌ 尝试3:直接在HTML

中设置base href

<head> 标签的 index.html 中,创建 base 标签后:

  <script>
    var baseHref = window.location.pathname;
    if (!baseHref.endsWith("/")) {
      baseHref += "/";
    }

    console.log("setting a baseHref of:", baseHref);
    document.getElementsByTagName("base")[0].href = baseHref;
  </script>

❓(尚未尝试)我必须在部署时修改它吗?

我是否需要我的部署脚本来物理修改 index.html 以在 <base> 标记中使用正确的 href?这将是令人惊讶的,但我认为这是可能的。看来这肯定是一个的答案;只是希望这不是 答案。

问题

如何在运行时修改基本路径,以便 Angular 从正确的 URL 下载资源,包括来自 CDN 端点的子路径?我可以在运行时执行此操作,还是需要在部署时以编程方式在 HTML 中进行设置?

你能把 base href 设置成相对的东西吗?

ng build --base-href ./

这将加载相对于索引文件所在位置的文件。

据我所知,修改基本 href 的唯一方法是:

  • ❌ 使用 angular.json file -- 但这将在构建时应用,因此不适用于 build-once、deploy-many 场景
  • ❌ 使用命令行,例如ng build --base-href="./dev" -- 但同样,这是在构建时应用的,不会应用于构建之后的所有部署环境。
  • <base> 标签的内容在 index.html 本身。但是同样,当部署到多个地方时,这将不能满足我的需求。

因此,据我所知,在这种情况下,我的解决方案是让部署脚本修改 index.html 以确保其中的 base 标记与部署到的环境。

我写了一个可以在部署过程中使用的小型 PowerShell 脚本,以防其他人方便使用:

<#
.SYNOPSIS
    Modifies an HTML file to set the base href with the appropriate CDN endpoint.
.EXAMPLE
    # Can use relative path to file
    .\TransformBaseHref.ps1 -PathToHtmlFile ..\code\webapp\dist\webapp\index.html -CDNPath "EnvironmentName"
.EXAMPLE
    # Can use full path to file
    .\TransformBaseHref.ps1 -PathToHtmlFile C:\path\to\index.html -CDNPath "EnvironmentName"
.EXAMPLE
    # When index.html is in Current directory
    .\TransformBaseHref.ps1 -PathToHtmlFile index.html -CDNPath "EnvironmentName"
#>
Param (
    [Parameter(Mandatory = $true, HelpMessage = "The path to the html file to be updated.")]
    [string] $PathToHtmlFile, 
    [Parameter(Mandatory = $true, HelpMessage = "The name of the CDN subfolder, e.g. 'develop' for the development environment")]
    [string] $CDNPath
)

((Get-Content $PathToHtmlFile).Replace("<base href=""/"">", "<base href=""/$CDNPath/"">")) | Set-Content $PathToHtmlFile