EncaikWorld
返回博客列表页

渲染方式对比

CSR(客户端渲染)

系统类网页应用渲染方式,即组装页面这个步骤,是在客户端进行,服务端提供异步数据请求的后端服务。

CSR

优点

  1. 适合有大量动态数据的网站,即常规后台等需要 api 获取数据的网站类型
  2. 构建时间短

缺点

  1. 没有良好的 seo(后台网站不对公众开放,不影响)
  2. 页面从请求到首屏时间长
  3. 对部署前端的服务器压力较大

框架

  1. React
  2. Vue
  3. Angular

SSR(服务端渲染)

门户类网站渲染方式

SSR

优点

  1. 有良好 seo
  2. 页面从请求到首屏时间较短,基本页面可以很快展示

缺点

  1. 只适合使用少量动态数据或完全不使用动态数据
  2. 构建时间较长
  3. 对部署前端的服务器压力较大

框架

  1. Next.js(React+Node.js)
  2. Nuxt.js(Vue+Node.js)
  3. Angular universal(Angular+Express.js)
  4. Nest.js(Node.js+Express.js)

SSG(静态网站生成)

博客及文档类网站渲染方式

SSG

优点

  1. 优秀的 seo
  2. 页面从请求到首屏时间极短
  3. 对部署前端的服务器压力小

缺点

  1. 构建时间长
  2. 不能使用动态数据,每次修改都需要重新构建

框架

  1. Vuepress
  2. Jekyll
  3. Gatsby
  4. Hugo

Angular universal 入门

安装

官网链接:https://angular.io/guide/universal

假设当前已有一个标准的 Angular 应用,安装 ssr 则需要先安装一个服务,这里是 express。

ng add @nguniversal/express-engine

安装前后目录对比:

安装前安装后
image.pngimage.pngimage.png

主要分为两部分,一部分是修改了配置,另一部分是创建了 server 端代码

image.png

客户端方面的配置改动,是把原本的应用打包输出放在了二级文件夹下

image.png

服务端则是完全重新生成了一套配置,用于 ssr 项目的开发

image.png

在脚本中添加了 ssr 的开发,运行,打包和预渲染命令

import "zone.js/dist/zone-node";

import { ngExpressEngine } from "@nguniversal/express-engine";
import * as express from "express";
import { join } from "path";

import { AppServerModule } from "./src/main.server";
import { APP_BASE_HREF } from "@angular/common";
import { existsSync } from "fs";

// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
  const server = express();
  const distFolder = join(process.cwd(), "dist/ssr-project/browser");
  const indexHtml = existsSync(join(distFolder, "index.original.html"))
    ? "index.original.html"
    : "index";

  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
  server.engine(
    "html",
    ngExpressEngine({
      bootstrap: AppServerModule
    })
  );

  server.set("view engine", "html");
  server.set("views", distFolder);

  // Example Express Rest API endpoints
  // server.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get(
    "*.*",
    express.static(distFolder, {
      maxAge: "1y"
    })
  );

  // All regular routes use the Universal engine
  server.get("*", (req, res) => {
    res.render(indexHtml, {
      req,
      providers: [{ provide: APP_BASE_HREF, useValue: req.baseUrl }]
    });
  });

  return server;
}

function run(): void {
  const port = process.env["PORT"] || 4000;

  // Start up the Node server
  const server = app();
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`);
  });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = (mainModule && mainModule.filename) || "";
if (moduleFilename === __filename || moduleFilename.includes("iisnode")) {
  run();
}

export * from "./src/main.server";

服务端最核心的则是根目录的 server.ts 文件,这里做了 Angular 页面与 express 服务的绑定,以及 express 服务的启动代码

image.png

image.png

main.ts 和 app.module.ts 都对服务端逻辑创建了一份新文件,当遇到服务端缓存数据或其他类型的需要操控服务端的逻辑,就需要在这里配置。如果只是普通的 ssr 网站,到这一步后则与开发常规 Angular 应用一样,正常开发即可

常见问题

NG-ZORRO 一起使用

官网链接:https://ng.ant.design/docs/universal/zh

文档中有两种方式可以选择,一种较为复杂,需要自己安装 ng-zorro 以后,然后修改服务端文件;另一种则是直接 clone 一个模板项目,修改项目名即可使用。

区分服务端逻辑与客户端逻辑

constructor(
    @Inject(PLATFORM_ID) private platformId: Object
) {}

func() {
  if (isPlatformBrowser(this.platformId)) {
    // do something
  }
}

跨域配置

跨域配置分为两种情况,分别是开发模式和生产模式

开发模式
"dev:ssr": "ng run xw_offical:serve-ssr"

因为 ssr 会有服务端请求和客户端请求同时存在,客户端请求代理即 Angular 的 proxy 文件中配置,而服务端需要在 express 的 server.ts 文件中添加中间件。

import { createProxyMiddleware } from "http-proxy-middleware";

const server = app().use(
  "/api",
  createProxyMiddleware({
    target: "http://ip:port/",
    secure: false,
    logLevel: "debug",
    changeOrigin: true
  })
);
生产模式
"serve:ssr": "node dist/xw_offical/server/main.js"

生产模式会将客户端请求拦截并从服务端发送,因此只需要 server.ts 文件中的中间件即可。