首页 > 其他分享 >使用Angular Universal时的重要注意事项

使用Angular Universal时的重要注意事项

时间:2023-05-05 21:22:44浏览次数:47  
标签:浏览器 渲染 Universal 注意事项 import 服务器 Angular

介绍

尽管Angular Universal项目的目标是能够在服务器上无缝渲染Angular应用程序,但您应该考虑一些不一致之处。首先,服务器和浏览器环境之间存在明显的差异。在服务器上渲染时,应用程序处于短暂或“快照”状态。应用程序被完全渲染一次,返回完整的HTML,而整个过程中的产生的状态被销毁,直到下一次渲染开始, 再重新计算这些状态。接下来,服务器环境本质上不具有与浏览器相同的功能(也有可能服务器拥有而浏览器没有的功能)。例如,服务器没有任何cookie的概念。您可以将此功能和其他功能polyfill,但没有完美的解决方案来弥合这种差异。在后面的部分中,我们将介绍潜在的缓解措施,以减少在服务器上渲染时的错误范围。
还请注意SSR的目标:提高应用程序的初始渲染时间。这意味着,应该避免或充分防范任何可能在初始渲染中降低应用程序速度的情况。同样,我们将在后面的部分中回顾如何实现这一点。

"window is not defined"

使用Angular Universal时最常见的问题之一是服务器环境中缺少浏览器全局变量。这是因为Angular Universal项目使用domino作为服务器DOM渲染引擎。因此,服务器上不存在或不支持某些功能。这包括window和document全局对象、cookie、某些HTML元素(如canvas)以及其他一些元素。没有详尽的列表,所以请注意,如果您看到这样的错误,其中没有定义以前可访问的全局对象,很可能是因为该全局无法通过domino获得。

Fun fact: Domino stands for "DOM in Node"

如何解决上述问题

策略1:Injection

通常,所需的全局可以通过依赖注入(DI)通过Angular平台获得。
例如,我们可以通过@Inject(DOCUMENT)获得document对象。此外,还可以通过DOCUMENT对象获取window和location对象。例如:


// example.service.ts
import { Injectable, Inject } from '@angular/core';
import { DOCUMENT } from '@angular/common';

@Injectable()
export class ExampleService {
  constructor(@Inject(DOCUMENT) private _doc: Document) {}

  getWindow(): Window | null {
    return this._doc.defaultView;
  }

  getLocation(): Location {
    return this._doc.location;
  }

  createElement(tag: string): HTMLElement {
    return this._doc.createElement(tag);
  }
}

但是我们不要期望这种方法能解决所有问题. localStorage就是一个例外, localStorage是一个经常被请求的API,它无法在浏览器以外良好的工作。如果您需要编写自己的库组件,而且使用到localstorage, 请考虑使用某种方法让其在服务器上和浏览器上提供相似的功能(这就是Angular CDK和Material所做的, 可以作为一种参考)。

策略2:Guard

如果我们不能从Angular platform注入所需的适当全局值,那么我们可以浏览器代码的调用的地方包装一层Guard,只要您不需要在服务器上访问该代码。例如,全局窗口元素的调用通常是为了获取窗口大小或其他一些视觉元素。然而,在服务器上,没有“Screen”的概念,因此很少需要此功能。
您可以在网上或其他地方阅读到,建议使用isPlatformBrowser或isPlatformServer。这种指南不太合适的方法。这是因为您最终会在应用程序代码中创建特定于平台的if-else分支代码。这不仅不必要地增加了应用程序的复杂度,而且还增加了必须维护的复杂性。通过依赖注入(DI)将代码分离为单独的特定于平台的模块和实现,您的业务代码可以保留关于业务逻辑的内容,而特定与平台的差异部分可以留给特定与平台的模块来处理, 并逐个案例逐个案例的完善抽象出来的Guard层。下面是一个例子:


// window-service.ts
import { Injectable } from '@angular/core';

@Injectable()
export class WindowService {
  getWidth(): number {
    return window.innerWidth;
  }
}


// server-window.service.ts
import { Injectable } from '@angular/core';
import { WindowService } from './window.service';

@Injectable()
export class ServerWindowService extends WindowService {
  getWidth(): number {
    return 0;
  }
}
// app-server.module.ts
import {NgModule} from '@angular/core';
import {WindowService} from './window.service';
import {ServerWindowService} from './server-window.service';

@NgModule({
  providers: [{
    provide: WindowService,
    useClass: ServerWindowService,
  }]
})

如果您有一个由第三方提供的组件,该组件与在各Angular platform上的行为不兼容,那么除了基本应用程序模块外,您还可以为浏览器和服务器创建两个单独的模块。基本应用程序模块将包含您所有的平台无关代码,浏览器模块将包含所有特定于浏览器的代码, 而服务器模块包含所有特定于服务器的代码. 而如果该组件对浏览器友好, 那么浏览器模块不必写太大代码,反之亦然。为了避免编辑过多的模板代码,可以创建一个无操作组件来放置库组件。下面是一个例子:


// example.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'example-component',
  template: `<library-component></library-component>`, // this is provided by a third-party lib
  // that causes issues rendering on Universal
})
export class ExampleComponent {}


// app.module.ts
import {NgModule} from '@angular/core';
import {ExampleComponent} from './example.component';

@NgModule({
  declarations: [ExampleComponent],
})


// browser-app.module.ts
import {NgModule} from '@angular/core';
import {LibraryModule} from 'some-lib';
import {AppModule} from './app.module';

@NgModule({
  imports: [AppModule, LibraryModule],
})


// library-shim.component.ts
import { Component } from '@angular/core';

@Component({
  selector: 'library-component',
  template: '',
})
export class LibraryShimComponent {}


// server.app.module.ts
import { NgModule } from '@angular/core';
import { LibraryShimComponent } from './library-shim.component';
import { AppModule } from './app.module';

@NgModule({
  imports: [AppModule],
  declarations: [LibraryShimComponent],
})
export class ServerAppModule {}

策略3:Shims

如果以上所有策略都不能符合要求,并且您只需要访问某种浏览器功能,那么您可以修补服务器环境的Global变量,以包括所需的全局。例如:

// server.ts
global['window'] = {
  // properties you need implemented here...
};

这种策略可以应用于任何浏览器环境有而服务环境未定义的元素。当你这样做的时候请小心,因为玩全局变量通常被认为是一种反模式。

fun fact:同样是功能补丁,shim在以及存在的各Angular platform上永远不受支持。而polyfill是计划被支持的功能补丁,或者在较新版本的platform上以及被支持的功能

应用程序速度慢,甚至无法渲染

Angular Universal渲染过程很简单,但也可以被善意或无意识的代码阻止或减慢。
首先,渲染过程的一些异步进程。当对platform-server (Angular Universal平台)发出渲染请求时,将执行单一路线导航。当导航完成时,也就是说所有Zone.js宏任务都完成了,无论当时处于什么状态(完整或不完整)的DOM都会返回给用户。

A Zone.js macrotask is just a JavaScript macrotask that executes in/is patched by Zone.js

这意味着,如果有一个进程(如microtask)需要占用一定数量的CPU时间片来才能完成,或者存在一个非常耗时的 HTTP连接请求,则渲染过程将无法完成或者需要相当长的时间。宏任务包括对全局变量(如 setTimeout 和 setInterval)以及 Observables 的调用。调用它们而不取消它们,或者让它们在服务器上运行的时间超过所需的时间可能会导致渲染效果欠佳。

如果您还不知道微任务和宏任务的差别,可能值得复习JavaScript事件循环并学习微任务和宏任务之间的区别。这里有一个很好的参考。

HTTP,Firebase,WebSocket等在渲染之前不会完成

与上面关于等待宏任务完成的部分类似,另一方面是平台不会等待微任务完成才完成渲染。在 Angular Universal 中,我们修补了 Angular HTTP 客户端,将其转换为宏任务,以确保给定渲染的任何所需的 HTTP 请求都已完成。但是,这种类型的补丁可能并不适合所有微任务,因此建议您对如何进行进行最佳判断。您可以查看代码参考,了解 Universal 如何包装任务以将其转换为宏任务,或者您可以简单地选择更改给定任务的服务器行为。

参考文档

Important Considerations when Using Angular Universal

标签:浏览器,渲染,Universal,注意事项,import,服务器,Angular
From: https://www.cnblogs.com/guoapeng/p/17375393.html

相关文章

  • string为接口的注意事项
    string为接口的注意事项问题描述​在一个应用程序中用到了另外一个库的dll,向dll的接口传递std::string参数时报错。由于这方面的问题比较多,所以我进行了深入研究。前置知识在vs项目右键->属性->C/C++->代码生成->运行库,有四个选项,/MD、/MDd、/MT、/MTd含有D的选项......
  • 【Angular】部署github.io上
    看angular官网的教程,发下这个就试了下,有坑啊!!emmm,官网介绍如下:https://angular.cn/guide/deployment发布到GitHubpages(页面服务)link另一种发布Angular应用的简单途径是使用 GitHubPages。你需要创建一个GitHub账号(如果没有的话),然后为你的项目创建一个仓库。记下GitHub中......
  • 要创建富文本内容?Kendo UI Angular组件有专门的编辑器应对!
    您的Angular应用程序可能需要允许用户添加带有格式化选项的文本、图像、表格、外观样式和/或链接,使用KendoUIforAngular的编辑器,可以轻松搞定这些!KendoUIforAngular是专业级的AngularUI组件库,不仅是将其他供应商提供的现有组件封装起来,telerik致力于提供纯粹高性能的Angul......
  • TypeScript 基础语法以及注意事项
    TypeScript(简称TS)是一种由Microsoft开发的静态类型检查器,它在JavaScript的基础上添加了强类型和其他一些特性。以下是TS的一些基本语法和注意事项:变量声明 在TS中,变量声明时需要指定其类型,例如:这样就声明了一个名为myString的字符串变量,并将其赋值为"Hello,TypeScript!"......
  • AngularJS快速上手,从安装到运行
    0、先决条件    在开始之前,请确保你的开发环境已经包含了 Node.js和npm包管理器。Node.jsAngular需要 Node.js 的8.x或10.x版本。要想检查你的版本,请在控制台窗口中运行 node-v 命令。C:\Users\Administrator>node-vv8.12.0要想安装 Node.js,请访问 nodejs......
  • ESXI 群辉安装注意事项
    系统/磁盘类型Linux系统选择其他4.x或更高版本的Linux(64位),选择其他64位不行,没有sata控制器查找设备使用这个而不要用域内iphttps://finds.synology.com或http://10.10.10.104:5000confirm:disk_format_dialog_input_model_title报错不要使用Safari浏览器!!......
  • Angular4_支持多选,分组,自动完成,带图标,清理输入框可配置的select
    Angular4_支持多选,分组,自动完成,过滤,带图标,清理输入框可配置的select效果图DocumentationUsageInstall ngx-select-ex through npm packagemanagerusingthefollowingcommand:npmingx-select-ex--saveForusagewithAngular4installusingthefollowingcomman......
  • Angular4_text-mask用法
    Angular2InputMaskGettingstartedFirst,installit.npmiangular2-text-mask--saveThen,importitintoyour @NgModule:import{NgModule}from'@angular/core';import{FormsModule}from'@angular/forms';import{TextMaskModu......
  • Angular4_下拉框多选(支持响应式表单验证和模板驱动表单验证)
    支持Angular的响应式表单验证和模板驱动表单验证效果图:UsingwithTemplatedrivenFormsSkills*requiredAngularNameEmailAddress*requiredSubmitName [email protected]{"name":"","email&qu......
  • Angular 中修改bootstrap的模态框(modal)大小
    Angular中修改bootstrap的模态框(modal)大小自己瞎搞改width的后果。。。看官网文档:https://ng-bootstrap.github.io/#/components/modal/exampleshttps://github.com/ng-bootstrap/ng-bootstrap/blob/master/src/modal/modal.ts最终解决:showWarnningModal(){this.modalServ......