Angular项目实战Angular服务器渲染常遇的坑,这份填坑指南请收好~

Posted 博读代码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Angular项目实战Angular服务器渲染常遇的坑,这份填坑指南请收好~相关的知识,希望对你有一定的参考价值。

前言

由于官网有 SEO 的需求,因此对现在官网的项目进行 ssr 整改兼容。本来在官网的 demo 项目中使用是比较顺畅的,但由于项目较大且复杂性高,在调试的过程中遇到了不少问题,踩了不少的坑。

以下是在调试过程中遇到的一些问题汇总,希望大家可以避开这些坑。


1. 使用浏览器 API报错问题

在运行服务的时候,通常会遇到的一些报错

ReferenceError: window is not defined

或者

ReferenceError: document is not defined

由于 Universal 应用并没有运行在浏览器中,因此该服务器上可能会缺少浏览器的某些 API 和其它能力。

比如,服务端应用不能引用浏览器独有的全局对象,比如 window、document、navigator 或 location,如果直接使用会导致运行的时候出现报错。

因此,我们需要对使用浏览器的 API 方法做好兼容。


方案1:在server.ts,引入domino做兼容

const domino = require(domino);
const win = domino.createWindow(template);
global[window] = win;
global[document] = win.document;
global[CSS] = null;
global[Prism] = null;
global[DOMTokenList] = win.DOMTokenList;
global[Node] = win.Node;
global[Text] = win.Text;
global[htmlElement] = win.HTMLElement;
global[object] = win.object;
global[navigator] = win.navigator;
global[localStorage] = null;
global[sessionStorage] = null;

需要注意的是,domino 并非兼容了所有浏览器的 API,只是兼容了大部分方法。

如果用到的 API 不多,那么可以考虑使用这个方案,但如果属于复杂项目建议还是用下面官方推荐的方法比较好。


方案2:使用Angular官方推荐的方法

通过 PLATFORM_ID 令牌注入的对象来检查当前平台是浏览器还是服务器,从而解决该问题。

若判断是浏览器环境,才执行使用到浏览器方法的代码片段。

不过个人觉得有些麻烦,因为在用到浏览器独有 API 方法的地方都得做引入判断兼容。

import  PLATFORM_ID  from @angular/core;
import isPlatformBrowser, isPlatformServer from @angular/common;

constructor(@Inject(PLATFORM_ID) private platformId: Object) ...

ngOnInit()
if (isPlatformBrowser(this.platformId))
// 浏览器代码
// eg:let url=window.location.href;
...

if (isPlatformServer(this.platformId))
// 服务器代码
...


2. 使用第三方库,例如jq,echart,layer等等报错

ReferenceError: $ is not defined
ReferenceError: layer is not defined

和上面一样,检查当前平台是浏览器还是服务器,执行相应的代码。

import  PLATFORM_ID  from @angular/core;
import isPlatformBrowser, isPlatformServer from @angular/common;

constructor(@Inject(PLATFORM_ID) private platformId: Object) ...

ngOnInit()
if (isPlatformBrowser(this.platformId))
// 浏览器代码
// eg:let userID =$.cookie(userID);
// eg: layer.msg(测试);
...


3. 懒加载路由无法加载问题

由于项目原来是 angular6 版本的,后来升级到了 angular9 后路由写法有所改变,在浏览器模式下是没有问题的。

但安装官方例子改造 ssr 之后加载路由无法正常使用,这里需要把路由改成 v9 版本推荐的写法,修改如下:

修改前


path: doc,
loadChildren: app/lazy-module/doc.module#DocModule

修改后


path: doc,
loadChildren: loadChildren: () => import(./lazy-module/doc.module).then(m => m.DocModule)


4. 打包node版本报错

报错信息如下:

【Angular项目实战】Angular服务器渲染常遇的坑,这份填坑指南请收好~_node

由于把 node 升级到12版本,这个报错是 node12 本身的 bug,官方暂时还没有修复,因此需要把 node 回滚到 10.15.3 版本,版本回滚之后就没有报错了。


5. 响应时间TTFB过长问题

【Angular项目实战】Angular服务器渲染常遇的坑,这份填坑指南请收好~_angular_02

Angular Universal 渲染过程中,如果代码中有一些延迟异步任务,可能会阻塞渲染。

主要包括例如: setTimeout、setInterval、全局调用Observables 等方法的使用。在不取消它们的情况下调用它们,或者让它们运行超过服务器所需的时间可能会导致渲染效果欠佳,从而使得首次加载响应时间过长。

因此,需要检查项目中是否有用到以上提到的方法,如有则需要在用完之后需求做清除。

此外,如果需要执行的异步任务没有必要在服务器端使用的话,可以参考问题2的方式,使用 isPlatformBrowser 跳过服务器执行。


小结

项目越复杂整改难度越大,在调试 universal 服务时需要注意以下几个点:

  1. 服务器没有的 API 或者第三方库需要用 isPlatformBrowser 跳过执行;
  2. 版本升级最好把一些相关的基本写法调整到新版;
  3. 注意 node 版本问题;
  4. 使用定时器需要及时清除;


下期给大家分享更多实战中的点滴,如果大家对此感兴趣,欢迎各位关注、留言,大家的支持就是我的动力!

以上是关于Angular项目实战Angular服务器渲染常遇的坑,这份填坑指南请收好~的主要内容,如果未能解决你的问题,请参考以下文章

angular10预渲染实践笔记

angular10预渲染实践笔记

使用Angular4(Angular Universal)的服务器端渲染[关闭]

Angular 5 Universal 在服务器端渲染期间等待 http 请求返回

webapp技术——angular项目实战视频教程

Angular项目实战Angular5项目模块划分