React SSR

ssr 增量开发 难点以及解决方案

什么是 ssr

为什么要做 ssr

ssr 同构是什么?

难点在哪里?注水和脱水

如果用 hook 的话,有什么技术难点

为什么增量开发中加了后会有难度,

单页面和多页面应用的结合会有什么奇妙的化学反应

市场上有什么解决方案?

这位大哥的解决方案?

我的思考

我们现在公司出现的具体情况,

原本是 Node 模板渲染,现在像用单页面应用 React 来解决问题

一句话解释:

同构:客户端和服务端渲染的一个整合,让代码执行两次(其实就一次,通过一些算法标识如果 key 一致就不在渲染)。这句话很奇怪,为什么 key 一致就不渲染了

react 所写的单页面应用,通过 react-dom/server 中的 renderToString 把 React 组件转换成 字符串片段 A,用户访问浏览器路由 A(路由扔给客户端),浏览器返回 字符串片段 A,因为已经在服务端渲染过,所有浏览器(客户端)加载页面的时候只是渲染字符串而已,速度很快。

session 和 token

token 我直接存在客户端里,去请求接口的时候,通过 header 携带过去,因为加密过,别人无法篡改,到了服务器端,服务端端解析 token,再做判断

1.用户登录校验,校验成功后返回 token 给客户端,

2.客户端收到 token 后保存至客户端(一般用 localStorage 保存)

3.客户端每次访问 API 时携带 Token 到服务器端

4.服务器端校验 token,成功返回请求数据,失败返回错误码

Token 的优势:

无状态、可扩展

在客户端存储的 token 是无状态的,并且能够被扩展。如果用 session,负载均衡一弄你的用户信息从一个服务器到另一个服务器,你还需要在两个服务器中打个桥,如果服务器一多,你在服务器端做的时候就更多了。

而 token 本身就是无状态的,存储在客户端,一省了 session 服务器,二少了很多运维的操作,可扩展性完爆 session

安全性

请求中发送 token 而不再是发送 cookie 能够防止 CSRF(跨站请求伪造)。即使在客户端使用 cookie 存储 token,cookie 也仅仅是个存储机制而不是用于认证。不将信息存储在 Session 中,让我们少了对 session 操作

token 是有时效的,一段时间之后用户需要重新验证。我们

这个人有点牛逼 zz 小册作者

https://juejin.im/post/5d7deef6e51d453bb13b66cdopen in new window

React 中同构(SSR)原理脉络梳理

https://juejin.im/post/5bc7ea48e51d450e46289eabopen in new window

前端同构渲染的思考与实践

https://mp.weixin.qq.com/s?__biz=MzI2NjkxMjAxNQ==&mid=2247487580&idx=1&sn=50559bd9271eb28d3d9fad7adc1187ee&chksm=ea87b83cddf0312a3d7af7219b56c85a7817ced218acca835ed69ecebd5d580fbe521410d7ff&mpshare=1&scene=1&srcid=&sharer_sharetime=1586519527731&sharer_shareid=778ad5bf3b27e0078eb105d7277263f6#rdopen in new window

React 代码需要经过打包编译才能执行

同构:客户端渲染和服务器端渲染的一个整合。让代码执行两次(其实就一次,通过一些算法标识如果 key 一致就不用再渲染)

同构:服务器一份代码,客户端一份代码

react 在 node 中写好,用 react-dom/server 中的 renderToString 把 React 组件转换成 字符串片段 A,用户访问浏览器路由 A,浏览器返回 字符串片段 A,因为已经在服务器端渲染过,所以这个字符串不用再重新渲染。

为什么模板不能共存。因为我要先打包文件,用 webpack 打包 node (express 搭起来的)服务。打包出来的是个 js 文件,就是用 webpack 打包出一个压缩过的 node 文件,里面的模板什么的就不能用了,为什么不能用。

模板依赖

react ssr 最终产物其实就是 SPA + SSR

其中 SSR 指的是在服务端渲染组件

而组件可以在服务端渲染的根本原因就是 虚拟 DOM

我们习惯使用 jsx 来编写 react 组件,但 jsx 只是一个抽象的语法糖,看上去是写组件,其实我们写的是对象。

react.hybrate

hydrate 是 React 中提供在初次渲染的时候,去复用原本已经存在的 DOM 节点,减少重新生成节点以及删除原本 DOM 节点的开销,来加速初次渲染的功能。

{
  "presets": [
      [
          "@babel/preset-env",
          {
            "targets": {
                "browsers": [
                    ">1%",
                    "last 2 versions",
                    "not ie <= 8"
                ]
            }
          }
      ],
      "@babel/preset-react"
  ]
}

直接 webpack-dev-server

loading 的坑

react-loadable 源码分析

数据同构

componentDidMount 生命周期只会在浏览器端执行

会话保持,如果用 localStorage 的话 session 就存在客户端

每次进入页面的时候,传数据会不会太麻烦?

但是好像也就这个方法

刚开始做的数据同构时,我在前端代码中模拟请求,然后抱起来后,浏览器支持 ComponentDidMount,我不知道服务器支不支持,但是浏览器报错

react-dom.development.js:88 Warning: Did not expect server HTML to contain the text node "
            " in <div>.

数据同构,就是将前端(客户端)的数据请求转移到服务端进行,将数据请求的行为写成一个静态方法,

等匹配对路由的情况下(例如在 list 页面),将数据写入到 html 上,用的是 react-router 中的 StaticRouter,用它的 context,但是这有个问题,服务端得到的 html 和客户端的不一致(客户端通过 react 的虚拟 dom 生成新的 dom 结构,但是因为没有数据,没有请求所以他会呈现另一个 dom 结构)。所以它会一闪之后(刚开始是有 html,后来被客户端接管),又变成了暂无数据。

我们需要让客户端的 dom 结构和服务端的一致。有很多方式,为了防止 xss,用 textarea 来注入数据。

这一过程,叫做 数据脱水,意思是将直出组件客户端的时候再传数据给客户端。

我们要讲数据赋值给 react 的虚拟 dom,这个过程叫做 数据注水

但还有有一个问题,如果当你从一个有数据的页面跳转到另一个页面,再从这个页面跳回去,数据会没有,因为我们现在的数据是通过服务端传递数据,做一下兼容,在初始化(componentDidMount)的时候,判断如果 this.state.data 中没有数据,那么就去请求数据

增量开发——从模板渲染到 react ssr

成为父亲

打包上线 ok

路由切换管理 ok

从 hbs 跳到单页面路由上,再从单页面路由跳回 hbs 上

可以用 window.location 跳转但是不是一本万利的

redux

热更新

按需加载 ok

高阶组件

eslint +prettie

sever 端压缩 production ok

cookie session 传值还没好

等级用户

ts

装饰器

生成一个中间件

放入到 express 中,使用这个中间件

ssr 的坑

less 不支持

按需加载不支持(起码以我的实力看不支持,主要自己太菜)

actions 异步

函数式写法

eslint

eslint-config-airbnb

eslint-config-prettier

eslint-plugin-prettier

eslint-plugin-react

  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  }

登录好了

权限问题

babel webpack 打包问题

babel 把 es6 的语言转换为 es5

如果 webpack 也想要用 需要配置 babel-loader

但是导出的文件是,你写什么导出什么

   output: {
        filename: 'app.js',
        path: resolvePath('../dist/server'),
        +libraryTarget: 'commonjs2',
    },
<List /> // 基础组件
<List.Group /> // “组”组件(主要是样式问题,上下划线,子组件的最后一项没有下划线)
<List.Addon /> // 前缀组件
<List.Suffix /> // 后缀组件
<List.Desc /> // 描述组件
<List.CardSelect /> // <CheckBox /> + <List.Group />
<List.Dashboard /> // <List.Addon>

// 使用
    <div>
    	{
            data.map((item) => {
                <List.Group>
                   	<List.Row></List.Row>
                    <List.Row></List.Row>
                    <List.Row></List.Row>
                    <List.Radio></List.Radio>
                </List.Group>
            })
        }
    </div>

Group 是强关联

react-router 如果要加二级路由,记得在 webpack output 处加 publicPath: '/'

Scroll 做成高阶组件

出现一个问题的时候要看一看是否是因为 没引入

跨域:

没有服务器帮忙的情况下

本地开发

webpack 里加 devServer 代理

https://juejin.im/post/5b972664f265da0a9624b50copen in new window

ssr react 组件加载

@type 类型错误

https://gist.github.com/oyvindym/ebd63bfa21fa5b07a9b31b2a4a5b3655open in new window

pre-render

后续添加

webpack 配置 alias 后 ts 找不到模块

https://www.xiejiahe.com/blog/detail/5cb2ec342bbcb67b4a5f3ed6open in new window

把 session 当做一个接口来调用

星期一:将 js 文件转换为 ts 文件。页面 processful 完成

星期二:数据窃取 模板页面与 ssr 页面的数据交互,页面 address 完成一半

Last Updated:
Contributors: johan