热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

浅谈react前后端同构渲染

前后端同构渲染:当客户端请求一个包含React组件页面的时候,服务端首先响应输出这个页面,客户端和服务端有了第一次交互。然后,如果加载组件的

前后端同构渲染:当客户端请求一个包含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 的生命周期在前后端渲染过程中也有所不同。前端渲染的组件拥有完整的生命周期,而后端渲染仅有 componentWillMount 的生命周期。这就意味着,如果我们想进行前后端共同操作的逻辑,如发送数据请求等,可以放在 componentWillMount 的生命周期中;如果想单独处理客户端的逻辑,可以放在其他生命周期,如 componentDidMount 中。

react-router

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

这里写图片描述

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

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)放上占位符

@@@
###

以上,当客户端发出首次请求,服务端渲染出组件的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,直出页面放上占位符




 
 



 
@@@
###

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('###', '')
 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

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


推荐阅读
  • 本文探讨了2019年前端技术的发展趋势,包括工具化、配置化和泛前端化等方面,并提供了详细的学习路线和职业规划建议。 ... [详细]
  • 本文介绍了如何利用npm脚本和concurrently工具,实现本地开发环境中多个监听服务的同时启动,包括HTTP服务、自动刷新、Sass和ES6支持。 ... [详细]
  • 利用 Jest 和 Supertest 实现接口测试的全面指南
    本文深入探讨了如何使用 Jest 和 Supertest 进行接口测试,通过实际案例详细解析了测试环境的搭建、测试用例的编写以及异步测试的处理方法。 ... [详细]
  • 本文详细介绍了 React 中的两个重要 Hook 函数:useState 和 useEffect。通过具体示例,解释了如何使用它们来管理组件状态和处理副作用。 ... [详细]
  • 本文详细介绍了在企业级项目中如何优化 Webpack 配置,特别是在 React 移动端项目中的最佳实践。涵盖资源压缩、代码分割、构建范围缩小、缓存机制以及性能优化等多个方面。 ... [详细]
  • 基于Node.js、Express、MongoDB和Socket.io的实时聊天应用开发
    本文详细介绍了使用Node.js、Express、MongoDB和Socket.io构建的实时聊天应用程序。涵盖项目结构、技术栈选择及关键依赖项的配置。 ... [详细]
  • 深入理解Vue.js:从入门到精通
    本文详细介绍了Vue.js的基础知识、安装方法、核心概念及实战案例,帮助开发者全面掌握这一流行的前端框架。 ... [详细]
  • 当unique验证运到图片上传时
    2019独角兽企业重金招聘Python工程师标准model:public$imageFile;publicfunctionrules(){return[[[na ... [详细]
  • 本文深入探讨了JavaScript中实现继承的四种常见方法,包括原型链继承、构造函数继承、组合继承和寄生组合继承。对于正在学习或从事Web前端开发的技术人员来说,理解这些继承模式对于提高代码质量和维护性至关重要。 ... [详细]
  • 本文将详细介绍如何在ThinkPHP6框架中实现多数据库的部署,包括读写分离的策略,以及如何通过负载均衡和MySQL同步技术优化数据库性能。 ... [详细]
  • 本文探讨了浏览器的同源策略限制及其对 AJAX 请求的影响,并详细介绍了如何在 Spring Boot 应用中优雅地处理跨域请求,特别是当请求包含自定义 Headers 时的解决方案。 ... [详细]
  • 深入解析ES6至ES8的新特性与应用
    本文详细介绍了自2015年发布的ECMAScript 6.0(简称ES6)以来,JavaScript语言的多项重要更新,旨在帮助开发者更好地理解和利用这些新特性进行复杂应用的开发。 ... [详细]
  • 前端开发中的代码注释实践与规范
    本文探讨了前端开发过程中代码注释的重要性,不仅有助于个人清晰地回顾自己的编程思路,还能促进团队成员之间的有效沟通。文章将详细介绍HTML、CSS及JavaScript中的注释使用方法,并提出一套实用的注释规范。 ... [详细]
  • 本文通过探讨React中Context的使用,解决了在多层级组件间传递状态的难题。我们将详细介绍Context的工作原理,并通过实际案例演示其在项目中的具体应用。 ... [详细]
  • 本文详细探讨了Xshell6评估版到期后无法使用的常见问题,并提供了有效的解决方案,包括如何合法购买授权以继续使用。 ... [详细]
author-avatar
书友60614683
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有