• Angular 服务器端渲染应用的一个错误消息 type ReferenceError - localStorage is not defined


    在 Angular 应用开发中,我们在 TypeScript 代码里调用 localStorage.

    它通过 key 从 local storage 中检索数据。 但是在服务器上,此代码崩溃并显示错误消息:
    ReferenceError: localStorage is undefined

    在服务器上运行 Angular 应用程序时,全局空间中缺少标准浏览器 API.

    例如,在服务器端渲染模式下,开发人员不能像在客户端渲染环境下那样,直接访问全局文档对象。 要获得对文档的引用,必须使用 DOCUMENT 令牌和 Angular 依赖注入机制 DI.

    不要通过全局空间使用浏览器 API,而是通过 DI 来替换或禁用浏览器实现,以便在服务器上安全使用这些 API.

    参考下面的代码:

    import {Component, Inject, NgModule} from '@angular/core';
    import {LOCAL_STORAGE} from '@ng-web-apis/common';
    
    @Component({...})
    export class SomeComponent {
      constructor(@Inject(LOCAL_STORAGE) localStorage: Storage) {
        localStorage.getItem('key');
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    上面的示例使用来自 @ng-web-apis/common 包的 LOCAL_STORAGE 令牌。 但是当我们在服务器上运行这段代码时,我们会得到一个错误。 只需从 AppServerModule 的 providers 中添加来自 @ng-web-apis/universal 包的 UNIVERSAL_LOCAL_STORAGE,并通过令牌 LOCAL_STORAGE,这样就能获得服务器的 localStorage 实现。

    import { NgModule } from '@angular/core';
    import {
    	ServerModule,
    } from '@angular/platform-server';
    import { AppModule } from './app.module';
    import { AppComponent } from './app.component';
    import { UNIVERSAL_LOCAL_STORAGE } from '@ng-web-apis/universal';
    
    @NgModule({
      imports: [
        AppModule,
        ServerModule,
      ],
      providers: [UNIVERSAL_LOCAL_STORAGE],
      bootstrap: [AppComponent],
    })
    export class AppServerModule {}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    看下面这段条件渲染代码:

    <>
    @Component({
      selector: 'ram-root',
      template: '<some-сomp *ngIf="isServer"></some-сomp>',
      styleUrls: ['./app.component.less'],
    })
    export class AppComponent {
      isServer = isPlatformServer(this.platformId);
    	
      constructor(@Inject(PLATFORM_ID) private platformId: Object){}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这个 Angular Component 需要获取 PLATFORM_ID、目标平台,并了解类的公共属性。此属性将在模板中与 ngIf 指令一起使用。

    我们有一种更加优雅的实现:

    首先创建一个 injection token:

    <>
    export const IS_SERVER_PLATFORM = new InjectionToken<boolean>('Is server?', {
      factory() {
        return isPlatformServer(inject(PLATFORM_ID));
      },
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    然后创建一个自定义指令:

    @Directive({
      selector: '[ifIsServer]',
    })
    export class IfIsServerDirective {
      constructor(
        @Inject(IS_SERVER_PLATFORM) isServer: boolean,
        templateRef: TemplateRef<any>,
        viewContainer: ViewContainerRef
      ) {
        if (isServer) {
          viewContainer.createEmbeddedView(templateRef);
        }
      }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    然后直接在 Component 上使用这个 structure Directive 就可以了:

    <>
    @Component({
      selector: 'ram-root',
      template: '<some-сomp *ifIsServer"></some-сomp>',
      styleUrls: ['./app.component.less'],
    })
    export class AppComponent {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    额外的属性已从组件中移除。Component 模板现在更简单了,只用专注于它要实现的业务逻辑。

  • 相关阅读:
    请不要忽略软件测试的业务能力
    opencv(5): 滤波器
    grafana 简介
    vue 如何预览pdf文件?
    基于MySql,Redis,Mq,ES的高可用方案解析
    利用python加速视屏
    测试工具链
    机器学习练习-决策树
    自动驾驶——为什么需要仿真?
    花2个月时间学习,面华为测开岗要30k,面试官竟说:你不是在搞笑。。。
  • 原文地址:https://blog.csdn.net/i042416/article/details/124844175