- CSR 客户端渲染
页面初始加载的 HTML 页面中无网页展示内容,需要加载执行JavaScript 文件中的 React 代码,通过 JavaScript 渲染生成页面,同时,JavaScript 代码会完成页面交互事件的绑定
- SSR 服务器端渲染
用户请求服务器,服务器上直接生成 HTML 内容并返回给浏览器。服务器端渲染来,页面的内容是由 Server 端生成的。一般来说,服务器端渲染的页面交互能力有限,如果要实现复杂交互,还是要通过引入 JavaScript 文件来辅助实现。
- 同构
我们把页面的展示内容和交互写在一起,让代码执行两次。在服务器端执行一次,用于实现服务器端渲染,在客户端再执行一次,用于接管页面交互,
- 加快TTFP(Time To First Page)
- 增加SEO
- 虚拟dom
- 虚拟 DOM 是真实 DOM 的一个 JavaScript 对象映射
- 在服务器,我可以操作 JavaScript 对象,判断环境是服务器环境,我们把虚拟 DOM 映射成字符串输出;
- 在客户端,我也可以操作 JavaScript 对象,判断环境是客户端环境,我就直接将虚拟 DOM 映射成真实 DOM,完成页面挂载。
- ssr差异 (C vs S)
- 代码:只有【组件】代码可以共用,路由不同【服务器用StaticRouter】【客户端用BrowserRouter】
- 异步获取数据:在服务器端,页面一旦确定内容,就没有办法 Rerender。判断路由,用promise队列获取所有数据ready再渲染
- 跨域:客户端有同源限制,可用【 express-http-proxy 】做代理,服务器端无限制
- 打包:
1. 入口不同
2. server 用 target: node
3. server 用【webpack-node-externals】排除打包node已有的包
4. css不同:客户端用 css-loader 和 style-loader,css-loader 不但会在 DOM 上生成 class 类名,解析好的 CSS 代码,还会通过 style-loader 把代码挂载到页面上。
5. 服务器端 css 用【 isomorphic-style-loader】拿到返回的样式代码,然后以字符串的形式添加到服务器端渲染的 HTML 之中
将React Component转化为HTML的字符串 都接受一个React Component参数,返回一个String
- renderToString
将React Component转化为HTML字符串,生成的HTML的DOM会带有额外属性:各个DOM会有data-react-id属性,第一个DOM会有data-checksum属性。
- renderToStaticMarkup
同样是将React Component转化为HTML字符串,但是生成HTML的DOM不会有额外属性,从而节省HTML字符串的大小。
- ps
- 如果使用renderToString渲染组件,会在组件的第一个DOM带有data-react-checksum属性,这个属性是通过adler32算法算出来:如果两个组件有相同的props和DOM结构时,adler32算法算出的checksum值会一样,有点类似于哈希算法。
- 当客户端渲染React组件时,首先计算出组件的checksum值,然后检索HTML DOM看看是否存在数值相同的data-react-checksum属性,如果存在,则组件只会渲染一次,如果不存在,则会抛出一个warning异常。也就是说,当服务器端和客户端渲染具有相同的props和相同DOM结构的组件时,该React组件只会渲染一次。
1.npm install --save-dev babel-loader@^7
- react-dom/server API renderToString 将组件转化为字符串做最基础的服务器端渲染
- vDOM是真实dom的js对象映射
- 用到jsx记得引入react
- ssr路由只发生在第一次
- express-http-proxy
服务器端渲染
- webpack利用isomorphic-style-loader
客户端端渲染
- webpack利用style-loader及css-loader
服务器端渲染
问题:不会运行componentDidMount,store一直为空,不会获取数据
- loadData:在server渲染之前,把数据加载好
- 路由 match 动态加载loadData
- 模拟路由匹配行为: map some push
- react-router-config match-routers 进行多级路由匹配
- 利用promise异步加载数据
最终解决方案流程:
- 创建空store
- matchRouters循环匹配并查看是否有loadData
- 执行loadData,放入promise
- promise.all成功后,获取全部数据成功
- 结合路由、请求、数据生产html返回给用户
客户端渲染
- store更新
- 页面重新渲染出数据
脱水&&注水
- 注水:将数据存入windows.context
- 脱水:从windows.context拿数据
热更新
- 自动打包 webpack --config webpack.server.js --watch
- 自动重启服务器 nodemon --watch --exec
- 打包&&重启服务器 npm-run-all
优化
- webpack-merge 复用相同的配置
注意
- 配置执行顺序:从下到上,从右到左
- 客户端路由渲染用BrowserRouter
- 服务器端路由渲染用StaticRouter,通过req.path动态获取路由路径
- 利用react-router 的link切换路由
浏览器=》node-server=》c++/java-server
- node服务器只负责拼装并返回页面,java/c++服务器负责底层数据处理
同构:一套react代码先在server执行,再在client执行
- 用script加载文件
- 静态资源文件用static中间件 app.use(express.static('public'))
- 将在客户端运行的代码也用webpack打包后放到public的index下
- 用hydrate来做client渲染
- 流程:
- 服务器运行react代码渲染出html
- 发送html给浏览器
- 浏览器接受内容并展示
- 浏览器加载js文件
- js中的react代码在浏览器中重新执行
- js的react代码接管页面操作
- 搜索引擎只认识html,客户端渲染js无法拿到数据,全能搜索引擎会根据全部数据来分析。
- 优化 title description 提升吸引力、转化率
- 提升seo:网站组成:文字【增加原创性】 链接【提升外部链接、增强内部链接】 多媒体
- react-helmet 定制化 title description
- 加nginx分析是否为蜘蛛,蜘蛛则走预渲染prerender