书格前端

潘绳杰的博客


  • Home

  • About

  • Tags

  • Categories

  • Archives

  • confucian

  • Search

思考致富书摘

Posted on 2020-06-13 | In 读书 | Comments:

要点

  • 心想才能事成
  • 欲望
  • 信心
  • 自我暗示
  • 专业知识
  • 想象力
  • 精心策划
  • 决心
  • 毅力
  • 智囊团的力量
  • 性欲转换的力量
  • 潜意识
  • 大脑
  • 第六感
  • 6种恐惧

心想才能事成

  • “如果一个人真想做一件事,那他一定会做成”
  • “他们最伟大的成功在于,面临失败时能坚持再迈出一步”

欲望

  • 他没有给自己留下任何退路,要么成功,要么失败

把欲望变黄金的6个步骤

  1. 在心中确定你想得到金钱的数目
  2. 明确自己能付出多大努力,去换取想要的财富
  3. 确定得到梦想中金钱数目的日期
  4. 制定一个实现梦想的明确计划,然后立刻开始执行
  5. 列一份清晰、具体的清单,写下你想得到的金钱数额,得到这笔钱的最后期限、需要付出的代价,以及积累这笔财富的明确计划。
  6. 每天把这份清单读两遍,睡觉前读一遍,早晨起来读一遍。读的时候,要让自己看的,感觉到并且相信自己已经拥有那笔财富。

信心

自信的秘诀

  1. 我知道,我有能力实现人生中的明确目标。
  2. 我知道,心中的主宰意念终会以外在、实际的形式表现出来,并逐渐转化为事实。
  3. 我知道,通过“自我暗示”原则,我心中任何积存已久的欲望,终究会经过某种能实现目标的方式表现出来。
  4. 我已经清楚地写下一生中确定的主要目标,我一定要不断努力,直到培养出实现目标所需的足够的自信。
  5. 我完全明白,财富与地位只有建立在真理与正义的基础上才会持久。

自我暗示

刺激潜意识

  1. 到一个不会被干扰或打断的地方(最好是晚上躺在床上时),闭上双眼,大声朗诵你写的那份声明,其中包括你想积累的金钱数量、时限以及得到这笔钱打算提供的服务或卖出的商品。履行这些指示时,要想象自己已经拥有了这笔钱。
  2. 每天早晚重复这一过程,直到你能看见(在想象中)自己想要获得的金钱。
  3. 把一份你写的声明放在早晚都看得到的地方,并且在睡觉前和起床后朗读,直到记住为止。

专业知识

获取知识的途径

  1. 自己的经验和受教育情况
  2. 通过与他人合作,可以拥有的经验和知识
  3. 高等院校
  4. 公共图书馆
  5. 专业培训课程

精心策划

  • 一个半途而废的人,永远不可能成功,成功的人,绝不会半途而废。

领导者的主要素质

  1. 因对自我以及所从事职业的认知而产生的毫不动摇的勇气。
  2. 自制力
  3. 强烈的正义感
  4. 果断的决策
  5. 明确的计划
  6. 不计报酬的工作习惯
  7. 愉悦随和的个性
  8. 同情与体谅
  9. 掌握细节
  10. 愿负全责
  11. 合作

领导失败的十大原因

  1. 无力驾驭细节
  2. 不愿从事卑微的工作
  3. 期待靠知识而非运用知识的行动有所收获
  4. 害怕下属超过自己
  5. 缺乏想象力
  6. 自私
  7. 放纵无度
  8. 不忠
  9. 强调领导权威
  10. 过分看重头衔

失败的31项主因

  1. 先天不足
  2. 没有明确的生活目标
  3. 没有非同寻常的雄心抱负
  4. 教育不足(人得到的回报,来自于“知道”的食物,但更重要的在于“实践”知道的一切)
  5. 缺乏自律
  6. 身体状况不佳
  7. 童年时期不良环境的影响
  8. 拖延症(不要等待。时机永远不会“适当”。)
  9. 缺乏毅力
  10. 消极的个性
  11. 对性冲动缺乏控制
  12. 无法克制“不劳而获”的欲望
  13. 缺乏果断的决策力
  14. 有6种基本恐惧中的一种或多种
  15. 择偶不当
  16. 过渡谨慎
  17. 事业伙伴选择不当
  18. 迷信与偏见
  19. 错误的职业选择
  20. 目标不专
  21. 肆意挥霍的习惯
  22. 缺乏热情
  23. 偏执
  24. 放纵
  25. 不善于合作
  26. 轻易得来的东西(一夜暴富比贫穷更可怕)
  27. 蓄意不忠
  28. 自私和虚荣
  29. 猜测而不思考
  30. 缺乏资金
  31. 其他原因

毅力

影响毅力的因素

  1. 明确的目的
  2. 欲望
  3. 自信
  4. 明确的计划
  5. 认清自我
  6. 合作
  7. 意志力
  8. 习惯

如何培养毅力

  1. 在强烈欲望的驱使下,拥有明确的目的。
  2. 不断用行动体现出明确计划。
  3. 不受消极懈怠思想的影响,包括来自亲人、朋友和熟人等思想的影响。
  4. 结交一个或几个能鼓励你依照计划和目标行事的人。

六种恐惧

6种基本恐惧

  • 恐惧贫穷
  • 恐惧批评
  • 恐惧病痛
  • 恐惧失去爱情
  • 恐惧衰老
  • 恐惧死亡

ecmascript-2019-new-feature

Posted on 2020-05-31 | In 前端开发 | Comments:

关于ECMAScript 2019规范的新增特性

可选的捕获绑定

在这之前你肯定使用过try...catch。

1
2
3
4
5
try {
// 抛出异常
} catch (error) {
// 异常处理
}

如果你不需要在捕获语句中绑定这个error参数呢?现在可以忽略这个参数绑定了。

1
2
3
4
5
try {
// 抛出异常
} catch {
// 异常处理
}
  • Optional catch binding proposal
  • 作者Michael Ficarra

JSON超集

这项特性相对于一个新的语言特性来讲,更像是一个规范的更新–它是完全向下兼容的。尽管ECMAScript文档称JSON是JSON.parse()的子集,实际上JSON标准并不是ECMAScript的子集。JSON可以包含非转义换行分隔符(U+2028)和段落分隔符(U+2029),但是ECMAScript必须要通过转义后才能添加到字符串中。这可能会引起bug或者给规范添加不必要的复杂度。这个提案引入了一些ECMAScript字符串文本常量和JSON字符串文本常量的一致性。JSON标准现在是ECMAScript的合法子集。

  • JSON superset proposal
  • [作者: Richard Gibson, Mark Miller and Mathias Bynens]

Symbol.prototype.description

为了改善调试体验,Symbol可以在创建时指定一个可选的描述信息。之前我们通常是通过Symbol.prototype.toString()返回一个包含在Symbol()字符串内的描述来进行访问。使用ECMAScript 2019我们可以更加直观的做这件事,Symbol.prototype.description仅返回一个描述而不包含任何装饰。

1
2
3
4
5
6
7
const foo = Symbol("My super symbol");

foo.toString();
// Symbol(My super symbol)

foo.description;
// My super symbol
  • Symbol.prototype.description proposal
  • [作者: Michael Ficarra]

Function.prototype.toString 修订

toString()的实现被再次修订,并且标准化返回实现独立的字符串(定义函数实现的源码)。这是在一个已经是较大提案上的增量式更新,规则在Function.prototype.toString提案介绍有很好的定义。

1
2
3
4
5
6
7
8
function hi(name) {
return `Hi ${name}`;
}

hi.toString();
// function hi(name) {
// return `Hi ${name}`;
// }
1
2
Array.isArray.toString();
// function isArray() { [native code] }
  • Function.prototype.toString提案
  • [作者: Michael Ficarra]

Object.fromEntries

一个非常便利的方法用来转换一个键值对列表到一个对象中。

1
2
3
const arr = [["name", "Pawel"], ["surname", "Grzybek"], ["age", 31]];
const obj = Object.fromEntries(arr);
// {name: "Pawel", surname: "Grzybek", age: 31}
  • Object.fromEntries 提案
  • [作者: Darien Maillet Valentine]

符合语法规则的JSON.stringify

这个向下兼容的改变阻止JSON.stringify()返回无法在UTF-8标准中表示的编码字符串。

1
2
3
4
5
6
7
// 之前
JSON.stringify("\u{D800}");
// '"�"'

// 之后
JSON.stringify("\u{D800}");
// "\ud800"
  • Well-formed JSON.stringify 提案
  • [作者: Richard Gibson, Mathias Bynens]

String.prototype.trimStart/String.prototype.trimEnd

String.prototype.trim()已经在标准中很多年了。这个提案引入了String.prototype.trimStart()和String.prototype.trimEnd()。它们实际上添加到浏览器中也有很多年了,现在是时候把它们标准化。

1
2
3
4
5
6
7
8
"   javascript   ".trim();
// "javascript"

" javascript ".trimStart();
// "javascript "

" javascript ".trimEnd();
// " javascript"
  • String.prototype.trimStart / String.prototype.trimEnd提案
  • [作者: Sebastian Markbåge, Mathias Bynens]

Array.prototype.flat / Array.prototype.flatMap

你还记得SmooshGate? Array.prototype.flat()将数组递归展开至指定深度。默认深度是1。让我们看一些实例:

1
2
3
4
5
[1, 2, [3, 4, [5, 6]]].flat();
// [ 1, 2, 3, 4, [ 5, 6 ] ]

[1, 2, [3, 4, [5, 6]]].flat(2);
// [ 1, 2, 3, 4, 5, 6 ]

Array.prototype.flatMap()返回一个Array.prototpye.map()方法的展开结果。可以想象一下arr.map(mapper.flat(1)。

1
2
[1, 2, 3].flatMap(item => [item, item * 100]);
// [1, 100, 2, 200, 3, 300]
  • Array.prototype.flat / Array.prototype.flatMap 提案
  • [作者: Brian Terlson, Michael Ficarra, Mathias Bynens]

Array.prototype.sort稳定性

在这之前,数组中超过10个元素会使用一个不稳定的快排算法。
往前发展,这个功能现在被稳定的TimSort算法。如果你很好奇,强烈推荐你看看V8团队Simon Zünd发的文章在V8中的排序。

  • Array.prototype.sort 稳定性提案
  • [Mathias Bynens]

ECMAScript 2020新增特性

Posted on 2020-04-12 | In 前端开发 | Comments:

ECMAScript 2020新特性

  • String.prototype.matchAll by Jordan Harband
  • import() by Domenic Denicola
  • BigInt – arbitrary precision integers by Daniel Ehrenberg
  • Promise.allSettled by Jason Williams, Robert Pamely and Mathias Bynens
  • globalThis by Jordan Harband
  • for-in mechanics by Kevin Gibbons
  • Optional chaining by Gabriel Isenberg, Claude Pache, Dustin Savery
  • Nullish coalescing Operator by Gabriel Isenberg
  • import.meta by Domenic Denicola
  • export * as ns from “mod”

Jordan Harband提出的String.prototype.matchAll

String.prototype上的match()方法仅返回完全匹配,但是没有返回关于特定正则组的任意信息。感谢Jordan Harband关于String.prototype.matchAll的提案,可以返回比match()多很多的信息。返回的迭代器除了精确匹配外还给了我们访问所有的正则匹配捕获组。你还记得Gorkem Yakin和Daniel Ehrenberg添加到ECMAScript 2018的具名捕获组吗?matchAll()方法和此能很好的协调。通过下面例子来解释一下。

1
2
3
4
5
6
const text = "From 2019.01.29 to 2019.01.30";
const regexp = /(?<year>\d{4}).(?<month>\d{2}).(?<day>\d{2})/gu;
const results = text.match(regexp);

console.log(results);
// [ '2019.01.29', '2019.01.30' ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const text = "From 2019.01.29 to 2019.01.30";
const regexp = /(?<year>\d{4}).(?<month>\d{2}).(?<day>\d{2})/gu;
const results = Array.from(text.matchAll(regexp));

console.log(results);
// [
// [
// '2019.01.29',
// '2019',
// '01',
// '29',
// index: 5,
// input: 'From 2019.01.29 to 2019.01.30',
// groups: [Object: null prototype] { year: '2019', month: '01', day: '29' }
// ],
// [
// '2019.01.30',
// '2019',
// '01',
// '30',
// index: 19,
// input: 'From 2019.01.29 to 2019.01.30',
// groups: [Object: null prototype] { year: '2019', month: '01', day: '30' }
// ]
// ]

Domenic Denicola提出的import()

不同于ECMAScript 2015中介绍的静态模块,Domenic Denicola提案的动态导入可以实现按需加载。这个类似函数的格式(不是继承自Function .prototype)返回一个很强大的promise。使用场景比如: 按需导入,在一个脚本中计算模块名并加载执行变得可能。

1
2
3
4
5
const modulePage = 'page.js';
import(modulePage)
.then((module) => {
module.default();
});
1
2
3
4
5
(async () => {
const helpersModule = 'helpers.js';
const module = await import(helpersModule)
const total = module.sum(2, 2);
})();

Daniel Ehrenberg提出的BigInt-任意精度整数

感谢Daniel Ehrenberg, Number.MAX_SAFE_INTEGER不再是JavaScript中的一个限制。BigInt是一个能表示任意精度整数的新基础类型。你可以通过使用BigInt方法或者在一个数字后添加n后缀来把一个数字转换为一个新的bigint类型。

1
2
3
4
5
6
7
8
Number.MAX_SAFE_INTERGER
// 9007199254740991

Number.MAX_SAFE_INTEGER + 10 -10
// 9007199254740990 👎

BigInt(Number.MAX_SAFE_INTEGER) + 10n -10n
// 9007199254740991n 👍

Jason Williams, Robert Pamely and Mathias Bynens提出的Promise.allSettled

自从ECMAScript 2015以来,JavaScript仅支持两种promise组合: Promise.all()和Promise.race()。感谢Jason Williams, Robert Pamely and Mathias Bynens,现在我们可以使用Promise.allSettled()。用这个方法来处理所有promise都解决时的场景(不管成功或失败)。看看下面的例子,并没有使用catch捕获异常!

1
2
3
4
5
Promise.allSettled([
fetch("https://api.github.com/users/pawelgrzybek").then(data => data.json()),
fetch("https://api.github.com/users/danjordan").then(data => data.json())
])
.then(result => console.log(`All profile settled`));

还有Promise.any()有潜力很快进入ECMAScript规范中,在文章“Promise组合解释”中介绍了相关内容。

Jordan Harband提出的globalThis

那么在JavaScript中什么是全局的this?是在浏览器中的window,在worker中的self,在Nodejs中的global或者其他… 这种混乱结束了!感谢Jordan Harband,我们现在可以使用globalThis关键字了。

Kevin Gibbons提出的for-in机制

ECMAScript遗留了一个关于for-in循环顺序的详细描述。感谢Kevin Gibbons所付出的努力,为for-in机制定义了一系列规则。(原文: Thanks to Kevin Gibbons who finally put some TLC and defined a set in stone set of rules for for-in mechanics.)

Gabriel Isenberg, Claude Pache and Dustin Savery提出的optional chaining

读取层次很深的对象属性时通常是容易出错并且对应代码也不易阅读。感谢Gabriel Isenberg, Claude Pache and Dustin Savery,这件事情现在变得简单了。如果你是一个TypeScript用户,那么你不会发现什么新的特性,因为在3.7版本中TypeScript已经实现了这个特性。喜欢!

1
2
3
4
5
// 之前
const title = data && data.article && data.article.title

// 现在
const title = data?.article?.title

Gabriel Isenberg 提出的空值联合

空值联合添加了一个新的短路原则操作符来处理默认值。Gabriel Isenberg做了很棒的工作。这个特性结合optional chanining特性使用。不同于||操作符,空值联合操作符??仅在左边的值为严格的null或undefined时起左右。

1
2
3
4
5
"" || "default value"
// default value

"" ?? "default value"
// ""
1
const title = data?.article?.title ?? "What's new in ECMAScript 2020"

Domenic Denicola提出的import.meta

Domenic Denicola提出的import.meta提案添加一个host相关的元数据对象到当前执行的模块中。

1
2
console.log(import.meta.url)
// file:///Users/pawelgrzybek/main.js

EXPORT * AS NS FROM “MOD”

这是一个添加到规范中的有用特性,可以让开发者导出其他模块命名空间下的对象到一个新的名称下。

1
export * as ns from "mod"

参考

  • WHAT’S NEW IN ECMASCRIPT 2020
  • ECMAScript 2020: the final feature set

React组件开发的十条最佳实践

Posted on 2020-03-22 | In 前端开发 | Comments:

原文地址: https://dev.to/selbekk/the-10-component-commandments-2a7f
译文地址: https://blog.bookcell.org/2020/03/22/the-10-react-component-best-practice/

译者备注: 这是一篇关于React组件开发最佳实践的文章,很值得一读,推荐有能力阅读英文的同学去读原文,翻译或多或少会丢失一些原意。

正文从这里开始:

创建被许多人使用的组件是困难的。尤其当组件的属性作为公开API的一部分,你不得不仔细考虑哪些属性应该接受。

这篇文章会快速介绍一些在API设计时的通用最佳实践,并且给出10条明确的最佳实践指导你创建让同事开发者喜欢使用的组件。

image

API是什么?

API或者应用程序接口(Application Programming Interface),主要是指两部分代码相遇的地方。这是你的代码和其他世界接触的地方。我们称这个接触表面为接口。这些是可交互的动作集合或者数据点。

介于前端和后端之间的接口就是一个API。你可以通过这个API访问一系列特定的数据和功能。

介于一个类和调用代码之间接口也是一个API。你可以在类上调用方法,来获取数据或者触发封装在其中的功能。

沿着这个思路,你的组件接受的属性也是其API。这是你的用户和组件交互的方式,因此当你决定暴露哪些属性时有很多类似的规则和考虑可以应用。

API设计的部分最佳实践

那么在设计一个API时有哪些规则和考虑可以应用?在这方面我们做了一些调查,并找到了许多极好的资源。我们挑选了两篇文章,Josh Tauberer的“What Makes a Good API?”和Ron Kurir的同名文章,从中提取了4条最佳实践来遵循。

稳定的版本管理

当你在创建一个API时要考虑的最重要的一点是尽可能保持稳定。这意味着破坏性变更的次数很少。如果你有破坏性的变更,确保写一份详细的升级指南,并且如果可能,提供一个重构件(code-mod)来给用户自动化处理这些变更。

如果你发布了API,确保遵守语义化版本。这让用户更容易决定使用什么版本。

描述性的错误信息

任何调用API出错的时候,你都应该尽可能的解释什么地方出错,并告知如何修复。返回一个错误使用且没有任何其他提示的响应会让使用者感到羞愧,这不是一种好的用户体验。

相反,提供描述性的错误来帮助用户修复他们调用API的方式。

少让开发者感到惊讶

开发者是脆弱的人类,因此当他们使用API时不应该让他们感到惊讶。换句话说,尽可能让API更直观。这可以通过遵守最佳实践和命名规范来达到。

另外需要放在心上是保持你的代码一致性。如果你在一处地方给布尔型属性名称前面添加了is或has,而在另外一处忽略了,这会让其他人感到困惑。

最小化API接口

当我们谈到最小化的时候-同样要最小化你的API。更多的特性当然是好的,但是API接口暴露得越少,使用者学习的成本也更低。从而让用户认为这是一个易用的API。

有很多方式可以控制API的规模,其中一个就是从旧的中重构一个新的来。

组件开发的十条最佳实践

image

这4条黄金法则在REST API和Pascal语言程序中使用得很好,那么如何将他们拿到React的现代世界中来呢?

像我们前面提到的,组件也有自己的API。我们称做props,这是给组件传递数据、回调和其他功能的方式。我们如何组织props对象才能不破坏上面的规则?我们如何开发组件才能让其他开发者在使用组件时更便捷?

我们创建了开发组件时的10条不错的规则清单,希望对你有帮助。

1. 组件使用文档

如果组件没有提供如何使用的文档,那么显然组件是无用的。好吧,大多时候,使用者总是可以通过查看实现来了解如何使用,但那很少是最好的用户体验。

有很多方式可以给组件编写文档,从我们的角度想推荐3个选项:

  • Storybook
  • Styleguidist
  • Docz

前两个在你开发组件时可以提供一个playground用来试验,第3个提供了MDX来更自由的书写文档。(注:最新版本三者都已支持Markdown语法)

不管选择哪一个,确保提供API文档,及组件如何、何时使用相关的文档。后者在共享组件库中更为重要,那样人们才能在合适的位置使用正确的按钮或者布局。

2. 允许上下文的语义

译者注: 标题的原文是Allow for contextual semantics,中文翻译不好把握供参考

HTML是一门以语义化方式组织信息的语言。但是大多数的组件是由<div />标签组成的。这在某种程度上是讲得通的,因为通用组件不能假设是否应该是<article />、<section />或者一个<aside />,但是这并不是理想。

相反,我们建议允许组件接受一个as属性,用以覆盖被渲染的DOM元素。下面是一个如何实现的例子:

1
2
3
4
5
6
7
function Grid({ as: Element, ...props }) {
return <Eelement className="grid" {...props} />
}

Grid.defaultProps = {
as: 'div',
};

我们将as属性重命名为一个本地的变量Element,并在JSX中使用。我们提供了一个通用的默认值,在你明确不需要传递更具语义化HTML标签的情况下使用。

当我们使用<Grid />组件时,你仅需要传递正确的标签:

1
2
3
4
5
6
7
function App() {
return (
<Grid as="main">
<MoreContent />
</Grid>
);
}

注意这在使用React组件时同样适用。一个很好的例子是,当你有一个<Button />组件想要渲染成React Router的<Link />组件时:

1
2
3
<Button as={Link} to="/profile">
Go to Profile
</Button>

3. 避免布尔型属性

布尔型属性听起来是个极好的主意。你可以不需要传值的情况下使用,这看起来很优雅:

1
<Button large>BUY NOW!</Button>

但是即使他们看起来很不错,单布尔属性只能允许两种可能性。开和关,显示和隐藏,1和0。

任何时候当你开始引入像尺寸、变形、颜色或其他任何像下面所列可能有两个之外的值,你就会有麻烦了。

1
2
3
<Button large small primary disabled secondary>
WHAT AM I??
</Button>

换句话说,布尔属性通常无法适应需求变更。因此,尝试使用字符串类型的枚举来作为属性,这样就有机会使用任何值而不是仅有两个值的选择。

1
2
3
<Button variant="primary" size="large">
I am primarily a large button
</Button>

这并不代表布尔值属性没有一点用武之地。当然是有的。上面列的disabled属性就应该是布尔型,因为在启用和禁用之间没有中间状态。保留他们作为真正的两个选项使用。

4. 使用props.children

React有一些与其他属性略有不同的特殊属性。一个是key,在列表项中被用来跟踪顺序,另一个是children。

任何放置在组件开和闭标签间的内容都会被放在props.children属性中。因此,你应该尽可能经常使用。

原因是这样做比通过添加一个content属性或者其他专门类似文本简单值属性的方式要更加容易使用。

1
2
3
4
5
<TableCell content="Some text" />

// 对比

<TableCell>Some text</TableCell>

使用props.children有很多积极的意义。第一点,这有点类似平常HTML的工作方式。第二,你可以自由地传递任何你想要传的内容。不用添加leftIcon和rightIcon属性到你的组件中,仅仅只要传到props.children属性中即可。

1
2
3
<TableCell>
<ImportantIcon /> Some text
</TableCell>

你可能会争辩组件应该只允许接收并渲染普通文本,这在某些情况下可能是对的。至少现在,通过使用props.children,能实现一个适应未来需求变更的组件。

5. 让父组件接入内部逻辑

有时我们会创建有很多内部逻辑和状态的组件,例如自动补全的下拉组件或者交互式图表。

这些类型的组件通常都会涉及比较繁琐的接口,其中一个原因就是要支持后续的一系列覆盖和特殊使用场景。

如何能做到仅仅提供一个简单、标准化属性来让用户控制、响应或者覆盖默认组件行为呢?

Kent C.Dodds写了一篇关于“state reducers”概念的好文章,关于概念本身,和另外一篇如何用React Hooks实现。

快速总结一下,这个模式通过传递一个“state reducer”函数给组件,从而让父组件可以访问任何在你的组件中派发的action。你可以改变状态,或者触发边界效应等。这是一个极好的实现高级定制的方式,而不用借助其他属性。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function MyCustomDropdown(props) {
const stateReducer = (state, action) => {
if (action.type === Dropdown.actions.CLOSE) {
buttonRef.current.focus();
}
};

return (
<>
<Dropdown stateReducer={stateReducer} {...props} />
<Button ref={buttonRef}>Open</Button>
</>
)
}

顺便说一下,你当然也可以创建更简单的方式来响应事件。上面的例子中提供一个onClose属性可能会更好的用户体验。保留好state reducer模式以备不时之需。

6. 展开剩余属性

任何时候当你创建一个新的组件时,确保展开剩余的属性到有意义的元素上。

你不需要持续添加那些原本在父组件或父元素上并会传递到组件上的属性到你的组件中。这会让你的API更稳定,并且不会因其他开发者需要一个新的事件监听或者arial标签而发布许多小的版本。

可以像下面这样做:

1
2
3
function ToolTip({ isVisivle, ...rest }) {
return isVisible ? <span role="tooltip" {...rest} /> : null;
}

任何时候你的组件传递一个属性到你的实现中,像一个类名或者一个onClick处理函数,确保外部的使用者同样可以做同样的事情。在类的情况,使用好用的classnames软件包来追加类名(或者使用字符串拼接)。

1
2
3
4
5
6
7
8
9
10
import classNames from 'classnames';

function ToolTip(props) {
return (
<span
{...prpos}
className={classNames('tooltip', props.className)}
/>
)
}

对于点击事件处理函数或者其他回调,通过一个辅助函数来组合一个单独的函数,这里有一种实现方式:

1
2
3
4
5
6
function combine(...functions) {
return (...args) =>
functions
.filter(func => typeof func === 'function')
.forEach(func => func(...args));
}

这里我们创建一个函数来接受一系列函数并组合,返回一个新的依次使用对应参数调用他们的回调函数。

你可以像这样使用:

1
2
3
4
5
6
7
8
9
10
11
function ToolTip(props) {
const [isVisible, setVisible] = React.useState(false);
return (
<span
{...props}
className={classNames('tooltip', props.className)}
onMouseIn={combile(() => setVisible(true), props.onMouseIn)}
onMouseOut={combile(() => setVisible(false), props.onMouseOut)}
/>
);
}

7. 设置足够的默认值

无论何时如果可以的话,确保给你的属性提供足够的默认值。这样做的话,可以最小化必须要传递的属性数量,并且这样能非常简化你的实现。

举一个onClick处理函数的例子。如果在你的代码中不强制要求,那么可以提供一个空函数作为默认属性。这样的话,你可以在代码中跟一直有传递对应属性一样调用。

另一个例子例如定制的输入。除非用户提供,否则的话假设输入的字符串是一个空字符串。这可以确保总是在处理一个字符串对象,而不是undefined或null。

8. 不要重命名HTML属性

HTML作为一门语言同样有其自己的属性,而这就是HTML元素自身的API。为什么不继续保持使用这个API呢?

就如我们之前提到的,最小化API接口暴露和保持某种程度的直观是两种改善组件API的极好方式。因此比起创建自己的screenReaderLabel属性,为何不直接使用HTML已经提供给你的aria-label呢?

因此不要为了自己的方便使用而去重命名既有的HTML属性。这样并不是用一个新的API来替换既存的API,而是在顶层添加一个自己的。人们通常还可以继续和你的screenReaderLabel属性一起传递aria-label,那么最终哪个才是该使用的呢?

再说一点,确保永远不要在组件中覆盖HTML属性。一个比较好的例子就是<button />元素的type属性,具有submit(默认)、button或者reset。然而,许多开发者倾向于把这个属性用于表示按钮的可视化类型(如primary, cta等等)。

属性他用之后,你必须要添加其他新的属性来覆盖表示type属性,这样会导致困惑、疑虑和让用户愤怒。

相信我,我一次又一次的犯了这个错误,我承认这真是一个狼狈的决定。

9. 属性类型定义

没有文档能比在代码中的文档更好的。React通过prop-types软件包提供了极好的方式来声明组件API。现在就开始使用吧。

你可以指定任意类型和形式的必选和可选属性,并可以通过JSDoc注释来改善。

如果你忽略了一个必选的属性,或者传递一个无效、非期望的值,那么你会在控制台中得到运行时警告。这对开发来讲是极好的,并可以在生产打包时被删掉。

如果你是通过TypeScript或Flow来开发React应用,那么你可以通过语言特性获得这种API文档。这可以获得更好的工具支持,和更好的用户体验。

如果你自己没有使用具有类型的JavaScript,那么你应该始终考虑给你的用户提供类型定义。这样,他们在使用你的组件时会更加容易。

10. 为开发者设计

最后,最重要的一个规则。确保你的API和组件体验是为那些将会使用的人优化的–你的同事开发者。

一种改善开发者体验的方式是为不合理使用提供足够的错误信息,并在有更好的方式使用组件的情况下提供仅在开发环境的警告。

当在提供错误和警告时,尽量通过链接引用你的文档或者提供简单的代码示例。让用户越快找到错误并修复,这会让用户感到你的组件越好用。

事实证明,这些冗长的错误和警告并不会影响最终打包的大小。感谢无用代码精简的帮助,在构建生产包的时候这些文本和错误的代码都会被移除。

在这方面做得非常好的一个库就是React本身。任何时候当你忘记在列表项中指定一个key时,或者拼错一个生命周期函数,忘记继承正确的基类或者用不正确的方式调用hook时,你会在控制台中得到大量的错误信息。为什么你的组件使用人员要期望的更少呢?

因此为你的未来用户设计,为5周后的你自己设计,为当你离开后必须要接手维护你代码的可怜家伙设计!为开发者设计。

总结

从经典的API设计中我们可以学到许多很好的建议。通过遵循文中的建议、技巧、规则和最佳实践,你应该可以创建简单易用,容易维护,直观并在需要的时候非常灵活的组件。

那么你在创建一个出色的组件时有哪些最喜欢的建议?

如何阅读一本书-笔记

Posted on 2020-01-30 | In 读书 | Comments:

笔记浓缩《如何阅读一本书》的精华,方便大家学习到阅读的要点。

阅读的目标与艺术

阅读的目标:为获得资讯而读,以及为求得理解而读

阅读的艺术: 这是一个凭借着头脑运作,除了玩味读物中的一些字句之外,不假任何外助,以一己之力来提升自我的过程。

阅读的层次

大多数人,即使是许多优秀的阅读者,都忽略了检视阅读的价值。他们打开一本书,从第一页开始读起,孜孜不倦,甚至连目录都不看一眼。因此,他们在只需要粗浅翻阅一本书的时候,却拿出了仔细阅读、理解一本书的时间。这就加重了阅读的困难。第三种层次的阅读,我们称之为分析阅读(analytical reading)。

检视阅读

检视阅读一:有系统的略读或粗读

略读或粗读是检视阅读的第一个子层次。你脑中的目标是要发现这本书值不值得多花时间仔细阅读。其次,就算你决定了不再多花时间仔细阅读这本书,略读也能告诉你许多跟这本书有关的事。

略读的建议:

  1. 先看书名页,然后如果有序就先看序。
  2. 研究目录页,对这本书的基本架构做概括性的理解。
  3. 如果书中附有索引,也要检阅一下——大多数论说类的书籍都会有索引。
  4. 如果那是本包着书衣的新书,不妨读一下出版者的介绍。
  5. 从你对一本书的目录很概略,甚至有点模糊的印象当中,开始挑几个看来跟主题息息相关的篇章来看。
  6. 最后一步,把书打开来,东翻翻西翻翻,念个一两段,有时候连续读几页,但不要太多。

检视阅读二:粗浅的阅读

头一次面对一本难读的书的时候,从头到尾先读完一遍,碰到不懂的地方不要停下来查询或思索。

阅读的速度

所谓阅读速度,理想上来说,不只是要能读得快,还要能用不同的速度来阅读——要知道什么时候用什么样的速度是恰当的。

略读或粗读一本书总是个好主意。尤其当你并不清楚手边的一本书是否值得细心阅读时(经常发生这种情况),必须先略读一下。略读过后,你就会很清楚了。一般来说,就算你想要仔细阅读的书也要先略读一下,从基本架构上先找到一些想法。

如何做一个自我要求的读者

主动的阅读基础:一个阅读者要提出的四个基本问题

你在阅读时要提出问题来—在阅读的过程中,你自己必须尝试去回答的问题。

  1. 整体来说,这本书到底在谈些什么?你一定要想办法找出这本书的主题,作者如何依次发展这个主题,如何逐步从核心主题分解出从属的关键议题来。
  2. 作者细说了什么,怎么说的?你一定要想办法找出主要的想法、声明与论点。这些组合成作者想要传达的特殊讯息。
  3. 这本书说得有道理吗?是全部有道理,还是部分有道理?
  4. 这本书跟你有什么关系?

任何一种超越基础阅读的阅读层次,核心就在你要努力提出问题(然后尽你可能地找出答案)。

如何让一本书真正属于你自己

要真正完全拥有一本书,必须把这本书变成你自己的一部分才行,而要让你成为书的一部分最好的方法——书成为你的一部分和你成为书的一部分是同一件事——就是要去写下来。

做笔记和标记的方法:

  1. 画底线——在主要的重点,或重要又有力量的句子下画线。
  2. 在画底线处的栏外再加画一道线——把你已经画线的部分再强调一遍,或是某一段很重要,但要画底线太长了,便在这一整段外加上一个记号。
  3. 在空白处做星号或其他符号——要慎用,只用来强调书中十来个最重要的声明或段落即可。你可能想要将做过这样记号的地方每页折一个角,或是夹一张书签。
  4. 在空白处编号——作者的某个论点发展出一连串的重要陈述时,可以做顺序编号。
  5. 在空白处记下其他的页码——强调作者在书中其他部分也有过同样的论点,或相关的要点,或是与此处观点不同的地方。
  6. 将关键字或句子圈出来——这跟画底线是同样的功能。
  7. 在书页的空白处做笔记——在阅读某一章节时,你可能会有些问题(或答案),在空白处记下来,这样可以帮你回想起你的问题或答案。

你读完一本书,在最后的空白页写下个人的索引后,再翻回前面的空白页,试着将全书的大纲写出来,用不着一页一页或一个重点一个重点地写(你已经在书后的空白页做过这件事了),试着将全书的整体架构写出来,列出基本的大纲与前后篇章秩序。

在检视阅读中,要回答的问题是:第一,这是什么样的一本书?第二,整本书在谈的是什么?第三,作者是借着怎样的整体架构,来发展他的观点或陈述他对这个主题的理解?

要做这些笔记最好的地方是目录页,或是书名页,这些是我们前面所提的笔记方式中没有用到的页数。

分析阅读

分析阅读的三阶段

一、分析阅读的第一阶段:找出一本书在谈些什么的规则

  1. 依照书的种类与主题来分类。
  2. 使用最简短的文字说明整本书在谈些什么。
  3. 将主要部分按顺序与关联性列举出来。将全书的大纲列举出来,并将各个部分的大纲也列出来。
  4. 确定作者想要解决的问题。

二、分析阅读的第二阶段:诠释一本书的内容规则

  1. 诠释作者的关键字,与他达成共识。
  2. 由最重要的句子中,抓住作者的重要主旨。
  3. 知道作者的论述是什么,从内容中找出相关的句子,再重新架构出来。
  4. 确定作者已经解决了哪些问题,还有哪些是没解决的。再判断哪些是作者知道他没解决的问题。

三、分析阅读的第三阶段:像是沟通知识一样地评论一本书的规则

A.智慧礼节的一般规则

  1. 除非你已经完成大纲架构,也能诠释整本书了,否则不要轻易批评。(在你说出:“我读懂了 !” 之前,不要说你同意、不同意或暂缓评论。)
  2. 不要争强好胜,非辩到底不可。
  3. 在说出评论之前,你要能证明自己区别得出真正的知识与个人观点的不同。

B.批评观点的特别标准

  1. 证明作者的知识不足。
  2. 证明作者的知识错误。
  3. 证明作者不合逻辑。
  4. 证明作者的分析与理由是不完整的。

主题阅读

TBD


笔记变更记录

初版 2020.1.30

123…22

Perry

110 posts
7 categories
215 tags
RSS
Twitter StackOverflow Linkedin
© 2016 – 2021 Perry
Powered by Hexo v3.7.1
|
Theme – NexT.Pisces v6.4.2
0%