whisperfairy

React Component 优化

whisperfairy · 2016-12-14翻译 · 1399阅读 原文链接

沃尔玛实验室 笔者与他的团队对React组件化的最佳实践做了大量的Code Review,他们从不同类型的天才工程师的整理了各种不同类型的关于React Component的写法,在这篇文章中笔者将分享五个他最喜欢与认同的构建模式与写法。

首先,我们需要明确

整洁的代码才是好代码

就像有不止一种方法去打理小猫咪的毛(- -!),写React组件的方法也各式各样,

我们可以按我们的习惯去写组件,但这并不意味着我们这么做就是合理的,代码是写给人看的计算机仅仅是解释那些你丢给他们的那些杂乱无章的字符。你的同行会感谢你将自己那部分代码写的整洁,正如他们为你做的那样。(我为人人,人人为我)。

整洁的组件有如下几个特征

  • 💡 即使没有注释,你也能很容易的明白它的作用
  • 🚀 精简且高效,没有冗余代码
  • 🐛 方便调试,让你能在别人给你找麻烦之前,更早的发现问题
  • 📝 让你的代码用最少的代码表达最多的内容 > “Less is more.” > > — Mies van der Rohe

1. 无状态组件(Stateless Functional Component SFC)

优点:优化应用渲染速度,专注render函数,精简代码 缺点:没有生命周期,但可以与高阶函数结合。 我最喜欢的React组件优化方式就是常常会被忽视无状态组件(后文用SFC代替),我钟情于SFC精简的写法,相比于继承自React Component 复杂的代码量还优化了组件的渲染效率(译者注 使用无状态组件 React 在渲染的时候也省掉了将“组件类” 实例化的过程。)

总而言之,SFC能有效地提高程序的运行效率

如果举个形象的例子,SFC就好像方程式赛车,剔除了无用的部件让赛车跑得更快。

从原理上解释,SFC就是把组件中的render部分抽出封装,通过减少流程,提高代码的运行效率。

> 我们将通过SFC来避免无用的判断逻辑和内存分配从而提高程序的运行效率。

> > — Ben Alpert, React Blog

代码范例

让我们通过编写一个简单的关系查询组件来看看SFC是如何减少代码量的。

首先当我们使用传统的React.Component继承的方式来写一个关系查询组件(部分代码省略)

export default class RelatedSearch extends React.Component {
  constructor(props) {
    super(props);
 this._handleClick = this._handleClick.bind(this);
  }
 _handleClick(suggestedUrl, event) {
    event.preventDefault();
    this.props.onClick(suggestedUrl);
  }
 render() {
    return (
      <section>
        <h1>Related Searches:</h1>
        &lt;Layout x-small={2} small={3} medium={4} padded={true}&gt;
          {this.props.relatedQueries.map((query, index) =&gt;
            &lt;Link
              className="related-search-link"
              onClick={(event) =&gt;
                this._handleClick(query.searchQuery, event)}
              key={index}&gt;
              {query.searchText}
            &lt;/Link&gt;
          )}
        &lt;/Layout&gt;
      </section>
    );
  }
}

然后使用SFC的方式,我们整整省略了29%的代码量

const _handleClick(suggestedUrl, onClick, event) =&gt; {
  event.preventDefault();
  onClick(suggestedUrl);
};
const RelatedSearch = ({ relatedQueries, onClick }) =&gt;
  <section>
    <h1>Related Searches:</h1>
    &lt;Layout x-small={2} small={3} medium={4} padded={true}&gt;
      {relatedQueries.map((query, index) =&gt;
        &lt;Link
          className="related-search-link"
          onClick={(event) =&gt;
            _handleClick(query.searchQuery, onClick, event)}
          key={index}&gt;
          {query.searchText}
        &lt;/Link&gt;
      )}
    &lt;/Layout&gt;
  </section>
`export default RelatedSearch;`

这明显的变化主要是通过省略了ES2015中类的拓展

  • 省略构造函数(减少5行)
  • 使用箭头函数精简渲染函数(减少4行)

这正是SFC令人惊叹的地方,然而更让我着迷的是他提高了代码的可读性。首先,SFC是充分体现了纯组件Pure Components 的函数式编程思想,他专注于管理组件的JSX。移除了不被需要构造函数,并将像handleClick()这样的函数逻辑合理的从组件中分离从而提高组件的可读性。 另一个让我着迷的优点就是SFC通过结构赋值的ES6语法Object Destructuring 将组件的props的统一定义在了最明显的位置,让我们能更轻松地在构造函数的参数中管理组件的Props。

`const RelatedSearch = (**{ relatedQueries, onClick }**) =&gt;`

当我们读到这样的代码的时候,能够更快的找到调用组件能传递到的props,也能让我们在修改组件使用this.props的时候更快的找到对应的参数。

SFC的适用范围

基本上所有的纯组件我们都可以使用SFC的方式进行定义,在沃尔玛实验室,我们使用Redux来管理应用程序的State,这让我们几乎所有的组件都能使用SFC。

然而有两个需要大家注意的地方不能使用SFC:

  • 组件需要使用到生命周期
  • 组件需要使用Refs来获取dom 虽然这两种情况并不常见,但我们还需要注意再设计程序的时候避免将他们与SFC一起使用

2. 条件渲染组件

JSX 并不允许使用if语法,当我们需要使用条件判断逻辑的时候,我们通常会使用三元表达式

render() {
  <div>
    {this.props.isGrid
      ? &lt;SearchResultsGrid /&gt;
      : &lt;SearchResultsList /&gt;}
  </div>
}

这种表达式适合表达(state?A:B)非黑即白的渲染逻辑,但如果我们需要表达上不上车(state?A:Null)的时候,我们可以有更好的选择使用判断条件与组件做与运算再取非(!!state&&A)

render() {
  <div>
    {this.props.isSoftSort
      ? &lt;SoftSortBanner /&gt;
      : null
    }
  </div>
}

render() {
  <div>
    {!!this.props.isSoftSort && &lt;SoftSortBanner /&gt;}
  </div>
}

当然这种写法更偏向与个人的偏好,萝卜青菜各有所爱 、

3. 箭头函数在React与Redux中的使用

ES2015的语法确实是非常的使用,箭头函数arrow functions.是我最喜欢的语法之一,尤其在写组件的时候,在精简代码的过程中他充分发挥了他的作用

const SoftSort = ({ hardSortUrl, sortByName, onClick }) =&gt; {
  return (
    <div>
      Showing results sorted by both Relevance and {sortByName}.
      &lt;Link
        href={`?${hardSortUrl}`}
        onClick={(ev) =&gt; onClick(ev, hardSortUrl)}&gt;
        Sort results by {sortByName} only
      &lt;/Link&gt;
    </div>
  );
};

当我们只需要返回JSX的时候我们能通过省略return让她更加简练

const SoftSort = ({ hardSortUrl, sortByName, onClick }) =&gt;
  <div>
    Showing results sorted by both Relevance and {sortByName}.
    &lt;Link
      href={`?${hardSortUrl}`}
      onClick={(ev) =&gt; onClick(ev, hardSortUrl)}&gt;
      Sort results by {sortByName} only
    &lt;/Link&gt;
  </div>

另一个我们能发挥箭头函数功效的地方就是定义redux的mapStateToProps函数

const mapStateToProps = ({isLoading}) =&gt; {
  return ({
    loading: isLoading,
  });
};

当我们只返回对象的时候我们还能进一步精简代码

const mapStateToProps = ({isLoading}) =&gt; ({
  loading: isLoading
});

使用箭头函数来优化代码的写法,让组件更加紧凑,更加集中在重要的部分,减少了return关键字与烦人的括号,提高代码的可读性。

4. 如何在组件中正确的使用结构赋值和余子式属性(spread 属性)

复杂的组件常常会让我们陷入this.props综合征,因此如何管理组件的props,并让组件方便阅读,便成了迫切需要解决的问题,使用ES2015的结构赋值语法就能充分的解决该问题,下面我们展示一段产品价格组件

render() {
  return (
    &lt;ProductPrice
      hidePriceFulfillmentDisplay=
       {this.props.hidePriceFulfillmentDisplay}
      primaryOffer={this.props.primaryOffer}
      productType={this.props.productType}
      productPageUrl={this.props.productPageUrl}
      inventory={this.props.inventory}
      submapType={this.props.submapType}
      ppu={this.props.ppu}
      isLoggedIn={this.props.isLoggedIn}
      gridView={this.props.isGridView}
    /&gt;
  );
}

Σ( °△°|||),竟然有这么多Props,让我们来改写下:

render() {
  const {
    hidePriceFulfillmentDisplay,
    primaryOffer,
    productType,
    productPageUrl,
    inventory,
    submapType,
    ppu,
    isLoggedIn,
    gridView
  } = this.props;
 return (
    &lt;ProductPrice
      hidePriceFulfillmentDisplay={hidePriceFulfillmentDisplay}
      primaryOffer={primaryOffer}
      productType={productType}
      productPageUrl={productPageUrl}
      inventory={inventory}
      submapType={submapType}
      ppu={ppu}
      isLoggedIn={isLoggedIn}
      gridView={isGridView}
    /&gt;
  );
}

这种方法虽然增加了代码量,但是却提高了程序的可读性,让我们更清晰的使用组件的props,这对于大型组件的维护有着显著地帮助,并且在管理子组件的时候,我们还能使用Spread Attributes 来精简代码

render() {
  const props = this.props;
  return &lt;ProductPrice {...props} /&gt;
}

在上文提到过,无状态组件充分的体现了结构赋值的作用,我们可以通过下述方式管理接收到的Props

const ProductPrice = ({
  hidePriceFulfillmentDisplay,
  primaryOffer,
  productType,
  productPageUrl,
  inventory,
  submapType,
  ppu,
  isLoggedIn,
  gridView
}) =&gt;
  &lt;ProductPrice
    hidePriceFulfillmentDisplay={hidePriceFulfillmentDisplay}
    primaryOffer={primaryOffer}
    productType={productType}
    productPageUrl={productPageUrl}
    inventory={inventory}
    submapType={submapType}
    ppu={ppu}
    isLoggedIn={isLoggedIn}
    gridView={isGridView}
  /&gt;

5.函数定义的简写

这个窍门虽然不是很实用,但写法确实精巧。 当我们想在对象内定义方法的时候,我们可以使用ES2015的语法 Method Definition Shorthand 这在定义组件默认props(defaultProps)时非常有用

Link.defaultProps = {
  onClick(event) {
    event.preventDefault();
    Logger.log(event);
  }
};

如果函数无参,写法会更加精简,

ProductRating.defaultProps = {
  onStarsClick() {}
};

In Conclusion

总结

我希望这些窍门能为大家的开发带来帮助,让大家的代码变得更加美观,并且激发大家优化代码的灵感,记住好代码做得多写得少

> “Speak softly and carry a big stick.” > > — Theodore Roosevelt

            译者注:新手翻译,如有不足欢迎指正,我会及时修改

译者whisperfairy尚未开通打赏功能

相关文章