在 Angular 17 SSR 中出现错误“窗口未定义”

问题描述 投票:0回答:1

我已将 SSR 集成到 Angular 17 现有项目中。 当我运行命令 npm run dev:ssr 时,我得到 ** Angular Universal Live Development Server 正在监听 http://localhost:4200,在 http://localhost:4200 打开浏览器 **

现在,在浏览器中打开 http://localhost:4200 时,终端中出现错误。

ERROR ReferenceError: window is not defined
    at PlatformRef2._moduleDoBootstrap (C:\Users\ak970\OneDrive\Desktop\FarmQ FE\FarmQ-FE-Version-14\FarmQ-FE\dist\farmQ\server\main.js:2:3355276)        
    at C:\Users\ak970\OneDrive\Desktop\FarmQ FE\FarmQ-FE-Version-14\FarmQ-FE\dist\farmQ\server\main.js:2:3354730
ReferenceError: window is not defined
    at new AppComponent2 (C:\Users\ak970\OneDrive\Desktop\FarmQ FE\FarmQ-FE-Version-14\FarmQ-FE\dist\farmQ\server\main.js:1:2062707)
    at AppComponent.#_.ɵfac [as factory] (C:\Users\ak970\OneDrive\Desktop\FarmQ FE\FarmQ-FE-Version-14\FarmQ-FE\dist\farmQ\server\main.js:1:2064189)      
    at getNodeInjectable (C:\Users\ak970\OneDrive\Desktop\FarmQ FE\FarmQ-FE-Version-14\FarmQ-FE\dist\farmQ\server\main.js:2:3157410)
    at createRootComponent (C:\Users\ak970\OneDrive\Desktop\FarmQ FE\FarmQ-FE-Version-14\FarmQ-FE\dist\farmQ\server\main.js:2:3260667)
    at ComponentFactory.create (C:\Users\ak970\OneDrive\Desktop\FarmQ FE\FarmQ-FE-Version-14\FarmQ-FE\dist\farmQ\server\main.js:2:3261173)
    at ApplicationRef3.bootstrap (C:\Users\ak970\OneDrive\Desktop\FarmQ FE\FarmQ-FE-Version-14\FarmQ-FE\dist\farmQ\server\main.js:2:3345290)
    at C:\Users\ak970\OneDrive\Desktop\FarmQ FE\FarmQ-FE-Version-14\FarmQ-FE\dist\farmQ\server\main.js:2:3355294
    at Array.forEach (<anonymous>)
    at PlatformRef2._moduleDoBootstrap (C:\Users\ak970\OneDrive\Desktop\FarmQ FE\FarmQ-FE-Version-14\FarmQ-FE\dist\farmQ\server\main.js:2:3355276)        
    at C:\Users\ak970\OneDrive\Desktop\FarmQ FE\FarmQ-FE-Version-14\FarmQ-FE\dist\farmQ\server\main.js:2:3354730

错误](https://i.sstatic.net/6rys3.png)

package.json

{
  "name": "farmQ",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "watch": "ng build --watch --configuration development",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "dev:ssr": "ng run farmQ:serve-ssr",
    "serve:ssr": "node dist/farmQ/server/main.js",
    "build:ssr": "ng build && ng run farmQ:server",
    "prerender": "ng run farmQ:prerender"
  },
  "private": true,
  "dependencies": {
    "@ag-grid-community/angular": "^31.0.0",
    "@angular/animations": "^17.3.2",
    "@angular/cdk": "^16.2.14",
    "@angular/common": "^17.3.2",
    "@angular/compiler": "^17.3.2",
    "@angular/core": "^17.3.2",
    "@angular/flex-layout": "^15.0.0-beta.42",
    "@angular/forms": "^17.3.2",
    "@angular/material": "^16.2.14",
    "@angular/platform-browser": "^17.3.2",
    "@angular/platform-browser-dynamic": "^17.3.2",
    "@angular/platform-server": "^17.3.2",
    "@angular/router": "^17.3.2",
    "@angular/ssr": "^17.3.2",
    "@ngx-translate/core": "^14.0.0",
    "@ngx-translate/http-loader": "^7.0.0",
    "ag-grid-angular": "^28.2.1",
    "ag-grid-community": "^28.2.1",
    "angularx-qrcode": "^17.0.0",
    "aws-sdk": "^2.1580.0",
    "bootstrap": "^5.3.3",
    "express": "^4.18.2",
    "font-awesome": "^4.7.0",
    "jquery": "^3.7.1",
    "lodash": "^4.17.21",
    "mock-browser": "^0.92.14",
    "moment": "^2.30.1",
    "ng-material-multilevel-menu": "^6.0.2",
    "ng-otp-input": "^1.9.3",
    "ngx-moment": "^6.0.2",
    "ngx-toastr": "^16.2.0",
    "primeflex": "^3.3.1",
    "primeicons": "^6.0.1",
    "primeng": "^17.12.0",
    "rxjs": "^7.4.0",
    "save": "^2.9.0",
    "sweetalert2": "^11.10.6",
    "tslib": "^2.3.0",
    "xlsx": "^0.18.5",
    "zone.js": "~0.14.4"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^17.3.2",
    "@angular/cli": "~17.3.2",
    "@angular/compiler-cli": "^17.3.2",
    "@types/express": "^4.17.17",
    "@types/jasmine": "~4.0.0",
    "@types/jquery": "^3.5.29",
    "@types/lodash": "^4.17.0",
    "@types/node": "^18.18.0",
    "@types/qrcode": "^1.5.5",
    "browser-sync": "^3.0.0",
    "jasmine-core": "~4.3.0",
    "karma": "~6.4.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage": "~2.2.0",
    "karma-jasmine": "~5.1.0",
    "karma-jasmine-html-reporter": "~2.0.0",
    "typescript": "~5.4.3"
  }
}

angular.json

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "farmQ": {
      "projectType": "application",
      "schematics": {
        "@schematics/angular:component": {
          "style": "scss",
          "strict": true
        }
      },
      "root": "",
      "sourceRoot": "src",
      "prefix": "app",
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist/farmQ/browser",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.app.json",
            "inlineStyleLanguage": "scss",
            "assets": [
              "src/favicon.ico",
              "src/assets",
              "src/web.config"
            ],
            "styles": [
              "src/styles.scss",
              "node_modules/ngx-toastr/toastr.css",
              "node_modules/font-awesome/css/font-awesome.css",
              "node_modules/primeng/resources/themes/saga-blue/theme.css",
              "node_modules/primeng/resources/themes/nova/theme.css",
              "node_modules/primeng/resources/primeng.min.css",
              "node_modules/primeicons/primeicons.css",
              "node_modules/bootstrap/dist/css/bootstrap.min.css",
              "node_modules/primeflex/primeflex.css",
              "node_modules/sweetalert2/src/sweetalert2.scss"
            ],
            "scripts": [],
            "vendorChunk": true,
            "extractLicenses": false,
            "buildOptimizer": false,
            "sourceMap": true,
            "optimization": false,
            "namedChunks": true,
            "allowedCommonJsDependencies": [
              "lodash",
              "moment",
              "qrcode",
              "sweetalert2"
            ]
          },
          "configurations": {
            "dev": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.dev.ts"
                }
              ],
              "optimization": {
                "scripts": true,
                "styles": {
                  "minify": true,
                  "inlineCritical": false
                },
                "fonts": true
              },
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "13mb",
                  "maximumError": "13mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "13mb",
                  "maximumError": "13mb"
                }
              ]
            },
            "qa": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.qa.ts"
                }
              ],
              "optimization": {
                "scripts": true,
                "styles": {
                  "minify": true,
                  "inlineCritical": false
                },
                "fonts": true
              },
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "13mb",
                  "maximumError": "13mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "13mb",
                  "maximumError": "13mb"
                }
              ]
            },
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": {
                "scripts": true,
                "styles": {
                  "minify": true,
                  "inlineCritical": false
                },
                "fonts": true
              },
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "13mb",
                  "maximumError": "13mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "13mb",
                  "maximumError": "13mb"
                }
              ]
            },
            "staging": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.stag.ts"
                }
              ],
              "optimization": {
                "scripts": true,
                "styles": {
                  "minify": true,
                  "inlineCritical": false
                },
                "fonts": true
              },
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "13mb",
                  "maximumError": "13mb"
                },
                {
                  "type": "anyComponentStyle",
                  "maximumWarning": "13mb",
                  "maximumError": "13mb"
                }
              ]
            }
          },
          "defaultConfiguration": ""
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "port": 5000,
            "host": "0.0.0.0",
            "buildTarget": "farmQ:build"
          },
          "configurations": {
            "production": {
              "buildTarget": "farmQ:build:production"
            },
            "dev": {
              "buildTarget": "farmQ:build:dev"
            },
            "qa": {
              "buildTarget": "farmQ:build:qa"
            },
            "staging": {
              "buildTarget": "farmQ:build:staging"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "buildTarget": "farmQ:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "tsconfig.spec.json",
            "karmaConfig": "karma.conf.js",
            "assets": [
              "src/favicon.ico",
              "src/assets",
              "src/web.config"
            ],
            "styles": [
              "src/styles.scss"
            ],
            "scripts": []
          }
        },
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "farmQ:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "farmQ:serve:production"
            }
          }
        },
        "server": {
          "builder": "@angular-devkit/build-angular:server",
          "options": {
            "outputPath": "dist/farmQ/server",
            "main": "server.ts",
            "tsConfig": "tsconfig.server.json",
            "buildOptimizer": false,
            "optimization": false,
            "sourceMap": true,
            "extractLicenses": false,
            "inlineStyleLanguage": "scss",
            "vendorChunk": true
          },
          "configurations": {
            "dev": {
              "buildOptimizer": true,
              "outputHashing": "media",
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.dev.ts"
                }
              ],
              "optimization": true,
              "sourceMap": false,
              "extractLicenses": true,
              "vendorChunk": false
            },
            "qa": {
              "buildOptimizer": true,
              "outputHashing": "media",
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.qa.ts"
                }
              ],
              "optimization": true,
              "sourceMap": false,
              "extractLicenses": true,
              "vendorChunk": false
            },
            "production": {
              "buildOptimizer": true,
              "outputHashing": "media",
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "sourceMap": false,
              "extractLicenses": true,
              "vendorChunk": false
            },
            "staging": {
              "buildOptimizer": true,
              "outputHashing": "media",
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.stag.ts"
                }
              ],
              "optimization": true,
              "sourceMap": false,
              "extractLicenses": true,
              "vendorChunk": false
            }
          },
          "defaultConfiguration": "production"
        },
        "serve-ssr": {
          "builder": "@angular-devkit/build-angular:ssr-dev-server",
          "configurations": {
            "dev": {
              "browserTarget": "farmQ:build:dev",
              "serverTarget": "farmQ:server:dev"
            },
            "production": {
              "browserTarget": "farmQ:build:production",
              "serverTarget": "farmQ:server:production"
            },
            "qa": {
              "browserTarget": "farmQ:build:qa",
              "serverTarget": "farmQ:server:qa"
            },
            "staging": {
              "browserTarget": "farmQ:build:staging",
              "serverTarget": "farmQ:server:staging"
            }
          },
          "defaultConfiguration": "dev"
        },
        "prerender": {
          "builder": "@angular-devkit/build-angular:prerender",
          "options": {
            "routes": [
              "/"
            ]
          },
          "configurations": {
            "production": {
              "browserTarget": "farmQ:build:production",
              "serverTarget": "farmQ:server:production"
            },
            "dev": {
              "browserTarget": "farmQ:build:dev",
              "serverTarget": "farmQ:server:dev"
            }
          },
          "defaultConfiguration": "production"
        }
      }
    }
  },
  "cli": {
    "analytics": "352769b2-c967-4d4a-8358-edf953619ff9"
  }
}

app.module.ts

import { BrowserModule, provideClientHydration } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgOtpInputModule } from 'ng-otp-input';
import { SidebarComponent } from './shared/sidebar/sidebar.component';
import * as $ from 'jquery';
import { LoginModule } from './login/login.module';
import { HeaderComponent } from './shared/header/header.component';
import { CarouselModule } from 'primeng/carousel';
import { ButtonModule } from 'primeng/button';
import { ToastModule } from 'primeng/toast';
import { AccordionModule } from 'primeng/accordion';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TableModule } from 'primeng/table';
import { DropdownModule } from 'primeng/dropdown';
import { LoaderService } from './loader/loader.service';
import { AppInterceptor } from './app.interceptor';
import { LoaderComponent } from './spinner/loader/loader.component';
import { MatLegacyProgressSpinnerModule as MatProgressSpinnerModule } from '@angular/material/legacy-progress-spinner';
import { ToastrModule } from 'ngx-toastr';
import { DialogModule } from 'primeng/dialog';
import { FooterComponent } from './shared/footer/footer.component';
import { RouterModule } from '@angular/router';
import { FarmDashboardModule } from './farm-dashboard/farm-dashboard.module';
import { AgGridModule } from 'ag-grid-angular';
import { AdminDashboardModule } from './admin-dashboard/admin-dashboard.module';
import { ColdStorePortalModule } from './cold-store-portal/cold-store-portal.module';
import { TraderPortalModule } from './trader-portal/trader-portal.module';
import { TreeModule } from 'primeng/tree';
import { CalendarModule } from 'primeng/calendar';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { CommonService } from './common-service/common.service';
import { MaterialModule } from './materialModule';
import { MatLegacyCheckboxModule as MatCheckboxModule } from '@angular/material/legacy-checkbox';
import { MatLegacyTableDataSource as MatTableDataSource } from '@angular/material/legacy-table';
import { SharedModuleModule } from './shared-module/shared-module.module';
import { MainDashboardComponent } from './main-dashboard/main-dashboard.component';
import { LeftSidebarComponent } from './shared/left-sidebar/left-sidebar.component';
import { HomeDashboardComponent } from './home-dashboard/home-dashboard.component';
@NgModule({
  declarations: [
    AppComponent,
    SidebarComponent,
    HeaderComponent,
    LoaderComponent,
    FooterComponent,
    MainDashboardComponent,
    LeftSidebarComponent,
    HomeDashboardComponent,
  ],
  imports: [
    CommonModule,
    RouterModule,
    TreeModule,
    FormsModule,
    ReactiveFormsModule,
    AccordionModule,
    TableModule,
    ButtonModule,
    BrowserAnimationsModule,
    ToastModule,
    DropdownModule,
    CarouselModule,
    ReactiveFormsModule,
    LoginModule,
    FarmDashboardModule,
    AdminDashboardModule,
    ColdStorePortalModule,
    TraderPortalModule,
    MatProgressSpinnerModule,
    DialogModule,
    ToastrModule.forRoot({
      positionClass :'toast-top-center'
    }),
    HttpClientModule,
    BrowserModule,
    AppRoutingModule,
    NgOtpInputModule,
    AgGridModule,
    CalendarModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient]
      }
    }),
    MaterialModule,
    MatCheckboxModule,
    SharedModuleModule
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS, useClass: AppInterceptor, multi: true
    },LoaderService, CommonService, provideClientHydration()
  ],
  exports: [NgOtpInputModule, ButtonModule, TableModule, DropdownModule,MatProgressSpinnerModule,
    ToastModule, AccordionModule, BrowserAnimationsModule, ReactiveFormsModule,
    CarouselModule,DialogModule,FormsModule],
  bootstrap: [AppComponent]
})
export class AppModule { }

// AoT requires an exported function for factories
export function HttpLoaderFactory(http: HttpClient) {
  return new TranslateHttpLoader(http);
}

node.js angular seo server-side-rendering angular17
1个回答
0
投票

这可能是由于外部库全局声明了一个使用

window
的函数引起的。预渲染期间发生错误,因为当时我们还没有
browser
window

对我来说,我通过在 app.component.ts 中添加这段代码来解决它

// add this
if (globalThis.window === undefined) {
  globalThis.window =
    ({
      addEventListener: () => {},
      // add more methods as you wish
    } as never);
}

// app.component.ts
@Component({
  selector: 'app-root',
  standalone: true,
  templateUrl: './app.component.html',
  styleUrl: './app.component.css',
  imports: [...],
})

这不是一个完美的解决方案,但它可以作为绕过阻止程序的临时解决方法。通过这样做,当没有定义的窗口时,我们将分配一个新的窗口对象。如果需要任何窗口的方法,我们可以将它们添加到我们定义的对象中。

© www.soinside.com 2019 - 2024. All rights reserved.