当前位置: 首页 > news >正文

Angular 客户端渲染时,从 ng-state 里读取 SSR 状态的具体逻辑

Angular 在客户端启动时,如何检测页面中的 ng-state 标签并从中读取 JSON 对象,进而还原应用的状态,是服务器端渲染(SSR)与客户端渲染(CSR)无缝衔接的核心环节之一。为了理解这个过程,我们可以结合 Angular 的源代码深入探讨这个过程,并通过实例说明它是如何工作的。

Angular 如何检测 ng-state

Angular 提供了 TransferState 服务来管理客户端和服务器之间的状态传递。这个服务允许在服务器端渲染时保存状态,并在客户端渲染时读取这些状态。这意味着,Angular 在服务器端已经生成并渲染好的数据可以通过 ng-state 标签传递给客户端。客户端在启动时,会检测页面中是否存在这个 ng-state 标签,并将其还原为应用的状态。

TransferState 的工作原理

TransferState 服务的工作流程非常关键,它的目的是从服务端传递状态给客户端。Angular 使用的机制是,通过 @angular/platform-browser 中的 TransferState 类和 makeStateKey 方法,将状态数据从服务端嵌入页面的 ng-state 脚本标签中,而客户端应用通过 TransferState 服务读取这些状态。

TransferState 服务的核心方法有两个:

  1. get:在客户端启动时,用来从 ng-state 标签中获取序列化的状态数据。
  2. set:在服务器端渲染时,将状态数据保存到 ng-state 标签中。
源代码分析

通过分析 Angular 的 TransferState 服务源代码,我们可以看到状态是如何在客户端被恢复的。关键代码如下:

import { Injectable, InjectionToken } from '@angular/core';
import { DOCUMENT } from '@angular/common';@Injectable()
export class TransferState {private store: { [key: string]: any } = {};constructor(@Inject(DOCUMENT) private document: Document) {}get<T>(key: string, defaultValue: T): T {return this.store[key] || defaultValue;}set<T>(key: string, value: T): void {this.store[key] = value;}hasKey<T>(key: string): boolean {return this.store.hasOwnProperty(key);}initialize() {const script = this.document.getElementById('ng-state');if (script && script.textContent) {try {this.store = JSON.parse(script.textContent);} catch (e) {console.error('Could not parse ng-state', e);}}}
}

TransferState 服务在客户端启动时会调用 initialize 方法,这个方法的核心是通过 document.getElementById('ng-state') 查找 HTML 页面中的 ng-state 脚本标签,并解析其中的 JSON 数据。JSON.parse(script.textContent) 会将 ng-state 标签中的序列化数据解析为 JavaScript 对象,并存储在 store 中。

当客户端应用需要状态时,get 方法会从 store 中读取状态。这样,Angular 应用就能够在客户端启动时继续使用服务器端已经准备好的状态,避免了重复请求数据。

具体的例子

可以通过一个具体的案例来详细说明这个过程。例如,一个新闻网站的主页,服务器端会预先加载新闻列表,并将其嵌入到 ng-state 标签中。我们希望客户端应用能够直接读取这些新闻数据,而不必再次向服务器发起请求。

  1. 服务器端渲染新闻数据

在服务器端渲染过程中,Angular 通过 HttpClient 请求新闻 API,并将获取的新闻列表存储在 TransferState 中。这个过程通常在 ngOnInit 钩子中完成:

export class NewsComponent implements OnInit {news: News[];constructor(private http: HttpClient, private transferState: TransferState) {}ngOnInit() {const newsKey = makeStateKey<News[]>('news');const newsFromState = this.transferState.get(newsKey, null);if (newsFromState) {this.news = newsFromState; // 从服务端状态中获取数据} else {this.http.get<News[]>('https://api.news.com/latest').subscribe((data) => {this.news = data;this.transferState.set(newsKey, data); // 设置服务端的状态});}}
}

在服务器端,TransferState 将新闻列表序列化并嵌入到 HTML 响应的 ng-state 标签中,生成如下的 HTML:

<script id="ng-state" type="application/json">
{"news": [{ "id": 1, "title": "新闻标题1", "content": "内容1" },{ "id": 2, "title": "新闻标题2", "content": "内容2" }]
}
</script>
  1. 客户端读取状态

客户端 Angular 应用在启动时,会自动通过 TransferState 服务检测并解析 ng-state 中的 JSON 数据。代码如下:

export class NewsComponent implements OnInit {news: News[];constructor(private http: HttpClient, private transferState: TransferState) {}ngOnInit() {const newsKey = makeStateKey<News[]>('news');const newsFromState = this.transferState.get(newsKey, null);if (newsFromState) {this.news = newsFromState; // 从 ng-state 中恢复数据} else {this.http.get<News[]>('https://api.news.com/latest').subscribe((data) => {this.news = data;});}}
}

当客户端启动时,TransferStateinitialize 方法会读取 ng-state 标签中的 JSON 数据,并将其存储在 store 对象中。当 NewsComponent 初始化时,transferState.get(newsKey, null) 会尝试从 store 中读取新闻列表数据。如果数据存在,组件就直接使用这些数据,而不需要再发起 API 请求。

优势和应用场景

通过这种机制,Angular 能够在客户端启动时直接使用服务器端渲染的状态数据,大大提高了应用的加载速度,并减少了网络请求。对于那些数据量较大的应用,尤其是用户首屏展示需要大量数据时,这个机制显著提升了性能和用户体验。

1. 加速首屏渲染

在实际项目中,用户通常希望网站能够快速加载并显示内容。通过 ng-state 标签,Angular 在服务器端已经加载好的数据会立即显示给用户,避免了页面白屏或内容延迟加载的问题。

2. 减少不必要的 API 请求

在传统的客户端渲染中,应用启动时可能会发起多个 API 请求,导致额外的网络流量。而通过 ng-state,这些请求只需在服务器端完成,客户端只需读取已经存在的数据,减少了不必要的重复请求。

状态还原的挑战

虽然 ng-stateTransferState 提供了极大的便利,但在实际开发中,也可能面临一些挑战。比如,如何管理状态的一致性以及如何优化大数据量的传输。

状态一致性问题

在客户端和服务器端的应用状态可能存在不一致的情况,尤其是在用户交互过程中。如果服务器端渲染的状态与客户端应用的逻辑不一致,可能会导致页面错乱或者数据冲突。因此,在开发过程中,保持状态的一致性至关重要。

大数据量优化

对于某些需要处理大量数据的应用,例如包含复杂表格或者图表的分析工具,序列化的大数据量可能会导致 HTML 响应体过大,影响页面的加载速度。为了解决这个问题,开发者可以采取以下优化措施:

  • 分页加载数据:仅传递当前页面所需的数据,减少一次性传递的大数据量。
  • 懒加载:使用懒加载策略,在客户端应用启动后再动态加载次要数据,减轻服务器端渲染的压力。

总结

Angular 在客户端启动时通过 TransferState 服务从 ng-state 标签中读取状态的机制,是 SSR 与 CSR 平滑衔接的关键。通过这一机制,应用可以避免重复请求数据,提升用户体验。结合源代码分析和实际应用案例,我们深入了解了 Angular 的状态传递与还原过程。

相关文章:

Angular 客户端渲染时,从 ng-state 里读取 SSR 状态的具体逻辑

Angular 在客户端启动时&#xff0c;如何检测页面中的 ng-state 标签并从中读取 JSON 对象&#xff0c;进而还原应用的状态&#xff0c;是服务器端渲染&#xff08;SSR&#xff09;与客户端渲染&#xff08;CSR&#xff09;无缝衔接的核心环节之一。为了理解这个过程&#xff0…...

C++的联合体union

联合体有点像class类型或者struct类型&#xff0c;只不过它一次只占用一个成员的内存。 通常我们有一个结构体&#xff0c;声明了4个浮点数&#xff0c;那么结构体中就有4*416字节。当我们不断向类或者结构体中添加成员时&#xff0c;其大小也会不断增大。 union只有一个成员…...

JavaScript 中的变量作用域

JavaScript 中的变量作用域 在 JavaScript 中&#xff0c;理解变量作用域是非常重要的&#xff0c;它决定了变量的可见性和生命周期。本文将深入探讨 JavaScript 中的变量作用域&#xff0c;帮助你更好地掌握这一关键概念。 一、什么是变量作用域&#xff1f; 变量作用域指的…...

【C++】二叉搜索树+变身 = 红黑树

&#x1f680;个人主页&#xff1a;小羊 &#x1f680;所属专栏&#xff1a;C 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言一、定义与性质二、红黑树节点的定义三、新增节点插入四、验证红黑树五、AVL树和红黑树比较 前言 本文仅适合了…...

万界星空科技MES数据集成平台

制造执行系统MES作为连接企业上层ERP系统和现场控制系统的桥梁&#xff0c;承担了实时数据采集、处理、分析和传递的重要任务。MES数据集成平台是一个集成各类数据源&#xff0c;将数据进行整合和统一管理的系统&#xff0c;通过提供标准化接口和协议&#xff0c;实现数据的无缝…...

Ajax和axios简单用法

Ajax Ajax&#xff08;Asynchronous JavaScript And XML&#xff0c;异步的JavaScript和XML&#xff09;。 作用是&#xff1a; 数据交换&#xff1a;通过Ajax可以给服务器发送请求&#xff0c;并获取服务器响应的数据。异步交互&#xff1a;可以在不重新加载整个页面的情况…...

Chillax2024.08.01 |免费的白噪音软件

支持多种声音叠加&#xff0c;单独调整音量&#xff0c;定时功能&#xff0c;完全免费。 大小&#xff1a;13.5M 百度网盘&#xff1a;https://pan.baidu.com/s/1dWpdYoO1bPCnHR1bXpTZEg?pwdolxt 夸克网盘&#xff1a;https://pan.quark.cn/s/89dc88c56e26 移动网盘&#xff…...

Python自动化办公:从Excel到PDF生成的全流程

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在现代办公环境中,数据处理和报表生成是日常工作中非常重要的一环。Python作为一门灵活且功能强大的编程语言,能够通过一系列开源库实现办公自动化。本文将详细讲解如何使用Python实现从Excel数据处理到生成PDF…...

allegro 不同页面相同网路的连接

一、cadence学习笔记&#xff08;1&#xff09;-原理图库制作 绘制好各个界面 放置OFFPAGE 绘制好单个界面是这个样子的&#xff0c;并将剩下的界面进行相同的操作 所有界面完成后&#xff0c;进入设计界面 右键design1.dsn选择Annotate… 点击OK后可以看到WiFi界面OFFPAGE旁边…...

医院管理新趋势:Spring Boot技术引领

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…...

Java 新手教程!面向对象设计一口气讲完![]~( ̄▽ ̄)~*(中)

目录 Java 内部类 Java面向对象的设计 - Java 内部类 什么是内部类&#xff1f; 例子 使用内部类的优点 访问局部变量的限制 内部类和继承 内部类中没有静态成员 生成的内部类的类文件 静态上下文中的内类 Java 内部类类型 Java面向对象设计 - Java内部类类型 成员内…...

驰骋低代码功能升级 - 实体功能权限控制

1. 权限控制升级概述 新增功能&#xff1a;对新建、保存、删除、归档、撤销归档等操作的按钮进行精细化的权限控制。展示位置&#xff1a;这些权限控制体现在查询页面和实体卡片页面的工具栏按钮上。 2. 权限控制方式 新建 0. 不控制&#xff1a;任何人都可以新建。1. 指定岗…...

Matlab|考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化

目录 1 主要内容 2 部分程序 3 程序结果 4 下载链接 1 主要内容 该程序复现《考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化》&#xff0c;主要内容&#xff1a;“双碳”背景下&#xff0c;为提高能源利用率&#xff0c;优化设备的运行灵活性&#xff0c;进一步降低…...

Midjourney零基础学习

Midjourney学习笔记TOP01 什么是AI艺术 AI艺术指的是使用AI技术创作的艺术作品&#xff0c;包括AI诗歌、AI音乐、AI绘画等多种艺术表现形式&#xff1b;AI艺术可以被视为计算机程序与人类合作创作作品&#xff1b;除了Midjourney&#xff0c;比较流行的AI图像生成工具还有Stab…...

词嵌入(Word Embedding)之Word2Vec、GloVe、FastText

简介&#xff1a;个人学习分享&#xff0c;如有错误&#xff0c;欢迎批评指正。 词嵌入&#xff08;Word Embedding&#xff09;是一种将词语映射到低维稠密向量空间的技术&#xff0c;能够捕捉词与词之间的语义关系。Word2Vec、GloVe、FastText 是常见的词嵌入方法&#xff0c…...

Vue82 路由器的两种工作模式 以及 node express 部署前端

笔记 对于一个url来说&#xff0c;什么是hash值&#xff1f;—— #及其后面的内容就是hash值。hash值不会包含在 HTTP 请求中&#xff0c;即&#xff1a;hash值不会带给服务器。hash模式&#xff1a; 地址中永远带着#号&#xff0c;不美观 。若以后将地址通过第三方手机app分享…...

[C#]使用纯opencvsharp部署yolov11-onnx图像分类模型

【官方框架地址】 https://github.com/ultralytics/ultralytics.git 【算法介绍】 使用纯OpenCvSharp部署YOLOv11-ONNX图像分类模型是一项复杂的任务&#xff0c;但可以通过以下步骤实现&#xff1a; 准备环境&#xff1a;首先&#xff0c;确保开发环境已安装OpenCvSharp和必…...

【机器学习-无监督学习】概率图模型

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…...

每日学习一个数据结构-AVL树

文章目录 概述一、定义与特性二、平衡因子三、基本操作四、旋转操作五、应用场景 Java代码实现 概述 AVL树是一种自平衡的二叉查找树&#xff0c;由两位俄罗斯数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明。想了解树的相关概念&#xff0c;请点击这里。以下是对AVL树的…...

课堂点名系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;论坛信息管理&#xff0c;基础数据管理&#xff0c;课程信息管理&#xff0c;课程考勤管理&#xff0c;轮播图信息 微信端账号功能包括&#xff1a;系统首页&#xff0c;论坛信…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

OPENCV形态学基础之二腐蚀

一.腐蚀的原理 (图1) 数学表达式&#xff1a;dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一&#xff0c;腐蚀跟膨胀属于反向操作&#xff0c;膨胀是把图像图像变大&#xff0c;而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...

IP如何挑?2025年海外专线IP如何购买?

你花了时间和预算买了IP&#xff0c;结果IP质量不佳&#xff0c;项目效率低下不说&#xff0c;还可能带来莫名的网络问题&#xff0c;是不是太闹心了&#xff1f;尤其是在面对海外专线IP时&#xff0c;到底怎么才能买到适合自己的呢&#xff1f;所以&#xff0c;挑IP绝对是个技…...

【Go语言基础【12】】指针:声明、取地址、解引用

文章目录 零、概述&#xff1a;指针 vs. 引用&#xff08;类比其他语言&#xff09;一、指针基础概念二、指针声明与初始化三、指针操作符1. &&#xff1a;取地址&#xff08;拿到内存地址&#xff09;2. *&#xff1a;解引用&#xff08;拿到值&#xff09; 四、空指针&am…...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中&#xff0c;可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中&#xff0c;必须做到&#xff1a; &#x1f50d; 追踪每一条 SQL 的生命周期&#xff08;从入口到数据库执行&#xff09;&#…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...