Angular 服务端渲染 Angular Universal 实例

news2025/1/23 4:06:58

        标准的 Angular 应用运行在浏览器中,它会在 DOM 中渲染页面,以响应用户的操作。 而Angular Universal 会在服务端运行,生成一些静态的应用页面,稍后再通过客户端进行启动。 这意味着该应用的渲染通常会更快,让用户可以在应用变得完全可交互之前,先查看应用的布局。

        本文以蜗牛双语阅读网站为例,说明网站整个SSR改造的过程。

1、创建服务端应用模块

        要创建服务端应用模块 app.server.module.ts,请在项目目录下运行 CLI 命令:

ng add @nguniversal/express-engine

        命令执行后,项目中会添加几个服务端渲染相关的文件

        根目录下会新增 server.ts 和 tsconfig.server.json

        src目录下会新增 main.server.ts 和 app/app.server.module.ts

2、调试运行

        先看一下客户端渲染方式的调试运行效果,使用 npm run start 启动页面,查看浏览器开发工具界面的网络标签页,可以看到请求的localhost网页文档,只是引用了相关的样式和js,并没有后台数据:

        查看预览窗口,也只能看到一个空白的页面:

         再看服务端渲染方式的调试运行效果,使用 npm run dev:ssr 启动页面,查看浏览器开发工具界面的网络标签页,可以看到请求的localhost网页文档已经完成了后台数据的渲染:

         查看预览窗口,可以看到大致的页面内容:

 3、程序发布

        使用指令 npm run build:ssr 生成发布包,发布包包含两个目录,一个browser,一个server

         browser目录下的文件,可以按照普通客户端渲染的方式进行部署。server目录下的文件,可以通过 node main.js 启动服务供浏览器访问。服务运行的端口可以通过配置nodejs运行的环境变量进行设置:

set PORT=8080 && node dist/mysite/server/main.js 

4、预先渲染

        不管是客户端渲染还是服务端渲染的部署方式,对于制定路由的页面都是路由请求后再进行渲染,如果页面内容较多或者渲染逻辑较复杂,就会消耗较长时间,对于网站用户来讲都是不好的体验。为解决这一问题,Angular Universal 提供了预先渲染的方法。

        使用指令 npm run prerender 即可进行预先渲染,prerender 有很多指令参数,通常可以设置按照路由列表文件进行预渲染。修改angular.json文件中prerender节点的routesFile参数可以指定路由列表文件路径:

{
    "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
    ...
    "projects": {
        "woniuyuedu": {
            "projectType": "application",
            ...
            "architect": {
                "build":{ ... },
                ...
                "prerender": {
                    "builder": "@nguniversal/builders:prerender",
                    "options": {
                        "routesFile": "./render-routes.txt"
                    },
                    ...
                }
            }
         }
    }
}

        对应的render-routes.txt文件里只要记录想要预渲染的路由路径就可以了,每个路径占一行:

/
/read/list/1
/read/book/flipped
/read/book/the-kite-runner
...

        此时运行 npm run prerender 就可以完成对配置页面的预渲染。预渲染生成的页面位于browser目录中:

         按照客户端渲染方式部署整个brower目录,对于已经预渲染的路由路径,服务器将自动把预先渲染好的文件返回给浏览器,对于没有预先渲染的路由路径,则由浏览器通过js即时渲染。

5、浏览器API兼容性问题

        顾名思义服务端渲染方式是在服务端完成,因此一些浏览器的 API 或功能将不可用。服务端无法使用浏览器中的全局对象window, document, location, localStorage等。为此Angular 提供了可注入对象,用于在服务端替换对等的对象,可以通过创建一个垫片service完成替换:

import { Injectable, Inject, PLATFORM_ID } from '@angular/core';
import { DOCUMENT, isPlatformBrowser, isPlatformServer } from '@angular/common';

@Injectable({
  providedIn: 'root'
})
export class SSRPolyfillService {

  constructor(
    @Inject(DOCUMENT) private _doc: Document,
    @Inject(PLATFORM_ID) private platformId: any
  ) { }

  // 判断当前运行环境是否是服务端
  isServer(): boolean {
    return isPlatformServer(this.platformId);
  }

 // 判断当前运行环境是否是浏览器端
  isBrowser(): boolean {
    return isPlatformBrowser(this.platformId);
  }

  // 替换全局window对象
  getWindow(): Window | null {
    return this._doc.defaultView;
  }

  // 替换全局location
  getLocation(): Location {
    return this._doc.location;
  }

  // 替换全局document对象
  getDocument(): Document {
    return this._doc;
  }
}

        对于一些需要使用localStorage的逻辑,在对应代码前添加判断,如果是浏览器端才运行相关代码,服务端需跳过。

6、后端数据重复渲染闪烁问题

        因为在服务端已经通过请求后端数据将对应内容完成渲染,到了浏览器端执行JS代码又请求了一次后端数据,对应内容就会重新渲染,从而产生页面闪烁的效果。

        通过上图可以发现浏览器拿到渲染好的localhost页面文档后,又请求了后台接口获取书籍列表。数据返回后重新渲染书籍列表就会出现页面闪烁的效果。

        针对这一问题 Angular 提出了 Transfer State 的方法,将服务端已获取的数据直接迁移到浏览器端,避免因数据的重复请求而出现的页面闪烁。通过在app.server.module.ts 中加入 ServerTransferStateModule 的引入就可以实现 Transfer State 的功能。

import { NgModule } from '@angular/core';
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';

import { AppModule } from './app.module';
import { AppComponent } from './app.component';

@NgModule({
  imports: [
    AppModule,
    ServerModule,
    ServerTransferStateModule
  ],
  bootstrap: [AppComponent],
})
export class AppServerModule {}

        ServerTransferStateModule通过使用http interceptor将后台数据以key-value的形式保存在页面文档中,浏览器请求后台数据时将先判断页面是否包含对应key的迁移数据,没有才进行请求。引入ServerTransferStateModule后再看页面文档,在文档最后就可以发现带有后台请求地址参数详细数据及其返回数据:

7、总结

        Angular Universal 通过后端渲染的方式能够很好的解决搜索引擎抓取不到完整渲染页面的问题。但是页面加载速度比较考验服务器的性能,服务器性能不好服务端渲染也就慢,浏览器拿到文档的时间就长。所以对于服务器条件不好的情况还是采用浏览器端渲染结合常用路由页面预先渲染的方式。可以减少页面加载时间。蜗牛双语阅读网站即是采用这种方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/25996.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一条SQL语句执行的顺序

1. 查询语句 1.1 总体流程 大体来说,MySQL可以分为Server层和存储引擎层两部分。 Server层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务 功能,以及所有的内置函数(如日期、时间、数学和加密函数等)&#xff0…

一篇文章教你如何用Telerik组件为桌面应用添加上下文菜单

Telerik DevCraft包含一个完整的产品栈来构建您下一个Web、移动和桌面应用程序。它使用HTML和每个.NET平台的UI库,加快开发速度。Telerik DevCraft提供最完整的工具箱,用于构建现代和面向未来的业务应用程序。 Telerik DevCraft R3 2022正式版下载 上下…

变化检测(Change Detection,CD) 综述2篇 CD代码 常用CD数据集及链接

变化检测综述 综述1:Change Detection Based on Artificial Intelligence: State-of-the-Art and Challenges 收录于:Remote Sens. 2020 论文地址:Remote Sensing | Free Full-Text | Change Detection Based on Artificial Intelligence:…

客快物流大数据项目(八十七):ClickHouse的使用案例

文章目录 ClickHouse的使用案例 一、电信行业用于存储数据和统计数据使用

ClickHouse教程 — 第二章 ClickHouse快速入门

ClickHouse教程 — 第二章 ClickHouse快速入门1 ClickHouse简介2 ClickHouse特点3 clickhouse缺点4 clickhouse为什么快5 ClickHouse引擎5.1 数据库引擎5.2 数据表引擎5.2.1 表引擎类型5.3 数据类型6 ClickHouse使用语法7 代码操作ClickHouse1 ClickHouse简介 参考1&#xff1…

D. Challenging Valleys

You are given an array a[0…n−1] of n integers. This array is called a “valley” if there exists exactly one subarray a[l…r] such that: 0≤l≤r≤n−1, alal1al2⋯ar, l0 or al−1>al, rn−1 or ar<ar1. Here are three examples: The first image shows…

网页数据抓取工具-数据采集软件

随着时代的不断的发展&#xff0c;我们已经进入一个大数据数字时代&#xff0c;每个人在互联网上都离不开数据的汇总分析以及数据的应用&#xff0c;不管是整理行业的数据&#xff0c;还是分析同行的数据。今天小编就教大家如何用数据采集软件快速抓取到你想要的信息&#xff0…

服务器cpu一直处于100%解决思路

故障描述 网友截图咨询:cpu一直处于100%,请问是什么原因所致?突然出现的,从昨天14点左右就这样子,服务器很卡,将所有网站,web服务,mysql服务,防火墙都停止了,cpu一直处于100%没有改变。 原因分析 cpu突然满载,可能的原因有很多,最常见的就是被cc攻击了导致的。 …

iTOP3399开发板Qt蜂鸣器和LED测试

QLed 测试资料在网盘“iTOP-3399 开发板\iTOP-3399 开发板\02_iTop-RK3399 开发资料汇总&#xff08;不含光盘内 容&#xff09;\05_iTOP-3399 开发板 Qt 应用开发资料\3399 开发板 QT 测试-QtLED 和 buzzer”目录下&#xff0c;我们将要运行 QLed 程序到开发板的 Qt 系统上。参…

【LeetCode每日一题】——118.杨辉三角

文章目录一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【解题思路】七【题目提示】八【时间频度】九【代码实现】十【提交结果】一【题目类别】 数组 二【题目难度】 简单 三【题目编号】 118.杨辉三角 四【题目描述】 给定一个非负整数 n…

CubeMX+VSCode+Ozone的STM32开发工作流(三)利用Ozone进行可视化调试和代码分析

neozng1hnu.edu.cn 本教程的示例代码是笔者参加RoboMaster机甲大师赛为机器人编写的控制器框架&#xff0c;你可以直接克隆仓库&#xff0c;阅读仓库下的Markdown文档获得更好的体验&#xff0c;记得点一个小⭐&#xff1a;basic_framework: basic_framework (gitee.com)所有安…

[附源码]Python计算机毕业设计宠物领养系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Word处理控件Aspose.Words功能演示:使用 Python 查找和替换 Word 文档中的文本

很多时候&#xff0c;您需要替换 Word 文档中的特定文本或短语。MS Word 具有针对此类情况的内置功能&#xff0c;您可以一键替换所需的文本。在本文中&#xff0c;您将学习如何使用 Python 以编程方式查找和替换 Word 文档中的文本。当您需要替换一堆文档中的文本时&#xff0…

Redis的分布式锁问题(九)Redis + Lua 脚本实现分布式锁

Redis的分布式锁问题&#xff08;九&#xff09;Redis Lua 脚本实现分布式锁 上集回顾 Lua的简单介绍 redis调用函数 set name jack set name Rose&#xff0c;再执行get name redis的 EVAL 命令 Lua脚本解决unLock业务流程 代码实现 unLock.lua RedisTemp…

APS自动排程在制药行业的应用

制药企业&#xff0c;作为流程工业的一种&#xff0c;既有流程工业所具有的共性也有特殊性&#xff0c;通过发酵、萃取、灌装等生物、化学、物理变化&#xff0c;产生新物质达到增值目的。通常以批量或连续的方式进行生产&#xff0c;需要严格的过程控制和安全措施。生产运行、…

hadoop 3.x大数据集群搭建系列4-安装Spark

文章目录一. 下载spark和scala并解压二. 配置SPARK_HOME环境变量三. 修改配置3.1 修改 spark-defaults.conf3.2 修改spark-env.sh3.3 修改slaves文件四. 将spark目录分发到其他节点五. 启动Spark集群六. 在web界面查看Spark UI七. 测试八. Yarn模式8.1 启动hdfs、yarn服务8.2 修…

笔记本电脑自带录屏吗?笔记本电脑怎么录屏

​现如今&#xff0c;电脑屏幕录制功能越来越成为我们生活中不可或缺的一部分。比如网课录制、游戏画面、软件教程等。很多小伙伴可能知道笔记本电脑拥有录屏功能&#xff0c;但是却不知道笔记本电脑怎么录屏。下面就让小编详细介绍一下&#xff0c;笔记本电脑录屏的方法。 一&…

骨传导耳机的利与弊有哪些?骨传导耳机到底好不好?

耳机大家应该都知道&#xff0c;但是骨传导耳机&#xff0c;大家或许就有点陌生了。目前网上也有很多关于骨传导耳机的科普和盘点&#xff0c;但是个人认为都过于零碎不明了&#xff0c;而我今天就用我玩骨传导耳机好几年的经验和知识&#xff0c;以骨传导用户的角度写了这篇文…

接口幂等设计

文章目录如何设计幂等幂等设计的基本流程建防重表根据状态机分布式锁获取 token如何设计幂等 既然这么多场景需要考虑幂等&#xff0c;那我们如何设计幂等呢&#xff1f; 幂等意味着 一条请求的唯一性。不管是你哪个方案去设计幂等&#xff0c;都需要一个全局唯一的ID&#xf…

【第五部分 | JS WebAPI】2:DOM 元素操作

目录 1-1 改变元素内容&#xff08;去除html和空格换行&#xff09; 1-2 改变元素内容&#xff08;保留html和空格换行 用的最多&#xff09; 1-3 获取元素的内容 [ 更多其它可操作的元素属性 ] 2-1 修改元素的属性 2-2 修改表单元素属性 2-3 使用this指向函数调用者 3…