LabHub
学习 学习路径 课程

SSR — 服务端先画

为什么要在服务器上渲染

在 LabHub 中继续学习

一句话总结

SSR是让第一个响应字节中包含内容的事情。作为回报,会产生生成字符串的代码,在那里会出现XSS和水合不一致。

流程图: 第一个响应字节中包含内容 · 没有内容。 · 搜索引擎和链接预览 · 慢速设备·网络

有什么不同

客户端渲染(CSR)的第一响应是这样的。

<body><div id="root"></div><script src="/app.js"></script></body>

**没有内容。**浏览器收到JS,执行,调用API,然后才出现文字。这段时间用户看到的是空白屏幕。而且阅读HTML不仅仅是人类。

SSR在第一次响应中包含完整的HTML。然后JavaScript到达,只在已经存在的DOM上附加事件——这就是激活

不是重新画,而是把手伸进已经画好的东西里

所以产生的问题1 — XSS

服务器渲染最终是字符串拼接

`<h1>${user.name}</h1>`

user.name这个<img src=x onerror=alert(1)>这样的话就直接执行了。必须亲自做前端框架代替阻止的事情。

在HTML正文中插入时必须更改的五个字。

&  →  &amp;      (먼저 바꿔야 한다)
<  →  &lt;
>  →  &gt;
"  →  &quot;
'  →  &#39;

&如果不先换的话已经制作好的&lt;&amp;lt;又变得一团糟。顺序是规则的一部分。

而且**每个位置的规则都不一样。**HTML正文、属性值、URL、JavaScript字符串、CSS都要求不同的逃避字符。没有一个函数可以全部用一个。

问题2 — 种植初始状态时

服务器已经把数据拿来了,所以客户端不需要再调用。所以植入HTML。

<script>window.__STATE__ = {"name":"...</script><script>alert(1)</script>"}</script>

**在数据中</script>如果包含的话,脚本标签就结束了。**之后是新的脚本。虽然是JSON,所以感觉很安全,但完全不是。

阻止的方法。

JSON.stringify(state).replace(/</g, '\u003C')

<将转换为Unicode escape字符时,JSON值保持不变,标签也不会断开。<!--<script也一起被堵住了。

更安全的办法是放在未执行的标签中。

<script type="application/json" id="state">{...}</script>

type这个application/json如果这样,浏览器将无法运行。在客户端中JSON.parse(el.textContent)读作罗。但是</script>仍然需要避难所。

问题3 — 水合不一致

是最难找到的种类。

**服务器生成的HTML和客户端第一次生成的HTML没有必要不同。**如果不同,框架会发出警告,最坏的情况是把那个部分重新画一遍。

原因几乎总是这四个中的一项。

  1. 时间new Date().toLocaleString(). 服务器绘制的视图和客户端绘制的视图不同
  2. 随机数Math.random()制作的id
  3. 本地化·时区 — 服务器为UTC,浏览器为KST
  4. 浏览器专用值window.innerWidthlocalStorage

**渲染函数必须是纯粹的。**相同的输入,相同的输出。

如果需要时间或随机数,**在服务器上生成,放入状态中发送下来。**不调用在渲染函数中。在这个练习的第5阶段,评分器Date.nowMath.random更换后再渲染两次进行比较——在渲染中呼叫它们就会立即显示出来。

流媒体SSR

如果制作完整个页面后发送的话,最慢的数据之一会抓住全部。

[셸: <head>, 스타일, 상단바]  ← 즉시 보낼 수 있다
[본문: DB 조회 300ms]        ← 이것 때문에 셸까지 늦어진다

流媒体首先发送一个头部,然后发送准备好的片段。浏览器会按照接收到的顺序解析,因此开始更早地接收CSS和字体。

这就是React的renderToPipeableStream<Suspense>是做的事情。和前面在SSE实训中看到的原理一样,也像屏蔽一样——代理缓冲和gzip。

现金——SSR中最危险的地方

SSR页面容易混淆**个性化内容。**根据登录名、购物车数量、权限的不同,会有不同的菜单。

对于那个回答Cache-Control: public如果发生这种情况,**CDN会把A的页面交给B。**这是实际发生的事故,发现时已经太晚了。

익명·동일한 페이지   Cache-Control: public, s-maxage=60, stale-while-revalidate=300
로그인 사용자 페이지  Cache-Control: private, no-store

**基本值private, no-store所以,选择只公开可以公开的,然后打开比较安全。**相反的话,总有一天会漏出来。

什么时候不是SSR呢

没有必要全部做SSR。

SSR会花服务器费用。每次请求都会花费渲染费用。如果没有缓存,流量就会直接占用CPU。选择的标准不是“是否快”,而是“第一页HTML必须有内容吗”

现场—在打开SSR之前要确定三件事

如果不一起决定粘贴的日子,以后一定会被咬到的。

这三者不是代码,而是决定。如果不提前决定,在出现障碍的日子里会当场决定。