整洁代码 or 糟糕代码: React 最佳实践

React   2017-11-23 20:07:13 发布
您的评价:
     
0.0
收藏     0收藏
文件夹
标签
(多个标签用逗号分隔)

本文将重点介绍适用于现代 React 软件开发的整洁代码实践。我还会谈谈 ES6/ES2015 带到台面上的一些“糖”。

什么是整洁代码?为什么我在乎?

整洁代码是一种一致的编程风格,使你的代码更易于编写,阅读和维护。开发人员通常花费时间解决问题,一旦问题解决,他们就会提出 pull 请求。我认为你不仅仅因为代码“工作”就完事。

现在是去清理代码的好时机,通过删除死代码(僵尸代码)、重构和删除任何注释掉的代码! 为了可维护性而努力。问问自己:“从现在开始六个月后,其他人能够理解这段代码吗?”

简单地说,写你会很自豪地带回家并展示给母亲看的代码。

你为什么在乎?因为如果你是一个优秀的开发人员,那么你很懒。请听我解释 – 那是一句褒扬的话。一个优秀的开发人员,在遇到不止一次需要做某事的情况下,通常会找到一个自动化(或更好的)解决方案来完成手头的任务。所以,因为你很懒,认同整洁代码的技巧将会减少来自 pull 请求代码评审的变化频率,以及减少一遍又一遍地回到同一段代码的频率。

整洁代码通过“气味测试”

整洁的代码应该通过气味测试。这是什么意思?我们都看过代码(我们自己的或别人的)并说:“这里不太对劲。” 记住,如果觉得它不对劲,那很可能就是不对劲。好的代码都是深思后一起来的。如果你觉得你正在试图把一个方形的钉子钉进一个圆形的洞里,那么就暂停一下,后退一步,然后休息一下。在多次尝试后,你会想出一个更好的解决方案。

整洁的代码是 DRY (不要重复自己)

DRY 是代表“不要重复自己”的首字母缩略词。如果你在多个地方做同样的事情,请合并重复的代码。如果你在你的代码中看到了模式(复写),那是表示应该进行 DRY 的重要迹象。有时这意味着从显示屏退后,直到你无法看清文本,然后从字面上寻找模式。

// Dirty
const MyComponent = () => (
  <div>
    <OtherComponent type="a" className="colorful" foo={123} bar={456} />
    <OtherComponent type="b" className="colorful" foo={123} bar={456} />    
  </div>
);
// Clean
const MyOtherComponent = ({ type }) => (
  <OtherComponent type={type} className="colorful" foo={123} bar={456} />
);
const MyComponent = () => (
  <div>
    <MyOtherComponent type="a" />
    <MyOtherComponent type="b" />
  </div>
);

有时候,正如我们上面的实例,对代码进行 DRY 实际上可能会增加代码的规模。然而,对代码进行 DRY 通常也会提高可维护性。

请注意,对代码进行 DRY 可能会过了头。因此,必须懂得适可而止。

整洁的代码是可预料和可测试的

写单元测试不仅仅只是一个好主意,还几乎应该成为强制性的。毕竟你怎能确保你的新功能没在其它地方引入 BUG 呢?

许多的 React 开发者依靠 Jest 的 zero-configuration 测试运行器,并生成代码覆盖率报告。如果你对视觉上的前后对比测试感兴趣,请查看美国运通公司自有的 Jest Image Snapshot

整洁的代码是自我注释的

你曾遇过这种情况吗?你写了些代码而且确保它有充分的注释。后来你发现了一个 BUG ,因此你倒回去修复代码,但你有记得更改注释去反映出最新的逻辑吗?可能有也可能没有。下一个人去看你的代码时可能会因为你的注释而掉入一个陷阱。

添加注释只是为了解释复杂的想法/逻辑。也就是不需要为显而易见的代码注释了。可以减少视觉上的杂乱。

// Dirty
const fetchUser = (id) => (
  fetch(buildUri`/users/${id}`) // Get User DTO record from REST API
    .then(convertFormat) // Convert to snakeCase
    .then(validateUser) // Make sure the the user is valid
);

在整洁版上我们重命名了一些方法去更好地描述它们是干嘛的,因此消除了原本需要的注释且减少了视觉上的混乱。并且限制了未来潜在的代码与注释不匹配的混乱。

// Clean
const fetchUser = (id) => (
  fetch(buildUri`/users/${id}`)
    .then(snakeToCamelCase)
    .then(validateUser)
);

给事物命名

在我之前的文章 作为子组件的函数是一种反模式 中,我强调了给事物命名的重要性。我们都应该认真考虑变量名、函数名,甚至文件名。

以下是一些指导原则:

  • 布尔变量或返回布尔值的函数,应该以 “is”、 “has” 或 “should”开头。

    // Dirty
    const done = current >= goal;
    // Clean
    const isComplete = current >= goal;
  • 函数的命名应该是它们能做的,而不是它们如何做的。换句话说,不要在命名中公开实现的细节。为什么?因为你怎么做有一天可能会改变,而你不应该因为它重构你的调用代码。例如,今天你可能会从 REST API 加载你的配置,但是你可能决定明天将其并入 JavaScript 中。

    // Dirty
    const loadConfigFromServer = () => {
      ...
    };
    // Clean
    const loadConfig = () => {
      ...
    };

整洁代码遵循成熟的设计模式和最佳实践

计算机已经存在了很长的一段时间。多年来,程序员通过解决某些问题,发现了模式。并称之为设计模式。换句话说,有些算法已经被证明是可以工作的,所以你应该站在那些在你之前的人的肩膀上,这样你就不必犯同样的错误了。

之后有了最佳实践。它们与设计模式类似,但是更广泛,它们不是针对编码算法。他们可能是诸如“你应该用 Lint 优化你的代码” 或者 “当编写一个库包时,包含 React 作为一个 peerDependency” 这些做法。

构建 React 应用程序时,请遵循以下最佳实践。

  • 使用函数,每个函数都有一个功能。 这就是所谓的单一责任原则。 确保每个功能都能完成一项工作,并做得很好。 这可能意味着将复杂的组件分解成许多较小的组件。 这也能使得测试更容易。

  • 留心观察脆弱的抽象。换句话说,就是不要将您的内部需求强加给您的代码使用者。

  • 遵循严格的 Lint 化规则。这将帮助您编写整洁,一致的代码。

整洁代码不(一定)要花更长的时间去写

我总是听到这样的说法:编写整洁的代码会降低生产力。这简直是胡扯。是的,最初你可能需要放慢速度然后才能加速,但是最终你的步伐会随着你写更少的代码而加快。

不要低估“重写因子”以及修正来自代码评审的意见所花的时间。如果你把代码分成几个小模块,每个模块都有一个单独的职责,那么很有可能你再也不需要碰触大多数模块了。在“写代码然后忘记它”上你会节约时间。

脏代码与整洁代码实例

DRY 化这些代码

让代码看起来像下面的例子中那样。就像我前面提到的,你前进一步处理或后退一步处理都来自于你所看到的。你有看到什么模式了么?注意组件 Thingie 与 ThingieWithTitle 是类似的,除了 Title 组件。这种代码就非常适合 DRY 化改造。

// Dirty
import Title from './Title';
export const Thingie = ({ description }) => (
  <div class="thingie">
    <div class="description-wrapper">
      <Description value={description} />
    </div>
  </div>
);
export const ThingieWithTitle = ({ title, description }) => (
  <div>
    <Title value={title} />
    <div class="description-wrapper">
      <Description value={description} />
    </div>
  </div>
);

这里,我们允许通过 children 来访问 Thingie。当我们创建 ThingieWithTitle 的时候,ThingieWithTitle 包装了 Thingie ,把 Title 当做了 children。

// Clean
import Title from './Title';
export const Thingie = ({ description, children }) => (
  <div class="thingie">
    {children}
    <div class="description-wrapper">
      <Description value={description} />
    </div>
  </div>
);
export const ThingieWithTitle = ({ title, ...others }) => (
  <Thingie {...others}>
    <Title value={title} />
  </Thingie>
);

默认值

看看下面代码段。它将 className 默认设置为 “icon-large” ,使用逻辑或(OR)语句,类似于你祖父可能做过的方式。

// Dirty
const Icon = ({ className, onClick }) => {
  const additionalClasses = className || 'icon-large';
  return (
    <span
      className={`icon-hover ${additionalClasses}`}
      onClick={onClick}>
    </span>
  );
};

这里我们使用 ES6 默认语法来替代空字符串表示的未定义值。这允许我们使用 ES6 中的 fat-arrow 函数的单语句,这就消除了对 return 语句的需求。

// Clean
const Icon = ({ className = 'icon-large', onClick }) => (
  <span className={`icon-hover ${className}`} onClick={onClick} />
);

在这个更简洁的版本中,React 是自动设置默认值的。

// Cleaner
const Icon = ({ className, onClick }) => (
  <span className={`icon-hover ${className}`} onClick={onClick} />
);
Icon.defaultProps = {
  className: 'icon-large',
};

为什么是这个清理器呢?并且它真的很好嘛?难道这三个版本不是做类似的事情吗?对大多数情况而言,答案是肯定的。React 支持设置 prop 默认值的优势是这样可以产生更高效的代码,在基于生命周期组件的类中设置默认 prop 的值,同时支持你的默认值通过 propType 检查。但这还有另一个优势:它把默认值逻辑从组件自身中分离出来。

例如,你可以这么做,将你所有的默认 prop 保存到一个地方。我并不是推荐你这样做;我只是说你可以有这么做的变通选择。

import defaultProps from './defaultProps';
...
Icon.defaultProps = defaultProps.Icon;

从渲染分离有状态的部分

将有状态的数据加载逻辑与渲染(或展示)逻辑混合可能导致组件的复杂性。反之,写一个专门负责加载数据的有状态的容器组件,然后写一个专门负责展示数据的组件。这被称为 容器模式

在下面的示例中,用户数据在单个组件中加载和展示。

// Dirty
class User extends Component {
  state = { loading: true };

  render() {
    const { loading, user } = this.state;
    return loading
      ? <div>Loading...</div>
      : <div>
          <div>
            First name: {user.firstName}
          </div>
          <div>
            First name: {user.lastName}
          </div>
          ...
        </div>;
  }

  componentDidMount() {
    fetchUser(this.props.id)
      .then((user) => { this.setState({ loading: false, user })})
  }
}

在整洁的版本中,关注点(加载数据、展示一个加载图标,以及展示数据)已经分离。这不仅使代码更容易理解,也减少了测试的工作量,因为可以独立测试每个关注点。而且由于 RenderUser 是一个无状态的功能组件,结果是可预测的。

// Clean
import RenderUser from './RenderUser';
class User extends Component {
  state = { loading: true };

  render() {
    const { loading, user } = this.state;
    return loading ? <Loading /> : <RenderUser user={user} />;
  }

  componentDidMount() {
    fetchUser(this.props.id)
      .then(user => { this.setState({ loading: false, user })})
  }
}

使用无状态函数组件

无状态函数组件 (SFCs) 是在 React v0.14.0 引入的,用来简化仅渲染组件编写。有些开发者思维还没有转过来,例如下面组件是可以很容易的转化为一个 SFC 组件的:

// Dirty
class TableRowWrapper extends Component {
  render() {
    return (
      <tr>
        {this.props.children}
      </tr>
    );
  }
}

该例子的简洁版本去掉了很多糟糕版本的无用代码。由于没有创建实例,通过 React 内核优化,可以节省很多内存:

// Clean
const TableRowWrapper = ({ children }) => (
  <tr>
    {children}
  </tr>
);

 

来自:https://www.oschina.net/translate/clean-code-dirty-code

 

扩展阅读

React.js的一些入门基础知识
React.js在Codecademy中的实际应用
React.js 最佳实践(2016)
前端之React实战-交互与动态UI
使用React Native来撰写跨平台的App

为您推荐

SpringMVC 清晰简单教程
Bootstrap3 CSS样式基本用法总结
基于jQuery的一个简单的图片查看器
AngularJS – 如何处理 XSS 漏洞
JQuery其他常用函数

更多

React
相关文档  — 更多
相关经验  — 更多
相关讨论  — 更多