React服务端渲染(总结)


Posted in Javascript onJuly 01, 2017

一、前言

为什么需要服务端渲染?什么情况下进行服务端渲染?笔者认为,当我们要求渲染时间尽量快、页面响应速度快时(优点),才会采用服务器渲染,并且应该“按需”对页面进行渲染 ——“首次加载/首屏”。即服务端渲染的优势在于:由中间层( node端 )为客户端请求初始数据、并由node渲染页面。那客户端渲染和服务端渲染有什么差别?服务端渲染究竟快在哪里呢?

二、原因与思路

客户端渲染路线:1. 请求一个html -> 2. 服务端返回一个html -> 3. 浏览器下载html里面的js/css文件 -> 4. 等待js文件下载完成 -> 5. 等待js加载并初始化完成 -> 6. js代码终于可以运行,由js代码向后端请求数据( ajax/fetch ) -> 7. 等待后端数据返回 -> 8. react-dom( 客户端 )从无到完整地,把数据渲染为响应页面

服务端渲染路线:2. 请求一个html -> 2. 服务端请求数据( 内网请求快 ) -> 3. 服务器初始渲染(服务端性能好,较快) -> 4. 服务端返回已经有正确内容的页面 -> 5. 客户端请求js/css文件 -> 6. 等待js文件下载完成 -> 7. 等待js加载并初始化完成 -> 8. react-dom( 客户端 )把剩下一部分渲染完成( 内容小,渲染快 )

说明:对同一个组件,服务端渲染“可视的”一部分( render/componentWillMount部分代码  ),为确保组件有完善的生命周期及事件处理,客户端需要再次渲染。即:服务端渲染,实际上也是需要客户端进行 再次地、但开销很小的二次渲染。

时间耗时比较:

1. 数据请求:由服务端请求数据而不是客户端请求数据,这是“快”的一个主要原因。服务端在内网进行请求,数据响应速度快。客户端在不同网络环境进行数据请求,且外网http请求开销大,导致时间差(主要原因)。

2. 步骤:服务端是先请求数据然后渲染“可视”部分,而客户端是等待js代码下载、加载完成再请求数据、渲染。即:服务端渲染不用等待js代码下载完成再请求数据,并会返回一个已经有内容的页面。

3. 渲染性能:服务端性能比客户端高,渲染速度快( 猜测,该项数据不详 )。

4. 渲染内容:服务端渲染会把”可视“部分先渲染,然后交给客户端再作部分渲染。而客户端渲染,则是从无到有,需要经历完整的渲染步骤。

React服务端渲染(总结)

三、注意事项与问题

0. 项目依赖什么?答:node端:express、react-dom/server、webpack。前端:React、mobx(一个更好的redux)、React-router、webpack

1. 前端/node端共用那部分代码?答:node端/前端有各自的入口文件,server.js/client.js,通过react-router的路由配置文件routes.js作中间层

// routes.js
module.exports = (
  <Route path="/" component={ IComponent } >
    <Route path="/todo" component={ AComponent }>
    </Route>
  </Route>
)

2. 代码是由前后端共享,那如何分平台地操作不同代码?答:通过webpack。对共享代码,进行不同平台的,webpack(babel)编译,通过在webpack.config.js中加入

// webpack.client.config.js
plugins: [
   new webpack.DefinePlugin({
     '__isServer__': false,
     '__isClient__': true
   })
 ]
// webpack.server.config.js
plugins: [
   new webpack.DefinePlugin({
     '__isServer__': true,
     '__isClient__': false
   })
 ]
// xxx.js
if( __isServer__ ) {
  ...
}else { ... }

React服务端渲染(总结)

4. 组件的生命周期是如何的呢?答:componentWillMount( node端 ) -> render( node端 ) -> 客户端生命周期和以前一样

5. 拉取数据后如何处理呢?答:先在node端根据数据渲染好,再把数据随页面返回至前端,再由React根据数据进行渲染校对( 若前后端渲染结果不一致将报错 )。应该在componentWillMount让组件进行本地的数据同步

// 组件.js
componentWillMount() {
  if( __isClient__ ) {
     this.todoStore.todos = window.initTodos; 
  }
}  
// node端返回
`
<!doctype html>
<html lang="utf-8">
    <head>
    <script> window.initTodo = ${...}</script>
    </head>
    <body> ... </body>
    <script src="/static/vendor.js"></script>
    <script src="/static/client.bundle.js"></script>

6. 前端/node端“入口文件”通过webpack构建有什么不同?答:前端是为了解析JSX与es6代码(包括mobx的es6 decorator),node端除了以上,还需要加入babel-plugin-transform-runtime,是为了在node良好地运行es7 async / awatit

7. 如何保证node端能够先请求数据然后再渲染?答:es7的async / await语法

8. 前端的react-router路由与node端路由如何配合?node如何知道该路由是渲染哪个数据呢?答:前端是以前的react-router配置,node端是react-router的match/RouterContext// 共享文件routes.js

const routes = (
  <Route path="/" component={ IComponent } >
    <Route path="/todo" component={ AComponent }>
    </Route>
  </Route>
)
// 前端入口文件client.js
render(
  <Router routes={ routes } history={ browserHistory } />,
  ele
)
// node端入口文件server.js
let app = express();
app.get('/todo', (req, res) => {

  match({ routes: routes, location: req.url }, async (err, redirect, props) => {
     // match会帮我们找到要渲染的组件链,注:上面一行使用了async语法,因此可以在render之前使用await运行拉取数据的代码
     let html = renderToString(<RouterContext {...props} />)
     res.send( indexPage(html) )
  }
}) 
// node端返回   
let indexPage = (html)=>{
  return `
  <!doctype html>
    <html lang="utf-8">
      <head>
        <script>
        </script>
      </head>
      <body>
        <section id="hzpapp" >${html}</section>
      </body>
      <script src="/static/vendor.js"></script>
      <script src="/static/client.bundle.js"></script>
    </html>
}

9. client.js中是否还能继续使用webpack的require.ensure ? 答:可以。但闪白明显,且node端返回html后会有报错,在加载脚本后该错误能忽略。 

10. 若我使用的是mobx,该如何实例化store ? 答:每一个node请求,都应该返回一个新的独立的store实例,而不是每个node请求共用一个store实例(笔者易犯)。

React服务端渲染(总结)

本demo地址( 前端库React+mobx+ReactRouter ):https://github.com/Penggggg/react-ssr

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
超简单的jquery的AJAX用法
May 10 Javascript
Extjs中常用表单介绍与应用
Jun 07 Javascript
DOM Scripting中的图片切换[兼容Firefox]
Jun 12 Javascript
jQuery事件绑定和委托实例
Nov 25 Javascript
jquery常用函数与方法汇总
Sep 01 Javascript
全面解析JavaScript里的循环方法之forEach,for-in,for-of
Apr 20 Javascript
Angular2 环境配置详细介绍
Sep 21 Javascript
JS两种类型的表单提交方法实例分析
Nov 28 Javascript
解决vue-cli + webpack 新建项目出错的问题
Mar 20 Javascript
微信小程序上传图片功能(附后端代码)
Jun 19 Javascript
原生js实现随机点名功能
Nov 05 Javascript
vue绑定class的三种方法
Dec 24 Vue.js
Express + Node.js实现登录拦截器的实例代码
Jul 01 #Javascript
详解node-ccap模块生成captcha验证码
Jul 01 #Javascript
vue元素实现动画过渡效果
Jul 01 #Javascript
JavaScript学习总结之正则的元字符和一些简单的应用
Jun 30 #Javascript
node.js + socket.io 实现点对点随机匹配聊天
Jun 30 #Javascript
详解vue中computed 和 watch的异同
Jun 30 #Javascript
JS 组件系列之Bootstrap Table 冻结列功能IE浏览器兼容性问题解决方案
Jun 30 #Javascript
You might like
php 图片加水印与上传图片加水印php类
2010/05/12 PHP
PHP取进制余数函数代码
2012/01/19 PHP
小议Function.apply()之二------利用Apply的参数数组化来提高 JavaScript程序性能
2006/11/30 Javascript
浅谈Javascript事件模拟
2012/06/27 Javascript
javascript实现的HashMap类代码
2014/06/27 Javascript
Jquery Ajax Error 调试错误的技巧
2015/11/20 Javascript
微信小程序 HTTPS报错整理常见问题及解决方案
2016/12/14 Javascript
JavaScript cookie详解及简单实例应用
2016/12/31 Javascript
ES6使用let命令更简单的实现块级作用域实例分析
2017/03/31 Javascript
javascript计算对象长度的方法
2017/10/25 Javascript
vue.js打包之后可能会遇到的坑!
2018/06/03 Javascript
Vue前后端不同端口的实现方法
2018/09/19 Javascript
vue中使用better-scroll实现滑动效果及注意事项
2018/11/15 Javascript
推荐一个基于Node.js的表单验证库
2019/02/15 Javascript
微信小程序3D轮播实现代码
2019/09/19 Javascript
vue实践---vue不依赖外部资源实现简单多语操作
2020/09/21 Javascript
Python脚本简单实现打开默认浏览器登录人人和打开QQ的方法
2016/04/12 Python
为Python的Tornado框架配置使用Jinja2模板引擎的方法
2016/06/30 Python
Python实现批量读取图片并存入mongodb数据库的方法示例
2018/04/02 Python
Pandas Shift函数的基础入门学习笔记
2018/11/16 Python
Python分布式进程中你会遇到的问题解析
2019/05/28 Python
PyQt5 QTableView设置某一列不可编辑的方法
2019/06/25 Python
使用Django xadmin 实现修改时间选择器为不可输入状态
2020/03/30 Python
tensorflow安装成功import tensorflow 出现问题
2020/04/16 Python
Python参数传递机制传值和传引用原理详解
2020/05/22 Python
简单了解Django项目应用创建过程
2020/07/06 Python
深入浅析css3 border-image边框图像详解
2015/11/24 HTML / CSS
百联网上商城:i百联
2017/01/28 全球购物
升职自荐信
2013/11/28 职场文书
校外活动方案
2014/08/28 职场文书
副校长竞聘演讲稿
2014/09/01 职场文书
工程承包协议书范本
2014/09/29 职场文书
先进班组事迹材料
2014/12/25 职场文书
萤火虫之墓观后感
2015/06/05 职场文书
Nginx配置SSL证书出错解决方案
2021/03/31 Servers
jQuery ajax - getScript() 方法和getJSON方法
2021/05/14 jQuery