ReactSSR-DEMO-移动端适配
ssr介绍
服务端渲染,将网页内容在服务器端生成发送到浏览器技术。(csr为客户端渲染)
应用场景
- 搜索引擎优化(SEO)-提升网站在搜索引擎中的排名
- 首屏加载速度-
ssr
可以在服务器端生成HTML
页面 3.隐藏某些数据-csr
需要从服务器将数据下载进行动态渲染,ssr
由于数据在服务端实现可以隐藏部分数据
实现
流程:服务端渲染组件为 string,拼接成 html 返回,浏览器渲染出返回的 html,然后执行 hydrate,把渲染和已有的 html 标签关联。
很早之前服务端就是通过 JSP、PHP 等模版引擎,渲染填充数据的模版,产生 html 返回的。只不过这时候没有组件的概念.
同样的组件在服务端渲染了一次,在客户端渲染了一次,这种可以在双端渲染的方式,叫做同构渲染。
服务端导入渲染组件,调用react-dom/server
中的renderToString
的方法将组件的渲染内容输出为字符串返回客户端
服务端组件和App组件
import express from "express";
import React from "react";
import { renderToString } from "react-dom/server";
import App from "../ui/App";
const app = express();
app.get("/chat", (_: unknown, res: express.Response) => {
console.log("res", res);
console.log("res.send", res.send(renderToString(<App />)));
res.send(renderToString(<App />));
});
app.listen(4000, () => {
console.log("Listening on port 4000");
});
使用npm run start:1
开启服务
import React, { useCallback } from "react";
export default ({ userName = "unknown" }: { userName?: string }) => {
const log = useCallback(() => {
console.log("Nice world");
}, []);
return (
<div>
<p>react ssr demo</p>
<p>{userName}</p>
<button onClick={log}> 点击 </button>
</div>
);
};
server
端就会使用 renderToString
将 <App />
渲染成 html
字符串,然后通过 send
返回给前端
<div>
<p>react ssr demo</p>
<p>unknown</p>
<button> 点击 </button>
</div>
hydrate 复活组件
浏览器接收到 html 就会把它渲染出来,这时候已经有标签了,只需要把它和组件关联之后,就可以更新和绑定事件了。
到此拿到了一个 html
并没有任何的 js
,事件绑定等无法实现的,要复活组件的交互需要很重要的一步 - hydrate
也就是常说的水合。
hydrate
即通过 react
将对应的组件重新渲染到 SSR
渲染的静态内容上,类似于 render
差异点在于 render
会忽略 root
元素中现有的 dom
而 hydrate
则会复用并会进行内容匹配检查。
hydrate 会在渲染的过程中,不创建 html 标签,而是直接关联已有的。这样就避免了没必要的渲染
客户端代码:
import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import App from './App';
hydrateRoot(document.getElementById('root')!, <App {...window.__initialState} />);
打包完成后生成一个 bundle.js
即可在客户端使用它来进行 hydrate
。
修改服务端代码:
import express from "express";
import { renderToString } from "react-dom/server";
import React from "react";
import App from "../ui/App";
const app = express();
app.get("/", (_: unknown, res: express.Response) => {
res.send(
`
<div id="root">${renderToString(<App />)}</div>
<script src="/bundle.js"></script>
`
);
});
app.use(express.static("static"));
app.listen(4000, () => {
console.log("Listening on port 4000");
});
在静态内容的外层套上 root
元素,然后在下方引入我们刚刚编译的脚本,然后就可以在客户端看到我们想要的结果
服务端是怎么 render 出字符串的,浏览器端又是怎么 hydrate 的呢?
服务端渲染就是拼接 html 的过程,组件和元素分别有不同的渲染逻辑:组件的话就传入参数执行,元素的话就拼接字符串。
响应式适配
//设置页面的字体大小
var init = function () {
var clientWidth = document.documentElement.clientWidth || document.body.clientWidth;
if (clientWidth >= 640) {
clientWidth = 640;
}
var fontSize = 20 / 375 * clientWidth;
document.documentElement.style.fontSize = fontSize + "px";
}
init();
window.addEventListener("resize", init);
该函数用于设置页面的字体大小。它首先获取当前浏览器窗口的宽度,使用document.documentElement.clientWidth
获取文档根元素<html>
元素的宽度,如果该值不存在,则使用document.body.clientWidth
获取body
元素的可视宽度的宽度。
通过判断获取到的宽度是否大于等于640像素,如果是,则将其值设为640,以限制最大宽度。接下来,根据计算公式 20 / 375 * clientWidth
,计算出需要设置的字体大小(以像素为单位)。
通过document.documentElement.style.fontSize
将字体大小应用到文档根元素上。
通过window.addEventListener("resize", init)
添加了一个事件监听器,当浏览器窗口的大小发生改变时,会触发init
函数,从而重新计算并设置字体大小以实现响应式布局效果
代码执行了一次init()
函数,用于初始化页面的字体大小。
知识点
-
useState
接受一个初始值,它返回一个数组,里面包含两个值,第一个是拿来使用的值(state),第二个是用来更新这个值的函数(setState) -
使用
setState
更新数据时,在它后面使用state
还是未更新的值 -
如果想要
setState
依赖上一个setState
的值,可以写一个函数,函数接收上一个state
,并返回要设置的state
,类似于setState(prevState => newState)
。 -
setState
会将原本的值与传过来的值进行浅比较,如果前后两个值对比相等,则不进行渲染操作,直接return
,所以对于Object类型的值在setState
时需要进行一个浅拷贝操作。