React-引领未来的用户界面开发框架-读书笔记(六)

第12章 服务端渲染

想让搜索引擎抓取到你的站点,服务端渲染这一步不可或缺,服务端渲染还可以提升站点的性能,因为在加载JavaScript脚本的同时,浏览器就可以进行页面渲染。

React的虚拟DOM是其可被用于服务端渲染的关键。首先每个React Component 在虚拟DOM中完成渲染,然后React通过虚拟DOM来更新浏览器DOM中产生变化的那一部分,虚拟DOM作为内存中的DOM表现,为React在Node.js这类非浏览器环境下的运行提供了可能,React可以从虚拟DOM中生成一个字符串。而不是更新真正的DOM,这使得我们可以在客户端和服务端使用同一个React Component。

React 提供了两个可用于服务端渲染组件的函数:React.renderToString 和React.renderToStaticMarkup。

在设计用于服务端渲染的ReactComponent时需要有预见性,考虑以下方面。

  • 选取最优的渲染函数。
  • 如何支持组件的异步状态。
  • 如何将应用的初始化状态传递到客户端。
  • 哪些生命周期函数可以用于服务端的渲染。
  • 如何为应用提供同构路由支持。
  • 单例、实例以及上下文的用法。

渲染函数

在服务端渲染React Component时,无法使用标准的React.render方法,因为服务端不存在DOM。React提供了两个渲染的函数,它们支持标准的React Component生命周期的一个子集。因而能够实现服务端渲染。

React.renderToString

React.renderToString是两个服务端渲染函数中的一个,也是开发主要使用的一个函数,和React.render不同,该函数去掉了用于表示渲染位置的参数。取而代之,该函数只返回一个字符串,这是一个快速的同步(阻塞式)函数,非常快。

var MyComponent = React.createClass({render:fucniton(){return <div> Hello World!</div>;}
});
var world= React.renderToString (<MyComponent/>);//这个示例返回一个单行并且格式化的输出
<divdata-reactid=".fgvrzhg2yo"data-ract-checksum="-1663559667">Hello World!
</div>

你会注意到,React为这个<div>元素添加了两个data前缀的属性。在浏览器环境下,React使用data-reactid区分DOM节点。这也是每当组件的state及props发生变化时,React都可以精准的跟新制定DOM节点的原因。

data-react-checksum仅仅存在于服务端。顾名思义,它是已创建DOM和校验和。这准许React在客户端服用与服务端结构上相同点的DOM结构。该属性只会添加到根元素上。

React.renderToStaticMarkup

React.renderToStaticMarkup是第二个服务端渲染函数,除了不会包含React的data属性外,它和React.renderToString没有区别。

varMyComponent=React.createClass({render:function(){return<div>Hello World!</div>;}
});
varworld= React.renderToStaticMarkup(<MyCompoent/>);//单行输出
<div>HelloWorld!</div>

用React.renderToString还是React.renderToStaticMarkup

每个渲染函数都有自己的用途,所以你必须明确自己的需求,再去决定使用哪个渲染函数。当且仅当你不打算在客户端渲染这个React Component时,才应该选择使用React.renderToStaticMarkup函数。

下面有一些示例:

  • 生成HTML电子邮件
  • 通过HTML到PDF的转化来生成PDF。
  • 组件测试。

大多数情况下,我们都会选择使用React.renderToString。这将准许React使用data-react-checksum在客户端更迅速的初始化同一个React Component。因为React可以重用服务端提供的 DOM,所以它可以跳过生成DOM节点以及把他们挂载到文档中这两个昂贵的进程。对于复杂些的站点,这样做就会显著的减少加载时间,用户可以更快的与站点进行交互。

确保React Component能够在服务端和客户端准确的渲染出一致的结构是很重要的。如果data-react-checksum不匹配,React会舍弃服务端提供的DOM,然后生成新的DOM节点,并且将它们更新到文档中。此时,React也不再拥有服务端渲染带来的各种性能上的优势。

服务端组件生命周期

一旦渲染为字符串,组件就会只调用位于render之前的组件生命周期方法,需要指出,componentDidMount和componentWillUnmount不会在服务端渲染过程中被调用,而componentWillMount在两种渲染方式下均有效。

当新建一个组件时,你需要考虑到它可能即在服务端又在客户端进行渲染。这一点在创建事件监听器时尤为重要,因为并不存在一个生命周期方法会通知我们React Component是否已经走完了整个生命周期。

在componentWillMount内注册的所有事件监听器及定时器都可能潜在的导致服务端内存泄漏。

最佳做法是只在componentDidMount内部创建事件监听器及定时器,然后在componentWilUnmount内清除这两者。

设计组件

服务端渲染时,请务必慎重考虑如何将组件的state传递到客户端,以充分利用服务端渲染的优势。在设计服务端渲染组件时,要时刻记得这一点。

在设计React Component时,需要保证同一个props传递到组件中,总会输出相同的初始渲染结果。坚持这样做将会提升组件的可测试性,并且可以保证组件在服务端和客户端渲染结果的一致性。充分利用服务端渲染的性能优势十分重要。

我们假设现在需要一个组件,它可以打印一个随机数。一个棘手问题是组件每次输出的结果总是不一致。如果组件在服务端而不是客户端进行渲染,checksum将会失效。

var MyComponent =React.createClass({render:dunction(){return <div>{Math.random()}</div>;}
});
var result = React.renderToStaticmarkup(<MyComponent/>);
var result2 = REact.renderToStaticMarkup(<MyComponent/>);//result
<div>0.5820949131157249</div>//result2
<div>0.420401582631672</div>

如果你打算重构它,组件将会通过props来接收一个随机数。然后,将props传递到客户端用于渲染。

var MyComponent= React.createClass({render :function(){retrun<div>{this.props.number}</div>}
});var num=Math.random();
//服务端
React.renderToString(<MyComponentnumber={num}/);//将num传递到客户端
React.render(<MyComponentnumber ={num}/>,document.body);

有多种方式可以将服务端的props传递到客户端。

最简单的方式之一是通过JavaScript对象将初始的props值传递到客户端。

<!DOCTYPEhtml>
<html><head><title>Example</title><!--bundle 包括MyComponent、React等--><script type =“text/javascript"src="bundle.js"></script></head><body><!--服务端渲染MyComponent的结果--><div data-reactid=".fgvrzhg2yo" data-react-checksum="-1663559667">0.5820949131157249</div><!--注入初始props,供服务端使用--><script type="text/javascript">var initialProps = {"num":0.5820949131157249};</script><!--使用服务端初始props--><script type-"text/javasript">var num = initialProps.num;React.render(<MyComponent number={num}/>, document.body);</script></body></html>

异步状态

很多应用需要从数据库或者网络服务这类远程数据源中读取数据,在客户端,这不是问题,在等待异步数据返回的时候,React Component可以展示一个加载图标。在服务端,React 无法直接复制该方案,因为render函数是同步的。为了使用异步数据,首先需要抓取数据,然后再渲染时将数据传递到组件中。

示例:

1、你可能需要从异步的store中转区用户记录;2、抓取到用户记录后,考虑到SEO以及性能等因素,需要在服务端渲染组件的状态;3、你需要让组件监听在客户端的变化,然后重新渲染

问题:React.renderToString是同步的,所以没有办法使用组件的任何一个生命周期方法,来抓区异步的数据

解决方案:使用statics函数来抓取异步数据,然后把数据传递到组件中用于渲染。将initialState作为props值传递到客户端。使用组件生命周期方法来监听变化,然后使用同一个statics函数更新状态。

var Username= React.createClass({statics:{getAsyncState:function()(props,setState){User.findById(props.userId).then(function(user){setState({user:user});}).catch(function(error){setState({error:error});});}},//客户端和服务器componentWillMount:function(){if(this.props.initialState){this.setState(this.props.initialState);}},//仅客户端componentDidMount:function(){//如果props中没有,则获取一部stateif(!this.props.initialState){this.updateAsynState();}//监听change事件User.on('change',this.updateAsyncState);},//仅客户端componentWillUnmount:funtion(){//停止监听change事件User.off(‘change’,this.undateAsyncState);},updateAsyncState:function(){//访问示例中的静态函数this.constructor.getAsyncState(this.props,this.setstate);},render:funciton(){if(this.state.error){return <div>{this.state.error.message}</div>;}if(!this.state.usr){retrun<div>Loading...</div>}return <div>{this.state.user.username}</div>;}
});//在服务器端渲染
var props={userId:123//也可以通过路由传递
};
Username.getAsyncState(props,funciton(initialState){props[initialState]=initialState;var result =React.renderToString(Username(props));//使用initialState将结果传递到客户端
});

上述解决方案中,预先抓取到异步数据这一步仅在服务端是必须的。在客户端,只有初次渲染时需要查找服务端所传递的initialState。后续客户端上的路由变化(比如HTML5,pushState或者fragment change)都会忽略掉服务端所有的initialState。同时,在抓取数据时最好加载文案信息。

同构路由

对于任意一个完整的应用来说,路由都至关重要。为了在服务端渲染出拥有路由的React应用,你必须确保路由系统支持无DOM渲染。

抓取异步数据是路由系统及其控制器的职责。我们假设一个深度嵌套的组件需要一些异步的数据。如果SEO需要这些数据,那么抓取数据的职责应该被提升至路由控制器,并且这些数据应该被传递到嵌套组件的最内层。如果不用考虑SEO,那么在客户端的componentDidMount方法内抓取数据是没问题的。这与传统的Ajax加载数据方式类似。

考虑一个React同构路由解决方案时,需确保它具有异步状态支持,或者可以轻易地更改以支持异步状态。理想情况下,你也会倾向于使用路由系统来控制,将initialState传递到客户端。

单例、实例及上下文

在浏览器端,你的应用如同包裹在独立的气泡中一样。每个实例之间的状态不会混在一起,因为每个实例通常存在于不同的计算机或者同一台计算机的不同沙箱之中。这使得我们可以在应用架构中轻松地使用单例模式。

当你开始迁移代码并在服务端运行时,你必须要小心,因为可能存在同一应用的多个实例在相同的作用域内同时运行的情况。有可能出现应用的两个实例都去更改单例状态的情况,这会导致异常的行为发生。

React渲染是同步的,所以你可以重置之前使用过的所有单例,而后在服务端渲染你的应用。如果异步状态需要使用单例,则又会遇到问题。同样,在渲染过程中使用抓取到的异步状态时,也需要考虑到这一点。

尽管可以在渲染前重置之前使用过的单例,但是在隔离的环境下运行你的应用总是有好处的。Contextify之类的包准许你在服务端彼此隔离地运行代码。这与客户端使用webworkers类似。Contextify通过将应用代码运行在一个隔离的Node.js V8实例中来工作。一旦加载完代码,你就可以调用环境中的所有函数。这种方法可以让你随意地使用单例模式,而不用考虑性能上的花销,因为每次请求都对应一个全新的Node.js V8实例。

React的核心开发小组不鼓励在组件树中传递上下文和实例。这种做法会降低组件的可移植性,并且应用内组件依赖的更改会对层级上的所有组件产生连动式的影响。这转而增加了应用的复杂性,而随着应用的增长,应用的可维护性也会降低。

当决定使用单例或者实例来控制你的上下文时,需要对两者权衡舍去。在选择一个方法之前,你需要估算出详细的需求,还需要考虑你所使用的第三方类库是如何架构的。

总结

服务端渲染是构建搜索引擎优化的Web站点和Web应用时的重要部分。React支持在服务端和客户端浏览器中渲染相同的React component。要有效地做到这一点,你需要保证整个应用都使用这一架构方式以支持服务端渲染。

第13章 周边类库

围绕着Ract,facebook还开发了一系列的前端工具。在你的React项目中,这些工具不是非用不可的,不过它们确实可以和React一起完美的工作。例如:

  • Jest
  • Immutable.js
  • Flux

Jest

Jest是Factbook开发的一个测试运行工具。它基于Jasmine测试框架提供相近的方式,使用大家熟悉的类似于expect(value).to(other)的断言。它提供了默认的模拟行为,会自动模拟require()返回的CommonJS模块。让现有的代码变成可测试的。它使用了模拟的DOM API ,同时通过小巧的Node.js命令行工具进行运行,缩短每次测试运行的时间。

page 108~112

Immutable.js

不可以变数据结构(Immutable Data Structures)中的数据是不允许修改的。相反,如果数据需要改变,它们会返回原始数据的一个经过修改的拷贝。React跟Flux可以很好的结合不可变数据结构,带来代码的简洁和性能的提升。

Immutable.js提供了多个数据结构,可以有原生的JavaScript数据结构构造而成,在需要的时候,也可以转会原生的JavaSctipt数据结构。

immutable.Map

Immutable.Map可作为常规JavaScript对象的替代者来使用:

var question = Immutable.Map({desctiption:'who is your favorite superhero?'});//使用.get从Map中取值
question.get('desctiption');//通过.set更新值时返回一个新的对象
//原始对象保持不变
question2 = question.set('desctiption', 'Who is your favorite comicbook hero?');//使用.merge合并两个对象得到第三个对象
//同样原来的对象没有任何变化
var title = {title : 'Question #1'};
var question3 = question.merge(question2,title);
question3.toObject();//{title: 'Question #1',desctiption":'who is your favorite comicbook hero'}

Immutable.Vector

可以使用Immutable.Vector代替数组:

var options = Immutable.Vector('Superman', 'Batman');
var option2 = options.push('Spiderman');
option2.toArray(); //['Superman','Batman','Spiderman']

你还可以对这些数据结构进行嵌套:

var options = Immutable.Vector('Superman', 'Batman');
var question = Immutable.Map({description : 'who is your favorite superhero?',options : options
});

Immutable.js还有更丰富的特性,你可以到immutable-js上获取更多的相关信息。

Flux

Flux是Facebook在发布React时发布的一种模式。它显著的特性是严格的单向数据流。

Facebook在GitHub发布了一份关于实习那Flux的参考,可以通过flux访问到。

Flux包含了三个重要的组件

  • Dispatcher
  • Store
  • View

下图清晰地展示了如何将这些部件组合到一起:

Flux没有强制的依赖,你可以任意选取自己需要的模块。

关于Flux更详细的讨论见第16章。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/286748.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TrimPath - Js模板引擎

当页面中引用template.js文件之后&#xff0c;脚本将创建一个TrimPath对象供你使用。 parseDOMTemplate(elementId,optionalDocument)  //获得模板字符串代码 得到页面中Id为elementId的DOM组件的InnerHTML&#xff0c;将其解析成一个模板&#xff0c;这个返回一个templateOb…

一、iVX简介(IVX 快速开发教程)

一、iVX简介 通过本节你将对 iVX 有一个大致的认识&#xff0c;并且了解 iVX 能够做些什么&#xff0c;有哪一些优势&#xff0c;这将帮助你更好的上手 iVX 进行应用的开发&#xff0c;初步了解 iVX 的强大之处。 文章目录一、iVX简介1.1 iVX 是什么&#xff1f;1.2 iVX适合怎…

WPF效果第一百八十六篇之又玩ListBox

大周末的接着上一篇玩耍TreeView,这二天又再次去玩耍ListBox;毕竟是我的最爱,没办法就喜欢玩耍他;闲话也不多扯了,直接看咱们最终效果:2、原来一直ItemTemplate,这次直接ListBoxItem的Template:<Setter Property"Template"><Setter.Value><ControlTem…

【十分钟】学会微信小游戏,攀登不止小游戏制作(IVX 快速开发教程十一)

十一、攀登不止小游戏制作 制作微信小游戏大致流程与微信小程序、Web类似&#xff0c;不同的在于是组件的使用。我们此节需要完成的小游戏需求为&#xff1a; 小球触碰矩形块会跳跃或攀爬小球触碰顶部或底部游戏结束点击屏幕将会使小球朝着该方向移动小球进行跳跃时分数会增加…

十天冲刺---Day8

站立式会议 站立式会议内容总结&#xff1a;燃尽图照片最近思考一个问题。项目是怎么进行到这一步的。算了&#xff0c;这个发在明天的冲刺总结吧。。还需继续努力&#xff0c;队友快回来快回来。。转载于:https://www.cnblogs.com/imguang/p/4965054.html

一篇文学会商用可编辑问卷表单制作【iVX 十二】

公共表单 在 iVX 快速教程中&#xff0c;我们使用一个公共表单项目作为 WebApp 应用的演示说明。公共表单项目可以用于企业内部或一个问卷公共平台做问卷调查&#xff0c;用户可以自由的设置表单元素以及样式&#xff0c;并且可以手动设置表单结束下载填写问卷后的调查数据。 …

【地图学】地图投影的定义和分类

一、地图投影 1、地图投影的定义 地图投影是利用一定数学法则把地球表面的经、纬线转换到平面上的理论和方法。 2、地图投影的分类 (1)按变形性质 • 等角投影: 投影面上两微分线段的夹角与地面上的相应两线段的夹角相等,及没有角度变形的投影叫 ~ 。

React-引领未来的用户界面开发框架-读书笔记(八)

第16章 架构模式 React主要功能在于渲染HTML。可以将其看成是MVC中的V&#xff0c;它不会影响到组件中直接调用AJAX请求之类的操作&#xff1a; var TakeSurveyReact.CreateClass({getInitialData&#xff1a;function(){return{survey:null}&#xff1b;},componentDidMount:…

confluence5.8.10的使用

之前在windows上安装了confluence5.8.10,结果有一天知什么缘故&#xff0c;数据库数据损坏&#xff0c;知识库彻底打不开了&#xff0c;所有的文档都付之东流&#xff0c;真的不是一般心痛。因此考虑将其装到linux机器上&#xff0c;因为tomcat和mysql实际上都为了linux而生的&…

Android之提示Unable to get provider com.google.android.gms.ads.MobileAdsInitProvider

1 问题 接入SDK提示错误如下 java.lang.RuntimeException: Unable to get provider com.google.android.gms.ads.MobileAdsInitProvider: java.lang.IllegalStateException: 2 解决办法 在AndroidManifest.xml文件下面配置如下 在application目录下面配置如下&#xff0c;…

RPA之PAD(Power Automate Desktop)组件开发

本文由网友蓝创精英团队投稿&#xff0c;欢迎转载、分享原文作者&#xff1a;蓝创精英团队原文链接&#xff1a;https://blog.csdn.net/i2blue/article/details/125040323其实&#xff0c;PAD&#xff0c;现在官方文档还没有对外组件式或者插件式开发接口。但是&#xff0c;有一…

【地图学】高斯-克吕格(Gauss-Kruger)投影原理、应用详解(3°带、6°带)

一、高斯克吕格投影概述 德国数学家、物理学家、天文学家高斯于19 世纪20 年代拟定,后经德国大地测量学家克吕格于1912 年对投影公式加以补充,故称为高斯-克吕格投影(Gauss-Kruger,简称GK),又名"等角横切椭圆柱投影”。中央经线和赤道投影为相互垂直的直线,其它经线…

小程序的 HelloWord 01《 程序员变现指南之 微信QQ 小程序 真的零基础开发宝典》

本系列教程是针对粉丝的变现教程&#xff0c;还不是粉丝的可以关注我并且到社区&#xff1a;https://bbs.csdn.net/topics/603436232 进行打卡&#xff0c;不是老粉的也可以获取最终的技术变现学习&#xff0c;最终还有详细的变现教程等你来。 前言 《 程序员变现指南之 微信…

octave中的一些基本操作

1.矩阵的表示&#xff1a;v [1 2 2] %表示1行3列的矩阵 v [1; 2; 2] %表示3行1列的矩阵 v [1 2; 2 3; 4 5] %3*2矩阵 size(v) % 求v的行与列 length(v) %求v的列 2.几个基本矩阵的表示&#xff1a;1&#xff09;s ones(2, 4) %2*4全1矩阵 2&#xff09;m zeros(3, 4) %3…

【大地信】新时代GIS发展趋势与未来展望

一、你必须知道的国内外知名学者 1. RogerTomlinson(罗杰汤姆林森)---GIS之父 Roger Tomlinson从1963年开始创建世界上第一个地理信息系统,即加拿大地理信息系统(CGIS)。因此,Tomlinson被誉为地理信息系统之父。 RogerTomlinson(1933.12---2014.2.9) 2. Michael Fran…

小程序获取头像试试水 02《 程序员变现指南之 微信QQ 小程序 真的零基础开发宝典》

本系列教程是针对粉丝的变现教程&#xff0c;还不是粉丝的可以关注我并且到社区&#xff1a;https://bbs.csdn.net/topics/603436232 进行打卡&#xff0c;不是老粉的也可以获取最终的技术变现学习&#xff0c;最终还有详细的变现教程等你来。 前言 《 程序员变现指南之 微信…

zookeeper 入门讲解实例 转

转 http://www.blogjava.net/BucketLi/archive/2010/12/21/341268.htmlzookeeper使用和原理探究&#xff08;一&#xff09;zookeeper介绍zookeeper是一个为分布式应用提供一致性服务的软件&#xff0c;它是开源的Hadoop项目中的一个子项目&#xff0c;并且根据google发表的&l…

小程序仿微信发现页 03《 程序员变现指南之 微信QQ 小程序 真的零基础开发宝典》

本系列教程是针对粉丝的变现教程&#xff0c;还不是粉丝的可以关注我并且到社区&#xff1a;https://bbs.csdn.net/topics/603436232 进行打卡&#xff0c;不是老粉的也可以获取最终的技术变现学习&#xff0c;最终还有详细的变现教程等你来。 前言 《 程序员变现指南之 微信…

Silverlight与WCF之间的通信(4)silverlight以net.tcp方式调用console上寄宿的wcf服务

&#xff08;由于最近是针对一个demo进行的研究&#xff0c;在之前公开过代码结构&#xff0c;这里只是对需要改动的地方加以说明&#xff09; WCF4.0使得编写wcf服务不再那么复杂&#xff0c;去掉了许多的配置信息&#xff0c;客户端只需要一个服务地址&#xff0c;便可在系统…

Maui学习之路(一)--Windows窗体设置

Maui的学习之路作为 Maui的先行者&#xff0c;我有话要说&#xff0c;微软你为了让我成为牛 B 的程序员真的是煞费苦心&#xff0c;你一定是觉得我不够牛逼所以针对我&#xff0c;存心想气死我。好了废话不多说&#xff0c;Maui现在也算是正式发布了&#xff0c;我有点想用它来…