本周做了啥
给日常使用的 vscode-hg 扩展提了两个没什么技术含量的 PR,顺便蹭了一个 contributor:
分享文章
一些本周阅读过的好文章、以及我的一些总结和个人思考;非常建议你直接阅读原文,毕竟一千个读者就有一千个哈姆雷特,而且我的理解可能是错的。
useMemo 和 useCallback 之间的深入比较
原文地址:《A Deep Dive Comparison Between useMemo And useCallback》 | Technical Blog
- 它们的目的:都是通过缓存提高性能,避免组件重复渲染
- 相似之处:
- 用法一致:与所有 hooks 一样,只能在组件的顶层调用
- 接收的参数一致:第一个为函数,第二个为依赖项
- 功能一致:返回缓存过的值,检测到依赖性发生时重新计算并缓存
- 区别:
- 它们表面上没有真正的区别
- 它们的内部实现也基本一致
- 使用场景的区别:
useCallback
缓存函数,而useMemo
缓存其它类型 - 实现原理的区别:
useCallback
是缓存函数本身,而useMemo
是缓存函数的返回值。
以下是它们的实现方式:
- useCallback
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
hook.memoizedState = [callback, nextDeps];
return callback;
}
- useMemo
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
const hook = updateWorkInProgressHook();
const nextDeps = deps === undefined ? null : deps;
const prevState = hook.memoizedState;
if (prevState !== null) {
if (nextDeps !== null) {
const prevDeps: Array<mixed> | null = prevState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
return prevState[0];
}
}
}
hook.memoizedState = [callback, nextDeps];
return callback;
}
初级开发人员如何为新团队提供价值
原文地址:《How You As a Junior Developer Can Immediately Provide Value When Joining a Team》 | Technical Blog
- **初级开发人员并不意味着不能提供价值:**相反,初级开发人员可以给团队带来很多东西。
- **质疑现状:**如果一个团队长期合作,人们会开始接受彼此的缺点,习惯于某种风格和工作方式,也就会陷入舒适区,这可能会使团队停滞不前。所以你需要保持批判性,寻找团队懒惰、坏习惯、效率低下或者可以改进的地方,并且付诸行动去改变现状。
- **新鲜血液:**任何一个项目组都不可能每时每刻使用最新的技术、工具,可能会使得团队成员没有动力跟上最先进的技术,陷入一种恶性循环之中。你作为一个团队的新成员,可能你反而会在当前领域了解更多最先进的技术和工具,从而打破团队的恶性循环。
- **意识到团队沟通的问题:**长期合作的团队可能面临习惯现有的沟通方式,会自动忽略对方的缺陷,导致缺乏真正的沟通。而你能够意识到这些问题,并帮助团队提高这方面的效率。
- 挑战团队现有的知识:每个团队内部都有一些的解决方案或流程,并且习惯于此,而你可以提出更多潜在的替代方案,帮助团队寻找更优的解决方案。
个人思考
作为一名初级开发人员可能认为自己只能负责最简单的一部分业务,并没有意识到自己能给团队带来这么多价值,但其实你可以通过你作为一个新成员的位置,去发现你刚加入的团队一些不好的习惯,通过正确的心态和行动去改变现状。
你的代码不必完美无缺
原文地址:《Your Code Doesn’t Have to Be Perfect》 | Technical Blog
作者通过一段经历,讲述他在实现某个功能时,由于想要实现最佳的解决方案,一开始就花费了大量的时间去进行完美的设计、抽象和封装,结果一个星期的时间没有任何真正的业务产出。
以下是作者一些教训:
- **不要从开始就重构:**不需要刚开始就寻找最优的实现方式,这会让你过早地陷入到某个细节当中,把大量的时间花费在无意义的抽象中。
- **复制粘贴还不错:**我们应该坚持 DRY(不要重复你自己),但这不应该是起点,而是最终目标。可以在刚开始时通过复制粘贴实现功能,但这不意味着它就是最终上线的代码,而是在这个功能工作之后,再通过重构来提高代码质量。
- **真正的重构需要适当的知识:**通过改进现有的代码会使你的重构过程更加高效,因为这时候你已经掌握了更多的信息,可以更好地了解哪些代码是有意义的抽象。
个人思考
每个开发者都经历过这个阶段,想要一开始就设计好所有的细节、编写最完美的代码,但这是不可能的,所有代码都是经过不断地重构。你的代码不必在一开始就完美无缺,在生产项目中更是如此,毕竟不存在没有 deadline 的项目,只要懂得这个道理,你的工作效率会大大提高。
关于编写可读代码的最重要的事情
原文地址:《The Most Important Thing I Learned About Writing Readable Code》
编写代码时最重要的是可读性,一段难以理解的代码,即使你已经知道它的目的,你也很难理解它。所以编写具有可读性的代码是非常必要的。
已经有非常多的经典书籍在探讨这个话题,例如:
- 《代码大全》
- 《重构》
- 《代码整洁之道》
本文作者之前也写过几篇关于代码可读性的文章,不过我认为大部分已经是老生常谈了:
-
- **代码长度:**更短的代码通常更容易阅读,但有时候并非如此。所以要根据场景,代码并不是越短越好。
- **代码分组:**将特定上下文的代码组合在一起,使得阅读性更高。React 的自定义组件、Hook 就是做这件事情。
- **复杂的 JavaScript 结构:**不是所有人都完全熟悉 JavaScript 的语言特性,如果依赖某些特性的固定或隐式行为,会使某些对 JavaScript 不太熟悉的开发人员难以理解这些代码。作者还特地拿
Array.reduce
来举了个例子,认为使用Array.reduce
虽然可以让代码更加紧凑,但内部需要跟踪太多细节,如果直接使用for-loop
会使代码更具有可读性。 - **条件运算符 &&:**这种短路的隐式行为没有 if-else 的可读性高。
- **一次处理多种情况:**例如在同一个
useEffect
处理多个 deps,会使代码更加混乱。 - **变量命名:**计算机两大难题之一,这个命题有点大。
-
- PR 的用途:Why、How
- 分享视觉变化的屏幕截图:根据代码变更很难想象视觉的变化,所以展示一个截图可以帮助 Reviewer 更快地知道界面变化。
- 列出功能要求:列出你想要实现的功能预期,否则很难通过代码上下文去预测你的实现是否正确、或者还有更优的解决方案。
- 列出新的依赖:如果新增了依赖,你是如何决定采用哪一个库的。
- 避免复杂的代码实现
- 提供有关如何 Review 的其他说明:告诉 Reviewer 从哪里开始 Review。
-
《帮助你对 React 代码进行 Review 的 10 个问题》
- 代码是否正常工作?
- 我明白了发生了什么吗?
- 代码是否可读?
- 组件或 Hook 是否做得太多?
- 这必须是组件或者 Hook 吗?
- 这个 API 设计可以简化吗?
- 有测试吗?
- 测试有意义吗?
- 这个功能的辅助功能方面如何?
- 是否更新了相应的文档?
但作者认为有一件更重要的事情被忽略了,那就是:沟通。
每个人对于「代码是否具有可读性」的理解都不一样,所以日常中经常会出现下面这种对话:
- “你觉得这段代码非常难以阅读,但我认为它很容易。”
- “我不同意,我经常使用这种实现方式,但理解它并没有难度。”
- “使用这种方式实现,而不是你提供的那种方式实现,意味着我们不需要担心 xxx,可以使代码更短。”
这种回答并非完全没有道理的,但它们都有共同点:他们之所以不同意使用这种实现会使代码可读性更差,是因为他们觉得自己能够理解这样的代码。
的确,他们确实非常熟悉这段代码是如何工作的,但他们搞错了一件事:他们认为我是因为理解不了这段代码,才觉得这段代码难以阅读。
然而事实并非如此,因为问题的根本在于:代码的可读性与你无关,而是与其他人有关,准确地说,是与未来接手这段代码的人有关,甚至这个人很可能就是六个月后的自己。
所以,你要为他们编写具有可读性的代码。
个人思考
首先我需要说明,我并不认同作者提到的「 for-loop
可读性比 Array.reduce
好」这个结论,我认为 Array.reduce
与 forEach
、map
这些标准方法并无不同,不是 JavaScript 的糟粕,甚至是精华部分;另外 Array.reduce
真正需要考虑的细节也不多,只要熟悉递归思想,它其实很好理解。
除此之外的大部分观点我都是非常认同的,特别是本文讲到的「沟通」二字。我曾待过一个团队,当时合并代码前是需要两人交叉 Review 的,也遇到过几次关于「这样实现的可读性好不好」的问题展开讨论,基本都是各执一词,往往这种时候都需要一个第三者来进行判断,由这个人决定采用哪一种实现。
还记得有一次更离谱,某位同学酷爱使用位运算符,他对此给出的理由是:这样实现会使代码更快。
首先我并不认同这种说法,因为他没有给出专业的对比分析,即便这是真的,但在我们负责的这种 Web 项目中,这种速度的提升简直是可以忽略不计的,所以我就「可读性」本身这件事与他讨论,结果他开始和我解释这个位运算符是如何工作的,这位同学就犯了上面提到的问题,其实我不是不理解位运算符如何工作,我还曾写过一篇《深入理解按位操作符》的文章,我只是单纯认为不应该在项目中使用位运算符罢了。