头图

Angular 服务器端预渲染(Server Prerendering):构建更快速、更友好的Web应用

Angular是一种强大的前端框架,用于构建现代Web应用程序。然而,随着应用规模的增长,性能问题也可能随之而来。为了提高Angular应用的性能和用户体验,开发人员可以采用各种技术和方法。其中之一就是服务器端预渲染(Server Prerendering),本文将详细介绍这一概念,并提供示例来说明其用途和优势。

什么是服务器端预渲染?

服务器端预渲染,通常简称为“SSR”(Server-Side Rendering),是一种用于改善Web应用性能和搜索引擎优化(SEO)的技术。它与传统的客户端渲染(Client-Side Rendering)相对立。在客户端渲染中,应用的初始化和渲染发生在用户的浏览器中,而在服务器端预渲染中,应用的初始化和部分渲染发生在服务器上。这意味着在将HTML发送到浏览器之前,服务器会在后端执行一些Angular应用的初始渲染工作,以生成已渲染的HTML页面。这个HTML页面可以直接呈现给用户,而不需要等待JavaScript加载和执行。

服务器端预渲染的主要优势在于:

  1. 更快的加载时间:由于服务器在后端执行初始渲染,用户可以更快地看到内容。这对于提高用户体验至关重要,尤其是在低带宽或性能有限的设备上。
  2. SEO友好:搜索引擎爬虫可以更容易地索引和理解已经渲染的HTML页面,从而提高了应用在搜索结果中的排名。这对于在线业务来说非常重要。
  3. 更好的性能:减少了客户端渲染所需的工作量,使浏览器可以更快地加载和交互。这对于减少跳出率和提高用户留存率非常有帮助。

下面,我们将通过一个具体的示例来演示服务器端预渲染是如何工作的。

示例:Angular服务器端预渲染

在这个示例中,我们将使用Angular框架和Angular Universal库来实现服务器端预渲染。假设我们正在构建一个博客网站,其中包含文章列表和文章详细信息页面。

步骤1:创建Angular应用

首先,我们创建一个基本的Angular应用。我们可以使用Angular CLI来快速生成项目骨架:

ng new my-blog-app

步骤2:安装Angular Universal

接下来,我们需要安装Angular Universal库,它将帮助我们实现服务器端预渲染。在项目根目录下执行以下命令:

ng add @nguniversal/express-engine

这个命令会自动为我们配置Angular Universal,并生成服务器端渲染所需的文件和代码。

步骤3:创建预渲染路由

在Angular Universal中,我们需要定义哪些路由应该在服务器端进行预渲染。例如,我们可能希望文章详细信息页面在服务器端进行预渲染,以提供更快的加载时间和SEO优化。我们可以通过编辑src/app/app.server.module.ts文件来定义预渲染路由:

// app.server.module.ts

import { NgModule } from '@angular/core';
import { ServerModule, ServerTransferStateModule } from '@angular/platform-server';
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';

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

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

步骤4:创建预渲染服务

接下来,我们创建一个预渲染服务,该服务将在服务器端执行初始渲染并生成HTML页面。我们可以在src/app/prerender.service.ts文件中创建这个服务:

// prerender.service.ts

import { Injectable } from '@angular/core';
import { renderModuleFactory } from '@angular/platform-server';
import { enableProdMode } from '@angular/core';
import { APP_BASE_HREF } from '@angular/common';
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';

import { AppServerModule } from './app.server.module';

enableProdMode();

@Injectable({ providedIn: 'root' })
export class PrerenderService {
  public async render(params: any): Promise<RenderResult> {
    const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = AppServerModule;
    const options = {
      document: params.data.originalHtml,
      url: params.url,
      extraProviders: [
        { provide: APP_BASE_HREF, useValue: params.baseUrl },
        { provide: 'BASE_URL', useValue: params.origin + params.baseUrl },
      ],
    };

    const renderModule = createServerRenderer((moduleFactory: any) => {
      return renderModuleFactory(moduleFactory, options);
    });

    return new Promise<RenderResult>((resolve, reject) => {
      renderModule(AppServerModuleNgFactory, {
        document: params.data.originalHtml,
        url: params.url,
      }).then((html: string) => {
        resolve({ html });
      }, reject);
    });
  }
}

步骤5:使用预渲染服务

最后,我们在服务器端路由处理程序中使用预渲染服务来处理特定路由。这是一个示例Express.js路由处理程序的代码:

// server.ts

import 'zone.js/dist/zone-node';
import 'reflect-metadata';

import { enableProdMode } from '@angular/core';
import * as express from 'express';
import { ngExpressEngine } from '@nguniversal/

express-engine';
import { join } from 'path';
import { AppServerModule } from './src/main.server';

enableProdMode();

const app = express();

app.engine(
  'html',
  ngExpressEngine({
    bootstrap: AppServerModule,
  })
);

app.set('view engine', 'html');
app.set('views', join(__dirname, 'dist/browser'));

app.get('*.*', express.static(join(__dirname, 'dist/browser')));

app.get('*', (req, res) => {
  // 使用预渲染服务处理请求
  const prerenderService = new PrerenderService();
  prerenderService
    .render({
      url: req.url,
      baseUrl: '/',
      origin: req.protocol + '://' + req.get('host'),
      data: {
        originalHtml: '',
      },
    })
    .then(({ html }) => {
      res.render('index', { req, res, html });
    })
    .catch((error) => {
      console.error(error);
      res.status(500).send('Internal Server Error');
    });
});

app.listen(3000, () => {
  console.log(`Server is listening on port 3000`);
});

在上述代码中,我们使用Angular Universal的ngExpressEngine来处理路由,并在需要预渲染的路由上使用PrerenderService

预渲染(Prerendering)是一种在服务器端对页面进行渲染的技术,它可以在用户请求页面时快速提供完全渲染的页面,以提高用户体验和搜索引擎优化(SEO)。

对于 Angular 或其他单页应用(SPA),它们在客户端进行大量的 JavaScript 运行以呈现页面。这意味着,当用户首次请求页面时,他们需要下载大量的 JavaScript,然后浏览器才能开始渲染页面。这会导致用户在看到完全渲染的页面之前需要等待一段时间,这可能会对用户体验产生负面影响。此外,许多搜索引擎的爬虫在索引页面时可能无法完全执行 JavaScript,这可能会导致页面的 SEO 排名降低。

预渲染可以解决这些问题。当我们在服务器端进行预渲染时,我们在用户请求页面时已经生成了完全渲染的 HTML。这意味着用户可以立即看到完全渲染的页面,而不需要等待 JavaScript 在客户端运行。此外,预渲染的页面对搜索引擎的爬虫更友好,因为它们可以直接读取已渲染的 HTML,而无需执行 JavaScript。

以 Angular Universal 为例,它是 Angular 的一个关键部分,用于在服务器端进行预渲染。以下是一个基本的 Angular Universal 预渲染的示例:

首先,我们需要安装 Angular Universal 的依赖项:

ng add @nguniversal/express-engine

这将为我们的 Angular 项目添加必要的服务器端渲染(SSR)支持。

然后,在我们的 Angular 服务中,我们可以使用 renderModuleFactory 函数来预渲染我们的应用。这个函数将 Angular 模块工厂和一些选项作为参数,并返回一个已渲染的 HTML 字符串。

import { renderModuleFactory } from '@angular/platform-server';
import { AppServerModuleNgFactory } from './app/app.server.module.ngfactory';

const html = await renderModuleFactory(AppServerModuleNgFactory, {
  document: '<app-root></app-root>',
  url: '/'
});

在这个例子中,AppServerModuleNgFactory 是我们应用的服务器端版本,document 是我们要预渲染的 HTML 模板,url 是我们要渲染的路由。

当我们的服务器接收到用户的请求时,它可以运行上述代码来生成预渲染的 HTML,然后将这个 HTML 发送给用户。用户的浏览器可以立即显示这个 HTML,然后在后台下载和运行 JavaScript,以便用户可以与页面进行交互。

结论

服务器端预渲染是一种强大的技术,可以显著改善Angular应用的性能和SEO。通过将初始渲染移到服务器端,我们可以实现更快的加载时间、更好的用户体验和更好的搜索引擎排名。虽然设置服务器端预渲染可能需要一些额外的工作,但它可以带来巨大的回报,特别是对于那些需要处理大量内容和重要SEO的Web应用。

希望本文提供了对Angular服务器端预渲染的详细理解,并通过示例演示了如何实施它。无论是构建博客网站还是复杂的企业应用,服务器端预渲染都是一个值得考虑的选项,可以显著提升用户体验和应用性能。如果您使用Angular构建Web应用,不妨考虑采用服务器端预渲染来优化您的应用。


注销
1k 声望1.6k 粉丝

invalid