今天看啥  ›  专栏  ›  缪宇

【译】我们能从 Redux 源码中学到什么?

缪宇  · 掘金  ·  · 2018-03-23 09:03

【译】我们能从 Redux 源码中学到什么?

我们能从 Redux 源码中学到什么?

我总是听人说,想拓展开发者自身视野就去读源码吧。

所以我决定找一个高质量的 JavaScript 库来深入学习。

我选择了 Redux,因为它的代码比较少。

这篇文章不是 Redux 教程,而是阅读源码后的收获。如果你对学习 Redux 感兴趣,强烈推荐你去看 Redux 教程,这个系列文章是 Redux 的作者 Dan Abramov 写的。

从源码中学习

一些新来的开发者经常问我,怎样才是最好的学习方式?我往往会告诉他们在项目中学习。

当你构建一个项目来实践你的想法时,由于你对它的热爱,会让你度过难熬的 debug 阶段,即使遇到困难也不会放弃。这是一个非常神奇的现象。

但是一个人闭门造车也是有问题的。你不会注意到你开发过程中的坏习惯,你也学不到任何最优的解决方案。你可能都不知道又出了哪些新的框架和技术。在独自写项目的过程中,你很快会发现你的技能达到瓶颈。

只要有可能,我建议你找些小伙伴和你一起开发。

试想一下,坐在你旁边的小伙伴(如果你够幸运,他恰好是个大神),你可以观察他思考问题的过程。你可以看他是如何敲代码的。你可以看他是如何解决算法问题的。你可以学到新的开发工具和快捷键。你会学到许多你一个人开发时学不到的东西。

斯特拉迪瓦里小提琴。

我用斯特拉迪瓦里的小提琴举个例子。斯特拉迪瓦里小提琴以出色的音质闻名世界,在业界可以说是一枝独秀。许多人尝试用各种方法去解释为什么它这么牛逼,从古老教堂抢救出来的木材到特殊的木材的防腐剂。许多人想要复制一把斯特拉迪瓦里小提琴,结果都失败了,因为他们不知道安东尼·斯特拉迪瓦里到底是怎么做的。

设想一下,如果你和安东尼·斯特拉迪瓦里在一个房间里工作,那么所有的独门秘籍你都可以学到。

这下你知道该如何与你的开发小伙伴相处了吧。你只需要安静的坐他旁边,看着他写出一行行斯特拉迪瓦里式的代码。

对于与多人来说,协同编程是一个很好的机会,可以通过别人的代码学到很多东西。

阅读高质量的代码就像读一本精彩的小说一样,比起直接和作者交流,你可能理解起来比较困难。但是你可以通过看注释和代码,获取到有价值的信息。

对于那些认为看源码没什么用的的同学,你可以去看一个故事,一个叫比尔·盖茨的高中生,为了了解某个公司的机密,他甚至去翻人家的垃圾桶找源码。

如果你也可以像比尔·盖茨那样不厌其烦的看源码,那还在等什么?找一个 github 仓库,看源码吧!

咦,源码呢?

阅读源码的同时,你也可以去看官方文档,官方文档的结构就像作者写的代码一样,写得好的官方文档就让你仿佛坐在作者旁边一样。你也可以在上面看到别人遇到的问题。官方文档中的超链接提供了丰富的扩展阅读的资源。在评论区你还可以和大神一起交流。

平时我也会在 YouTube 看别人写代码,我推荐大家去看SuperCharged 直播写代码系列,来自 Google Chrome 开发者的 Youtube 频道。看两个 Google 工程师直播写一个项目,看他们是如何处理性能问题的,和大家一样,他们也会被自己拼写错误导致的 bug 卡住。

读 Readux 源码的收获

ESLint

Linting 用于检查代码,发现潜在的错误。它帮助我们保持代码风格的一致性和整洁。你可以自己定制规则,也可以用预设的规则(比如 Airbnb 提供的规则)。

Linting 在团队开发中特别有用。它让所有代码看起来像一个人写的。它可以强迫开发人员按照公司的代码风格来写代码(同事不用在阅读代码上花太多时间)。

Linters 不仅仅是为了美观,它会让你的代码更符合语言特性。比如它会告诉你什么时候使用 “const” 关键字来处理那些没有被重新赋值的变量。

如果你使用了 React 插件,它会警告你关于组件可以被重构成无状态的函数式组件。也是可以让你学习 ES6 语法,告诉你的某段代码可以用语法新特性来写。

在你的项目中轻松使用 ESlint:

  1. 安装 ESlint。
$ npm install --save-dev eslint
  1. 配置 ESlint。
./node_modules/.bin/eslint --init
  1. 在你的 package.json 文件中设置 npm 脚本来运行你的 Linter(可选)。
"scripts": {
  "lint": "./node_modules/.bin/eslint"
}
  1. 运行 Linter.
$ npm run lint

查看它们的官方文档,了解更多。

许多编辑器也有插件来检查你的代码。

有些时候 Linter 会对一些正确的代码报错,比如 console.log。你可以告诉 Linter 忽略这行代码,不对其进行检查。

在 ESlint 中忽略检查,你可以这样写代码注释:

// 忽略一行
 console.log(‘Hello World’); // eslint-disable-line no-console
// 忽略多行
 /* eslint-disable no-console */
 console.log(‘Hello World’);
 console.log(‘Goodbye World’);
 /* eslint-enable no-console */

检查代码是否被压缩了

在源码中我发现一个 “isCrushed()” 的空函数,很奇怪。

后来我发现它的目的是为了检查代码是否被压缩了。在代码压缩过程中,函数名字和变量会被缩写。当你在开发的时候如果使用了压缩后的代码,如果一个条语句被检测到仍然有 “isCrushed()” 存在,就会有警告提示。

不要害怕报错

在学习 Redux 源码之前我很少在代码中抛异常。JavaScript 是一个弱类型,所以我们不知道函数中传入参数的类型。所以我们必须要像强类型语言那样对于错误要抛出异常。

使用 try…catch…finally 语句来抛出异常。这样做可以方便你 debug,以及理清代码逻辑。

在控制台中产生的错误,可以很方便堆栈跟踪。

很有用的栈跟踪。

做异常信息处理让你的代码逻辑清晰。比如,如果有一个 "add()" 函数,只允许传入数字,如果传入的不是数字就要抛出异常。

function add(a, b) {
    if(typeof a !== ‘number’ || typeof b !== ‘number’) {
	throw new Error(‘Invalid arguments passed. Expected numbers’);
    }
    return a + b;
}
var sum = add(‘foo’, 2); 
// 抛出异常后会终止代码执行

组合函数

源码中有一个 “compose()” 函数,根据已有的函数构建出新的函数:

 function compose(…funcs) {
   if (funcs.length === 0) {
     return arg => arg
   }
   if (funcs.length === 1) {
     return funcs[0]
   }
   const last = funcs[funcs.length — 1]
   const rest = funcs.slice(0, -1)
   return (…args) => rest.reduceRight((composed, f) => f(composed),    last(…args))
 }

如果我有两个已知的 square 函数和另一个 double 函数,我可以把它们组成一个新函数。

 function square(num) {
   return num * num;
 }
function double(num) {
   return num * 2;
}
function squareThenDouble(num) {
   return compose(double, square)(num);
}
console.log(squareThenDouble(7)); // 98

如果我没看过 Redux 的源码,我都不知道还有这种犀利的操作。

原生方法

当我在看 “compose” 函数的时候,我发现了一个原生数组方法 “reduceRight()”,我之前都没听到过。这让我想知道还有多少我没听过的原生方法。

我们来看一个代码片段,一个使用了原生数组方法 “filter()”,一个没有,通过对比看原生方法存在的价值。

 function custom(array) {
   let newArray = [];
   for(var i = 0; i < array.length; i++) {
     if(array[i]) {
       newArray.push(array[i]);
     }
   }
   return newArray;
 }
 function native(array) {
   return array.filter((current) => current);
 }
 const myArray = [false, true, true, false, false];
 console.log(custom(myArray));
 console.log(native(myArray));

你可以看到使用 “filter()” 会让你的代码变得简洁。更重要的是,避免了重复造轮子。“filter()” 会被使用上百万次,比起你自己造轮子,可以避免很多 bug。

当你想造轮子的时候,先看看你的问题是否已经被原生方法解决了。你会惊喜的发现有非常多的实用方法在你用的编程语言中。(比如,可以看看 Ruby 的数组的重新排列的方法

描述性的函数名

在源码中,我看到了许多有很长名字的函数。

  1. getUndefinedStateErrorMessage
  2. getUnexpectedStateShapeWarningMessage
  3. assertReducerSanity

虽然这函数名读起来会让你的舌头打结,但你可以清楚的知道这个函数是做什么的。

在你的代码中使用描述性的函数名,让你更多的是读代码而不是写代码,别人也可以很轻松的阅读你的代码。

用较长的描述性函数名带来的好处远超过敲击键盘所带来的快感。现代的文本编辑器都有自动补全功能,它可以帮助你输入,所以没有理由再使用类似 “x” 或者 “y” 的变量名。

console.error vs. console.log

不要总是使用 console.log,如果你要抛出异常,请使用 console.error,你可以在 console 中看到红色的打印内容和栈的跟踪。

console.error()

查看 console 文档,看看其他的方法。比如计算运行时间的计时器(console.time()),用表格方式打印信息(console.table()),等等。


不要害怕去读源代码。你肯定会学到一些东西,甚至可以为它贡献代码。

在评论中分享你在阅读源码中的收获吧!


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏




原文地址:访问原文地址
快照地址: 访问文章快照