react进阶漫谈

本文主要谈本身在react学习的过程当中总结出来的一些经验和资源,内容逻辑参考了“深刻react技术栈”一书以及网上的诸多资源,但也并不是彻底照抄,代码基本都是本身实践,主要为平时我的学习作一个总结和参考。css

本文的关键内容:样式处理与css模块化、组件间通讯(非flux架构)、组件抽象、组件性能优化以及React 动画五种内容。html

1.样式处理与css模块化

在react出现以前,咱们写样式通常是将css分离的,而且用less/sass预处理器,我我的在用backbone等MV*框架的时候就习惯用less而且用nodejs配置一个模块用来编译less。前端

但这样写会有一些问题:node

  • 命名冲突是一个很常见的问题,所以,咱们要制定出一套本身的完整命名规范来,而且要防止和项目中引入的库出现冲突。react

  • 充分利用优先级是一个比较好的实践,可是这样写出的less代码有点像回调函数塔,虽然我本人并不以为这有什么很差甚至还比较享受这种编程,但这的确不利于充分压缩css代码。git

因而咱们引入css modules。github

简单的说,若是咱们配置了css modules的话,那么你在css中写的类名和你在组件中写的class = ...都会被从新编译成一个哈希字符串,这样咱们就不用考虑命名冲突的问题了,另外也能够比较自由的在local和global的css变量之间切换(实际上,这样的css变量默认都是local的,若是须要global,咱们须要:global前缀,这样的话css变量就不会被转化成特殊的哈希值了)web

须要注意的是写法问题,这个时候咱们就不能在jsx中仅仅用className了,css module实际上限制了咱们必需要用className={style.title}这样的写法,实际上我在尝试的时候由于这个地方的bug调试了好久,而这也在某一种程度上给利用css module进行重构代码带来了一些困难。算法

关于css modules的入门介绍,没错,阮一峰老师写了一份:http://www.ruanyifeng.com/blo... spring

另外,有的同窗认为css modules并不够优雅,实际上上文的写法限定的问题就是一个麻烦事,因此咱们能够用react-css-modules库,这个库解决了css modules的一些不是很好的问题,由于上手并不难,这里不详细介绍了(能够参考这里以及“深刻react技术栈”73页)

2.组件间通讯(非flux架构)

接下来咱们总结一下react组件间通讯的几种方式,虽然如今有了redux等最佳实践,可是不少时候咱们仍是须要原生可用的组件通讯机制。

父组件向子组件之间

很是常见,经过props机制传递便可。

子组件向父组件通讯
  • 利用回调函数,回调函数自己定义在父组件中,经过props方式传递给子组件,在子组件中调用回调函数。

  • 利用自定义事件机制,这种方法更通用方便,而且能够简化API,关于自定义事件机制的详细使用方法咱们在接下来展开。

跨级组件通讯
  • context机制。不过这种机制react并非特别推荐(不是特别推荐并不表明会在未来的版本没有,只是说明可能会产生必定的弊端所以要慎用少用),context机制须要在上级组件(能够是父组件的父组件)定义一个getChildContext函数以下:

getChildContext(){
    return{
        color:"red",
    }
}
  • 固然也能够用事件机制

没有层级关系的组件通讯

这回只能用事件机制了,虽然我以前分析过别的框架的事件机制部分均可以单独拎出来用,可是这里面实际上有好多方式。

我首先试了一下js-signals这个库,这个也是React团队使用的,用起来也还简单,npm install signals以后,咱们能够单独写一个Signal文件:

const signals= require('signals');

var Signal = {
  started : new signals.Signal()
};

咱们能够把接收事件的函数定义在组件B中:

onStarted(param1, param2){
        alert(param1 + param2);
 }
 constructor(props){
        super(props);
        Signal.started.add(this.onStarted); //add listener
 }

而后在组件A中(注意dispatch的时候要保证B已经被构造出来了):

handlethis () {
     Signal.started.dispatch('foo', 'bar'); //dispatch signal passing custom parameters
 }

 render(){
     return (
         <button onClick={this.handlethis}>发射事件</button>
     )
 }

其实还有不少相似的组件,固然咱们本身写一个功能弱的也不成问题,更多的方式,这篇文章介绍的不错。

3.组件抽象

mixin

mixin是一个饱受诟病的东西,另外蛋疼的是在ES6的写法下也不能用,笔者如今写react的时候都已经不用了,因此这里进行简单介绍。

咱们能够经过在createClass的时候传入一个mixins数组,这个数组里是咱们的一些通用的方法:

React.createClass({
    mixins:[method1,method2]
    //...
})

这在ES6的class形式下是不能“直接”使用的。

ES7 decorator 与 mixin

ES7 的 decorator,做用就是返回一个新的 descriptor,并把这个新返回的 descriptor 应用到目标方法上。稍后咱们将会看到,decorator 并不是只能做用到类的方法/属性上,它还能够做用到类自己。

固然,这个我只言片语确定说不明白的,这个我要推荐淘宝前端团队的这篇文章

另外,core-decorators这个库值得关注,它里面有一个mixin方法用于实现mixin,原理就是用了ES7 decorator,实现起来也不是很是复杂。

最后,提醒一下ES7 decorator虽然很酷,可是目前还处于提案阶段,虽然借助babel咱们已经能够体验了,可是距离真正支持还有一段距离

高阶组件(HOC)

这是一个颇值得一提的话题。

咱们应该据说太高阶函数,这种函数接受函数做为输入,或者是输出一个函数,好比map、reduce以及sort等函数。

一个高阶组件只是一个包装了另一个 React 组件的 React 组件, 这种包装一般有两种方式:

一、属性代理(Props Proxy):高阶组件操控传递给 WrappedComponent 的 props,
二、反向继承(Inheritance Inversion):高阶组件继承(extends)WrappedComponent。

高阶组件的功能主要有如下几点:

一、代码复用,逻辑抽象,抽离底层准备(bootstrap)代码
二、渲染劫持

  • 渲染劫持主要经过反向继承来实现,咱们能够选择是否渲染原组件,也能够改变原组件的渲染结果(注意:咱们经过 var elementsTree = super.render()能够拿到原组件的渲染结果,而后咱们能够改变props以后,经过原生cloneElement方法建立出新的节点树)

三、State 抽象和更改

  • 所谓抽象state的目的,就是将原组件做为一个纯粹的展现型组件,分离内部状态,将state交给高阶组件来控制。好比:咱们能够抽象出一个控制input的高阶组件,从而不用在input中来有不少控制state的代码。

四、Props 更改

  • 咱们能够读取、增长、编辑、删除被包裹组件的props

我在这里没有给出代码,为了不文章过于冗长以及和网上其余专题文章大部分重复,我主要是进行一些总结,具体内容我这里仍然是推荐一篇文章

4.组件性能优化

PureRender

PureRender这个概念实际上和纯函数有关,Pure指的是对一样的输入(对于react来讲就是props和state)老是获得相同的输出,针对这个问题,React有一个shouldComponentUpdate钩子,这个钩子默认返回true,用于props或者state改变或者接收到新的值时候,能够供用户重写,这样在接受到相同的props的时候咱们就能够防止其从新渲染。

PureRenderMixin在这个时候要派上用场了,这是一个可以实现上述功能的官方插件,react是这样介绍它的:

If your React component's render function renders the same result given the same props and state, you can use this mixin for a performance boost in some cases.

其实是经过一个浅比较来肯定是否是该被渲染,这其实是一个性能上的权衡和妥协,深比较真的是耗费太多(咱们在下一节会提出一个更好的解决方案)。
写法也比较简单:

import PureRenderMixin from 'react-addons-pure-render-mixin';
class FooComponent extends React.Component {
  constructor(props) {
    super(props);
    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
  }

  render() {
    return <div className={this.props.className}>foo</div>;
  }
}

咱们能够在这里查看更多信息。

Immutable.js

在传递数据的时候,咱们能够用Immutable Data进一步提升性能。

Immutable.js定义了不可变对象,一个数据结构(MapListArraySet)一旦被定义,就不可变了,咱们把它若是用于state,那么每次变化的时候须要将state整个从新赋值。

上文提到,在shouldComponentUpdate使用PureRenderMixin因为性能权衡咱们只能使用浅比较,可是若是咱们用了Immutable.js,咱们有更好的方式:直接用=== or is就能够判断,由于Immutable.js比较的是两个对象的hashCode或者valueOf,而且内部使用了trie数据结构(好比字典树)来存储,所以性能很高。

另外,因为Immutable.js中提供的数据结构是不可变的,咱们不用担忧js中源对象跟随引用对象的变化而变化的问题,也不用考虑函数中所谓的引用赋值,这给咱们的编程带来了不少方便。

固然也有不方便的是Immutable.js的数据结构并不能和原生的数据结构混用,所以写法上须要格外注意,关于更多资料请看这里.

无状态组件

生命周期让react的组件变得功能很是强大而且复杂,从而难以维护,而有的时候咱们又要常常写一些自身没有状态,只从父组件接受props的组件,这种组件能够提升react的渲染性能,也被官方推荐。

const HelloWorld = (props) => <div>{props.name}</div>
ReactDOM.render(<HelloWorld name="HelloWorld" />,App)

简单,高效,在有些不须要改变的地方,好比没有用户交互纯声明性质的内容,能够用无状态组件。

react的diff算法

咱们想要让效率更高,还要注意的一点就是要照顾react的diff算法,react虽然有一个复杂度仅为O(N)的diff算法,可是这个算法也不是万能的,咱们要想让react效能最大化,就要去照顾这个diff算法。

总的说来,这个diff算法大概有三点实现概要:

  • 对两棵树进行比较,react认为,对节点的跨层级操做移动较少,因此只会对相同层级的dom节点进行比较,即同一个父节点下的字节点,当发现节点已经不存在时,就会删除节点,当发现节点新增时候,就会插入节点。

    • 为了迎合这个策略,咱们尽可能不要对dom节点进行跨层级操做(好比把某一个字节点转而挂在到某一个孙节点下面),由于这样效率是比较低的。

  • 对组件之间进行比较:若是是同一个类型的组件,按照第一条策略进一步比较虚拟dom树;若是不是,就将该组件判断为dirty,从而替换全部字节点;对于同一类型的组件,有可能其虚拟dom树没有发生变化,若是可以确切知道这一点,那么就能够节省大量diff的操做时间,所以,react容许用户经过shouldComponentUpdate钩子来判断组件是否发生变化。

    • 为了迎合这个策略,咱们可使用上面提到的PureRender或者Immutable.js。

  • 当节点处于同一个层级,react提供了插入、移动、删除操做,这里主要指类似节点,好比<li>标签,所以react容许开发者将同一个层级的节点添加惟一key进行操做,同一个key认为是相同节点。以后react有一套本身的算法规则,对节点进行移动操做以达到要求(具体能够参考“深刻react技术栈”176页)。

    • 为了迎合这一规则,咱们要给li标签等添加一个key(实际上已经被react强制),另外,在开发过程当中尽可能减小将最后一个节点移动到第一个的状况,由于这个时候react要进行不少的移动操做。

5.React 动画

缓动函数

对于各类动画来讲,缓动体验通常是:linear < ease淡入淡出 < spring弹性动画\cubic bezier贝塞尔曲线

动画的方式有css动画和js动画,可是不少时候咱们都是一块儿用的,因此区分的太详细彷佛必要性也不大。

成熟的动画库

实际上动画常常是笔者比较忽视的一个方面,因为还没毕业,大多时候都是本身作小东西,最后动画就成了无关紧要的环节,另外如今的各类动画库不少,方便到只须要一个class、只写一行代码就能够作出相对过得去的效果,本身也就疏于探索。

这部份内容主要推荐一些成熟的动画库。

首先是ReactCSSTransitionGroup,这个动画库提供了一些生命周期钩子,咱们能够利用此加动画,具体学API的过程至关简单,我相信看懂上面各个部分的同窗直接按照给出的连接确定能顺利学会。

还有react-smooth动画库,这也是一个比较有意思的动画库,写法相似css的多关键帧动画。而且几种缓动函数动画这里都能实现。

react-motion也是一个值得推荐的动画库,若是想用spring动画这个彷佛是更好的选择。

另外,不说react,还有一个让我印象深入不得不提的就是vivus.js这个svg动画库,不得不说真是酷毙了。

相关文章
相关标签/搜索