浅谈react前后端同构渲染


Posted in Javascript onSeptember 20, 2017

前后端同构渲染:当客户端请求一个包含React组件页面的时候,服务端首先响应输出这个页面,客户端和服务端有了第一次交互。然后,如果加载组件的过程需要向服务端发出Ajax请求等,客户端和服务端又进行了一次交互,这样,耗时相对较长。前后端同构渲染可以在页面初次加载时把所有地方渲染好一次性响应给客户端

实现方式:保证包管理工具和模块依赖方式一致

包管理工具-npm管理,保证前后端都使用同一个兼容包

模块依赖方式-webpack,保证前后端都采用commonjs的依赖方式,确保代码可以互相依赖

服务端如何渲染:
react全家桶:react、react-router、redux

react 和 reactDOM

reactDOM在这里提供的支持就是reactDOM.render和reactDOM.renderToString函数,其中欠着会在浏览器生成DOM结构,后者会在服务端生成对应的HTML字符串模板。react会在生成的DOM结构上添加一个 data-react-checksum的属性,这是一个 adler32 算法的校验和,用以确保两份模板的一致性。

浅谈react前后端同构渲染 

同时 react 的生命周期在前后端渲染过程中也有所不同。前端渲染的组件拥有完整的生命周期,而后端渲染仅有 componentWillMount 的生命周期。这就意味着,如果我们想进行前后端共同操作的逻辑,如发送数据请求等,可以放在 componentWillMount 的生命周期中;如果想单独处理客户端的逻辑,可以放在其他生命周期,如 componentDidMount 中。

react-router

react-router是react的路由-视图控制库,可以书写边界的声明式路由以控制不同页面的渲染。react-router 本身是一个状态机,根据配置好的路由规则,和输入的 url 路径,通过 match 方法找到对应的组件并进行渲染。

浅谈react前后端同构渲染

这套机制在前端和后端都是相通的,例如在后端,就是下面这样一种实现形式来进行渲染:

app.use(async (ctx, next) => { 
match({ 
location: ctx.originalUrl, 
routes 
}, callback) 
// 渲染完成之后,调用 callback 回调 
// 将 组件 renderToString 返回前端即可 
})

对于前端来说,其实也是处理的上面这些逻辑,不过它被很好的封装在 组件中,我们只需要写好声明式的路由,这一切就可以随着 url 的变化自动发生。

redux

redux是react的数据流管理库,他对服务端渲染的支持很简单,就是单一的store和状态可初始化。后端在进行渲染的时候会构建好单一的store,并将构建好的初始状态通过以json格式,通过全局变量写到生成好的html字符串模板上。
前端通过获取初始状态,生成跟后端徐然完成后一模一样的store,就可以保证前后端渲染数据的一致性,以确保前后端生成的dom结构一致。

优化结果:

开发效率低的问题:同构应用只有一个项目和一套技术栈,只要拥有 react 开发经验,就可以快速投入前端和后端的开发当中;
可维护性差的问题:同构应用可以进行大量的代码公用,包括工具方法、常量、页面组件和 redux 的大部分逻辑等,可重用性大大提高; 首屏性能、SEO 等

处理过程:

客户端发出请求-服务端渲染出组件-返回给客户端

1、在需要同构直出的页面(比如是index.html)放上占位符

<div id="root">@@@</div>
###

以上,当客户端发出首次请求,服务端渲染出组件的html内容放@@@这个位置,然后服务端再渲染出类似这样的js代码段把组件最终渲染到DOM上。也就是说,renderApp方法实际上就是在渲染组件。

2、而为了直接调用renderApp方法,必须让renderApp方法成为window下的方法

window.renderApp = function(){ReactDOM.render(...)}

3、服务端取出index.html,渲染出占位符的内容,替代占位符,并一次性响应给客户端

案例

文件结构

browser.js(在这里把渲染组件的过程赋值给window.renderApp)

bundle.js(把browser.js内容bundle到这里)

Component.js(组件在这里定义)

express.js(服务端)

index.html(同构直出的页面)

package.json

index.html,直出页面放上占位符

<!doctype html>
<html>
<head>
 <meta charset="UTF-8">
 <title>Untitled Document</title>
</head>
<body>

 <div id="root">@@@</div>
 <script src="bundle.js"></script>
 ###
</body>
</html>

Component.js,在这里定义组件

var React = require('react');
var ReactDOM = require('react-dom');

var Component = React.createClass({
 clickHandler: function(){
  alert(this.props.msg)
 },

 render: function(){
  return React.createElement('button', {onClick: this.clickHandler}, this.props.msg)
 }

})

module.exports = Component;

browser.js,把组件渲染过程赋值给window对象

var React = require('react');
var ReactDOM = require('react-dom');

var Component = React.createFactory(require('./Component'));

window.renderApp = function(msg){
 ReactDOM.render(Component({msg: msg}), document.getElementById('root')); 
}

可以通过来触发组件的渲染。稍后,在服务端会把这段代码渲染出来。

express.js,服务端

以上,需要直出的页面有了占位符,定义了组件,并把渲染组件的过程赋值给了window对象,服务端现在要做的工作就是:生成组件的html和渲染组件的js,放到直出页面index.html的占位符位置。

var express = require('express');
var React = require('react');
var ReactDOMServer = require('react-dom/server');
var fs = require('fs');
var Component = React.createFactory(require('./Component'));

//原先把文件读出来
var BUNDLE = fs.readFileSync('./bundle.js',{encoding:'utf8'});
var TEMPLATE = fs.readFileSync('./index.html',{encoding:'utf8'});

var app = express();

function home(req, res){
 var msg = req.params.msg || 'Hello';
 var comp = Component({msg: msg});

 //@@@占位符的地方放组件
 var page = TEMPLATE.replace('@@@', ReactDOMServer.renderToString(comp));

 //###占位符的地方放js
 page = page.replace('###', '<script>renderApp("'+msg+'")</script>')
 res.send(page);
}

//路由
app.get('', home);
app.get('/bundle.js', function(req, res){
 res.send(BUNDLE);
})
app.get('/:msg', home);

app.listen(4000);

package.json中的配置

"scripts": {
"start": "watchify ./browser.js -o ./bundle.js"
},

启动方式
运行:npm start
运行:node express.js
浏览:localhost:4000

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

Javascript 相关文章推荐
Js组件的一些写法
Sep 10 Javascript
TextArea不支持maxlength的解决办法(jquery)
Sep 13 Javascript
jQuery获取浏览器中的分辨率实现代码
Apr 23 Javascript
固定网页背景图同时保持图片比例的思路代码
Aug 15 Javascript
javaScript的函数对象的声明详解
Feb 06 Javascript
纯javascript判断查询日期是否为有效日期
Aug 24 Javascript
谈谈JavaScript中的几种借用方法
Aug 09 Javascript
JavaScript编写一个简易购物车功能
Sep 17 Javascript
JavaScript中this的用法及this在不同应用场景的作用解析
Apr 13 Javascript
微信小程序 wx.request方法的异步封装实例详解
May 18 Javascript
使用vue实现简单键盘的示例(支持移动端和pc端)
Dec 25 Javascript
彻底理解js面向对象之继承
Feb 04 Javascript
微信页面弹出键盘后iframe内容变空白的解决方案
Sep 20 #Javascript
微信小程序 页面跳转事件绑定的实例详解
Sep 20 #Javascript
微信小程序 数据绑定及运算的简单实例
Sep 20 #Javascript
Angularjs使用过滤器完成排序功能
Sep 20 #Javascript
微信小程序 swiper组件构建轮播图的实例
Sep 20 #Javascript
node+koa实现数据mock接口的方法
Sep 20 #Javascript
详解angularjs popup-table 弹出框表格指令
Sep 20 #Javascript
You might like
Yii框架防止sql注入,xss攻击与csrf攻击的方法
2016/10/18 PHP
PhpStorm本地断点调试的方法步骤
2018/05/21 PHP
Windows平台PHP+IECapt实现网页批量截图并创建缩略图功能详解
2019/08/02 PHP
TP5框架实现的数据库备份功能示例
2020/04/05 PHP
Jquery写一个鼠标拖动效果实现原理与代码
2012/12/24 Javascript
js判断设备是否为PC并调整图片大小
2014/02/12 Javascript
如何根据百度地图计算出两地之间的驾驶距离(两种语言js和C#)
2015/10/29 Javascript
JavaScript严格模式详解
2015/11/18 Javascript
微信小程序小组件 基于Canvas实现直播点赞气泡效果
2020/05/29 Javascript
JS查找英文文章中出现频率最高的单词
2017/03/20 Javascript
JS实现加载和读取XML文件的方法详解
2017/04/24 Javascript
AngularJs 禁止模板缓存的方法
2017/11/28 Javascript
Vue加载组件、动态加载组件的几种方式
2018/08/31 Javascript
antd Upload 文件上传的示例代码
2018/12/14 Javascript
微信小程序上传多图到服务器并获取返回的路径
2019/05/05 Javascript
js前端如何写一个精确的倒计时代码
2019/10/25 Javascript
mpvue微信小程序开发之实现一个弹幕评论
2019/11/24 Javascript
Javascript实现关闭广告效果
2021/01/29 Javascript
学习 Vue.js 遇到的那些坑
2021/02/02 Vue.js
[00:20]DOTA2荣耀之路7:-ah fu-抢盾
2018/05/31 DOTA
Python使用htpasswd实现基本认证授权的例子
2014/06/10 Python
Python遍历指定文件及文件夹的方法
2015/05/09 Python
用python写的一个wordpress的采集程序
2016/02/27 Python
python将ansible配置转为json格式实例代码
2017/05/15 Python
python中matplotlib实现最小二乘法拟合的过程详解
2017/07/11 Python
Python回文字符串及回文数字判定功能示例
2018/03/20 Python
通过python实现随机交换礼物程序详解
2019/07/10 Python
Python基于Hypothesis测试库生成测试数据
2020/04/29 Python
html5 canvas 简单画板实现代码
2012/01/05 HTML / CSS
让ie浏览器成为支持html5的浏览器的解决方法(使用html5shiv)
2014/04/08 HTML / CSS
美国网上花店:JustFlowers
2017/02/12 全球购物
TripAdvisor土耳其网站:全球知名旅行社区,真实旅客评论
2017/04/17 全球购物
单位消防安全责任书
2014/07/23 职场文书
机关领导干部作风整顿整改措施
2014/09/19 职场文书
暑期社会实践个人总结
2015/03/06 职场文书
详解Python+OpenCV进行基础的图像操作
2022/02/15 Python