原文摘自:https://dmitripavlutin.com/7-architectural-attributes-of-a-reliable-react-component/
对于一个 意义明确(meaningful) 的组件,是易于理解其行为的。
不要低估代码可读性的重要。你有多少次曾纠结于混乱的代码中,每个字都看懂了,但就是猜不出什么意思呢?
相比于真正写代码,开发者们花费了大把的时间去阅读和理解代码。编码活动中的 75% 的时间都在理解代码,20% 的时间用来修改既有的代码,仅仅只有 5% 的时间是在写新的代码。
把少量的额外时间花费在可读性上,将减少以后同事和自己的理解时间。在应用规模增长时,命名变得重要,因为代码量越大,理解起来也越难。
阅读意义明确的代码很容易。然而要书写有意义的代码,就要保证代码的整洁,并且持续努力让自己更条理清楚。
组件命名
Pascal 拼写法
组件名称就是把一个或多个单词(大部分是名词)用 Pascal 拼写法 (也称驼峰拼写法 Camel case)串联起来。比如 <DatePicker>
、<GridItem>
、<Application>
、<Header>
。
特殊化
组件越特殊,其名称中包含的单词可能就越多。
一个叫做 <HeaderMenu>
的组件表示一个头部的菜单;而叫做 <SidebarMenuItem>
的组件表示边栏中的一个菜单项。
当名称蕴含的意图清楚易懂时,组件就容易理解了。为此,时常要使用冗长的名字。这是很好的:冗长总比不清楚好。
假如你从一堆项目文件中分辨出两个组件:<Authors>
和 <AuthorsList>
。仅从名称来看,你能推测出两者的区别吗?够呛。
为了弄清楚,就不得不打开并浏览 <Authors>
的源码。这样做之后,你意识到 <Authors>
从服务器请求得到一个作者列表并渲染了 <AuthorsList>
这个表现组件。
给 <Authors>
取一个更特殊化的名字就能避免此情此景,比如 <FetchAuthors>
、<AuthorsContainer>
或 <AuthorsPage>
就都更好。
清楚优于简洁
一词一意
一个词表达一个概念。比如,用 list 这个词表示一个渲染过的项目的集合。
为每个概念挑选一个词,并在整个应用中始终保持这种叙述。最终将形成一个由曾经使用过的 词语->概念 组成的可预测意图映射。
当使用不同的词语去描述同一个概念的时候,可读性问题就会显现。举例来说,你将一个负责渲染订单列表的组件定义为 <OrdersList>
,又将另一个费用列表组件命名为 <ExpensesTable>
。
都表示渲染过的项目集合,但这个同样的概念被表达为了两个截然不同的单词:list 和 table。并没有理由这样做,这增加了命名的混乱,也破坏了一致性。
使用 list 这个词,将以上组件叫做 <OrdersList>
和 <ExpensesList>
;或是用 table 这个词,称其为 <OrdersTable>
和 <ExpensesTable>
-- 根据你的喜好来,只要保持一致性就好。
注释
意义明确的组件名称、方法名及变量名,已经足以让代码可读。在这种情况下,注释就已经多余了。
案例学习:编写自解释型的代码
常见滥用注释情况就是,对没有意义而含糊的命名进行解释。看看下面的案例:
代码语言:javascript复制// <Games> 渲染了一个 games 的列表
// "data" prop 包含一个 game 数据的列表
function Games({ data }) {
// 显示前 10 个游戏
const data1 = data.slice(0, 10);
// 把 data1 映射到 <Game> 组件
// "list" 有一个 <Game> 组件的列表
const list = data1.map(function(v) {
// "v" 表示一个 game 数据
return <Game key={v.id} name={v.name} />;
});
return <ul>{list}</ul>;
}
<Games
data=[{ id: 1, name: 'Mario' }, { id: 2, name: 'Doom' }]
/>
例子中的注释用来解释一堆混乱的代码。<Games>
、data、data1、v、魔法数字 10都是没有意义而难以理解的。
如果把组件重构,让其拥有意义明确的 props 和变量,则注释就可以轻易的省去了:
代码语言:javascript复制const GAMES_LIMIT = 10;
function GamesList({ items }) {
const itemsSlice = items.slice(0, GAMES_LIMIT);
const games = itemsSlice.map(function(gameItem) {
return <Game key={gameItem.id} name={gameItem.name} />;
});
return <ul>{games}</ul>;
}
<GamesList
items=[{ id: 1, name: 'Mario' }, { id: 2, name: 'Doom' }]
/>
不要用注释去手动的解释,而要编写自解释型(self-explanatory)和自文档化(self-documenting)的代码。
可表达性阶梯
我把组件的可表达性分为了 4 种层次。所处的层次越低,则理解组件需要付出的努力就越多。
可以从以下方面理解组件的用途:
- 阅读命名和 props
- 求助于文档
- 浏览代码
- 询问作者
如果命名和 props 提供了组件之于应用的足够信息,那就是一种强表达性。要努力保持这种高水准。
有些组件具有复杂的逻辑,甚至良好的命名也无法表达出必要的细节。此时求助于文档就是个好习惯了。
当文档也有所缺失或是无法回答所有问题是,就不得不浏览一下代码了。虽说因为增加了额外的时间成本而算不上最好的选项,但这也是可以接受的。
而通读了代码后仍看不懂组件的话,下一步就要像组件的作者询问其细节了。这一步肯定是要尽量避免的。更好的做法是请求作者重构其代码,或是你自己(译注:确定弄清楚后)动手重构。