当前位置: 首页 > Web前端 > HTML

React进阶漫谈

时间:2023-04-03 00:55:15 HTML

这篇文章主要说说我在react学习过程中总结的一些经验和资源。内容逻辑参考了《深入React技术栈》一书和网上很多资源,但没有完全照搬。代码基本都是自己练的,主要是为自己平时的个人学习做一个总结和参考。本文重点内容:样式处理与css模块化、组件间通信(非flux架构)、组件抽象、组件性能优化与React动画。1.样式处理和css模块化在react出现之前,我们通常在写样式的时候将css分开,使用less/sass预处理器。我个人在使用backbone等MV*框架时使用less和nodejs配置一个模块来编译less。但是这样写也有一些问题:命名冲突是一个很常见的问题,所以我们需要制定自己完整的命名规范,防止与项目中引入的库发生冲突。充分利用优先级是一个很好的做法,但是这样写的代码少有点像回调函数塔。虽然我个人并不认为这有什么不妥,甚至很享受这样的编程方式,但确实不利于完全压缩css代码。所以我们引入了css模块。简单的说,如果我们配置css模块,那么你在css中写的class名和你在component中写的class=...都会被重新编译成一个hash字符串,这样我们就不用担心命名冲突了另外,可以自由切换局部和全局css变量(其实这样的css变量默认都是局部的,如果需要全局,我们需要加:global前缀,所以css变量不会被转换成特殊的hash值)需要注意措辞问题。这个时候我们不能只在jsx中使用className。css模块其实限制了我们使用className={style.title}的写法,其实我在尝试的时候因为这个地方的bug,调试了很久,这也给我们带来了一些困难在一定程度上使用css模块重构代码。关于cssmodules的入门介绍,不错,阮一峰老师写了一份:http://www.ruanyifeng.com/blo...另外有同学觉得cssmodules不够优雅。其实上面的写法有限的问题是一件比较麻烦的事情,所以我们可以使用react-css-modules这个库。这个库解决了css模块的一些不太好的问题,因为入门不难,这里就不详细介绍了(可以参考这里和《深入react技术栈》第73页)2.组件间的通信(非flux架构)下面总结一下React组件间通信的几种方式。虽然有redux等最佳实践,但在很多情况下我们仍然需要原生的可用性。组件通信机制。父组件和子组件之间很常见,可以通过props机制传递。子组件使用回调函数与父组件通信。回调函数本身是在父组件中定义的,通过props传递给子组件,在子组件中调用回调函数。使用自定义事件机制,这种方式更加通用和方便,并且可以简化API。下面我们将展开自定义事件机制的详细使用。跨层组件通信上下文机制。但是react的这种机制并不是特别推荐(不特别推荐也不代表以后的版本不提供,但是可能会造成一定的弊端,还是要慎用)。父组件)定义一个getChildContext函数如下:getChildContext(){return{color:"red",}}当然也可以使用事件机制与没有层级关系的组件通信。这个时候就只能用事件机制了,虽然之前分析过其他框架的事件机制部分可以单独使用,但是里面其实有很多方式。我首先尝试了js-signals这个库,React团队也在用这个库,简单易用。npminstallsignals之后,我们可以单独写一个Signal文件:constsignals=require('signals');varSignal={started:newsignals.Signal()};我们可以在组件B中定义接收事件的函数:onStarted(param1,param2){alert(param1+param2);}构造函数(道具){超级(道具);信号.started.add(this.onStarted);//addlistener}然后在A组件中(注意dispatch的时候已经构造了B):handlethis(){Signal.started.dispatch('foo','bar');//dispatchsignalpassingcustomparameters}render(){return(launchevent)}其实类似的组件还有很多,当然我们自己写个函数Weakones都不是问题,方法还有很多,这篇文章就介绍的很好。3.组件抽象mixinmixin是一个被人诟病的东西,还有一个就是不能在ES6写法下使用。我现在写react的时候不会用到,这里简单介绍一下。我们可以在creatingClass的时候传入一个mixins数组,里面包含了一些我们常用的方法:React.createClass({mixins:[method1,method2]//...})这个在ES6的class形式中是不能用的”直接地”。ES7装饰器和mixinES7装饰器的作用是返回一个新的描述符,并将新返回的描述符应用到目标方法中。正如我们稍后将看到的,装饰器不仅可以作用于类的方法/属性,它们还可以作用于类本身。当然,我不是三言两语就能理解的。推荐淘宝前端团队的这篇文章。此外,core-decorators库也值得关注。它有一个mixin方法来实现mixins。原理是使用ES7的装饰器,实现起来也不是很复杂。最后提醒大家,ES7的装饰器虽然很酷,但目前还处于提案阶段。虽然我们已经可以借助babel进行体验,但是离真正的支持还有很长的路要走。高阶组件(HOC)是一个值得一提的话题。我们应该听说过高阶函数,它接受一个函数作为输入或者输出一个函数,比如map、reduce、sort。高阶组件只是一个包装了另一个React组件的React组件。这种封装通常有两种方式:1.PropsProxy:高阶组件操作传递给WrappedComponent的props,2.反向继承(InheritanceInversion):高阶组件继承(扩展)WrappedComponent。高层组件的功能主要有以下几点:1.代码复用、逻辑抽象、从底层准备(bootstrap)代码中提取2.渲染劫持渲染劫持主要是通过反向继承来实现的。我们可以选择是否渲染原组件,也可以改变原组件的渲染结果(注意:我们可以通过varelementsTree=super.render()获取原组件的渲染结果,然后我们可以创建一个改变props后通过原生的cloneElement方法新建节点树)3.State抽象,改变所谓抽象状态的目的是将原组件当成纯展示组件,内部状态分离,状态交接到高级组件进行控制。例如:我们可以抽象出一个控制输入的高级组件,这样就不需要有很多代码来控制输入中的状态。4.道具更改我们可以读取、添加、编辑和删除包装组件的道具。我没有在这里给出代码。为了避免文章太长重复网上其他大部分的专题文章,我主要做了一些总结,具体内容我这里还是推荐一篇文章4.组件性能优化PureRenderPureRender的概念其实和pure相关功能。Pure指的是始终为相同的输入获得相同的输出(react的props和state)。针对这个问题,React有一个shouldComponentUpdate钩子,它默认返回true,可以在props或state改变或接收到新值时由用户重写,这样我们就可以防止在接收到相同的props时重写。使成为。PureRenderMixin这个时候就派上用场了。这是一个官方插件,可以实现以上功能。React是这样介绍它的:如果你的React组件的渲染函数在给定相同的props和状态的情况下呈现相同的结果,那么在某些情况下你可以使用这个mixin来提高性能。其实就是通过浅比较来判断是否应该渲染。这其实是一种性能的取舍和妥协。深度比较的代价确实很大(我们将在下一节中提出更好的解决方案)。写法也比较简单:importPureRenderMixinfrom'react-addons-pure-render-mixin';classFooComponentextendsReact.Component{constructor(props){super(props);this.shouldComponentUpdate=PureRenderMixin.shouldComponentUpdate.bind(this);}render(){returnfoo

;我们可以在这里看到更多。当Immutable.js传输数据时,我们可以使用ImmutableData来进一步提高性能。Immutable.js定义不可变对象。一旦定义了数据结构(MapListArraySet),它就是不可变的。如果我们将它用于状态,我们需要在每次更改时重新分配整个状态。上面说了,在shouldComponentUpdate中使用PureRenderMixin时,出于性能上的取舍,我们只能使用浅比较,但是如果使用Immutable.js,我们有更好的方法:直接使用===或者is来判断,因为Immutable.js比较的是两个对象的hashCode或者valueOf,内部使用一个trie数据结构(比如字典树)来存储,所以性能非常高。另外,由于Immutable.js提供的数据结构是不可变的,所以我们不用担心js中的源对象会随着引用对象的变化而变化,也不必考虑所谓的引用函数中的赋值,给我们编程带来了很多方便。当然,Immutable.js的数据结构不能和原来的数据结构混用也是不方便的,写的时候要特别注意。有关更多信息,请参阅此处。无状态的组件生命周期让React的组件变得非常强大。而且它很复杂,所以很难维护,有时我们经常要写一些没有状态的组件,只接受来自父组件的props。这种组件可以提高react的渲染性能,也是官方推荐的。constHelloWorld=(props)=>
{props.name}
ReactDOM.render(,App)简单高效,有些地方不需要需要改变的,比如没有纯声明性内容的用户交互,可以使用无状态组件。我们想让react的diff算法更高效。需要注意的另一件事是注意反应的差异算法。虽然react有一个diff算法,复杂度只有O(N),但是这个算法并不是万能的。我们如果想要最大限度地发挥react的性能,就必须要照顾好这个diff算法。总的来说,这个diff算法有3个实现提纲:比较两棵树,react认为节点的跨层操作移动较少,所以只会比较同层的dom节点,也就是同一parent下的字节点节点,当发现该节点不存在时,删除该节点,当发现添加该节点时,插入该节点。为了迎合这种策略,我们尽量不要对dom节点进行跨级操作(比如将某个字节点转移到某个孙节点),因为效率比较低。组件间比较:如果是同类型组件,则按照第一种策略进一步比较虚拟dom树;如果不是,则判断该组件为脏并替换所有字节点;对于同一类型的组件,有可能它的虚拟dom树没有发生变化。如果你能确切地知道这一点,你可以节省大量的diff操作时间。因此,react允许用户使用shouldComponentUpdatehook来判断一个组件是否发生了变化。为了迎合这种策略,我们可以使用上面提到的PureRender或Immutable.js。当节点处于同一层级时,react提供了insert、move、delete操作,主要是指类似的节点,比如
  • 标签,所以react允许开发者为同一层级的节点添加唯一的key进行操作。相同的key被认为是同一个节点。之后react有自己的一套算法规则来移动节点以满足要求(详见《深入react技术栈》第176页)。为了迎合这个规则,我们需要给li标签加上一个key(其实已经被react强制了)。另外,在开发过程中,尽量减少将最后一个节点移动到第一个节点的情况,因为此时react需要进行大量的移动操作。5.React动画缓动函数对于各种动画,缓动体验一般为:线性<缓动淡入淡出<弹簧弹性动画\cubicbezier贝塞尔曲线。动画的方式有css动画和js动画,但是很多时候我们是一起使用的,所以没有必要太细的去区分。成熟的动画库其实,动画往往是作者忽略的一个方面。由于我还没有毕业,大部分时间都是自己做一些小东西。最终,动画成为可有可无的一环。另外,现在有很多各种各样的动画库,太方便了,只需要一个类一行代码就可以做出比较像样的效果,懒得去探究了。这部分主要推荐一些成熟的动画库。第一个是ReactCSSTransitionGroup。这个动画库提供了一些生命周期钩子,我们可以使用它们来添加动画。学习API的过程非常简单。相信了解以上部分的同学可以直接按照给出的链接进行学习。还有react-smooth动画库,也是比较有意思的一个动画库,写的类似css多关键帧动画。并且可以在这里实现几个缓动函数动画。react-motion也是推荐的动画库。如果你想使用弹簧动画,这似乎是一个更好的选择。另外,不说react,还有一个让我印象深刻的不得不提的就是vivus.js的svg动画库,真的很酷。