Angular 16使用@angular-builders/custom-webpack动态设置base href构建时提示"window is not defined"问题排查
你遇到的这个错误其实很好理解——咱们先捋清楚根源:
ng build构建Angular应用的过程是在Node.js服务器环境里跑的,这个环境根本没有浏览器特有的window全局对象!你直接在@angular-builders/custom-webpack的index转换函数里访问window.location.pathname,自然会触发"window is not defined"的报错。
接下来根据你的实际场景,给你两种可行的解决方案:
场景1:base href路径在构建时就能确定(比如固定部署路径/环境配置)
如果你的应用部署路径是提前确定的(比如不同环境对应固定的代理前缀),那咱们可以在构建阶段就把路径传进去,不用碰浏览器的window对象:
步骤1:修改custom-webpack的index转换逻辑
更新你的index.ts,改为从构建参数或环境变量中读取base href,而不是访问window:
import { TargetOptions } from '@angular-builders/custom-webpack'; export default (targetOptions: TargetOptions, indexHtml: string): string | Promise<string> => { // 这里可以从几个地方取路径: // 1. 构建命令传递的--base-href参数(targetOptions.options.baseHref) // 2. 自定义环境变量(比如构建前设置CUSTOM_BASE_HREF) // 3. 写死默认值(比如"/") const baseHref = process.env.CUSTOM_BASE_HREF || targetOptions.options.baseHref || '/'; const baseTag = `<base href="${baseHref}">`; indexHtml = indexHtml.replace(/<%= changeMe %>/g, baseTag); return indexHtml; };
步骤2:构建时传递路径参数
比如通过环境变量传递自定义路径:
# 示例:设置代理路径为/proxy-inner-app/ CUSTOM_BASE_HREF="/proxy-inner-app/" ng build
或者直接用Angular原生的--base-href参数:
ng build --base-href "/proxy-inner-app/"
场景2:base href必须在浏览器运行时动态确定(比如依赖iframe嵌入后的实际路径)
如果你的应用是嵌入在iframe中,路径前缀只有在浏览器加载时才能拿到(比如当前页面的window.location.pathname),那绝对不能在构建阶段处理,得把逻辑放到Angular应用的运行时初始化流程里:
方案A:动态修改页面的base标签
在Angular应用启动前,动态设置base标签的href属性:
更新你的index.html,先保留一个默认的base标签:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>Inner Application</title> <base href="/"> <!-- 默认值,后续会被替换 --> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="icon" type="image/x-icon" href="assets/Images/favicon.ico"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> </head> <body> <app-root></app-root> </body> </html>
然后在app.module.ts中添加初始化逻辑,用APP_INITIALIZER确保在应用启动前完成替换:
import { NgModule, APP_INITIALIZER } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { AppComponent } from './app.component'; // 初始化函数:动态设置base href function setupDynamicBaseHref() { return () => { const currentPath = window.location.pathname; const baseElement = document.querySelector('base'); if (baseElement) { baseElement.href = currentPath; } else { // 如果页面没有base标签,就新建一个插入head const newBaseTag = document.createElement('base'); newBaseTag.href = currentPath; document.head.prepend(newBaseTag); } }; } @NgModule({ declarations: [AppComponent], imports: [BrowserModule], providers: [ { provide: APP_INITIALIZER, useFactory: setupDynamicBaseHref, multi: true // 允许多个初始化器 } ], bootstrap: [AppComponent] }) export class AppModule { }
方案B:给Angular路由注入动态base href
如果你的应用用了Angular路由,还可以直接给APP_BASE_HREF提供动态值,让路由系统自动适配:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { APP_BASE_HREF } from '@angular/common'; import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], imports: [BrowserModule], providers: [ { provide: APP_BASE_HREF, useValue: window.location.pathname } ], bootstrap: [AppComponent] }) export class AppModule { }
这种方式不需要修改base标签,Angular路由会直接使用这个值来处理导航路径。
总结一下:如果路径是提前确定的,优先用构建时传参的方式,更高效;如果必须依赖浏览器环境的路径(比如iframe代理场景),就用运行时初始化的方案,彻底避开构建阶段的Node.js环境限制。
内容来源于stack exchange

