Jacky's blog Jacky's blog
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Jacky Wang

行到水穷处,坐看云起时
首页
  • 编码专题
  • 深入浅出 Vite
  • 深入浅出 babel
  • 快速上手API
  • 深入浅出 react
  • Node

    • code-notebook
  • 状态管理

    • redux
  • 前端工程化

    • Wepack
  • React源码

    • React源码
  • 组件库封装

    • 组件库
  • 开发工具

    • Vscode 插件
  • 项目展示
  • 案例中心 (opens new window)
  • First Project
  • 基础算法题
  • 链表题
  • 动态规划
  • 双指针
  • 递归
  • 数据结构
  • 前端学习计划 (opens new window)
  • 技术随笔
  • 转载文章
  • 包管理工具
  • 前端学习周报
  • VSCode插件
  • Promise 专题
  • 函数技巧
  • React 专题
  • 配置文件

    • TSCONFIG-配置 (opens new window)
    • NGINX-配置 (opens new window)
    • 正则规则查询手册 (opens new window)
    • Lint 配置 (opens new window)
  • 教程

    • GIT-教程
    • NPM SCRIPTS-工作流 (opens new window)
    • DOCKER-教程 (opens new window)
    • LERNA-教程 (opens new window)
    • GIT-常用操作整理 (opens new window)
  • VSCode

    • LAUNCH.JSON (opens new window)
  • 指令

    • NPM 指令 (opens new window)
    • NVM 指令 (opens new window)
    • Nginx 指令 (opens new window)
    • YARN 指令 (opens new window)
    • PNPM 指令 (opens new window)
  • 库

    • FS-EXTRA 库 (opens new window)
    • NODE 库-PATH (opens new window)
  • 永远的神

    • 魔法师卡颂-自顶向下学 React 源码 (opens new window)
    • 全栈潇晨 (opens new window)
    • 博客-程序员山月-Daily (opens new window)
    • 淘系前端:冴羽 (opens new window)
  • 系列文章

    • 《图解HTTP》 (opens new window)
    • 《ES6标准入门》 (opens new window)
    • 《现代JavaScript教程》 (opens new window)
    • 《深入浅出Webpack》 (opens new window)
    • VSCode 插件系列:小茗同学 (opens new window)
    • JEST 教程 (opens new window)
    • 前端精读周刊:各种精读系列 (opens new window)
    • 一文吃透系列 (opens new window)
    • 图解 REACT 原理 (opens new window)
  • 实用网站

    • MDN (opens new window)
    • CAN I USE (opens new window)
    • TYPESCRIPT-ESLint-RULES (opens new window)
    • ESLint-RULES (opens new window)
    • FRONT-END TREND (opens new window)
    • NPM TREND (opens new window)
    • 在线分析 Node 依赖 (opens new window)
    • FIND NPM (opens new window)
    • CODE PEN (opens new window)
    • 印记中文 (opens new window)
    • TOOL.LU (opens new window)
    • 阮一峰-网道 (opens new window)
    • DIGITAL OCEAN (opens new window)
    • DEVDOCS.IO (opens new window)
    • JOI (opens new window)
  • 算法

    • 小浩算法 (opens new window)
    • LABULADONG 的算法小抄 (opens new window)
    • 力扣 SOLUTION (opens new window)
    • HACKER RANK (opens new window)
    • 代码随想录 (opens new window)
  • 博客系列

    • 美团大佬 (opens new window)
    • 蜡笔小伟 (opens new window)
    • 优秀博客1 (opens new window)
    • 优秀博客2-umi (opens new window)
    • 优质博客 (opens new window)
  • CSS

    • CSS-EASING 库 (opens new window)
    • ROUGH.JS (opens new window)
    • CSS 网站收集
    • UNOCSS (opens new window)
  • 前端

    • PROMISE (opens new window)
    • UNDERSCORE.JS (opens new window)
    • study with BGM (opens new window)
    • nginx【B站视频】 (opens new window)
    • 机器学习
    • Js基础
  • 掘金已购课程

    • 前端自动化测试精讲 (opens new window)
    • 深入浅出 Vite (opens new window)
    • 现代 Web 布局 (opens new window)
    • 前端算法与数据结构 (opens new window)
    • 基于 Vite 的 SSG 框架开发实战 (opens new window)
    • SSR 实战:官网开发指南 (opens new window)
    • WebGL 入门与实践 (opens new window)
    • 玩转 CSS 的艺术之美 (opens new window)
    • 前端调试通关秘籍 (opens new window)
    • React 进阶实践指南 (opens new window)
    • TypeScript 全面进阶指南 (opens new window)
    • 前端缓存技术与方案解析 (opens new window)
    • npm scripts 前端工作流 (opens new window)
    • Webpack5 核心原理与应用实践 (opens new window)
  • 购物车

    • 张鑫旭-技术写作指南 (opens new window)
    • 深入剖析 Node.js 底层原理 (opens new window)
    • 前端开发者的现代 C++ 课 (opens new window)
    • 从前端到全栈 (opens new window)
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 编码专题

  • 深入浅出 Vite

    • 00-目录大纲
    • 07-Vite预构建上手
    • 09-Esbuild 插件开发实战
    • 10-Rollup 打包基本概念及使用
    • 11-Rollup打包机制及插件开发
    • 12-Vite插件开发实战
    • 13-Vite热更新
    • 16-Vite搭建 SSR 框架
      • 0.前言
      • 1.概念
        • 问题1:CSR 存在的问题,SSR 的优势?
        • 问题2:SSR 的两大生命周期是什么?
      • 2.使用 Vite 搭建 SSR 框架
        • 问题3:基础的 SSR 骨架
        • 问题4:中间件是如何拼接出 html 的?【生产版】
        • 问题5:中间件是如何拼接出 html 的?【开发版】
      • 3.SSR工程化需要关注的点
        • 问题6:如何处理路由?
        • 问题7:如何处理状态?
        • 问题8:需要对 CSR 进行降级?
        • 问题9:如何自定义 Head 头?
        • 问题10:流式渲染?
        • 问题11: SSR 缓存
        • 问题12:性能监控
        • 问题13:如何将 SSR 退化为 SSG?
  • 快速上手 API

  • 深入浅出Babel

  • 深入浅出 React

  • 百问掘金
  • 深入浅出 Vite
wangjiasheng
2023-04-11
目录

16-Vite搭建 SSR 框架

# 0.前言

本节博客代码仓库 (opens new window) ,使用 Vite 从零开始搭建一个 SSR 框架

# 1.概念

# 问题1:CSR 存在的问题,SSR 的优势?

发展历史:

  1. 早期使用的是 JSP 模板语法编写页面,存放在服务端,在服务端填入数据并渲染完整页面,此种做法为天然的服务端渲染。

  2. 随着 Ajax 页面无刷技术,以及各种前端框架 Vue/React ,自此进入到前后端分离时代,此时是由浏览器处理页面的渲染【 Client Side Render 客户端渲染】。

  3. 随着客户端渲染发展到一定阶段,对首屏性能以及 SEO 的要求越来越高, Nuxt/Next 等服务端框架,以及 vuepress/vitepress/island 等 SSG 框架又火起来了。

  4. 现今,前端轮子已经造到了一个极致,每天都有大量的新技术出现,而这些技术的更新是需要成本的,由此产生出各种元框架,国外有 Next/Astro/Remix 等,国内有umi/morden/icejs 等,其核心目的就是帮助完成基础技术选型,提供一揽子服务,由框架层面负责对底层技术的维护及更新,如路由(文件式路由系统)、编译工具(vite/webapck/turbopack....),样式(less/sass/unocss....),状态管理(dva/redux)。开发者只需面向业务开发,在框架约定的特定位置特定文件处进行编码即可,框架会读取这些文件并转化为真实浏览器可执行代码。

CSR 存在的问题:

以下为客户端渲染模式的 HTML 结构:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title></title>
  <link rel="stylesheet" href="xxx.css" />
</head>
<body>
  <!-- 一开始没有页面内容 -->
  <div id="root"></div>
  <!-- 通过 JS 执行来渲染页面 -->
  <script src="xxx.chunk.js"></script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

可以发现没有任何数据,客户端如果先把数据渲染在页面上,需要经过如下两步:

  1. 拉取远端的 js 代码,比如基于 React 编写的页面组件。
  2. 执行代码,拉取远端数据,并通过 React.render 等方式将组件渲染到页面上。

这样做存在的问题:

  1. 首屏加载速度比较慢,在网络不佳的场景下,会有一段空白期。
  2. 对搜索引擎优化不优化,无法被搜索引擎爬虫获取信息。

**SSR 的特性 **

  1. 优点:

    • 解决 CSR 存在的两个问题。

    • 直接返回带有数据的 html 信息,并且在博客等纯静态页面而言,可退化为 SSG 模式。

  2. 缺点:

    • 服务端的压力比较大。
    • 只解决首屏加载问题,但此时不具备交互能力,需要在注水 hydrate 后生效。由于一套代码同时在客户端 + 服务端进行渲染,因此 SSR 应用也被称为 同构应用。

# 问题2:SSR 的两大生命周期是什么?

SSR 应用有两大生命周期:构建时 和 运行时

image-20230411211204064

构建阶段:打包两份代码

  • csr-entry:就是将 React.hydrate 代替原有入口 React.createRoot/render
  • ssr-entry:本质就是暴露出 renderToString 组件,准备两份东西(首屏数据+组件 html 格式)

运行阶段:服务器(案例中使用 express 搭建)

  • 基础:需要准备一份模板 html 代码,后续需通过 ssr-entry 拼接出完整的 html 代码。
  • 实现方案:本案例需要接入 vite 中间件机制,如果是开发阶段,可通过 vite.ssrLoadModule 导入 ssr-entry 代码。若是生产阶段,则直接 require("dist/server/ssr-entry.js") 代码。

详细的实现细节,见后续的代码实践。

# 2.使用 Vite 搭建 SSR 框架

# 问题3:基础的 SSR 骨架

客户端入口:cient-entry.tsx

// 客户端入口文件
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";

/* 注此时,渲染方式不再是 ReactDOM.creatRoot 或 render */
ReactDOM.hydrate(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById("root"),
);
1
2
3
4
5
6
7
8
9
10
11
12
13

服务端入口:ssr-entry.tsx

服务端与客户端入口文件的差异是,服务端只需要提供 html + css ,为后续拼接 html 做原始素材。因此 client-entry 打包后的产物可以移出掉 css 资源文件。

import App from "./App";
import "./index.css";

export function ServerEntry(props: any) {
  return <App />;
}

export async function fetchData() {
  return {
    user: "xxx",
  };
}
1
2
3
4
5
6
7
8
9
10
11
12

基于 Express框架编写的后端服务代码:

import express from 'express';

async function createServer() {
  const app = express();
  
  // 通过中间件的方式嵌入核心处理逻辑
  app.use(await createSsrMiddleware(app));
  
  app.listen(3000, () => {
    console.log('Node 服务器已启动~')
    console.log('http://localhost:3000');
  });
}

createServer();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 问题4:中间件是如何拼接出 html 的?【生产版】

首先讨论生产阶段的代码实践(和仓库中的最终代码有差别),因为生产阶段不需要处理开发阶段的冗余功能(静态服务器、hmr)

将 html 代码=》通过 ssr-entry 中取出 fetchData + renderToString =》拼接出完整的 html =》最后返回给浏览器(res.end(html))

/* 中间件 */
async function createSsrMiddleware(app: Express): Promise<RequestHandler> {
  return async (req, res, next) => {
    try {
      /* 获取 template html */
      const templatePath = path.join(cwd, "dist/client/index.html")
      let template = fs.readFileSync(templatePath, "utf-8");

      /* 浏览器地址:处理如 `localhost:3000/` */
      const url = req.originalUrl;

      /* 传入 vite , 在生产阶段为 null */
      /* 1、加载 ssr-entry.js 文件: */ 
      const { ServerEntry, fetchData } = await import(path.join(cwd, "dist/server/ssr-entry.js"))

      const data = await fetchData();
      const appHtml = renderToString(
        React.createElement(ServerEntry, { data }),
      );

      /* 2、手动模板引擎 */
      const html = template
        .replace("<!-- SSR_APP -->", appHtml)
        .replace(
          "<!-- SSR_DATA -->",
          `<script>window.__SSR_DATA__=${JSON.stringify(data)}</script>`,
        );
      /* 3、通过中间件给客户端返回 html 字符 */
      res.status(200).setHeader("Content-Type", "text/html").end(html);
    } catch (e: any) {
      vite?.ssrFixStacktrace(e);
      console.error("ssr端构建失败:", e);
      res.status(500).end(e.message);
    }
  };
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 问题5:中间件是如何拼接出 html 的?【开发版】

开发版相对于生产版多了很多功能,主要有如下:

  1. 手动创建 vite-dev-server,特别注意的是 express 服务端是通过接入 vite 中间件形式接入的,如app.use(vite.middlewares) 。

    if (!isProd) {
      vite = await (
        await import("vite")
      ).createServer({
        root: cwd,
        server: {
          middlewareMode: true,
        },
        appType: "custom",
      });
    
      /* 将 vite 内部的中间件继承到 express 中间件 */
      app.use(vite.middlewares);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  2. 改造文件获取入口,主要是处理 template html 以及 ssr-entry.tsx 这两部分。

html 的处理

/* html 文件不会发生改变,只是位置挪了下 */
export function resolveTemplatePath() {
  return isProd
    ? path.join(cwd, "dist/client/index.html")
    : path.join(cwd, "index.html");
}
1
2
3
4
5
6

ssr-entry 的处理

export async function loadSsrEntryModule(vite: ViteDevServer | null) {
  if (isProd) {
    /* 生产模式:直接从 dist 取出构建后的 js 文件 */
    const entryPath = path.join(cwd, "dist/server/ssr-entry.js");
    return import(entryPath);
  } else {
    /* 如果是开发模式,代码就在 client-entry下 */
    const entryPath = path.join(cwd, "src/ssr-entry.tsx");
    /* 使用 vite.ssrLoadModule 传入的模块无需打包,vite 帮会自动将依赖构建打包成 js 可运行的文件 */
    return vite!.ssrLoadModule(entryPath);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

开发阶段,这里可以使用 vite.ssrLoadModule 这个 api 导入 ts 模块。

  1. 开发阶段如何处理静态文件,如 css、js 以及图片?

    注意,前面已使用 app.use(vite.middlewares) 接入代码,且自动享有vite-dev-server静态服务器的能力,我们只需要在中间中改造路由即可:

    /* 如浏览器访问: localhost:3000/ 地址 */
    const url = req.originalUrl;
    /* 1. 对于 `/` 路径,则返回 `html` 代码 */
    if (!matchPageUrl(url)) {
      /* 2. 对于 http:localhost:3000/src/csr-entry.js 等静态资源,则 next 接入静态服务器 */
      return await next();
    }
    
    1
    2
    3
    4
    5
    6
    7
  2. 处理 React 的热更新处理。

    /* 若为开发阶段 */
    if (!isProd && vite) {
      /* html 代码由 vite.transformIndexHtml 获取 */
      template = await vite.transformIndexHtml(url, template);
    }
    
    1
    2
    3
    4
    5

    可以观察 vite.transformIndexHtml 构建后的结果

    <head>
    <script type="module">
    import RefreshRuntime from "/@react-refresh"
    RefreshRuntime.injectIntoGlobalHook(window)
    window.$RefreshReg$ = () => {}
    window.$RefreshSig$ = () => (type) => type
    window.__vite_plugin_react_preamble_installed__ = true
    </script>
      <script type="module" src="/@vite/client"></script>
      <meta charset="UTF-8" />
      <link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>Vite App</title>
    </head>
    
    <body>
      <div id="root"><!-- SSR_APP --></div>
      <script type="module" src="/src/client-entry.tsx"></script>
      <!-- SSR_DATA -->
    </body>
    
    </html>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    由于存在 plugin-react 插件,因此通过 vite.transformIndexHtml 会在 html 头部注入 react 热更新相关代码。

# 3.SSR工程化需要关注的点

# 问题6:如何处理路由?

暂时没有代码验证

# 问题7:如何处理状态?

ssr 状态处理的逻辑,在 ssr-entry 中获取数据,将 <!-- SSR_DATA --> 转译为<script>window.__SSR_DATA__=${JSON.stringify(data)}</script>,流程如下:

也就是使用 window 对数据进行共享:

const data = window.__SSR_DATA__;
ReactDOM.hydrate(
    <App data={data} />,
  document.getElementById("root"),
);
1
2
3
4
5

# 问题8:需要对 CSR 进行降级?

在某些比较极端的情况下,我们需要降级到 CSR,也就是客户端渲染。一般而言包括如下的降级场景:

  1. 数据预处理失败,保证客户端也具有获取数据的能力。

    import { fetchData } from "./ssr-entry";
    const data = window.__SSR_DATA__ ?? fetchData();
    
    1
    2
  2. 服务器出错,需要在模板中返回兜底的 CSR 模板,完全降级。

    async function createSsrMiddleware(app: Express): Promise<RequestHandler> {
      return async (req, res, next) => {
        try {
          // SSR 的逻辑省略
        } catch(e: any) {
          vite?.ssrFixStacktrace(e);
          console.error(e);
          // 响应 CSR 模板内容
          const templatePath = path.join(cwd, "dist/client/index.html");
          let template = fs.readFileSync(templatePath, "utf-8");
          res.status(500).setHeader("Content-Type", "text/html").end(template);
        }
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
  3. 当 localhost:3000/?csr 本地开发时,具备跳过 SSR 能力,仅进行 CSR。

    if ("csr" in req.query) {
      // 响应 CSR 模板内容, 在开发模式中,需要 vite.transformIndexHtml 转换后返回。
      const html123 = await vite?.transformIndexHtml(url, template);
      res.status(200).setHeader("Content-Type", "text/html").end(html123);
      return;
    }
    
    1
    2
    3
    4
    5
    6

# 问题9:如何自定义 Head 头?

# 问题10:流式渲染?

ssr 生成 html 也是需要一定时间的,框架需要具备 边渲染边响应 的能力。

在 React 中使用 renderToNodeStream 实现流式渲染的能力,需要将 res.stats().end(html) 改为如下代码:

// 占位符:不会写......后续填坑
1

# 问题11: SSR 缓存

SSR 需要实时返回渲染的 html 页面,需尽可能减少 CPU 密集型操作,具体解决思路就是缓存:

  1. 服务器内存。需额外阅读缓存淘汰方案 lru-cache(基于 LRU 算法)

    如读取文件时,避免重复磁盘:

    // 高阶函数
    function crateMemoReadFile(){
      const fileConentMap = new Map(); // 使用键值对缓存文件的 key
      return async (filePath) => {
        // 1. 先看有没有缓存
        const cacheResult = fileContentMap.get(filePath);
        if(cacheResult) return cacheResult;
        
        // 2. 如果没有缓存,则存进去
        const fileContent = await fs.readFile(filePath);
        fileContentMap.set(filePath, fileContent);
        return fileContent;
      }
    };
    
    // 这里高阶函数,主要目的是:为了使用闭包创建独立的 map 对象
    const memoReadFile = createMemoReadFile(); 
    
    memoReadFile('/filePath');
    // 直接复用缓存
    memoReadFile('/filePath');
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
  2. Redis 数据库缓存。

  3. CDN 对静态资源缓存,可将资源信息提前存到 OSS 服务器上。

# 问题12:性能监控

在实际项目中,会遇到线上新能问题,需要简历一个完整的性能监控机制,常用指标如下:

  1. SSR 产物构建时间。(仓库有示例)
  2. 数据预获取的时间。
  3. 组件渲染的时间。
  4. 服务端接受请求到响应的时间。
  5. SSR 命中缓存情况。
  6. SSR 成功率、错误日志。

具体可见另一篇博客《perf_hooks》 对 node 内置的模块的一个简单上手。

# 问题13:如何将 SSR 退化为 SSG?

具体可见 hello-island-dev 项目代码 (opens new window),此项目为一个博客站点类框架。

此框架在 build 阶段将 md对应的路由基于 mpa 模式生成响应的html(拼接后) ,并通过 island 架构对 SSG 模式进行优化。

编辑 (opens new window)
上次更新: 2023/04/13, 9:04:00
13-Vite热更新
perf_hooks

← 13-Vite热更新 perf_hooks→

最近更新
01
如何理解浏览器的 user agent 用户代理的含义?
11-05
02
浏览器事件循环机制
10-31
03
浏览器页面渲染机制【2023】
10-15
更多文章>
Theme by Vdoing | Copyright © 2020-2023
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式