【问题标题】:Angular 2+ - Set base href dynamicallyAngular 2+ - 动态设置基本href
【发布时间】:2016-11-01 22:42:32
【问题描述】:

我们有一个为客户端使用 Angular 2 的企业应用程序。我们的每个客户都有自己独特的网址,例如:https://our.app.com/customer-onehttps://our.app.com/customer-two。目前我们可以使用document.location动态设置<base href...>。所以用户点击https://our.app.com/customer-two<base href...> 被设置为/customer-two...完美!

问题是,例如,如果用户位于 https://our.app.com/customer-two/another-page 并且他们刷新页面或尝试直接点击该 url,则 <base href...> 被设置为 /customer-two/another-page 并且找不到资源。

我们尝试了以下方法均无济于事:

<!doctype html>
<html>

<head>
  <script type='text/javascript'>
    var base = document.location.pathname.split('/')[1];
    document.write('<base href="/' + base + '" />');
  </script>

...

不幸的是,我们还没有新的想法。任何帮助都会很棒。

【问题讨论】:

  • 你有没有尝试过使用APP_BASE_HREF?我不太确定它将如何实现,但它似乎是有用的......
  • 当然这也取决于您当前使用的路由器版本。你在路由器版本 3.0.0-alpha.x 上吗?
  • 我正在使用“@angular/router”:“3.0.0-alpha.7”
  • Angular 7 - 2018 年 11 月。这仍然是一个问题。当前的答案提供了部分解决方案。在 Angular 模块中做事为时已晚。当您的索引 HTML 被提供时,它需要知道从哪里提取脚本。我看到的方式只有 2 种方式 - 使用 asp\php\whatever 为 index.html 提供动态 basehref 内容。或在 index.html 文件上使用本机 javascript 在 DOM 内容变为 Angular 之前对其进行更改。两者都是对一个共同问题的糟糕解决方案。伤心。
  • 有人也可以回答我的问题吗? stackoverflow.com/questions/53757931/…

标签: angular


【解决方案1】:

您可以使用以下方法在 app.module.ts 文件中动态设置基本 href:https://angular.io/api/common/APP_BASE_HREF

import {Component, NgModule} from '@angular/core';
import {APP_BASE_HREF} from '@angular/common';

@NgModule({
  providers: [{provide: APP_BASE_HREF, useValue: '/my/app'}]
})
class AppModule {}

【讨论】:

    【解决方案2】:

    在 package.json 中将标志 --base-href 设置为相对路径:

    "script": {
        "build": "ng build --base-href ./"
    }
    

    【讨论】:

      【解决方案3】:

      我刚刚换了:

        <base href="/">
      

      到这里:

        <base href="/something.html/">
      

      别忘了以 / 结尾

      【讨论】:

        【解决方案4】:

        有一个类似的问题,实际上我最终在我的网络中探测了一些文件的存在。

        probePath 将是您要检查的文件的相对 URL(如果您现在位于正确的位置,则为一种标记),例如'assets/images/thisImageAlwaysExists.png'

        <script type='text/javascript'>
          function configExists(url) {
            var req = new XMLHttpRequest();
            req.open('GET', url, false); 
            req.send();
            return req.status==200;
          }
        
          var probePath = '...(some file that must exist)...';
          var origin = document.location.origin;
          var pathSegments = document.location.pathname.split('/');
        
          var basePath = '/'
          var configFound = false;
          for (var i = 0; i < pathSegments.length; i++) {
            var segment = pathSegments[i];
            if (segment.length > 0) {
              basePath = basePath + segment + '/';
            }
            var fullPath = origin + basePath + probePath;
            configFound = configExists(fullPath);
            if (configFound) {
              break;
            }
          }
        
          document.write("<base href='" + (configFound ? basePath : '/') + "' />");
        </script>
        

        【讨论】:

          【解决方案5】:

          附加组件:如果您想要例如:/users 作为路由器的应用程序基础,/public 作为资产使用的基础

          $ ng build -prod --base-href /users --deploy-url /public
          

          【讨论】:

            【解决方案6】:

            此处标记的答案没有解决我的问题,可能是不同的角度版本。我能够在终端/shell 中使用以下angular cli 命令实现预期的结果:

            ng build --base-href /myUrl/

            ng build --bh /myUrl/ng build --prod --bh /myUrl/

            这会将仅内置版本中的&lt;base href="/"&gt; 更改为&lt;base href="/myUrl/"&gt;,这非常适合我们在开发和生产之间更改环境。最好的部分是没有代码库需要使用这种方法进行更改。


            总而言之,将您的 index.html 基本 href 保留为:&lt;base href="/"&gt; 然后使用 angular cli 运行 ng build --bh ./ 以使其成为相对路径,或者将 ./ 替换为您需要的任何内容。

            更新:

            如上例所示,如何从命令行执行此操作,以下是如何将其添加到您的 angular.json 配置文件中。

            这将用于所有ng serving 用于本地开发

            "architect": {
                "build": {
                  "builder": "@angular-devkit/build-angular:browser",
                  "options": {
                    "baseHref": "/testurl/",
            

            这是特定于配置构建的配置,例如 prod:

            "configurations": {
               "Prod": {
                 "fileReplacements": [
                    {
                      "replace": src/environments/environment.ts",
                      "with": src/environments/environment.prod.ts"
                    }
                  ],
                  "baseHref": "./productionurl/",
            

            officialangular-cli 文档涉及使用情况。

            【讨论】:

            • 此解决方案需要为每个客户构建,这可能无法解决 OP 的问题。
            • @MaximeMorin 以我目前的经验来看,./ 适用于所有地点。所以实际上我认为你不必为每个客户构建。
            • @Zze 我对./ 的体验非常不同。它一直有效,直到用户导航到路线并点击浏览器的刷新按钮或按键。此时,我们的重写处理调用,提供索引页面,但浏览器假定./ 是当前路由而不是 Web 应用程序的根文件夹。我们通过设置基本 href 的动态(.aspx、.php、.jsp 等)索引解决了 OP 的问题。
            • 在构建过程中使用--prod 不会改变你的base href 值,你不应该在这里谈论它。 --prod 告诉 angular-cli 使用 environment.prod.ts 文件而不是 environments 文件夹中的默认文件 environment.ts
            • Angular 7.1:Unknown option: '--bh'--base-href 工作。
            【解决方案7】:

            坦率地说,你的情况对我来说很好!

            我正在使用 Angular 5。

            <script type='text/javascript'>
                var base = window.location.href.substring(0, window.location.href.toLowerCase().indexOf('index.aspx'))
                document.write('<base href="' + base + '" />');
            </script>
            

            【讨论】:

            【解决方案8】:

            如果您使用的是 Angular CLI 6,则可以使用 angular.json 中的 deployUrl/baseHref 选项(项目 > xxx > 架构师 > 构建 > 配置 > 生产)。这样,您可以轻松地为每个项目指定 baseUrl。

            通过查看已构建应用的 index.html 来检查您的设置是否正确。

            【讨论】:

            • 这需要针对每个环境进行不同的构建,包括所有影响。
            【解决方案9】:

            在 angular4 中,您还可以在 .angular-cli.json 应用程序中配置 baseHref。

            在 .angular-cli.json 中

            {   ....
             "apps": [
                    {
                        "name": "myapp1"
                        "baseHref" : "MY_APP_BASE_HREF_1"
                     },
                    {
                        "name": "myapp2"
                        "baseHref" : "MY_APP_BASE_HREF_2"
                     },
                ],
            }
            

            这会将 index.html 中的基本 href 更新为 MY_APP_BASE_HREF_1

            ng build --app myapp1
            

            【讨论】:

            • 这也需要为每个app做一个build。
            【解决方案10】:

            这在生产环境中对我来说很好

            <base href="/" id="baseHref">
            <script>
              (function() {
                document.getElementById('baseHref').href = '/' + window.location.pathname.split('/')[1] + "/";
              })();
            </script>
            

            【讨论】:

            • 此解决方案在 IIS 中设置多个 VD 时存在很大限制。它会导致重复加载块。
            • 使用硬编码的 base-href 并调用 http://my.app.com/app1 将 url 更改为 http://my.app.com/app1/ (最后 / )这个解决方案在 http://my.app.com/app1 的情况下也适用于我,因为更改为 http://my.app.com/app1/ - 否则无法加载资源。
            【解决方案11】:

            将现有的answer 简化为@sharpmachine

            import { APP_BASE_HREF } from '@angular/common';
            import { NgModule } from '@angular/core';
            
            @NgModule({
                providers: [
                    {
                        provide: APP_BASE_HREF,
                        useValue: '/' + (window.location.pathname.split('/')[1] || '')
                    }
                ]
            })
            
            export class AppModule { }
            

            如果您为 APP_BASE_HREF 不透明令牌提供值,则不必在 index.html 中指定基本标记。

            【讨论】:

              【解决方案12】:

              根据您 (@sharpmachine) 的回答,我能够进一步对其进行一些重构,这样我们就不必破解 index.html 中的函数。

              import { NgModule } from '@angular/core';
              import { BrowserModule } from '@angular/platform-browser';
              import { APP_BASE_HREF, Location } from '@angular/common';
              
              import { AppComponent } from './';
              import { getBaseLocation } from './shared/common-functions.util';
              
              @NgModule({
                declarations: [AppComponent],
                imports: [
                  BrowserModule,
                  HttpModule,
                ],
                bootstrap: [AppComponent],
                providers: [
                  appRoutingProviders,
                  {
                      provide: APP_BASE_HREF,
                      useFactory: getBaseLocation
                  },
                ]
              })
              export class AppModule { }
              

              而且,./shared/common-functions.util.ts 包含:

              export function getBaseLocation() {
                  let paths: string[] = location.pathname.split('/').splice(1, 1);
                  let basePath: string = (paths && paths[0]) || 'my-account'; // Default: my-account
                  return '/' + basePath;
              }
              

              【讨论】:

              • 我已将 myapp 部署到子文件夹。我的网址现在看起来像这样:http://localhost:8080/subfolder/myapp/#/subfolder/myroute 这是你的样子吗?你知道如何去掉 # 之后的子文件夹,让它变成http://localhost:8080/subfolder/myapp/#/myroute吗?
              • 哦。我认为 href base 只有 LocationStrategy 才需要。就我而言,我使用的是 HashLocationStrategy。所以我什至不需要设置它。您是否编写了特殊代码来处理 LocationStrategy 的刷新?
              • 是的,base href 仅适用于 LocationStrategy AFAIK。至于使用 LocationStrategy 编写特殊代码来处理刷新,不确定您的确切意思。您是在问,如果我的“基本 href”在我的应用程序中的活动期间发生变化怎么办?然后,是的,我不得不做一件像window.location.href = '/' + newBaseHref; 这样需要更改的肮脏事情。我对此并不感到骄傲。此外,为了提供更新,我在我的应用程序中放弃了这些黑客解决方案,因为它已成为我的设计问题。路由器参数工作得很好,所以我坚持使用它。
              • 你的appRoutingProvider 是什么样的,克里希南?我看到接受的答案是一样的;也许你只是复制了他的providers,但如果没有,你能给我一个样本或描述它的用途吗? documentation only imports routing modules,它不使用任何与路由相关的提供程序(据我所知)
              • @Krishnan 以下是我必须使用 nginx 做的事情:location /subfolder/myapp/ { try_files $uri /subfolder/myapp/index.html; } location /subfolder2/myapp/ { try_files $uri /subfolder2/myapp/index.html; }
              【解决方案13】:

              当在同一个域中构建多个应用程序时,我使用当前工作目录./

              <base href="./">
              

              附带说明,我使用.htaccess 来协助我在页面重新加载时路由:

              RewriteEngine on
              RewriteCond %{REQUEST_FILENAME} !-f
              RewriteRule .* index.html [L]
              

              【讨论】:

              • 非常感谢您提供.htaccess 文件
              • 这个答案不正确,它不适用于/your-app/a/b/c 等路由。如果您从这样的路由刷新页面,Angular 将在 /your-app/a/b/ 下查找运行时、polyfills 和主 JS 文件和样式 CSS 文件。他们显然不在那个位置,而是在 /your-app/
              • @toongeorges 这个答案是大约两年前写的,使用的配置文件由 Apache Web 服务器软件检测并执行,以处理页面重新加载时的路由。你说这个解决方案不正确并且不起作用是一种误导,但你的意思是你无法弄清楚如何让它与你的配置一起工作。
              • 我忽略了 Apache 重写规则,我对此知之甚少,不能说这在 Apache 上不起作用,所以我可能是错的,你的答案可能是正确的。尽管在任何情况下我都更喜欢不需要您修改服务器配置的解决方案,因为这里已经给出了多个答案,因为这使得应用程序可以跨不同的服务器移植。
              • 使用./ 会中断路由到多级深度的地址。例如http://localhost:4200/foo/bar.
              【解决方案14】:

              这就是我们最终要做的事情。

              将此添加到 index.html。应该是&lt;head&gt;部分的第一件事

              <base href="/">
              <script>
                (function() {
                  window['_app_base'] = '/' + window.location.pathname.split('/')[1];
                })();
              </script>
              

              然后在app.module.ts 文件中,将{ provide: APP_BASE_HREF, useValue: window['_app_base'] || '/' } 添加到提供者列表中,如下所示:

              import { NgModule, enableProdMode, provide } from '@angular/core';
              import { BrowserModule } from '@angular/platform-browser';
              import { APP_BASE_HREF, Location } from '@angular/common';
              
              
              import { AppComponent, routing, appRoutingProviders, environment } from './';
              
              if (environment.production) {
                enableProdMode();
              }
              
              @NgModule({
                declarations: [AppComponent],
                imports: [
                  BrowserModule,
                  HttpModule,
                  routing],
                bootstrap: [AppComponent],
                providers: [
                  appRoutingProviders,
                  { provide: APP_BASE_HREF, useValue: window['_app_base'] || '/' },
                ]
              })
              export class AppModule { }
              

              【讨论】:

              • 错误 TS7017:元素隐式具有“任何”类型,因为类型“窗口”没有索引签名。 useValue: (window as any) ['_app_base'] || '/'helps
              • 对于最新的ng2真的不行,有解决办法吗? (加载错误的脚本路径,然后加载好的脚本路径,但不在移动设备上)
              • 将其添加为下面的答案,因为内容长度太长,无法发表评论。
              • @Amit 请在下面查看我的答案,以便在最新的 ng2 中轻松解决。
              • 您是如何解决其他文件的问题的。我的浏览器正在尝试加载localhost:8080/main.bundle.js,但因为我的 contextPath 不同而无法加载? (应该是获取localhost:8080/hello/main.bundle.js.
              猜你喜欢
              • 2017-10-30
              • 2019-06-18
              • 2023-03-22
              • 2017-03-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-12-17
              • 2016-04-04
              相关资源
              最近更新 更多