关于 Angular Universal 应用渲染两次的问题

简介: 关于 Angular Universal 应用渲染两次的问题

Angular Repository url:https://github.com/angular/angular-cli/issues/7477


现象:


I built a sample repo using angular-cli and followed the steps in the Universal Rendering story to enable server side rendering. The application loads well on running, but I see the client rendering also happening after the page is served from the server. So the page gets rendered from the server and then the content disappears for a second as the client rendering happens.


观察到 page 从服务器端 serve 之后,client side rendering 还会再次发生。有一瞬间的功夫,服务器端渲染的内容消失了,client rendering 接着发生。


有网友回复:


The server renders the page without any state and outputs static HTML to view in the browser while the angular application is being bootstrapped. A re-render when the application has been bootstrapped is required to use state saved in the browser such as logged in user information.


SSR 渲染出的是纯粹的 Static HTML,没有任何状态,也无法响应用户的交互。


当应用在客户端完成 Boostrap 之后,re-render 是必要的,以便使用服务器端静态 HTML 里嵌入的通过 State Transfer 传递的业务数据(这些业务数据在 SSR 时通过服务器端执行的 AJAX,从 API server 读取)。


The initial page contains static HTML which needs to be replaced with your angular application once bootstrapped.


这些静态页面的内容,需要被 Angular 应用在客户端 Bootstrap 之后重新渲染的内容所替换。


To make the transfer state work, the Angular application must be bootstrapped after the DOM is loaded. This is because, the data transferred from the server is stored in an HTML element and this element should be accessible to Angular to get the data. Open the file main.ts and move the application bootstrap logic inside the DOMContentLoaded event as shown below:


因为 Transfer State 的工作机制是解析嵌入在 HTML 页面里的 DOM 元素,所以客户端渲染的 Angular 应用,需要等待 DOM 加载之后再进行 Bootstrap.


为此我们需要在 main.ts 文件里编写下面的代码:


document.addEventListener('DOMContentLoaded', () => {  
  platformBrowserDynamic().bootstrapModule(AppModule)    
  .catch(err => console.log(err));
});

这段代码是Angular应用的入口点,它有以下作用:


  1. 等待文档加载完成:document.addEventListener('DOMContentLoaded', ...) 表示在文档(DOM)加载完成后执行特定的操作。这是为了确保JavaScript代码在HTML文档完全加载后运行,以避免在DOM元素还未准备好的情况下执行操作,从而避免潜在的错误。
  2. 动态启动Angular应用:platformBrowserDynamic().bootstrapModule(AppModule) 是Angular的核心方法之一,它用于动态启动(bootstrap)一个Angular应用。在这里,它将 AppModule 作为应用的根模块来启动。
  3. 处理启动失败:.catch(err => console.log(err)) 表示在启动过程中如果出现错误,将错误信息打印到控制台。这是为了帮助开发人员调试应用并捕获潜在的问题。


让我们详细解释每个部分,并举例说明其作用:


1. 等待文档加载完成 (document.addEventListener('DOMContentLoaded', ...))


这是为了确保在HTML文档完全加载后再执行Angular应用的启动过程。当浏览器加载HTML页面时,它按照从上到下的顺序逐步构建DOM树。如果你的JavaScript代码在DOM还没有完全加载之前执行,那么它可能无法找到所需的DOM元素,导致错误或不稳定的行为。


例如,假设你的Angular应用需要在特定的HTML元素上渲染内容,如果你在DOM未加载完成时尝试访问这些元素,可能会出现问题。通过等待DOMContentLoaded 事件,你可以确保DOM已准备就绪,然后再执行Angular应用的启动。


document.addEventListener('DOMContentLoaded', () => {
  // 在这里启动Angular应用
});

2. 动态启动Angular应用 (platformBrowserDynamic().bootstrapModule(AppModule))


这部分代码是启动Angular应用的核心。它使用了Angular的 platformBrowserDynamic 类的实例来动态启动一个Angular应用,然后将 AppModule 作为应用的根模块。这意味着你的Angular应用将从 AppModule 开始加载,并构建整个应用。


让我们来看一个示例,假设有一个名为 AppModule 的Angular模块,它定义了应用的根组件和其他相关配置。这是一个典型的Angular模块定义:


import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
@NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }

在这个示例中,AppModule 使用 @NgModule 装饰器来定义模块,它引入了 BrowserModule 作为依赖,声明了根组件 AppComponent,并将 AppComponent 标记为启动组件(bootstrap 属性)。这意味着 AppComponent 将成为应用的根组件。


在主要的 main.ts 文件中,我们使用 platformBrowserDynamic().bootstrapModule(AppModule) 来启动应用,这将启动整个应用并将 AppComponent 渲染到页面上。


document.addEventListener('DOMContentLoaded', () => {
  platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.log(err));
});

这段代码会在文档加载完成后,启动Angular应用,并且任何错误(如果有)都会被捕获并打印到控制台。


3. 处理启动失败 (.catch(err => console.log(err)))


最后一部分代码是使用 .catch() 方法处理启动过程中可能出现的错误。在实际应用中,启动Angular应用可能会面临各种问题,如依赖注入失败、组件未找到、模块配置错误等。通过使用 .catch() 方法,你可以捕获这些错误并将它们记录到控制台,以便进行调试。


示例代码如下:

document.addEventListener('DOMContentLoaded', () => {
  platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.log(err));
});

在这个示例中,如果在启动应用过程中出现了任何错误,错误信息将被传递给 err,然后使用 console.log() 打印到控制台,以便开发人员可以查看错误详情。


总结来说,这段代码的主要作用是等待文档加载完成后,动态启动Angular应用,并在启动过程中处理可能出现的错误。这是Angular应用的入口点,确保应用在稳定的环境中启动,并为开发人员提供了一种捕获和调试潜在问题的机制。

相关文章
|
26天前
|
存储 前端开发 API
浅谈 Angular 应用前端消息显示机制的一个实际需求
浅谈 Angular 应用前端消息显示机制的一个实际需求
12 0
|
3月前
|
设计模式 JavaScript 前端开发
什么是 Angular 应用里的 Custom provider
什么是 Angular 应用里的 Custom provider
32 2
|
4月前
Angular 应用里异步打开对话框的技术实现
Angular 应用里异步打开对话框的技术实现
22 0
|
26天前
|
存储 JavaScript 前端开发
Angular 应用 node_modules 子文件夹 @types 的作用介绍
Angular 应用 node_modules 子文件夹 @types 的作用介绍
14 1
|
3天前
|
JavaScript 前端开发 开发者
【TypeScript技术专栏】TypeScript在Angular开发中的应用
【4月更文挑战第30天】本文探讨了TypeScript在Angular开发中的应用。Angular与TypeScript的结合利用了静态类型检查和ECMAScript特性,简化了大型Web应用的开发。文章涵盖组件、数据绑定、依赖注入、服务、守卫和路由以及模块化等方面,展示了如何在Angular中有效使用TypeScript。此外,还提到了TypeScript的高级应用,如泛型、高级类型和装饰器。掌握这些知识将有助于提升Angular应用的可维护性和可扩展性。
|
4月前
|
Web App开发 UED 开发者
谈谈企业级 Angular 应用的二次开发 - 基于 Angular Component 替换的 Extensibility 支持案例介绍
谈谈企业级 Angular 应用的二次开发 - 基于 Angular Component 替换的 Extensibility 支持案例介绍
40 1
|
1月前
|
搜索推荐 SEO 缓存
为什么 Angular 服务器端渲染只面向匿名用户,没有用户上下文
为什么 Angular 服务器端渲染只面向匿名用户,没有用户上下文
10 0
|
2月前
|
开发框架 前端开发 安全
Angular:构建现代Web应用的终极选择
Angular:构建现代Web应用的终极选择
25 0
|
3月前
|
Go
Mac安装Angular并部署运行应用
Mac安装Angular并部署运行应用
|
4月前
|
缓存 前端开发 JavaScript
Angular Service Worker 在 PWA 应用 HTTP 交互中扮演的角色
Angular Service Worker 在 PWA 应用 HTTP 交互中扮演的角色
45 0

热门文章

最新文章