reactssr

同构渲染的原理是相同的,弄清楚客户端和服务端能共用哪些以及不能共用哪些,针对不能共用的进行处理。共用的是渲染的页面组件,下面以React深入研究需要做处理的点。
render
- 客户端部分不再需要
index.html,node服务拦截请求,返回在服务端渲染好的html ReactDOMServerrenderToString,渲染组件为HTML字符串renderToStaticMarkup,渲染组件为静态标记(static Markup),与renderToString的唯一区别是渲染时忽略所有React添加到DOM的HTML attributes(比如,key)
dangerouslySetInnerHTML避免被转义或造成XSS攻击__html
ReactDOMhydrate,初次渲染时会复用已经存在的DOM节点,减少重新生成节点以及删除原本DOM节点的开销。render和hydrate都会调用legacyRenderSubtreeIntoContainer(入参不同),render会触发shouldHydrateDueToLegacyHeuristic判断是否循环删除DOMContainer的子节点
- 客户端与服务端渲染的内容不一致时,客户端会重新渲染,右键查看源代码是服务端返回的html结构,页面显示的是客户端组件的内容
useEffect不会在服务端渲染时执行(原理探究)
router
StaticRouter(是无状态的)locationreq.urlcontext
BrowserRouter- 地址栏访问不同的路径服务端返回的内容是不同的,即当前路径对应的页面由服务端渲染
- 地址栏访问url或刷新 之外的导航(包括前进后退),都由客户端处理
- 重定向的处理
- 如果仅仅使用
Redirect,虽然也能正常渲染重定向后的页面,但是只有一次页面请求,查看页面的请求发现服务端返回的并不是重定向后的页面Html - 在
Redirect的基础上,结合StaticRouter的context,使用node服务的response的redirect进行服务端重定向,此时会有两次页面请求,一次是302一次是200
- 如果仅仅使用
store
- 服务端
configureStore并Provider组件,客户端也configureStore并Provider组件,共用初始state以及对应的reducer(而不是共用store实例) - 初始
state通过注入的script挂载到window上供客户端获取以共用 - 每个请求都创建一个
store实例,避免状态越权及污染
初始数据/预取数据
- 由服务端请求数据,并将数据更新到对应的
store的state,然后在被渲染的组件中使用,最终渲染成包含数据的html返回给客户端 - 避免导航到其他页面刷新丢失
store数据,在初始页面的useEffect中判断如果store中没有相应的初始数据则客户端发起请求去获取数据并更新store的state(createAsyncThunk) - 服务端不能使用
fetch API
bundle
- 服务端和客户端的打包配置处理,可以公用
babel-loader对j(t)sx的编译以及resolve - 客户端
webpack配置不需要HtmlWebpackPlugin - 配置服务端的
webpack时,webpack5使用externalsPresets:{ node:true } - 服务端注入初始html的js文件是客户端编译后的bundle
- 先打包客户端代码,再打包服务端代码,然后node执行服务端bundle