diff --git a/.github/workflows/WebPageToMarkdown.yml b/.github/workflows/WebPageToMarkdown.yml index 4be220993..2ac3e7515 100644 --- a/.github/workflows/WebPageToMarkdown.yml +++ b/.github/workflows/WebPageToMarkdown.yml @@ -21,7 +21,7 @@ jobs: fi; shell: bash - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: ref: ${{ github.head_ref }} - name: Crawl pages and generate Markdown files diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d70ba03a1..8fb896837 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -8,11 +8,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Use Node.js - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: - node-version: '14' + node-version: 'lts/*' - name: Install & Lint run: | npm install diff --git a/README.md b/README.md index b252413f0..a3ec4b399 100644 --- a/README.md +++ b/README.md @@ -39,14 +39,14 @@ freeCodeCamp 英文专栏发布了大量[优质文章][6],分享前端、后 - 微信公众号发布:翻译者如有自己的公众号,可在自己的公众号首发译文,可给 freeCodeCamp 公众号设置白名单转载权限。 -对于以上内容,如果你有疑问,请在 [freeCodeCamp Discord 频道](https://discord.gg/RzWXEche) 提问,我们随时在线和你交流:) +对于以上内容,如果你有疑问,请在 [freeCodeCamp Discord 频道](https://discord.gg/PRyKn3Vbay) 提问,我们随时在线和你交流:) ## 译者交流 ### 开放周会 - 时间:每两周一次,周日上午 10:00 - 11:00(北京时间) -- 地点:会议室链接(有两种方法可以获得会议室链接:1、请加入 [freeCodeCamp Discord 频道](https://discord.gg/RzWXEche),搜索 #chinese 频道并留言,2、请添加小助手微信 fcczhongguo 并留言) +- 地点:会议室链接(有两种方法可以获得会议室链接:1、请加入 [freeCodeCamp Discord 频道](https://discord.gg/PRyKn3Vbay),搜索 #chinese 频道并留言,2、请添加小助手微信 fcczhongguo 并留言) 参与者无需提前报名,在以上时间内直接进入会议室。请所有参与者务必遵守 [freeCodeCamp 社区行为规范](https://chinese.freecodecamp.org/news/code-of-conduct/)。 @@ -62,7 +62,7 @@ freeCodeCamp 英文专栏发布了大量[优质文章][6],分享前端、后 #### Cloud -This repository has [a GitHub action][15] to lint **Markdown & Native Language syntax** on **Pull Request commits**, translaters should follow the **Lint Report** to fix **Syntax Warnings & Errors** of the own article. +This repository has [a GitHub action][15] to lint **Markdown & Native Language syntax** on **Pull Request commits**, translators should follow the **Lint Report** to fix **Syntax Warnings & Errors** of the own article. #### Local @@ -97,4 +97,4 @@ npm test [13]: https://github.com/freeCodeCamp/chinese/issues?q=is%3Aissue+is%3Aopen+label%3AReview-awaiting [14]: https://chinese.freecodecamp.org/news/ [15]: https://github.com/freeCodeCamp/chinese/actions?query=workflow%3A%22CI+%26+CD%22 -[16]: https://prettier.io/ \ No newline at end of file +[16]: https://prettier.io/ diff --git a/chinese/articles/a-30-000-foot-introduction-to-data-analytics-and-its-foundational-components.md b/chinese/articles/a-30-000-foot-introduction-to-data-analytics-and-its-foundational-components.md index c767249d9..fe074ea88 100644 --- a/chinese/articles/a-30-000-foot-introduction-to-data-analytics-and-its-foundational-components.md +++ b/chinese/articles/a-30-000-foot-introduction-to-data-analytics-and-its-foundational-components.md @@ -125,7 +125,7 @@ c. 现在,这位经理一共有三个可选择的激励计划:(a) 没有基 那么相应地,那些发起 IPO 的公司就会获得巨大的收益。当我成立一个[帮助人们远程办公]的网站时,我认为成功的概率最多只有 10%。 -Jeff Bezos 曾说他认为 Amazon 成功的概率有30%。 +Jeff Bezos 曾说他认为 Amazon 成功的概率有 30%。 我们经常用标准差来描述数据的离散度,标准差就是方差的算术平方根。 diff --git a/chinese/articles/a-brief-history-of-responsive-web-design.md b/chinese/articles/a-brief-history-of-responsive-web-design.md index 4698df3b0..7d3fb84eb 100644 --- a/chinese/articles/a-brief-history-of-responsive-web-design.md +++ b/chinese/articles/a-brief-history-of-responsive-web-design.md @@ -15,9 +15,9 @@ 1991 年 8 月 6 日,第一个网站上线。 该站点由 Tim Berners \- Lee 创建,网站详细介绍了万维网(W3)项目。 它最初是在欧洲核研究组织 CERN 的 NeXT 计算机上运行的。 -尽管今天原始站点已经离线,但 CERN 于 2013 年 [启动了一个项目](https://first-website.web.cern.ch/first-website/),以 “保留与 web 的诞生相关的一些数字资产” 。从最初的计算机名称、IP地址到第一个网站的 URL,所有内容均已恢复到可用状态。 +尽管今天原始站点已经离线,但 CERN 于 2013 年 [启动了一个项目](https://first-website.web.cern.ch/first-website/),以 “保留与 web 的诞生相关的一些数字资产” 。从最初的计算机名称、IP 地址到第一个网站的 URL,所有内容均已恢复到可用状态。 -虽然该网站的原始 1991 年的版本丢失了,但是恢复了1992 年的版本。如果感兴趣,可以访问 [http://info.cern.ch/hypertext/WWW/TheProject.html](http://info.cern.ch/hypertext/WWW/TheProject.html)。 +虽然该网站的原始 1991 年的版本丢失了,但是恢复了 1992 年的版本。如果感兴趣,可以访问 [http://info.cern.ch/hypertext/WWW/TheProject.html](http://info.cern.ch/hypertext/WWW/TheProject.html)。 ## 早期的 web 设计 @@ -87,7 +87,7 @@ Adams 的分辨率相关的布局例子 ([地址](https://www.themaninblue.com/e 一种巧妙方法是创建一个仅适用于移动设备的网站版本,并使其在子域名中访问。 -移动子域名网站(有时称为 m\-dots 或 m子域名)就是 - 托管在网站子域名中的的移动特定版本,通常是 `m` 子域名。 +移动子域名网站(有时称为 m-dots 或 m 子域名)就是 - 托管在网站子域名中的的移动特定版本,通常是 `m` 子域名。 例如,Facebook 的桌面版本是 `facebook.com`,或更具体地说,位于 `www` 子域名 `www.facebook.com` 下: @@ -95,7 +95,7 @@ Adams 的分辨率相关的布局例子 ([地址](https://www.themaninblue.com/e Facebook 的桌面版本网站 -而Facebook 的移动版本位于 `m.facebook.com` 下: +而 Facebook 的移动版本位于 `m.facebook.com` 下: ![](https://www.freecodecamp.org/news/content/images/2021/02/image-118.png) @@ -234,7 +234,7 @@ Flexible images 是指图像应随其所在的 fluid grid 一起增大或缩小 ### Media queries -CSS 媒体查询 2010 年就已经可用,但直到 2012 年 [W3建议](https://www.w3.org/TR/2012/REC-css3-mediaqueries-20120619/ )正式发布后才被广泛采用。 +CSS 媒体查询 2010 年就已经可用,但直到 2012 年 [W3 建议](https://www.w3.org/TR/2012/REC-css3-mediaqueries-20120619/ )正式发布后才被广泛采用。 媒体查询只是一个 CSS 规则,它是根据诸如媒体类型(`screen`、`print`等)和媒体功能(`width`、 `height`等)之类的选项触发: @@ -316,8 +316,8 @@ CSS 媒体查询 2010 年就已经可用,但直到 2012 年 [W3建议](https:/ 现在,你对响应式 Web 设计的历史有了一点了解,以及开发人员在我们今天拥有的一切之前经历的点滴。 -如果您想深入研究响应式网页设计,Flexbox 和其他现代技术,请在我们的 YouTube 频道(Bilibili)上查看以下为时4小时的教程: +如果您想深入研究响应式网页设计,Flexbox 和其他现代技术,请在我们的 YouTube 频道(Bilibili)上查看以下为时 4 小时的教程: -如果您想了解CSS Grid(创建复杂,灵活的布局的新方法)的方法,请查看我们的[教程](https://www.freecodecamp.org/news/search/?query=css%20grid)。 +如果您想了解 CSS Grid(创建复杂,灵活的布局的新方法)的方法,请查看我们的[教程](https://www.freecodecamp.org/news/search/?query=css%20grid)。 你的响应式网页设计的经历是怎么样的? 如果哪里不准确,在 [Twitter](https://twitter.com/kriskoishigaw) 联系我。 diff --git a/chinese/articles/a-month-ago-i-knew-nothing-about-podcasting-50-000-downloads-later-heres-what-i-ve-learned-2d7082a28f27.md b/chinese/articles/a-month-ago-i-knew-nothing-about-podcasting-50-000-downloads-later-heres-what-i-ve-learned-2d7082a28f27.md index 878f6ec0a..ff2688f18 100644 --- a/chinese/articles/a-month-ago-i-knew-nothing-about-podcasting-50-000-downloads-later-heres-what-i-ve-learned-2d7082a28f27.md +++ b/chinese/articles/a-month-ago-i-knew-nothing-about-podcasting-50-000-downloads-later-heres-what-i-ve-learned-2d7082a28f27.md @@ -5,7 +5,7 @@ ![如何开始播客——录制和发布的分步指南](https://cdn-media-2.freecodecamp.org/w1280/5f9cb1bf740569d1a4cabd5a.jpg) -## 播客正在流行。 +## 播客正在流行 截至 2019 年,大约[四分之一的美国人每月收听播客](http://www.edisonresearch.com/infinite-dial-2017/)。而且她们听得很多,每月约 20 集。 @@ -166,7 +166,7 @@ freeCodeCamp 是一个由软件开发人员和培训中的开发人员组成的 当他为 《freeCodeCamp 播客》朗读他写的文章时,能够添加更多细节并谈论此文的后果。 -## 你应该为《freeCodeCamp 播客》作贡献或开始你自己的播客。 +## 你应该为《freeCodeCamp 播客》作贡献或开始你自己的播客 总的来说,我发现播客非常有益。我乐观地认为,我们可以从 freeCodeCamp 社区吸引更多人来帮助制作这些内容,并且我们可以比每周一次更频繁地发布——同时尽可能保持高质量。 diff --git a/chinese/articles/a-new-chapter-begins-i-am-joining-runcloud-as-a-full-time-technical-writer-afba58ec6a13.md b/chinese/articles/a-new-chapter-begins-i-am-joining-runcloud-as-a-full-time-technical-writer-afba58ec6a13.md index dee51b3c9..46f676a80 100644 --- a/chinese/articles/a-new-chapter-begins-i-am-joining-runcloud-as-a-full-time-technical-writer-afba58ec6a13.md +++ b/chinese/articles/a-new-chapter-begins-i-am-joining-runcloud-as-a-full-time-technical-writer-afba58ec6a13.md @@ -1,43 +1,43 @@ -> - 原文地址:[A new chapter begins — I am joining RunCloud as a full-time technical writer](https://www.freecodecamp.org/news/a-new-chapter-begins-i-am-joining-runcloud-as-a-full-time-technical-writer-afba58ec6a13/) +> - 原文地址:[A new chapter begins—I am joining RunCloud as a full-time technical writer](https://www.freecodecamp.org/news/a-new-chapter-begins-i-am-joining-runcloud-as-a-full-time-technical-writer-afba58ec6a13/) > - 原文作者:[Charles Freeborn](https://www.freecodecamp.org/news/author/charles/) > - 译者:abbie-321 > - 校对者: ![A new chapter begins — I am joining RunCloud as a full-time technical writer](https://cdn-media-1.freecodecamp.org/images/1*df8jz18-yVOlvawC-qpW2w.jpeg) -如果你曾经在 freeCodeCamp出版物上提交(或阅读)过文章,你的文章可能被我(或由志愿者编辑团队的另一位成员)编辑过。 +如果你曾经在 freeCodeCamp 出版物上提交(或阅读)过文章,你的文章可能被我(或由志愿者编辑团队的另一位成员)编辑过。 -那是2017年的夏天我接到了freeCodeCamp的创始人 Quincy Larson的电话。于是我成为了一名志愿者编辑,帮助编辑来自世界各地的开发人员提交的技术文章。 +那是 2017 年的夏天我接到了 freeCodeCamp 的创始人 Quincy Larson 的电话。于是我成为了一名志愿者编辑,帮助编辑来自世界各地的开发人员提交的技术文章。 -过去的一年半对我来说是一段奇妙的旅程。从我有幸编辑的文章中,我收获颇丰,作为一名前端开发者,我的写作能力也得到了提升。我也被评为[2018 freeCodeCamp突出贡献者](https://www.freecodecamp.org/forum/t/announcing-our-freecodecamp-2018-top-contributor-award-winners/201353),并且[2018 年我在我的家乡瓦尼日利亚地区](https://medium.freecodecamp.org/how-i-started-a-google-developers-group-gdg-chapter-in-warri-nigeria-and-reached-100-members-22cbd622d070)创建了开发者社群,它在不到十个月的时间发展到超过550+的活跃成员。 +过去的一年半对我来说是一段奇妙的旅程。从我有幸编辑的文章中,我收获颇丰,作为一名前端开发者,我的写作能力也得到了提升。我也被评为[2018 freeCodeCamp 突出贡献者](https://www.freecodecamp.org/forum/t/announcing-our-freecodecamp-2018-top-contributor-award-winners/201353),并且[2018 年我在我的家乡瓦尼日利亚地区](https://medium.freecodecamp.org/how-i-started-a-google-developers-group-gdg-chapter-in-warri-nigeria-and-reached-100-members-22cbd622d070)创建了开发者社群,它在不到十个月的时间发展到超过 550+的活跃成员。 -如果你想知道freeCodeCamp对作为一名志愿者的我的生活影响有多大,[我将它记录在此](https://link.medium.com/aW8bvhZ4GT)。 +如果你想知道 freeCodeCamp 对作为一名志愿者的我的生活影响有多大,[我将它记录在此](https://link.medium.com/aW8bvhZ4GT)。 -## RunCloud开启了我的新篇章 +## RunCloud 开启了我的新篇章 我一直坚信在科技生态系统中分享和支持知识的传播,[RunCloud](https://runcloud.io)这个团队也有着同样的理念—这正是我为何加入他们的原因 -因此,2019年的一月开始我加入[RunCloud](https://runcloud.io) 作为一名全职技术文档工程师,编辑也是非洲大使。RunCloud是一家SaaS初创公司,可帮助你在Digital Ocean、AWS、Google Cloud Platform等云托管服务器上设置、配置、管理和监控你的VPS,尤其适用于PHP和WordPress应用程序。 +因此,2019 年的一月开始我加入[RunCloud](https://runcloud.io) 作为一名全职技术文档工程师,编辑也是非洲大使。RunCloud 是一家 SaaS 初创公司,可帮助你在 Digital Ocean、AWS、Google Cloud Platform 等云托管服务器上设置、配置、管理和监控你的 VPS,尤其适用于 PHP 和 WordPress 应用程序。 -作为一名技术文档工程师,我将为Runcloub的产品文档提供支持,制作高质量的博客文章,为开发者社群提供培训资源以及编辑文章。 +作为一名技术文档工程师,我将为 Runcloub 的产品文档提供支持,制作高质量的博客文章,为开发者社群提供培训资源以及编辑文章。 -我也将更多在meetups 和技术会议上发言(并支持非洲各地的开发者活动),作为我们公司支持非洲开发者社区的一部分。你可以通过charles@runcloud.io联系我,如果你是一个开发者社区的负责人我将很乐意讨论并支持你的活动。 +我也将更多在 meetups 和技术会议上发言(并支持非洲各地的开发者活动),作为我们公司支持非洲开发者社区的一部分。你可以通过charles@runcloud.io联系我,如果你是一个开发者社区的负责人我将很乐意讨论并支持你的活动。 -## 我是如何加入RunCloud——从一条推特开始 +## 我是如何加入 RunCloud——从一条推特开始 -2018年12月,我决定不写“年度回顾”,即使我对我取得的成就感到开心:我成长为一名开发者和作家,在不到一年的时间里,我建立了一个超过550活跃会员的开发者社群同时举办了8次聚会。 +2018 年 12 月,我决定不写“年度回顾”,即使我对我取得的成就感到开心:我成长为一名开发者和作家,在不到一年的时间里,我建立了一个超过 550 活跃会员的开发者社群同时举办了 8 次聚会。 -2019年我想要更大的挑战,所以我在推特上发布了我2019年新的决定。后来我删除了这条推特,但我是这样写的: +2019 年我想要更大的挑战,所以我在推特上发布了我 2019 年新的决定。后来我删除了这条推特,但我是这样写的: > 新年决心,找一份前端开发的工作,技术文档工程师,又或是开发者倡导者。 #100DaysOfCode -我从技术社区获得了大力的支持—Raj,RunCloud团队的一员,联系到我说了以一名技术文档工程师加入RunCloud的可能性,正如他们所说,余下的都是历史。 +我从技术社区获得了大力的支持—Raj,RunCloud 团队的一员,联系到我说了以一名技术文档工程师加入 RunCloud 的可能性,正如他们所说,余下的都是历史。 -## 对于freeCodeCamp——谢谢你 +## 对于 freeCodeCamp——谢谢你 我想表达对[Quincy Larson](https://www.freecodecamp.org/news/a-new-chapter-begins-i-am-joining-runcloud-as-a-full-time-technical-writer-afba58ec6a13/undefined)、 [Abbey](https://twitter.com/abbeyrenn)、[Jesse](https://twitter.com/JesseRWeigel)、[Beau Carnes](https://www.freecodecamp.org/news/a-new-chapter-begins-i-am-joining-runcloud-as-a-full-time-technical-writer-afba58ec6a13/undefined)、[Toni Shortsleeve](https://www.freecodecamp.org/news/a-new-chapter-begins-i-am-joining-runcloud-as-a-full-time-technical-writer-afba58ec6a13/undefined) 和编辑团队深深的感谢。我们是一小群技术的弄潮儿,和你们所有人一起工作是我人生中最骄傲的时刻之一,我将永远珍惜! -作为回馈freeCodeCamp的一种方式,我正计划一个[月度捐款](https://donate.freecodecamp.org/),也将鼓励你加入我一起回馈这个帮助了数百万人免费学习编程的组织 [点击这里](https://donate.freecodecamp.org/other-ways-to-donate/)。 +作为回馈 freeCodeCamp 的一种方式,我正计划一个[月度捐款](https://donate.freecodecamp.org/),也将鼓励你加入我一起回馈这个帮助了数百万人免费学习编程的组织 [点击这里](https://donate.freecodecamp.org/other-ways-to-donate/)。 此刻,新的篇章开始了。 -你可以点击[这儿](https://runcloud.io),也可以在我们的[博客](https://blog.runcloud.io)发现更多关于RunCloud。 +你可以点击[这儿](https://runcloud.io),也可以在我们的[博客](https://blog.runcloud.io)发现更多关于 RunCloud。 diff --git a/chinese/articles/a-no-code-intro-to-the-9-most-important-machine-learning-algorithms-today.md b/chinese/articles/a-no-code-intro-to-the-9-most-important-machine-learning-algorithms-today.md index b7516a097..99e218bdd 100644 --- a/chinese/articles/a-no-code-intro-to-the-9-most-important-machine-learning-algorithms-today.md +++ b/chinese/articles/a-no-code-intro-to-the-9-most-important-machine-learning-algorithms-today.md @@ -9,7 +9,7 @@ 机器学习从未像在今天这样重要。但与此同时,机器学习这一领域也充斥着各种术语,晦涩难懂,各种机器学习的算法每年层出不穷。 -本篇文章将向您介绍机器学习领域的基础概念。更确切地说,我们将讨论时下9大核心机器学习算法背后的基本概念。 +本篇文章将向您介绍机器学习领域的基础概念。更确切地说,我们将讨论时下 9 大核心机器学习算法背后的基本概念。 # 推荐系统 @@ -29,7 +29,7 @@ 也正因如此,如果你此前并未学习过线性代数,本节可能会出现一些你尚不理解的概念。 -但别担心,scikit-learn Python图书馆已让构建推荐系统变得非常容易。换句话说,即便你没有很深厚的线性代数背景,你也可以在现实世界中构建出一个推荐系统。 +但别担心,scikit-learn Python 图书馆已让构建推荐系统变得非常容易。换句话说,即便你没有很深厚的线性代数背景,你也可以在现实世界中构建出一个推荐系统。 ### 推荐系统的工作原理是什么呢? @@ -69,11 +69,11 @@ ### 线性回归的历史 -19世纪,线性回归由 [Francis Galton][4]提出。 +19 世纪,线性回归由 [Francis Galton][4]提出。 Galton 是一名研究父母-子女关系的科学家。具体而言,他研究的是父亲身高同其儿子身高之间的关系。 -Galton的第一个发现是,儿子倾向于大致同父亲一样高,这并不足为奇。 +Galton 的第一个发现是,儿子倾向于大致同父亲一样高,这并不足为奇。 但后来,获得一个更有趣的发现。那就是,一个儿子的身高 __倾向于更接近所有人身高的平均值,__ 比其同其父亲身高的接近度还要高__。__ @@ -91,7 +91,7 @@ Galton 为这一现象命名为: ****回归****。 用他的话说,“一 ![An example of the math behind least squares regression](https://nickmccullum.com/images/python-machine-learning/introduction-linear-regression/least-squares-regression.gif) -在创建回归模型时,你最终会产出一个方程式。利用它,你可以预测给定x值时的y值,而无需事先知道y值。 +在创建回归模型时,你最终会产出一个方程式。利用它,你可以预测给定 x 值时的 y 值,而无需事先知道 y 值。 # 逻辑回归 @@ -125,23 +125,23 @@ Galton 为这一现象命名为: ****回归****。 用他的话说,“一 ![Linear Regression vs Logistic Regression](https://nickmccullum.com/images/python-machine-learning/introduction-logistic-regression/linear-vs-logistic-regression.png) -### S型函数 +### S 型函数 -逻辑回归模型生成的曲线之所以有一个偏向是因为它不是用线性方程计算得的,而是由S型函数(因其在逻辑回归中的应用又名逻辑函数)构建。 +逻辑回归模型生成的曲线之所以有一个偏向是因为它不是用线性方程计算得的,而是由 S 型函数(因其在逻辑回归中的应用又名逻辑函数)构建。 -要在机器学习领域有所成就,你并不需要记忆 [S型函数][6] ,但是理解它的外形还是对你有帮助的。 +要在机器学习领域有所成就,你并不需要记忆 [S 型函数][6] ,但是理解它的外形还是对你有帮助的。 其方程如下: ![The Sigmoid Equation](https://nickmccullum.com/images/python-machine-learning/introduction-logistic-regression/sigmoid-equation.png) -有关S型函数值得知道的一点是,它有这样一个主要特征:无论你输入的值有多大,该函数输出的值始终分布在0到1之间。 +有关 S 型函数值得知道的一点是,它有这样一个主要特征:无论你输入的值有多大,该函数输出的值始终分布在 0 到 1 之间。 ### 利用逻辑回归模型开展预测 要利用线性回归模型进行预测,通常需要设定一个临界点,该临界点通常为 `0.5`。 -我们不妨使用先前图片中的癌症诊断示例来在实践中理解这一原理。如果逻辑回归模型输出的值低于0.5,该数据点将被归为良性肿瘤。反之,如果S型函数输出的值大于0.5,则将被归为恶性。 +我们不妨使用先前图片中的癌症诊断示例来在实践中理解这一原理。如果逻辑回归模型输出的值低于 0.5,该数据点将被归为良性肿瘤。反之,如果 S 型函数输出的值大于 0.5,则将被归为恶性。 ### 使用混淆矩阵来衡量逻辑回归的性能 @@ -151,7 +151,7 @@ Galton 为这一现象命名为: ****回归****。 用他的话说,“一 ![An Example Confusion Matrix](https://nickmccullum.com/images/python-machine-learning/introduction-logistic-regression/confusion-matrix.png) -图中,TN代表“真阴性”,FN代表“假阴性”。FP代表“假阳性”,TP代表“真阳性”。 +图中,TN 代表“真阴性”,FN 代表“假阴性”。FP 代表“假阳性”,TP 代表“真阳性”。 混淆矩阵可用于评估模型在混淆矩阵的某一特定象限中是否性能很差。举例来说,图中模型的假阳性异常偏高。 @@ -166,42 +166,42 @@ Galton 为这一现象命名为: ****回归****。 用他的话说,“一 以下是你就逻辑回归学到的知识总结: - 哪些类型的分类问题适合利用逻辑回归模型解决 -- 逻辑函数(也称为S型函数)的输出值永远在0到1之间 +- 逻辑函数(也称为 S 型函数)的输出值永远在 0 到 1 之间 - 如何借助临界点来利用逻辑回归机器学习模型进行预测 - 为什么混淆矩阵可用于衡量逻辑回归模型的性能 -# K近邻算法 +# K 近邻算法 -[K近邻算法][7] 可以帮助解决类别多于两个的分类问题。 +[K 近邻算法][7] 可以帮助解决类别多于两个的分类问题。 -### 什么是K近邻算法? +### 什么是 K 近邻算法? -K近邻算法是一种分类算法。其基于的原理非常简单,通过一个示例我们便能对其进行很好的理解。 +K 近邻算法是一种分类算法。其基于的原理非常简单,通过一个示例我们便能对其进行很好的理解。 -假设你有一些足球运动员和篮球运动员的身高、体重数据,那么当数据中出现了一个新运动员时,K近邻算法便可帮你推测这名运动员是足球运动员还是篮球运动员。 +假设你有一些足球运动员和篮球运动员的身高、体重数据,那么当数据中出现了一个新运动员时,K 近邻算法便可帮你推测这名运动员是足球运动员还是篮球运动员。 -为实现这一推测,K近邻算法会识别最接近新观测值的 `K` 个数据点。 +为实现这一推测,K 近邻算法会识别最接近新观测值的 `K` 个数据点。 -下图是这一过程的展示,其中K值为 `3`: +下图是这一过程的展示,其中 K 值为 `3`: ![A visualization of k nearest neighbors](https://nickmccullum.com/images/python-machine-learning/introduction-k-nearest-neighbors/k-nearest-neighbors.jpg) 图中,我们将足球运动员数据点标记为了蓝色,篮球运动员标记为了橙色,待归类的数据点标记为了绿色。 -由于新数据点最邻近的数据点中大多数(2/3)是蓝色(足球运动员),因此K近邻算法预测这一新数据点也应为蓝色(足球运动员)。 +由于新数据点最邻近的数据点中大多数(2/3)是蓝色(足球运动员),因此 K 近邻算法预测这一新数据点也应为蓝色(足球运动员)。 -### 建立K近邻算法的步骤 +### 建立 K 近邻算法的步骤 -通常,建立一个K近邻算法有以下步骤: +通常,建立一个 K 近邻算法有以下步骤: 1. 存储所有数据 2. 计算从新数据点 `x` 到数据集中其它所有数据点的 [欧式距离][8] 3. 按照距新数据点 `x` 的距离递增的顺序对数据集中的各数据点进行排序 4. 根据距新数据点 `x` 最近的 `K` 个数据点中绝大多数所属的类别推测新数据点的类别 -### K近邻算法中K值的重要性 +### K 近邻算法中 K 值的重要性 -尽管在一开始可能并不明显,但在K近邻算法中更改 `K` 值的大小或将改变对新数据点所属类别的推测结果。 +尽管在一开始可能并不明显,但在 K 近邻算法中更改 `K` 值的大小或将改变对新数据点所属类别的推测结果。 具体而言,如果 `K` 值很小,那么一个模型便能完美地预测训练数据,但在预测测试数据时会表现较差。同样, `K` 值太大则会导致模型过于复杂。 @@ -209,11 +209,11 @@ K近邻算法是一种分类算法。其基于的原理非常简单,通过一 ![K value and error rates](https://nickmccullum.com/images/python-machine-learning/introduction-k-nearest-neighbors/k-value-error-rates.png) -### K近邻算法的优缺点 +### K 近邻算法的优缺点 -在K近邻算法介绍的最后,我想简要讨论一下使用该模型的利弊。 +在 K 近邻算法介绍的最后,我想简要讨论一下使用该模型的利弊。 -这是使用K近邻算法的一些主要优点: +这是使用 K 近邻算法的一些主要优点: - 该算法简单易懂 - 训练模型时没太大必要在新的训练数据上训练 @@ -228,12 +228,12 @@ K近邻算法是一种分类算法。其基于的原理非常简单,通过一 ### 本节小节 -以下是刚刚讲到的有关k近邻算法的知识总结: +以下是刚刚讲到的有关 k 近邻算法的知识总结: -- K近邻算法可以解决的分类问题示例(足球运动员与篮球运动员的例子) -- K近邻算法如何依据相邻数据点的欧几里得距离来预测新数据点属于哪个类别 +- K 近邻算法可以解决的分类问题示例(足球运动员与篮球运动员的例子) +- K 近邻算法如何依据相邻数据点的欧几里得距离来预测新数据点属于哪个类别 - 为什么 `K` 值的大小对预测结果很重要 -- 使用K近邻算法的优缺点 +- 使用 K 近邻算法的优缺点 # 决策树和随机森林 @@ -304,7 +304,7 @@ K近邻算法是一种分类算法。其基于的原理非常简单,通过一 ### 什么是支持向量机? -[支持向量机][13] 是一种监督机器学习模型,简称SVMs(Support vector machines),该类模型具有用于分析数据和识别模式的关联学习算法。 +[支持向量机][13] 是一种监督机器学习模型,简称 SVMs(Support vector machines),该类模型具有用于分析数据和识别模式的关联学习算法。 支持向量机既可用于分类问题也可用于回归问题。本文将专门研究使用支持向量机解决分类问题。 @@ -314,15 +314,15 @@ K近邻算法是一种分类算法。其基于的原理非常简单,通过一 给定一组训练样本 – 标记每个样本属于两个类别中的一个 – 支持向量机训练算法建立模型。这样,输入新样本时,该模型便会将其归为两个类别之一。由此,支持向量机便成了一个非概率二进制线性分类器。 -SVM进行分类预测时所依据的是几何学。 +SVM 进行分类预测时所依据的是几何学。 -具体而言,SVM模型会将数据点映射为空间中的点并将每个单独的类别分隔开来,从而使划分的开放间隙尽可能宽。根据新数据点属于间隙的哪一侧,SVM将预测它们属于哪个类别。 +具体而言,SVM 模型会将数据点映射为空间中的点并将每个单独的类别分隔开来,从而使划分的开放间隙尽可能宽。根据新数据点属于间隙的哪一侧,SVM 将预测它们属于哪个类别。 下图是一个可视化示例,可以帮你更好理解支持向量机背后的机理: ![](https://www.freecodecamp.org/news/content/images/2020/06/image-57.png) -如图所示,如果新数据点位于绿线的左侧,则SVM会将其标记为红色类别。反之,如果新数据点位于绿线的右侧,SVM则会将其标记为蓝色类别。 +如图所示,如果新数据点位于绿线的左侧,则 SVM 会将其标记为红色类别。反之,如果新数据点位于绿线的右侧,SVM 则会将其标记为蓝色类别。 而这条绿线被称为 **超平面**,这是支持向量机算法相关的一个重要术语。 @@ -343,17 +343,17 @@ SVM进行分类预测时所依据的是几何学。 - 支持向量机是如何利用(可最大化数据集中类别之间间距的) **超平面** 对数据点进行分类的 - 支持向量机中,接触边距线的数据点被称为 **支持向量**,支持向量机也因其得名。 -# K均值聚类 +# K 均值聚类 -[K均值聚类][14] 是一种可用于识别数据集内类似数据片段的机器学习算法。 +[K 均值聚类][14] 是一种可用于识别数据集内类似数据片段的机器学习算法。 -### 什么是K均值聚类? +### 什么是 K 均值聚类? -K均值聚类是一种无监督机器学习算法。 +K 均值聚类是一种无监督机器学习算法。 意思是说,接收未标记的数据后它会尝试将数据中类似的观测组归在一起。 -K均值聚类算法在解决现实问题时非常有用。下面是该机器学习模型的几个应用举例: +K 均值聚类算法在解决现实问题时非常有用。下面是该机器学习模型的几个应用举例: - 帮助营销团队对客户进行细分 - 文件分类 @@ -362,61 +362,61 @@ K均值聚类算法在解决现实问题时非常有用。下面是该机器学 - 专业运动分析 - 预防网络犯罪 -K均值聚类算法的主要功能是将数据集划分为不同的组,使每组内的观测结果彼此相似。 +K 均值聚类算法的主要功能是将数据集划分为不同的组,使每组内的观测结果彼此相似。 下图是其实际运作的可视化呈现: ![A Visualization of a K Means Clustering Algorithm](https://nickmccullum.com/images/python-machine-learning/introduction-k-means-clustering/k-means-clustering.png) -我们将在下一节探索K均值聚类背后的数学原理。 +我们将在下一节探索 K 均值聚类背后的数学原理。 -### K均值聚类算法如何运作? +### K 均值聚类算法如何运作? -运行K均值聚类算法的第一步是选择要将数据划分为多少个集群。这一集群数便是该算法名称中的 `K` 值。 +运行 K 均值聚类算法的第一步是选择要将数据划分为多少个集群。这一集群数便是该算法名称中的 `K` 值。 -使用K均值聚类算法时, `K` 值的选择非常关键。稍后我们会讨论如何选择合适的 `K` 值。 +使用 K 均值聚类算法时, `K` 值的选择非常关键。稍后我们会讨论如何选择合适的 `K` 值。 下一步,必须随机将数据集中的每个数据点都分配到随机聚类中,这也是第一步任务的根源所在。然后运行以下迭代,直到集群停止更改: - 通过计算该集群内数据点的平均向量来计算每个集群的几何中心 - 将每个数据点重新分配到同其先前所在集群几何中心最接近的集群 -下面的动画是这一实践过程的示例。其中,K均值聚类算法的 `K` 值为 `3`。可以看到,每个集群的几何中心均由黑色的 `+` 字符表示。 +下面的动画是这一实践过程的示例。其中,K 均值聚类算法的 `K` 值为 `3`。可以看到,每个集群的几何中心均由黑色的 `+` 字符表示。 ![A Visualization of a K Means Clustering Algorithm](https://nickmccullum.com/images/python-machine-learning/introduction-k-means-clustering/k-means-iteration.gif) 如图所示,此迭代将一直持续到集群停止更改为止,这一停止更改意味着数据点将不会再被分配到新的集群。 -### 为K均值聚类算法选择合适的K值 +### 为 K 均值聚类算法选择合适的 K 值 -要为K均值聚类算法选择一个合适的 `K` 值实际上相当困难。该值的“最佳”选择没有“标准”答案。 +要为 K 均值聚类算法选择一个合适的 `K` 值实际上相当困难。该值的“最佳”选择没有“标准”答案。 机器学习从业者经常使用的一种方法叫做 **肘部法**。 -使用肘部法时,第一步要先计算一组 `K` 值下K均值聚类算法的误差平方和(SSE,sum of squared errors)。SSE表示一个集群中的每个数据点与该集群几何中心之间的距离的平方之和。 +使用肘部法时,第一步要先计算一组 `K` 值下 K 均值聚类算法的误差平方和(SSE,sum of squared errors)。SSE 表示一个集群中的每个数据点与该集群几何中心之间的距离的平方之和。 -举例来说,计算 SSE时,你可以选择 `K` 值为 `2`, `4`, `6`, `8`, `10`。 +举例来说,计算 SSE 时,你可以选择 `K` 值为 `2`, `4`, `6`, `8`, `10`。 -接着,将不同 `K` 值下的SSE绘制在图表中。可以看到,随着 `K` 值的增大,错误逐渐减少。 +接着,将不同 `K` 值下的 SSE 绘制在图表中。可以看到,随着 `K` 值的增大,错误逐渐减少。 这也是符合常理的——数据集中创建的类别越多,每个数据点接近其特定集群几何中心的可能性就越大。 -说了这么多,肘部法的用意就在于选择SSE下跌速度突然放缓处的 `K` 值,这种突然的放缓在图表中往往会生成了一个 `肘` 形。 +说了这么多,肘部法的用意就在于选择 SSE 下跌速度突然放缓处的 `K` 值,这种突然的放缓在图表中往往会生成了一个 `肘` 形。 -下图是一个SSE和 `K` 的关系图表示例。图中,肘部法建议的 `K` 值大约为 `6`。 +下图是一个 SSE 和 `K` 的关系图表示例。图中,肘部法建议的 `K` 值大约为 `6`。 ![A Visualization of a K Means Clustering Algorithm](https://nickmccullum.com/images/python-machine-learning/introduction-k-means-clustering/elbow-method.png) -值得注意的是, `6` 只是对最佳 `K` 值的一种估计。 K均值聚类算法中并没有标准的“最佳” `K` 值。同机器学习领域的许多事物一样,这一最佳选择高度依赖具体情况。 +值得注意的是, `6` 只是对最佳 `K` 值的一种估计。 K 均值聚类算法中并没有标准的“最佳” `K` 值。同机器学习领域的许多事物一样,这一最佳选择高度依赖具体情况。 ### 本节小节 以下是本节内容的小结: -- K均值聚类算法能够解决的无监督机器学习问题示例 -- K均值聚类算法的基础机理 -- K均值聚类算法的工作原理 -- 如何利用肘方法为K均值聚类模型选择合适的 `K` 值 +- K 均值聚类算法能够解决的无监督机器学习问题示例 +- K 均值聚类算法的基础机理 +- K 均值聚类算法的工作原理 +- 如何利用肘方法为 K 均值聚类模型选择合适的 `K` 值 # 主要组件分析 @@ -438,7 +438,7 @@ K均值聚类算法的主要功能是将数据集划分为不同的组,使每 线性回归通过数据集确定一条最佳拟合线,而主要组件分析会确定出几条最拟合数据集的正交线。 -**正交**的意思可以理解为线与线之间呈直角(90度)相交——就像地图上指示北、东、南、西的方向坐标那样。 +**正交**的意思可以理解为线与线之间呈直角(90 度)相交——就像地图上指示北、东、南、西的方向坐标那样。 我们不妨通过下面的示例来帮助你更好的理解。 @@ -446,9 +446,9 @@ K均值聚类算法的主要功能是将数据集划分为不同的组,使每 仔细看图中的轴标。 -图中,x轴的主要组件代表了该数据集中73%的变量,y轴的主要组件则解释了数据集中23%的变量。 +图中,x 轴的主要组件代表了该数据集中 73%的变量,y 轴的主要组件则解释了数据集中 23%的变量。 -这就意味着数据集中仍有4%的变量未得到解释。通过在分析中添加更多主要组件,这一未解释变量的数量便能减至更少。 +这就意味着数据集中仍有 4%的变量未得到解释。通过在分析中添加更多主要组件,这一未解释变量的数量便能减至更少。 ### 本节小节 diff --git a/chinese/articles/a-practical-guide-to-start-opensource-contributions.md b/chinese/articles/a-practical-guide-to-start-opensource-contributions.md index bac416cfd..78cfd7a95 100644 --- a/chinese/articles/a-practical-guide-to-start-opensource-contributions.md +++ b/chinese/articles/a-practical-guide-to-start-opensource-contributions.md @@ -11,9 +11,9 @@ 在开始之前,让我简单介绍一下我自己以及我对开源社区的兴趣。 -我每天都会使用开源项目、产品和服务,我也参与贡献开源项目提升自我。我维护着一些向初学者提供web编程教学的开源项目。 +我每天都会使用开源项目、产品和服务,我也参与贡献开源项目提升自我。我维护着一些向初学者提供 web 编程教学的开源项目。 -你可以在我的[GitHub资料](https://github.com/atapas)上查看我的开源项目。 +你可以在我的[GitHub 资料](https://github.com/atapas)上查看我的开源项目。 如果你尚未开始贡献开源项目,我将通过这篇文章分享我自己的经验帮助你开启开源之旅。 @@ -29,19 +29,19 @@ 项目越大,贡献者越多。任何人都可以查看项目代码、修改代码、请求校对并将改变并入项目中。 -`源代码和文档库`: 维护者将项目源代码保存在一个中心化的源代码库 (例如GitHub)。这样所有贡献者就可以访问代码并且参与贡献。 +`源代码和文档库`: 维护者将项目源代码保存在一个中心化的源代码库 (例如 GitHub)。这样所有贡献者就可以访问代码并且参与贡献。 `项目许可证`: 每一个开源项目都必须指定一个分发许可证,给用户和使用者提供清晰的信息。 有各种个样的许可证,维护者可以根据项目做选择。比较广泛被采用的许可证包括:MIT、 Apache License 2.0、 GNU General Public License (GPL) 3.0 等。 -`贡献指南`: OSS项目的维护者创建贡献指南帮助贡献者理解合并请求(pull request)的流程、标准、范围等。 +`贡献指南`: OSS 项目的维护者创建贡献指南帮助贡献者理解合并请求(pull request)的流程、标准、范围等。 -`行为准则指南`: 行为准则指南讨论各种指导方针、对贡献者的协同和行为期望,以及如何提出和解决issue(问题)。 +`行为准则指南`: 行为准则指南讨论各种指导方针、对贡献者的协同和行为期望,以及如何提出和解决 issue(问题)。 `项目文化`: 项目文化随着项目社区发展而变化。虽然维护者对其影响重大,但贡献者也有同样的责任来维护一个健康学习、分享和成长的文化。 -`社区`: 随着项目发展,围绕项目的社区也发展起来。组织社区交流的常用平台有GitHub Discussions和Discord。 +`社区`: 随着项目发展,围绕项目的社区也发展起来。组织社区交流的常用平台有 GitHub Discussions 和 Discord。 `分发`: 开源项目应该接触到终端用户和消费者,所以应该有一个分发模型将代码转化为最终的产品,进行交付。 @@ -55,19 +55,19 @@ 开源项目运作模型的高层视角 -如果你不熟悉fork、 branch、 merge这些词,不用担心!我们马上就会讨论这些词,请继续阅读。 +如果你不熟悉 fork、 branch、 merge 这些词,不用担心!我们马上就会讨论这些词,请继续阅读。 现在让我们讨论一下开源软件是如何交付给用户/客户的。 下面的图片显示了一种可能性的高层视角。开源项目应具有构建-打包-部署机制,该机制使用持续集成和持续部署(CI/CD)流程。 -每当`main branch(主分支)`的代码发生改动,CI/CD工作流就开始自动工作:构建源代码、打包并部署,供终端用户和目标客户能够使用。 +每当`main branch(主分支)`的代码发生改动,CI/CD 工作流就开始自动工作:构建源代码、打包并部署,供终端用户和目标客户能够使用。 ![deploy](https://www.freecodecamp.org/news/content/images/2022/07/deploy.png) 从源代码到用户的高层视角 -请注意: CI/CD或者其他部署机制并不是开源软件开发的一部分,但了解它有助于理解OSS端到端的工作模型。 +请注意: CI/CD 或者其他部署机制并不是开源软件开发的一部分,但了解它有助于理解 OSS 端到端的工作模型。 ## 开源项目贡献是什么意思? @@ -93,7 +93,7 @@ - 提高软件/应用的代码和文档水平 - 接触想法相似的朋友、建立人脉和社区 - 理解应用开发和维护的流程 -- 从Pull Request反馈中学习 +- 从 Pull Request 反馈中学习 - 学习如何管理自己的开源项目 ## 开源迷思 @@ -122,7 +122,7 @@ ❌ **迷思**: 开源软件总是免费的。 -✅ **事实:** 大多数开源项目是免费的,但不是所有的都是。这是由项目的许可证所决定的。有一些许可证要求必须让使用和分发免费。你需要格外注意项目许可证信息,了解OSS到底免费到什么程度。 +✅ **事实:** 大多数开源项目是免费的,但不是所有的都是。这是由项目的许可证所决定的。有一些许可证要求必须让使用和分发免费。你需要格外注意项目许可证信息,了解 OSS 到底免费到什么程度。 ❌ **迷思**: 开源项目只适合初学者。 @@ -132,44 +132,44 @@ 开发者想要快速开始参与开源项目需要了解一些基本技能。这些是可选的,但是如果具备,你将更享受参与开源贡献。 -### 了解Git基础 +### 了解 Git 基础 -如果你已经了解Git的概念及其主要用途,那么你已经前进了一大步。Git在开源世界中无处不在,你不能忽视它。 +如果你已经了解 Git 的概念及其主要用途,那么你已经前进了一大步。Git 在开源世界中无处不在,你不能忽视它。 你至少要了解以下话题: -- 什么是Git,它是怎么运作的? +- 什么是 Git,它是怎么运作的? - 仓库是什么? - 如何复制(clone)一个仓库? -- 如何stage/un-stage(暂存/取消暂存)修改? -- 如何commit(提交)你的修改? +- 如何 stage/un-stage(暂存/取消暂存)修改? +- 如何 commit(提交)你的修改? - 如何编写更好的提交消息? - 如何解决合并冲突? - 如何将你的修改推送(push)到远程仓库? - 如何从远程仓库拉取(pull)修改? -如果你尚不了解Git,我推荐你观看这个视频,了解Git的概念和上面提到的用例: [为初学者揭开Git的神秘面纱](https://www.youtube.com/watch?v=vWtu4mzUgQo). +如果你尚不了解 Git,我推荐你观看这个视频,了解 Git 的概念和上面提到的用例: [为初学者揭开 Git 的神秘面纱](https://www.youtube.com/watch?v=vWtu4mzUgQo). -### 熟悉GitHub +### 熟悉 GitHub -[GitHub](https://github.com/)上有超过1.28亿个公开仓库,其中很大一部分是开源项目。你想要参与贡献的开源项目可能在GitHub上,所以你应该学习如何使用GitHub。 +[GitHub](https://github.com/)上有超过 1.28 亿个公开仓库,其中很大一部分是开源项目。你想要参与贡献的开源项目可能在 GitHub 上,所以你应该学习如何使用 GitHub。 作为开源项目的贡献者,你应该知道: -- 如何fork(分叉)一个仓库? -- 如何找到仓库的URL并clone(克隆)仓库? -- 如何创建一个Pull Request? -- 如何校对一个Pull Request? +- 如何 fork(分叉)一个仓库? +- 如何找到仓库的 URL 并 clone(克隆)仓库? +- 如何创建一个 Pull Request? +- 如何校对一个 Pull Request? 作为一个项目维护者,你应该知道: - 如何创建一个仓库? - 如何为项目添加许可证信息? - 如何创建贡献指南和行为准则指南? -- 如何创建提交issue和pull requests的标准? -- 如何合并pull requests? +- 如何创建提交 issue 和 pull requests 的标准? +- 如何合并 pull requests? -你可以关注下面的Twitter thread,它分步骤讲解了上面的所有信息: +你可以关注下面的 Twitter thread,它分步骤讲解了上面的所有信息: > Do you have public GitHub repositories? > @@ -188,19 +188,19 @@ -### 学习如何Fork(分叉)一个仓库 +### 学习如何 Fork(分叉)一个仓库 Fork(分叉)是一个重要的概念。 大部分`开源` 项目不允许贡献者直接在仓库创建分支。取而代之的工作流如下: - fork(分叉)仓库。 - clone(克隆)分叉后的仓库。 - 编写修改。 -- 从UPSTREAM(上游)提取修改。 -- 创建从分叉仓库到基础仓库的pull request。 +- 从 UPSTREAM(上游)提取修改。 +- 创建从分叉仓库到基础仓库的 pull request。 -在我和众多贡献者的工作经验中,大部分人觉得理解Fork(分叉)的概念有点困难。 +在我和众多贡献者的工作经验中,大部分人觉得理解 Fork(分叉)的概念有点困难。 -你可以通过这个视频学习Fork的概念 [如何分叉一个Github仓库](https://www.youtube.com/watch?v=h8suY-Osn8Q)。同时你可以在这个[GitHub仓库](https://github.com/atapas/fork-me)练习分叉,这是为初学者设计来提升信心的仓库。 +你可以通过这个视频学习 Fork 的概念 [如何分叉一个 Github 仓库](https://www.youtube.com/watch?v=h8suY-Osn8Q)。同时你可以在这个[GitHub 仓库](https://github.com/atapas/fork-me)练习分叉,这是为初学者设计来提升信心的仓库。 ### 学习解决合并冲突(Merge Conflicts) @@ -208,7 +208,7 @@ Fork(分叉)是一个重要的概念。 大部分`开源` 项目不允许贡 这里有一份[解决合并冲突实用指南](https://www.freecodecamp.org/news/resolve-merge-conflicts-in-git-a-practical-guide/),欢迎查阅。 -### 学习Markdown语法 +### 学习 Markdown 语法 文档是任何开源项目的基础。`Readme.md`文件介绍了项目,如何设置、运行、部署等。 @@ -216,9 +216,9 @@ Fork(分叉)是一个重要的概念。 大部分`开源` 项目不允许贡 `CODE_OF_CONDUCT.md`文件介绍了对贡献者行为和参与的期望。当然,你可以基于自己的项目,编写任意 `.md`文件。 -`md`代表着markdown(标记语言),是一种在Github上用于写文档的语法。了解基础的语法有助于你流畅地编写文档。 +`md`代表着 markdown(标记语言),是一种在 Github 上用于写文档的语法。了解基础的语法有助于你流畅地编写文档。 -这里有一个开源项目提供了Markdown语法,可以复制和使用。欢迎查阅。 +这里有一个开源项目提供了 Markdown 语法,可以复制和使用。欢迎查阅。 [ @@ -240,7 +240,7 @@ A single place for all the markdown syntaxes I have learned so far. - GitHub - a 让我们来看看开发者需要具备的一些软技能: -- **耐心**: `耐心`是所有开发者必备的技能。当你在学习新知识,排查复杂问题的bug,和其他人一起工作、谈判,给予和吸收意见的时候都需要耐心。事情常常不如预期的节奏推进,所以你需要保持耐心面对现状。 +- **耐心**: `耐心`是所有开发者必备的技能。当你在学习新知识,排查复杂问题的 bug,和其他人一起工作、谈判,给予和吸收意见的时候都需要耐心。事情常常不如预期的节奏推进,所以你需要保持耐心面对现状。 - **好奇心**: `好奇`使人进步。参与开源贡献将拥有无限可能。你需要对解决问题充满好奇心。好奇心不仅适用于解决技术难题,也适用于和他人一起工作。 - **责任心**: 在开源生态中,你可能不会经常和人见面及交流。你必须对所有查询、任务、请求以及委任与你的事情`负责`。许多伟大的倡议往往因为人们缺乏责任心而不了了之。 - **谦逊之心**: `谦虚`是成功的关键。有学识但不谦逊的人往往不适合团队协作。 @@ -251,18 +251,18 @@ A single place for all the markdown syntaxes I have learned so far. - GitHub - a ### GitHub Explore -GitHub Explore会向你展示你感兴趣的仓库,你可以给特定仓库设置提醒。 +GitHub Explore 会向你展示你感兴趣的仓库,你可以给特定仓库设置提醒。 -同时,你可以使用话题和趋势来寻找仓库。使用GitHub explore来寻找最合适你的技能、需求以及对你最有启发的项目。你可以通过这个地址查看: [https://github.com/explore/](https://github.com/explore/) +同时,你可以使用话题和趋势来寻找仓库。使用 GitHub explore 来寻找最合适你的技能、需求以及对你最有启发的项目。你可以通过这个地址查看: [https://github.com/explore/](https://github.com/explore/) -### 如何参与freeCodeCamp的开源项目 +### 如何参与 freeCodeCamp 的开源项目 -freeCodeCamp的这个仓库绝对是一个宝藏,它提供了许多资源和指示帮助你开启开源之旅。你可以通过这个地址查看:[https://github.com/freeCodeCamp/how-to-contribute-to-open-source](https://github.com/freeCodeCamp/how-to-contribute-to-open-source) +freeCodeCamp 的这个仓库绝对是一个宝藏,它提供了许多资源和指示帮助你开启开源之旅。你可以通过这个地址查看:[https://github.com/freeCodeCamp/how-to-contribute-to-open-source](https://github.com/freeCodeCamp/how-to-contribute-to-open-source) ### Contributor Ninja -这个网站供你选择一系列编程语言:JavaScript、 HTML、 Rust、 Go等。你可以通过卡片选择对应的仓库,简单方便,是一个不错的开始。你可以通过这个地址查看: [https://contributor.ninja/](https://contributor.ninja/) +这个网站供你选择一系列编程语言:JavaScript、 HTML、 Rust、 Go 等。你可以通过卡片选择对应的仓库,简单方便,是一个不错的开始。你可以通过这个地址查看: [https://contributor.ninja/](https://contributor.ninja/) ### First Contributions @@ -271,7 +271,7 @@ freeCodeCamp的这个仓库绝对是一个宝藏,它提供了许多资源和 ### CodeTriage -CodeTriage是一个包含巨量开放issue项目的清单。它将issue和文档归类后分发给你。这个网站十分有用,你可以通过这个地址查看: [https://www.codetriage.com/](https://www.codetriage.com/) +CodeTriage 是一个包含巨量开放 issue 项目的清单。它将 issue 和文档归类后分发给你。这个网站十分有用,你可以通过这个地址查看: [https://www.codetriage.com/](https://www.codetriage.com/) ### Up For Grabs @@ -288,7 +288,7 @@ CodeTriage是一个包含巨量开放issue项目的清单。它将issue和文档 你本周五或者下周五有什么打算?花点时间给你使用或者喜欢的软件做点贡献?推荐你查看这个网站并且注册。你可以通过这个地址查看: [https://opensourcefriday.com/](https://opensourcefriday.com/) -希望这些资源对你来说有帮助。另外,你还可以查看这个Twitter thread以及相关的回应,你或许会有所收获。 +希望这些资源对你来说有帮助。另外,你还可以查看这个 Twitter thread 以及相关的回应,你或许会有所收获。 > Looking forward to contributing to OPENSOURCE? Here are 8 RESOURCES to get you started immediately. > @@ -303,17 +303,17 @@ CodeTriage是一个包含巨量开放issue项目的清单。它将issue和文档 作为项目维护者,你应该遵循一些标准使得其他人能够理解并且参与贡献你的项目仓库。 - 提供项目明确的名字和介绍。你也可以添加项目相关的话题。 -- 添加清晰的`Readme.md`文件介绍项目的目标、如何使用、设置等。如果说源码是项目的心脏,拿README文件就是项目的脸面。 +- 添加清晰的`Readme.md`文件介绍项目的目标、如何使用、设置等。如果说源码是项目的心脏,拿 README 文件就是项目的脸面。 - 建立社区档案。开源仓库维护者可以通过它来检查工作、了解哪里需要帮助。 - 建立编码行为准则。 - 创建贡献指南。 -- 决定issue的模板。 -- 创建Pull Request (PR)模板。 -- 激活GitHub赞助。 +- 决定 issue 的模板。 +- 创建 Pull Request (PR)模板。 +- 激活 GitHub 赞助。 [你可以通过阅读这篇文章](https://www.freecodecamp.org/news/increase-engagement-on-your-public-github-repositories/) 了解更多细节。 -## 在结束之前... +## 在结束之前 就这么多!到这里,我们就到了本文的结尾。希望这篇文章对你有所启发,并且给了你足够的动力开始为开源做贡献。 diff --git a/chinese/articles/about-freeCodeCamp-frequently-asked-questions.md b/chinese/articles/about-freeCodeCamp-frequently-asked-questions.md index 7ba78ae69..7d4eb65a4 100644 --- a/chinese/articles/about-freeCodeCamp-frequently-asked-questions.md +++ b/chinese/articles/about-freeCodeCamp-frequently-asked-questions.md @@ -9,7 +9,7 @@ ### freeCodeCamp 是什么? -这是一个非营利性社区,帮助你通过构建项目的方式来学习编程。 +这是一个非营利性社区,帮助你通过构建项目的方式来学习编程 ======= ### 你们如何帮助我学习编程呢? diff --git a/chinese/articles/ajax-tutorial.md b/chinese/articles/ajax-tutorial.md index 55e25d73b..9c332c3cc 100644 --- a/chinese/articles/ajax-tutorial.md +++ b/chinese/articles/ajax-tutorial.md @@ -4,24 +4,24 @@ ![AJAX Tutorial: What AJAX Is and How to Use it](https://images.unsplash.com/photo-1557234396-e1506d9a85b3?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2000&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ) -## **什么是AJAX** +## **什么是 AJAX** -**AJAX** 是 **Asynchronous JavaScript And XML** 的缩写。它不是一种编程语言。它是一种基于HTML、CSS、JavaScript和XML,让开发更好、更快和更有互动的Web应用的技术。 +**AJAX** 是 **Asynchronous JavaScript And XML** 的缩写。它不是一种编程语言。它是一种基于 HTML、CSS、JavaScript 和 XML,让开发更好、更快和更有互动的 Web 应用的技术。 1. HTML : 超文本标记语言(HTML)用于定义网络应用程序的结构。 2. CSS : 层叠样式表(CSS)用于为 Web 应用程序提供外观和样式。 3. JavaScript : JavaScript 用于使网络应用程序互动、提供趣味和提高用户友好性。 4. XML : 可扩展标记语言(XML)是一种用于存储和传输网络服务器数据的格式。 -### AJAX中的异步是什么意思 +### AJAX 中的异步是什么意思 异步意味着网络应用程序可以从网络服务器发送和接收数据,而无需刷新页面。这个向服务器发送数据和接收服务器数据以及更新网页不同部分的后台过程,就是 AJAX 的异步属性/功能。 -## AJAX是如何工作的 +## AJAX 是如何工作的 -AJAX 利用浏览器内置的 **XMLHttpRequest对象** 从网络服务器请求数据,并利用 **HTML DOM** 显示或使用数据。 +AJAX 利用浏览器内置的 **XMLHttpRequest 对象** 从网络服务器请求数据,并利用 **HTML DOM** 显示或使用数据。 -**XMLHttpRequest 对象** : 它是一个对象形式的API,其方法用于网络浏览器和网络服务器之间传输数据。 +**XMLHttpRequest 对象** : 它是一个对象形式的 API,其方法用于网络浏览器和网络服务器之间传输数据。 **HTML DOM** : 当一个网页被加载时,浏览器会创建一个页面的文档对象模型。 @@ -33,7 +33,7 @@ var xhttp = new XMLHttpRequest(); **XMLHttpRequest 对象的属性 :** -`readystate` 是XMLHttpRequest对象的一个属性,它是XMLHttpRequest的一种状态值。 +`readystate` 是 XMLHttpRequest 对象的一个属性,它是 XMLHttpRequest 的一种状态值。 - 0: 请求未被初始化 - 1: 服务器连接建立 @@ -41,7 +41,7 @@ var xhttp = new XMLHttpRequest(); - 3: 处理请求 - 4: 请求完成,响应准备就绪 -`onreadystatechange`是XMLHttpRequest对象的一个属性,它定义了一个当 readyState 属性改变时要调用的函数。 +`onreadystatechange`是 XMLHttpRequest 对象的一个属性,它定义了一个当 readyState 属性改变时要调用的函数。 `status` 是 XMLHttpRequest 对象的一个属性,用于返回一个请求的状态值。 @@ -49,7 +49,7 @@ var xhttp = new XMLHttpRequest(); - 403: "Forbidden" - 404: "Not Found" -**XMLHttpRequest对象方法:** 为了向Web服务器发送请求,我们使用 XMLHttpRequest 对象的 open() 和 send() 方法。 +**XMLHttpRequest 对象方法:** 为了向 Web 服务器发送请求,我们使用 XMLHttpRequest 对象的 open() 和 send() 方法。 ```javascript xhttp.open('GET', 'content.txt', true); @@ -101,4 +101,4 @@ function changeContent() { ``` -文件 `content.txt` 应该存在于Web应用程序的根目录中。 +文件 `content.txt` 应该存在于 Web 应用程序的根目录中。 diff --git a/chinese/articles/an-introduction-to-programming-paradigms.md b/chinese/articles/an-introduction-to-programming-paradigms.md index 1c5950abd..a35afe7f3 100644 --- a/chinese/articles/an-introduction-to-programming-paradigms.md +++ b/chinese/articles/an-introduction-to-programming-paradigms.md @@ -11,7 +11,7 @@ 虽然我会提供一些伪代码和代码示例,但是这篇文章的讨论仅停留在范式表层和理论层面。 -我计划将在之后用JavaScript示例来深入讲解每一个范式,如果你感兴趣的话可以关注我(关注方式见文章结尾)。 +我计划将在之后用 JavaScript 示例来深入讲解每一个范式,如果你感兴趣的话可以关注我(关注方式见文章结尾)。 让我们开始吧! @@ -46,7 +46,7 @@ 编程语言并不总是和一个特定范式相关联。有些语言在创建的时候就考虑到了某种范式,并且具有比其他语言更利于这种范式的特性。([Haskel](https://www.haskell.org/) 和函数式编程就是很好的例子)。 -但也存在“多范式”语言,意味着你可以以这样或那样的范式来调整代码(JavaScript和Python就是非常好的例子)。 +但也存在“多范式”语言,意味着你可以以这样或那样的范式来调整代码(JavaScript 和 Python 就是非常好的例子)。 同时,编程和范式并不是相互排斥的,也就是说你可以在同一个项目中同时使用不同的范式。 @@ -84,7 +84,7 @@ 7- Let chill //冷却 ``` -若使用真实的代码,假设我们想要从一个数组中过滤出大于5的所有元素,命令式编程如下: +若使用真实的代码,假设我们想要从一个数组中过滤出大于 5 的所有元素,命令式编程如下: ```javascript const nums = [1,4,3,6,7,8,9,2] @@ -97,7 +97,7 @@ for (let i = 0; i < nums.length; i++) { console.log(result) // 输出: [ 6, 7, 8, 9 ] ``` -我们告诉程序要遍历整个数组的每一个元素,并且和5来比较,如果这个元素的值大于5,就将它推入另一个数组。 +我们告诉程序要遍历整个数组的每一个元素,并且和 5 来比较,如果这个元素的值大于 5,就将它推入另一个数组。 我们的指令非常的具体详尽,这就是命令式编程的特点。 @@ -192,19 +192,19 @@ console.log(nums.filter(num => num > 5)) // 输出: [ 6, 7, 8, 9 ] 使用过滤函数,我们不再告诉计算机具体的操作是需要遍历数组并且将结果存储到另一个数组中,我们只需要告诉计算机我们需要过滤("filter"),过滤的条件是("num > 5")。 -这样做的好处是代码更易读易理解,并且更简洁。JavaScript的`filter`、 `map`、 `reduce`和`sort` 函数都是声明式编程的例子。 +这样做的好处是代码更易读易理解,并且更简洁。JavaScript 的`filter`、 `map`、 `reduce`和`sort` 函数都是声明式编程的例子。 -另一个例子是JS框架/库——React,如下面的代码: +另一个例子是 JS 框架/库——React,如下面的代码: ```javascript ``` -这里有一个按钮元素,绑定了一个事件监听器,一旦点击按钮就会触发console.log函数。 +这里有一个按钮元素,绑定了一个事件监听器,一旦点击按钮就会触发 console.log 函数。 -React使用的JSX语法结合了HTML和JS,这样编写应用就更加简单快捷。但是JSX并不是浏览器读取和执行代码的方式。React的代码之后会被编译成正常的HTML和JS代码,这才是浏览器实际执行的代码。 +React 使用的 JSX 语法结合了 HTML 和 JS,这样编写应用就更加简单快捷。但是 JSX 并不是浏览器读取和执行代码的方式。React 的代码之后会被编译成正常的 HTML 和 JS 代码,这才是浏览器实际执行的代码。 -JSX就是声明式的,目的是为了使得开发者有一个更加友好、高效的工作接口。 +JSX 就是声明式的,目的是为了使得开发者有一个更加友好、高效的工作接口。 需要注意即便我们书写的是声明式代码,但是计算机还是以命令式来执行信息。 @@ -216,11 +216,11 @@ JSX就是声明式的,目的是为了使得开发者有一个更加友好、 最受欢迎的编程范式之一莫过于面向对象的编程(OOP). -OOP的核心概念是将关注点分离成对象实体,每一个实体包含一组信息(属性)和行为(方法),可以由实体来执行。 +OOP 的核心概念是将关注点分离成对象实体,每一个实体包含一组信息(属性)和行为(方法),可以由实体来执行。 -OOP大量使用类(是由程序员设定的从蓝图或者模板创建新对象的方法),由类创建的对象叫做实例。 +OOP 大量使用类(是由程序员设定的从蓝图或者模板创建新对象的方法),由类创建的对象叫做实例。 -改写之前的伪代码烤蛋糕例子,假设我们蛋糕房有一个主厨(叫Frank),和一个厨师助理(叫Anthony),他俩分别有各自的烘培任务。如果使用OOP,代码如下: +改写之前的伪代码烤蛋糕例子,假设我们蛋糕房有一个主厨(叫 Frank),和一个厨师助理(叫 Anthony),他俩分别有各自的烘培任务。如果使用 OOP,代码如下: ``` // 给每一个实体创建一个类 @@ -262,9 +262,9 @@ Frank.mixAndBake() Anthony.chillTheCake() ``` -OOP的优势在于,通过清晰的关注点和责任分离,编程更加易于理解。 +OOP 的优势在于,通过清晰的关注点和责任分离,编程更加易于理解。 -在这个例子中我只介绍了OOP的表面,如果你感兴趣的话,推荐观看下面两个视频: +在这个例子中我只介绍了 OOP 的表面,如果你感兴趣的话,推荐观看下面两个视频: - [OOP 视频 1](https://www.youtube.com/watch?v=cg1xvFy1JQQ) - [OOP 视频 2](https://www.youtube.com/watch?v=pTB0EiLXUC8) diff --git a/chinese/articles/an-introduction-to-software-architecture-patterns.md b/chinese/articles/an-introduction-to-software-architecture-patterns.md index 7b1671a7b..356211e98 100644 --- a/chinese/articles/an-introduction-to-software-architecture-patterns.md +++ b/chinese/articles/an-introduction-to-software-architecture-patterns.md @@ -18,7 +18,7 @@ - [什么是软件架构](#what-is-software-architecture)? - [重要的软件架构概念](#important-software-architecture-concepts-to-know) - [什么是客户端——服务器模型](#whats-the-client-server-model)? - - [什么是API](#what-are-apis)? + - [什么是 API](#what-are-apis)? - [什么是模块化](#what-is-modularity)? - [你的基础架构是什么样的?](#what-s-your-infrastructure-like) - [单体式架构](#monolithic-architecture) @@ -36,7 +36,7 @@ - [不同的文件夹结构](#different-folder-structures-to-know) - [全在一个文件夹中的结构](#all-in-one-place-folder-structure) - [分层文件夹结构](#layers-folder-structure) - - [MVC文件夹结构](#mvc-folder-structure) + - [MVC 文件夹结构](#mvc-folder-structure) - [总结](#conclusion)

什么是软件架构

@@ -51,11 +51,11 @@ - **实现细节** (即你仓库的文件夹结构) - **实现** **设计** 决策 (你是使用服务端还是客户端渲染?使用关系型还是非关系性数据库) -- 你选择的**技术** (你是使用REST还是GraphQl API? 后端使用Python/Django还是Nod/Express技术栈?) +- 你选择的**技术** (你是使用 REST 还是 GraphQl API? 后端使用 Python/Django 还是 Nod/Express 技术栈?) - **系统** **设计** 决策(你的系统是采用单体式架构还是微服务架构?) - **基础设施**决策 (你是在本地还是在云提供商上托管软件?) -以上概括了非常多的选择和可能性。让情况变得更复杂的是,在这个五个类别中,不同的模式可以结合。比方说,我可以采用一个单体式的REST或者GraphQL的API,或者一个微服务架构的应用托管在云供应商或者本地。 +以上概括了非常多的选择和可能性。让情况变得更复杂的是,在这个五个类别中,不同的模式可以结合。比方说,我可以采用一个单体式的 REST 或者 GraphQL 的 API,或者一个微服务架构的应用托管在云供应商或者本地。 为了更好地解释这些混沌的概念,首先我们将讨论一些基础的概念,然后再逐条讲解这些分类,并解释时下搭建应用最常用的架构模式和选择。 @@ -67,9 +67,9 @@ 简言之,客户端就是请求信息或者行为的应用程序;服务器就是根据客户端的请求,发送信息或者执行行为的程序。 -客户端通常是前端应用,可以在web或者手机应用上运行(虽然也可以通过其他平台使用以及后端应用也可以被当作客户端);服务器通常是后端应用。 +客户端通常是前端应用,可以在 web 或者手机应用上运行(虽然也可以通过其他平台使用以及后端应用也可以被当作客户端);服务器通常是后端应用。 -举个例子,想象你在浏览你最喜欢的社交网络,当你在浏览器输入URL并点击回车之后,你的浏览器就像客户端应用一样,向社交网络服务器**发送请求**,社交网络服务器**响应**请求,并向你发送网站内容。 +举个例子,想象你在浏览你最喜欢的社交网络,当你在浏览器输入 URL 并点击回车之后,你的浏览器就像客户端应用一样,向社交网络服务器**发送请求**,社交网络服务器**响应**请求,并向你发送网站内容。 时下大部分应用都采用客户端-服务器模型,最重要的概念是**客户端请求资源和服务**,**服务器实现**。 @@ -79,15 +79,15 @@

什么是API

-我们刚刚讲解了客户端和服务器是两个相互通信的实体,前端发送请求,后端响应请求。两者相互通信通常是通过API(应用程序接口)。 +我们刚刚讲解了客户端和服务器是两个相互通信的实体,前端发送请求,后端响应请求。两者相互通信通常是通过 API(应用程序接口)。 -API只不过是一系列确定应用间如何通信的规则,就像两方之间的协议:“如果你发送A,我就响应B;如果你发送C,我就响应D……”。 +API 只不过是一系列确定应用间如何通信的规则,就像两方之间的协议:“如果你发送 A,我就响应 B;如果你发送 C,我就响应 D……”。 有了这一系列规则,客户端就知道完成特定任务需要发送什么请求;而服务器也知道客户端特定行为意味着什么需求。 -API的实现方式多种多样,时下最常用的是REST、SOAP和GraphQL。 +API 的实现方式多种多样,时下最常用的是 REST、SOAP 和 GraphQL。 -在API通信中,HTTP协议是最常使用的,内容通常采用JSON或者XML格式。不过也存在其他的协议和内容格式。 +在 API 通信中,HTTP 协议是最常使用的,内容通常采用 JSON 或者 XML 格式。不过也存在其他的协议和内容格式。 如果你想要进一步了解这个话题, 推荐你阅读[这篇文章](https://www.freecodecamp.org/news/http-request-methods-explained/) 。 @@ -103,19 +103,19 @@ API的实现方式多种多样,时下最常用的是REST、SOAP和GraphQL。 这听上去有些笼统,但是模块化或者说将项目细分是软件架构中非常重要的一部分。所以只要记住这个概念,通过一些例子,你对它的理解会更加清晰。 ;) -如果你想要阅读更多该话题相关内容,我最近写了一篇 [关于在JS中使用模块的文章](https://www.freecodecamp.org/news/modules-in-javascript/) ,希望对你有帮助。 +如果你想要阅读更多该话题相关内容,我最近写了一篇 [关于在 JS 中使用模块的文章](https://www.freecodecamp.org/news/modules-in-javascript/) ,希望对你有帮助。

你的基础架构是什么样的?

好的,我们进入文章的精华部分了。我们将讨论构建软件应用程序的不同方式,从项目的基础架构开始。 -为了让概念不那么抽象,我将创建一个虚构的应用,叫做Notflix。🤔🤫🥸 +为了让概念不那么抽象,我将创建一个虚构的应用,叫做 Notflix。🤔🤫🥸 注意:请记住这个例子可能不太现实,我仅以此作为讲解概念的例子。这里只是为了帮助你通过例子来了解架构的核心概念,而不是分析现实例子。

单体式架构

-Notflix将是一视频流媒体应用,用户可以使用它观看电影、剧集、纪录片等。用户可以在web浏览器、手机和TV应用上使用它。 +Notflix 将是一视频流媒体应用,用户可以使用它观看电影、剧集、纪录片等。用户可以在 web 浏览器、手机和 TV 应用上使用它。 这个应用的主要服务包括: **验证** (用户可以创建账户、登陆等)、 **支付** (用户可以订阅并获取内容,你不希望服务完全免费,对吧? 😑) 和**流媒体**(用户可以观看付费内容)。 @@ -125,9 +125,9 @@ Notflix将是一视频流媒体应用,用户可以使用它观看电影、剧 经典的单体式架构 -左手边是三种不同的前端应用,将作为系统中的客户端。它们可以通过React和React-native开发。 +左手边是三种不同的前端应用,将作为系统中的客户端。它们可以通过 React 和 React-native 开发。 -一个服务器接受三个客户端应用的请求,并在必要的时候和数据库通信,并返回给对应的前端。后端可以由Node和Express开发。 +一个服务器接受三个客户端应用的请求,并在必要的时候和数据库通信,并返回给对应的前端。后端可以由 Node 和 Express 开发。 这种形式的架构就被称为**单体式**,因为仅有一个服务器应用来负责系统的所有功能。在我们的例子中,如果用户需要注册、支付或者观看任意一部影片,所有的请求都发送到同一个服务器应用。 @@ -135,11 +135,11 @@ Notflix将是一视频流媒体应用,用户可以使用它观看电影、剧

微服务架构

-结果Noflix表现相当不错。我们刚刚发布了最新一季的《怪奇物语》,这是一部关于青少年说唱歌手的科幻片,以及电影《特工404》(是关于一个潜入公司假扮资深程序员的特工,其实这个特工完全不懂编程),创造了新的收视纪录。 +结果 Noflix 表现相当不错。我们刚刚发布了最新一季的《怪奇物语》,这是一部关于青少年说唱歌手的科幻片,以及电影《特工 404》(是关于一个潜入公司假扮资深程序员的特工,其实这个特工完全不懂编程),创造了新的收视纪录。 -每个月来自世界各地成千上万的新用户注册Noflix,这对于我们的经营状况来说是好事,但对于单体式的应用来说可不妙。 +每个月来自世界各地成千上万的新用户注册 Noflix,这对于我们的经营状况来说是好事,但对于单体式的应用来说可不妙。 -最近我们一直在经历服务器响应时间延迟,尽管我们已经**垂直扩展**了服务器(增加了RAM和GPU),但是服务器还是超负载了。 +最近我们一直在经历服务器响应时间延迟,尽管我们已经**垂直扩展**了服务器(增加了 RAM 和 GPU),但是服务器还是超负载了。 此外,我们也在系统中开发新的功能(如根据用户喜好推荐电影的推荐工具),**代码库变得臃肿且复杂**。 @@ -157,7 +157,7 @@ Notflix将是一视频流媒体应用,用户可以使用它观看电影、剧 当需要登陆的时候,客户端应用与认证服务通信,用户需要支付时,向支付服务器通信,需要观看视频时向流媒体服务器通信。 -所有 **通信都通过API实现**,这和单体式架构一样 (或者通过如 [Kafka](https://kafka.apache.org/) 或[RabbitMQ](https://www.rabbitmq.com/)等通信系统)。 唯一的区别是,现在我们使用不同的服务器负责不同的行为,而不是采用一个服务器解决所有问题。 +所有 **通信都通过 API 实现**,这和单体式架构一样 (或者通过如 [Kafka](https://kafka.apache.org/) 或[RabbitMQ](https://www.rabbitmq.com/)等通信系统)。 唯一的区别是,现在我们使用不同的服务器负责不同的行为,而不是采用一个服务器解决所有问题。 听上去有一点点复杂,确实如此,微服务的优势在于: @@ -175,21 +175,21 @@ Notflix将是一视频流媒体应用,用户可以使用它观看电影、剧 通常解决这个问题的方式是在前端应用和微服务之间增加一个中间层。这个中间层将接受所有前端的请求,重定向到对应的微服务,接受微服务的回应,然后重定向到对应的前端应用。 -BFF模式的好处在于我们在使用了微服务架构的同时,没有复杂化前端应用的通信。 +BFF 模式的好处在于我们在使用了微服务架构的同时,没有复杂化前端应用的通信。 ![Untitled-Diagram.drawio--2-](https://www.freecodecamp.org/news/content/images/2022/07/Untitled-Diagram.drawio--2-.png) -BFF实现 +BFF 实现 -如果你想了解更多相关内容,这里有一期[解释BFF模式的视频](https://www.youtube.com/watch?v=SSo-z16wEnc)。 +如果你想了解更多相关内容,这里有一期[解释 BFF 模式的视频](https://www.youtube.com/watch?v=SSo-z16wEnc)。

如何使用负载均衡器和水平扩展

-我们的流媒体应用正在呈指数型增长,来自世界各地百万量级的用户全天候使用Noflix观看电影,马上我们又要面临新的性能问题。 +我们的流媒体应用正在呈指数型增长,来自世界各地百万量级的用户全天候使用 Noflix 观看电影,马上我们又要面临新的性能问题。 我们再一次发现是流媒体服务承受最大的压力,我们已经尽我们所能**垂直扩展**了这个服务器,进一步细分这个服务成更多微服务没有意义。所以我们决定**水平扩展**服务器。 -在前文中我们提到 **垂直扩展** 就是给单个服务器或者计算机增加更多资源(RAM、磁盘空间、GPU等);**水平扩展** 就是设置更多的服务器来处理同一个任务。 +在前文中我们提到 **垂直扩展** 就是给单个服务器或者计算机增加更多资源(RAM、磁盘空间、GPU 等);**水平扩展** 就是设置更多的服务器来处理同一个任务。 我们不再只使用一个服务器来负责所有流媒体工作,而是使用三个。这样来自客户端的请求将被平均分配到这三个服务器处理,每一个服务器的负载就被控制在可承受范围内。 @@ -207,14 +207,14 @@ BFF实现 这是我们想要的形式 -水平扩展可以在服务器实现就可以在代码库实现。其中一个实现办法是通过源-副本模型(source-replica model),一个特定的源DB将接受所有写入的请求然后复制这些数据到更多的副本DB,副本DB将接受和响应所有读取的请求。 +水平扩展可以在服务器实现就可以在代码库实现。其中一个实现办法是通过源-副本模型(source-replica model),一个特定的源 DB 将接受所有写入的请求然后复制这些数据到更多的副本 DB,副本 DB 将接受和响应所有读取的请求。 -DB副本的优势在于: +DB 副本的优势在于: - 更优的性能:这一模型使得更多个请求可以并行。 -- 可靠性和可用性:如果一个数据库服务器因为任何原因被破坏或者无法访问,其他DB仍保有数据。 +- 可靠性和可用性:如果一个数据库服务器因为任何原因被破坏或者无法访问,其他 DB 仍保有数据。 -实现了负载均衡器、水平扩展和DB副本之后,我们的架构如图: +实现了负载均衡器、水平扩展和 DB 副本之后,我们的架构如图: ![Untitled-Diagram.drawio--3--2](https://www.freecodecamp.org/news/content/images/2022/07/Untitled-Diagram.drawio--3--2.png) @@ -242,7 +242,7 @@ DB副本的优势在于: ![image-221](https://www.freecodecamp.org/news/content/images/2022/07/image-221.png) -我们想象中的Notflix服务器机房 +我们想象中的 Notflix 服务器机房 ![image-222](https://www.freecodecamp.org/news/content/images/2022/07/image-222.png) @@ -262,11 +262,11 @@ DB副本的优势在于:

云托管

-如果你在科技圈呆过一阵子,你可能已经听说过不止一次”云“。咋一听,这好像是某种抽象的魔术,实际上云只不过是由Amazon、Google和Microsoft这样的大公司拥有的超大数据中心。 +如果你在科技圈呆过一阵子,你可能已经听说过不止一次”云“。咋一听,这好像是某种抽象的魔术,实际上云只不过是由 Amazon、Google 和 Microsoft 这样的大公司拥有的超大数据中心。 这些大公司拥有巨大的算力,这些算力并不是时时被利用。与其让这些硬件白白浪费钱,更聪明的做法是将这些算力商业化。 -这就是云计算。数据中心可以利用这些算力,使用 **AWS** (Amazon的web服务)、 **Google Cloud**或 Microsoft的 **Azure**。 +这就是云计算。数据中心可以利用这些算力,使用 **AWS** (Amazon 的 web 服务)、 **Google Cloud**或 Microsoft 的 **Azure**。 ![image-219](https://www.freecodecamp.org/news/content/images/2022/07/image-219.png) “云”实际的样子 @@ -281,7 +281,7 @@ DB副本的优势在于: 第二种方法利用了大多数供应商提供的“弹性”算力。“弹性”意味着你的应用使用的硬件大小会根据使用情况,自动放大或缩小。 -例如,你开始使用的是8gb内存和500gb磁盘空间的服务器。如果服务器收到越来越多的请求并且这些容量不再足以提供良好的性能,系统可以自动执行垂直或水平扩展。 +例如,你开始使用的是 8gb 内存和 500gb 磁盘空间的服务器。如果服务器收到越来越多的请求并且这些容量不再足以提供良好的性能,系统可以自动执行垂直或水平扩展。 这样做的好处是,你预先配置服务器后,就没有必再担心它的变化。服务器自动扩展和缩减,你只需为使用的资源付费。 @@ -289,9 +289,9 @@ DB副本的优势在于: 使用云计算的另一种方式是使用无服务架构。 -在这个模式中,没有接受所有请求并响应的服务器,而是独立的函数映射到访问点(类似于API端点)。 +在这个模式中,没有接受所有请求并响应的服务器,而是独立的函数映射到访问点(类似于 API 端点)。 -每当接受到一个请求,这些函数就会执行你编写的程序(链接数据库、执行CRUD等普通服务器会做的事情)。 +每当接受到一个请求,这些函数就会执行你编写的程序(链接数据库、执行 CRUD 等普通服务器会做的事情)。 无服务架构的好处是可以免去服务器维护和扩展。如果需要使用,你只需要编写执行的函数,函数会自动根据需要扩展或者缩小。 @@ -305,13 +305,13 @@ DB副本的优势在于: 除了提供服务器相关服务,云供应商还提供许多其他的解决方案,如:关系型和非关系型数据库、文件存储服务、缓存服务、认证服务、机器学习和数据处理服务、监控和性能分析等。这些服务都托管在云。 -通过如[Terraform](https://www.terraform.io/)或AWS的[Cloud formation](https://aws.amazon.com/es/cloudformation/)这样的工具,我们甚至可以通过编写代码来设置基础架构,也就是说我们可以花几分钟编写脚本来设置服务器、数据库等在云上的内容。 +通过如[Terraform](https://www.terraform.io/)或 AWS 的[Cloud formation](https://aws.amazon.com/es/cloudformation/)这样的工具,我们甚至可以通过编写代码来设置基础架构,也就是说我们可以花几分钟编写脚本来设置服务器、数据库等在云上的内容。 对于软件工程来说这是颠覆想象的举措,这也给开发者提供了巨大的便利。云计算提供了丰富的解决方法应对小微项目,也可以处理好非常大的数字产品。这也是为什么越来越多的软件工程项目选择在云上搭建基础架构。 如前文所诉,时下最知名且最常用的云有[AWS](https://aws.amazon.com/)、[Google Cloud](https://cloud.google.com/)和[Azure](https://azure.microsoft.com/)。 当然还有其他的选择如[IBM](https://www.ibm.com/cloud)、[DigitalOcean](https://www.digitalocean.com/)和[Oracle](https://www.oracle.com/cloud/)。 -大部分云供应商都提供同样的服务,虽然服务的命名不相同。同样是无服务功能,在AWS被叫做"lambdas",在Google Cloud被叫做"cloud functions“。 +大部分云供应商都提供同样的服务,虽然服务的命名不相同。同样是无服务功能,在 AWS 被叫做"lambdas",在 Google Cloud 被叫做"cloud functions“。

不同的文件夹结构

@@ -319,13 +319,13 @@ DB副本的优势在于:

全在一个文件夹中的结构

-为了演示为什么文件夹结构很重要,我们一起来搭建一个简单的示例API。我们将使用一个模拟的数据库,名为兔子🐰🐰,这个API会执行[CRUD](https://www.freecodecamp.org/news/crud-operations-explained/) 操作,我们将使用Node和Express来搭建。 +为了演示为什么文件夹结构很重要,我们一起来搭建一个简单的示例 API。我们将使用一个模拟的数据库,名为兔子🐰🐰,这个 API 会执行[CRUD](https://www.freecodecamp.org/news/crud-operations-explained/) 操作,我们将使用 Node 和 Express 来搭建。 下图是我们的第一步,没有任何文件夹结构,我们的仓库包含`node modules`文件夹,`app.js`、 `package-lock.json` 和 `package.json` 文件。 ![image-227](https://www.freecodecamp.org/news/content/images/2022/07/image-227.png) -在`app.js`文件包含一个小服务器,虚拟DB(数据库)和两个端点: +在`app.js`文件包含一个小服务器,虚拟 DB(数据库)和两个端点: ```javascript // App.js @@ -391,13 +391,13 @@ http://localhost:7070/rabbits/1 # } ``` -这有什么问题吗?其实没有,一切运行良好。但当代码库变得更大更复杂,我们在API中添加新的功能后,问题就会浮现。 +这有什么问题吗?其实没有,一切运行良好。但当代码库变得更大更复杂,我们在 API 中添加新的功能后,问题就会浮现。 这和我们讨论单体式架构的问题一样,一开始把所有内容放在一个地方很方便,但是随着内容变得更大更复杂,这个方式就会让人困惑。 根据模块化原则,更好的处理方法是使用不同的文件夹和文件来执行不同的责任和行为。 -为了更好地演示,让我们给API添加新的功能,看看我们怎么使用模块的方法来给文件夹结构添加不同层级。 +为了更好地演示,让我们给 API 添加新的功能,看看我们怎么使用模块的方法来给文件夹结构添加不同层级。

分层文件夹结构

@@ -533,7 +533,7 @@ const deleteRabbit = (req, res) => { module.exports = { listRabbits, getRabbit, editRabbit, addRabbit, deleteRabbit } ``` -- `rabbits.models.js` 定义了使用CRUD处理数据库的函数。每一个函数都代表了一种行为(读取一个数据、读取所有数据、编辑数据、删除数据等),这个文件与DB连接。 +- `rabbits.models.js` 定义了使用 CRUD 处理数据库的函数。每一个函数都代表了一种行为(读取一个数据、读取所有数据、编辑数据、删除数据等),这个文件与 DB 连接。 ```javascript // rabbits.models.js @@ -610,7 +610,7 @@ module.exports = db 如你所见,现在就有更多的文件夹和文件。但是最为回报,我们的代码库变得结构感更加明显,并且组织更加清晰。每一个代码都呆在应该在的地方,文件之间的通信也被清晰地定义了。 -这样的组织形式能够极大地方便添加新的功能、修改代码和改bug。 +这样的组织形式能够极大地方便添加新的功能、修改代码和改 bug。 一旦你熟悉了这样的文件夹结构,知道去哪儿找你想要的内容。你就会发现在更短更小的文件中工作,比在一到两个巨大的文件中滑动寻找想要的内容要方便得多。 @@ -620,11 +620,11 @@ module.exports = db

MVC文件夹结构

-MVC的全称是**Model View Controller(模型视图控制器)**。 我们可以说 MVC结构就像是分层结构的简化版,并包含了应用程序的前端 (UI)。 +MVC 的全称是**Model View Controller(模型视图控制器)**。 我们可以说 MVC 结构就像是分层结构的简化版,并包含了应用程序的前端 (UI)。 在这个结构中只有三层: -- 视图层负责渲染UI. +- 视图层负责渲染 UI. - 控制层负责定义路由和路由背后的逻辑 - 模型层负责和数据库的交互 @@ -636,11 +636,11 @@ MVC的全称是**Model View Controller(模型视图控制器)**。 我们可 另一种展现层级的方式 -有许多实现MVC结构的框架 (如[Django](https://www.djangoproject.com/) 或 [Ruby on Rails](https://rubyonrails.org/) )。如果要在Node和Express中使用这个结构,我们需要借助模版引擎,如[EJS](https://ejs.co/)。 +有许多实现 MVC 结构的框架 (如[Django](https://www.djangoproject.com/) 或 [Ruby on Rails](https://rubyonrails.org/) )。如果要在 Node 和 Express 中使用这个结构,我们需要借助模版引擎,如[EJS](https://ejs.co/)。 -如果你对模版引擎这个概念不是太熟悉的话,可以把它理解成更容易渲染的HTML,它利用了如变量、循环和条件句这些编程特性使得渲染更加容易(和React中的JSX很像)。 +如果你对模版引擎这个概念不是太熟悉的话,可以把它理解成更容易渲染的 HTML,它利用了如变量、循环和条件句这些编程特性使得渲染更加容易(和 React 中的 JSX 很像)。 -在下面的例子中,我们会使用EJS文件来创建每一个页面,并且由控制器来处理响应,传入到对应的响应变量。 +在下面的例子中,我们会使用 EJS 文件来创建每一个页面,并且由控制器来处理响应,传入到对应的响应变量。 文件夹结构如下: @@ -737,7 +737,7 @@ router.delete('/:id', (req, res) => { module.exports = router ``` -- 最后,在视图文件中,我们将变量作为参数并且渲染为HTML。 +- 最后,在视图文件中,我们将变量作为参数并且渲染为 HTML。 ```html @@ -783,7 +783,7 @@ module.exports = router ![image-233](https://www.freecodecamp.org/news/content/images/2022/07/image-233.png) -这就是MVC! +这就是 MVC! ![bugs-bunny-looney-tunes](https://www.freecodecamp.org/news/content/images/2022/07/bugs-bunny-looney-tunes.gif) diff --git a/chinese/articles/asynchronous-javascript-explained.md b/chinese/articles/asynchronous-javascript-explained.md index 265782c3a..03821db68 100644 --- a/chinese/articles/asynchronous-javascript-explained.md +++ b/chinese/articles/asynchronous-javascript-explained.md @@ -34,9 +34,9 @@ console.log(sum); 为了正确地实现这种异步行为,多年来开发人员使用了几种不同的解决方案。每个解决方案都在前一个解决方案的基础上进行改进,这使得代码更加优化,并且在代码变得复杂时更容易理解。 -为了进一步理解 JavaScript 的异步特性,我们将介绍回调函数、promise、async和await。 +为了进一步理解 JavaScript 的异步特性,我们将介绍回调函数、promise、async 和 await。 -# JavaScript中的回调函数是什么? +# JavaScript 中的回调函数是什么? 回调是在另一个函数内部传递的函数,然后在该函数中调用以执行任务。 @@ -72,7 +72,7 @@ fired third 这种方法非常有效,但仅限于一点。有时候,开发人员必须对代码中的不同源代码进行多次调用。为了进行这些调用,回调将被嵌套,直到它们变得非常难以读取或维护。这被称为 **回调地狱** -为了解决这个问题,引入了Promise。 +为了解决这个问题,引入了 Promise。 # JavaScript 中的 Promise 是什么? @@ -85,7 +85,7 @@ fired third Promise 的出现是为了解决回调函数的问题。Promise 接受两个函数作为参数。即`resolve`和`reject`。请记住,resolve 表示成功时,reject 表示错误发生时。 -让我们看一下Promise的作用: +让我们看一下 Promise 的作用: ```JavaScript const getData = (dataEndpoint) => { @@ -104,7 +104,7 @@ const getData = (dataEndpoint) => { }; ``` -上面的代码是一个Promise,包含在对某个端点的请求中。就像我前面提到的,这个Promise包含了`resolve`和`reject`。 +上面的代码是一个 Promise,包含在对某个端点的请求中。就像我前面提到的,这个 Promise 包含了`resolve`和`reject`。 例如,在调用端点之后,如果请求成功,我们将解决承诺并继续对响应执行任何我们想要的操作。但是如果出现错误,承诺就会被拒绝。 @@ -142,15 +142,15 @@ const asyncFunc = async () => { } ``` - `async` 关键字是我上面提到的用来定义 async 函数的。但是 `await`呢?好吧,它阻止 JavaScript 在解决promise之前将 `fetch` 赋值给response变量。一旦 promise 被解决,现在可以将 fetch 方法的结果分配给 response 变量。 + `async` 关键字是我上面提到的用来定义 async 函数的。但是 `await`呢?好吧,它阻止 JavaScript 在解决 promise 之前将 `fetch` 赋值给 response 变量。一旦 promise 被解决,现在可以将 fetch 方法的结果分配给 response 变量。 第 3 行代码也是相同的行为。 `.json` 方法返回一个 Promise 对象,我们可以使用 `await` 延迟分配直到 promise 被解决。 # 阻塞还是非阻塞 -当我说“暂停”,你一定认为实现Async和Await以某种方式阻塞代码执行。因为我们的请求花费的时间太长了,对吧? +当我说“暂停”,你一定认为实现 Async 和 Await 以某种方式阻塞代码执行。因为我们的请求花费的时间太长了,对吧? -事实上并不是。async函数内部的代码会阻塞,但不会以任何方式影响程序的执行。代码的执行仍然是异步的。正如下面的代码: +事实上并不是。async 函数内部的代码会阻塞,但不会以任何方式影响程序的执行。代码的执行仍然是异步的。正如下面的代码: ```javascript const asyncFunc = async () => { diff --git a/chinese/articles/benefits-of-rest.md b/chinese/articles/benefits-of-rest.md index 9fd9a9ce2..5c79ae6d4 100644 --- a/chinese/articles/benefits-of-rest.md +++ b/chinese/articles/benefits-of-rest.md @@ -8,40 +8,40 @@ 在这篇文章中,我们将看看表现层状态转换(REST)原则,了解它们是什么,以及你能从应用它们中得到什么好处。 -我相信了解你为什么要学习一些东西是很重要的--包括REST。因此,让我们来看看REST原则带来了什么。 +我相信了解你为什么要学习一些东西是很重要的--包括 REST。因此,让我们来看看 REST 原则带来了什么。 -## 什么是REST? +## 什么是 REST? 表现层状态转换(REST)是一种架构风格,由于其简单性和可扩展性,近年来得到了很多人的青睐。 -在REST得到普及之前,SOAP是访问资源和通过网络进行通信的事实方式。 +在 REST 得到普及之前,SOAP 是访问资源和通过网络进行通信的事实方式。 -## 你为什么要考虑REST? +## 你为什么要考虑 REST? -在本节中,我将讨论为什么REST原则很重要,以及为什么值得努力去了解它们。你还将学习如何将它们应用于你的后端项目。 +在本节中,我将讨论为什么 REST 原则很重要,以及为什么值得努力去了解它们。你还将学习如何将它们应用于你的后端项目。 -### 1) REST很容易理解和实施 +### 1) REST 很容易理解和实施 -EST是为了在HTTP上工作(实际上HTTP受到了REST的影响)。因此,它使用了我们大多数人都知道的HTTP动词,如GET、POST和PUT。 +EST 是为了在 HTTP 上工作(实际上 HTTP 受到了 REST 的影响)。因此,它使用了我们大多数人都知道的 HTTP 动词,如 GET、POST 和 PUT。 即使你不知道这些动词是干什么的,它们的名字也是不言自明的。另外,客户端和服务器代码的明确分离使得不同的团队很容易在应用程序的不同部分(前端或后端)工作。 -由于它很容易理解,也很容易实现,REST原则可以帮助提高你的开发团队的生产力。如果你要发布一个公共API供人们开发应用,它们也很重要。 +由于它很容易理解,也很容易实现,REST 原则可以帮助提高你的开发团队的生产力。如果你要发布一个公共 API 供人们开发应用,它们也很重要。 -许多人知道REST和HTTP,所以他们会更容易理解和使用你的API。 +许多人知道 REST 和 HTTP,所以他们会更容易理解和使用你的 API。 ![How to Keep Your Developer Team Happy: Lead Dev New York 2019 | Arc Blog](https://ucarecdn.com/f9a4640d-ba7f-4f85-82eb-901a56362a9a/) Happy Developers -### 2) REST使你的应用程序更具可扩展性 +### 2) REST 使你的应用程序更具可扩展性 -有2个主要的原因,REST可以帮助使你的应用程序更具可扩展性: +有 2 个主要的原因,REST 可以帮助使你的应用程序更具可扩展性: #### 无状态 -正如我们将在下一节(REST的原则)中看到的,REST的核心原则之一是它在服务器端是无状态的。因此,每个请求的处理都将独立于之前的请求。 +正如我们将在下一节(REST 的原则)中看到的,REST 的核心原则之一是它在服务器端是无状态的。因此,每个请求的处理都将独立于之前的请求。 在具有服务器端状态或会话的应用程序中,可能为每个登录的用户存储一个会话。这种会话数据很容易变得臃肿,并开始占用服务器上的大量资源。 @@ -49,31 +49,31 @@ Happy Developers 由于目前可扩展性的趋势是水平扩展(通常是在云上),存储服务器端的会话也会使你的应用程序难以扩展,因为它产生了一些困难的问题。 -例如,假设你有许多服务器,它们在一个负载平衡器后面运行。如果客户在他们的第一个请求中到达了服务器1(服务器1现在拥有客户的会话),而在后来,由于服务器1的负载,客户到达了服务器2,而服务器2并不知道他们之前存储在服务器1上的会话数据,那么会发生什么?当然,这个问题有解决办法,但它使可扩展性更加困难。 +例如,假设你有许多服务器,它们在一个负载平衡器后面运行。如果客户在他们的第一个请求中到达了服务器 1(服务器 1 现在拥有客户的会话),而在后来,由于服务器 1 的负载,客户到达了服务器 2,而服务器 2 并不知道他们之前存储在服务器 1 上的会话数据,那么会发生什么?当然,这个问题有解决办法,但它使可扩展性更加困难。 #### 更快的数据交换格式 -RESTful APIs通常使用JSON作为数据交换格式。与XML相比,JSON更紧凑,尺寸更小。它也可以比XML更快地被解析。([对比数据来源](http://ijcsn.org/IJCSN-2014/3-4/JSON-vs-XML-A-Comparative-Performance-Analysis-of-Data-Exchange-Formats.pdf)) +RESTful APIs 通常使用 JSON 作为数据交换格式。与 XML 相比,JSON 更紧凑,尺寸更小。它也可以比 XML 更快地被解析。([对比数据来源](http://ijcsn.org/IJCSN-2014/3-4/JSON-vs-XML-A-Comparative-Performance-Analysis-of-Data-Exchange-Formats.pdf)) -虽然他们大多使用JSON操作,但也要记住,REST APIs仍然能够通过使用 [Accept header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept)来响应不同的格式。 +虽然他们大多使用 JSON 操作,但也要记住,REST APIs 仍然能够通过使用 [Accept header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept)来响应不同的格式。 -### 3) 使用REST缓存更容易 +### 3) 使用 REST 缓存更容易 缓存是现代网络应用程序可扩展性和性能的一个关键因素。一个完善的缓存机制(具有最佳的命中率)可以大大减少服务器的平均响应时间。 -REST的目的是使缓存更容易。由于服务器是无状态的,每个请求都可以单独处理,GET请求通常应该返回相同的响应,而不考虑之前的请求和会话。 +REST 的目的是使缓存更容易。由于服务器是无状态的,每个请求都可以单独处理,GET 请求通常应该返回相同的响应,而不考虑之前的请求和会话。 -这使得GET请求很容易被缓存,浏览器通常也会这样对待它们。我们也可以使用 **Cache-Control** 和 **Expires** 头来使我们的POST请求可以缓存。 +这使得 GET 请求很容易被缓存,浏览器通常也会这样对待它们。我们也可以使用 **Cache-Control** 和 **Expires** 头来使我们的 POST 请求可以缓存。 -### 4) REST是灵活的 +### 4) REST 是灵活的 -我所说的灵活性是指它很容易修改,而且它还能回答许多客户的要求,他们可以要求不同的数据类型(XML、JSON等)。 +我所说的灵活性是指它很容易修改,而且它还能回答许多客户的要求,他们可以要求不同的数据类型(XML、JSON 等)。 -客户端可以使用**Accept**头来指定类型(正如我前面提到的),REST API可以根据这一点返回不同的响应。 +客户端可以使用**Accept**头来指定类型(正如我前面提到的),REST API 可以根据这一点返回不同的响应。 -另一个值得一提的机制是 [HATEOAS](https://www.wikiwand.com/en/HATEOAS#:~:text=Hypermedia%20as%20the%20Engine%20of,provid%20information%20dynamically%20through%20hypermedia。)。如果你不知道这个词,不要担心,它的基本意思是。在服务器响应中返回某一特定资源的相关URLs。 +另一个值得一提的机制是 [HATEOAS](https://www.wikiwand.com/en/HATEOAS#:~:text=Hypermedia%20as%20the%20Engine%20of,provid%20information%20dynamically%20through%20hypermedia。)。如果你不知道这个词,不要担心,它的基本意思是。在服务器响应中返回某一特定资源的相关 URLs。 -看一下维基百科上的这个例子。客户端从银行的API中请求带有 `account_number` 的账户信息,得到这样的响应: +看一下维基百科上的这个例子。客户端从银行的 API 中请求带有 `account_number` 的账户信息,得到这样的响应: ```json @@ -95,19 +95,19 @@ REST的目的是使缓存更容易。由于服务器是无状态的,每个请 ``` -这个服务器利用HATEOAS并返回相应的行动链接。这使得探索API非常容易,同时也通过允许服务器改变端点而使其变得灵活。 +这个服务器利用 HATEOAS 并返回相应的行动链接。这使得探索 API 非常容易,同时也通过允许服务器改变端点而使其变得灵活。 -可以这样想:如果服务器没有应用HATEOAS,客户就需要对端点进行硬编码,如 "/accounts/:account-id/deposit"。但如果服务器将URL改为 "/accounts/:account-id/depositMoney",客户端的代码也需要改变。 +可以这样想:如果服务器没有应用 HATEOAS,客户就需要对端点进行硬编码,如 "/accounts/:account-id/deposit"。但如果服务器将 URL 改为 "/accounts/:account-id/depositMoney",客户端的代码也需要改变。 -在HATEOAS链接的帮助下,客户端可以通过解析这个JSON来检查链接,并轻松提出请求。如果端点发生变化,他们将得到新的端点,而不需要改变客户端的代码。 +在 HATEOAS 链接的帮助下,客户端可以通过解析这个 JSON 来检查链接,并轻松提出请求。如果端点发生变化,他们将得到新的端点,而不需要改变客户端的代码。 关于这个话题的更多见解,你可以查看 Roy Fielding 的[此](https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven)博文。 ## 总结 -在这篇文章中,我试图表达我为什么重视REST,以及为什么我相信你也应该重视它。我希望在读完这篇文章后,你会更清楚应用REST标准的原因。 +在这篇文章中,我试图表达我为什么重视 REST,以及为什么我相信你也应该重视它。我希望在读完这篇文章后,你会更清楚应用 REST 标准的原因。 -这篇文章可以作为学习更多相关知识的动力。而且我有个好消息:我计划在不久的将来写关于REST最佳实践和常见错误的文章。 +这篇文章可以作为学习更多相关知识的动力。而且我有个好消息:我计划在不久的将来写关于 REST 最佳实践和常见错误的文章。 如果你有兴趣,你可以继续关注或订阅我的[博客](http://erinc.io/)。你也可以在那里看一下我以前的文章:) diff --git a/chinese/articles/best-practices-for-react.md b/chinese/articles/best-practices-for-react.md index 19c1f6ba0..d9b7a7525 100644 --- a/chinese/articles/best-practices-for-react.md +++ b/chinese/articles/best-practices-for-react.md @@ -5,7 +5,7 @@ ![React Best Practices – Tips for Writing Better React Code in 2022](https://www.freecodecamp.org/news/content/images/size/w2000/2022/02/React-Best-Practices-Thumbnail.png) -两年前,我开始学习和使用React。今天,我仍然在我的日常工作中使用它,作为一个软件开发人员和我自己的业余项目。 +两年前,我开始学习和使用 React。今天,我仍然在我的日常工作中使用它,作为一个软件开发人员和我自己的业余项目。 在这段时间里,我遇到了很多典型的问题。所以我四处搜寻,找到了一些最佳实践,并将其整合到我的工作流程中,我想出了一些让我的生活或我的团队成员的生活更轻松的东西。 @@ -13,15 +13,15 @@ 这就是我写这个指南的原因。我认为它就像两年前我开始工作时给自己的技巧收集。 -## 目录: +## 目录 -- [React开发者面临的三大挑战](#three-major-challenges-react-developers-face) -- [学习React的构建模块](#learn-the-building-blocks-of-react) -- [学习如何构建简洁、性能良好、可维护的React组件](#learn-how-to-build-clean-performant-and-maintainable-react-components) -- [帮助你写出更好的React代码的技巧 – The Cherries on Top](#tips-to-help-you-write-better-react-code-the-cherries-on-top) +- [React 开发者面临的三大挑战](#three-major-challenges-react-developers-face) +- [学习 React 的构建模块](#learn-the-building-blocks-of-react) +- [学习如何构建简洁、性能良好、可维护的 React 组件](#learn-how-to-build-clean-performant-and-maintainable-react-components) +- [帮助你写出更好的 React 代码的技巧 – The Cherries on Top](#tips-to-help-you-write-better-react-code-the-cherries-on-top) - [结束语](#final-words) -首先,你会了解到每个React开发者必须面对的 **三个主要挑战** ,这很重要,因为当你意识到潜在的挑战时,你会更深入地理解这些最佳实践背后的原因。从一开始就有这种心态,也有助于你在设计你的组件或组织你的项目。 +首先,你会了解到每个 React 开发者必须面对的 **三个主要挑战** ,这很重要,因为当你意识到潜在的挑战时,你会更深入地理解这些最佳实践背后的原因。从一开始就有这种心态,也有助于你在设计你的组件或组织你的项目。 在这第一个重要步骤之后,我将向你介绍**的三个最佳实践**。它们是理论和实践技巧的混合体,带有代码实例。我尽量减少 _hello world_ 的问题,并拿出我在 _真实世界_ 看到的代码。 @@ -29,7 +29,7 @@ ![christian-erfurt-sxQz2VfoFBE-unsplash](https://www.freecodecamp.org/news/content/images/2022/01/christian-erfurt-sxQz2VfoFBE-unsplash.jpg) -在我日常使用React的两年时间里,我认识到React开发者在构建他们的应用时面临的三大挑战。忽视这些挑战可能会带来困难,损害你的应用程序的发展。 +在我日常使用 React 的两年时间里,我认识到 React 开发者在构建他们的应用时面临的三大挑战。忽视这些挑战可能会带来困难,损害你的应用程序的发展。 因此,在协调你的应用程序时要记住这些挑战,这将节省你的时间和精力。 @@ -37,7 +37,7 @@ 这与 _可重用性_ 是相辅相成的。一开始,当应用程序和组件非常轻巧时,它们很容易维护。但是,一旦需求开始增长,组件就会变得非常复杂,因此可维护性就会降低。 -我经常看到一个组件有很多不同的情况,每个都代表不同的结果。JSX中充斥着条件渲染(三元运算符和简单的`&&`运算符),`classnames`被有条件地应用,或者组件使用巨大的`switch`语句。有许多可能的`props`和`state`值,每个都负责不同的结果。 +我经常看到一个组件有很多不同的情况,每个都代表不同的结果。JSX 中充斥着条件渲染(三元运算符和简单的`&&`运算符),`classnames`被有条件地应用,或者组件使用巨大的`switch`语句。有许多可能的`props`和`state`值,每个都负责不同的结果。 在我看来,这些技术本身并没有错。但我认为每个人都应该培养一种感觉,知道什么时候一个组件开始变得不那么可维护,什么时候这些技术被过度使用。我们将在文章的后面学习如何更好地控制这个问题。 @@ -47,19 +47,19 @@ 我看到的另一个关键因素是没有或很少进行测试。我知道,测试并不是很多开发人员喜欢的工作,但从长远来看,它确实可以帮助你。测试本身不会是这篇文章的一个主要话题,所以请留意我的另一篇关于测试的博文。 -### 🧠 对React的深刻理解 +### 🧠 对 React 的深刻理解 -React开发者出现问题的另一个根本原因是对React在工作底层原理缺乏基本了解。我也遇到过这种情况。 +React 开发者出现问题的另一个根本原因是对 React 在工作底层原理缺乏基本了解。我也遇到过这种情况。 -我见过很多人在没有坚实基础的情况下过快地进入中级或高级概念。但这并不仅仅是React的问题。这也是编程中的一个普遍问题。 +我见过很多人在没有坚实基础的情况下过快地进入中级或高级概念。但这并不仅仅是 React 的问题。这也是编程中的一个普遍问题。 -对React没有扎实的了解也会给你这个开发者带来问题。我记得当我想使用不同的组件生命周期,但不知道如何真正使用它们时,我很头疼。所以我不得不退回去,深入了解这个话题。 +对 React 没有扎实的了解也会给你这个开发者带来问题。我记得当我想使用不同的组件生命周期,但不知道如何真正使用它们时,我很头疼。所以我不得不退回去,深入了解这个话题。 因为我认为这是最重要的事情之一,所以我在下面这篇博文中专门用了一整章来介绍。 ### 📈 可扩展性 -这个挑战与 _可维护性_ 并驾齐驱。它不仅是React所特有的,而且普遍适用于软件中。 +这个挑战与 _可维护性_ 并驾齐驱。它不仅是 React 所特有的,而且普遍适用于软件中。 我已经了解到,制作优秀的软件不仅仅是用户体验、干净的代码模式或聪明的架构,例如。对我来说,一个软件的质量也随着它的扩展能力而上升或下降。 @@ -67,43 +67,43 @@ React开发者出现问题的另一个根本原因是对React在工作底层原 我认为,当你在调整你的组件和组织你的项目结构时,把 _可维护性_ 和 _可扩展性_ 放在心上,你就不太可能以需要重大重构的混乱的源代码。 -# 如何学习React +# 如何学习 React -好了,现在让我们深入了解学习React的一些最佳实践。 +好了,现在让我们深入了解学习 React 的一些最佳实践。 ## Learn the Building Blocks of React ![brett-jordan-Lzfxzip-pNI-unsplash](https://www.freecodecamp.org/news/content/images/2022/01/brett-jordan-Lzfxzip-pNI-unsplash.jpg) -正如我们在上面简单讨论的那样,building blocks不仅与学习React有关,而且与其他技术或编程语言也有关。你不能在浮沙上建造一座摩天大楼,并期望它是牢固的。 +正如我们在上面简单讨论的那样,building blocks 不仅与学习 React 有关,而且与其他技术或编程语言也有关。你不能在浮沙上建造一座摩天大楼,并期望它是牢固的。 -这对你们中的许多人来说可能是显而易见的,但我见过一些开发者在没有真正理解基础知识的情况下就跳进React的中级或高级概念。 +这对你们中的许多人来说可能是显而易见的,但我见过一些开发者在没有真正理解基础知识的情况下就跳进 React 的中级或高级概念。 -这对一般的Javascript来说也是如此。我非常相信,如果你没有坚实的Vanilla Javascript基础,学习React是没有意义的。 +这对一般的 Javascript 来说也是如此。我非常相信,如果你没有坚实的 Vanilla Javascript 基础,学习 React 是没有意义的。 -所以,如果这听起来很熟悉,而且你正在考虑学习React,但对Vanilla Javascript已经感觉不是很舒服,那就先多花些时间加强Javascript。这将为你在未来节省大量的头痛和时间。 +所以,如果这听起来很熟悉,而且你正在考虑学习 React,但对 Vanilla Javascript 已经感觉不是很舒服,那就先多花些时间加强 Javascript。这将为你在未来节省大量的头痛和时间。 -如果你想回顾一下,这里有一份关于[学习React之前你需要知道的核心JavaScript概念](https://www.freecodecamp.org/news/top-javascript-concepts-to-know-before-learning-react/) +如果你想回顾一下,这里有一份关于[学习 React 之前你需要知道的核心 JavaScript 概念](https://www.freecodecamp.org/news/top-javascript-concepts-to-know-before-learning-react/) -但仅仅了解基础知识对我来说是不够的。知道React的底层是如何工作的,这有点强制性。如果你想成为一个优秀的React开发者(我想你是这样想的,因为你正在阅读这篇文章),你必须了解你所使用的工具。这对作为开发者的你和你的客户来说都是有益的。 +但仅仅了解基础知识对我来说是不够的。知道 React 的底层是如何工作的,这有点强制性。如果你想成为一个优秀的 React 开发者(我想你是这样想的,因为你正在阅读这篇文章),你必须了解你所使用的工具。这对作为开发者的你和你的客户来说都是有益的。 一方面,它可以为你节省大量的时间来调试你的应用程序。另一方面,它使你更有效率,因为你不必一次又一次地来重新学习基本知识。你基本上知道你在说什么。 当然,你不可能知道所有的东西,你不应该在这个话题上给自己压力。当你通过实际问题和建立更多项目时,你会学到越来越多的东西。但是,有了良好扎实的知识,你可以事半功倍。 -好的,这很有意义。但你可能想知道,为了在React方面有一个坚实的基础,你到底需要知道什么? +好的,这很有意义。但你可能想知道,为了在 React 方面有一个坚实的基础,你到底需要知道什么? -作为一个最低要求,你应该了解所有的主题。 在官方的React Docs里面的[**主要概念** 章节](https://reactjs.org/docs/hello-world.html)。 +作为一个最低要求,你应该了解所有的主题。 在官方的 React Docs 里面的[**主要概念** 章节](https://reactjs.org/docs/hello-world.html)。 -另一个 [你应该非常熟悉的章节是关于 **Hooks**](https://reactjs.org/docs/hooks-intro.html),因为它们已经成为一种惯例,并且到处都在使用,特别是在第三方React包中。 +另一个 [你应该非常熟悉的章节是关于 **Hooks**](https://reactjs.org/docs/hooks-intro.html),因为它们已经成为一种惯例,并且到处都在使用,特别是在第三方 React 包中。 当然,有一些你可能更经常使用,如`useState`和`useEffect`,但了解其他的如`useMemo`、`useCallback`或`useRef`也是必不可少的。 -还有[另一章叫做**高级指南**](https://reactjs.org/docs/accessibility.html),我不认为这是开始时的必修课,但我强烈建议你在React旅程中掌握这些概念。 +还有[另一章叫做**高级指南**](https://reactjs.org/docs/accessibility.html),我不认为这是开始时的必修课,但我强烈建议你在 React 旅程中掌握这些概念。 一如既往,当你已经有一些实践经验时,往往更容易理解高级主题。但你在早期了解的那些东西越多越好。 -当然,你不应该把自己限制在只遵循React文档上。通过涵盖这些构件的在线课程,观看教程或阅读其他博客文章也是打下坚实基础的一部分。所以,测试一下什么对你最有效。 +当然,你不应该把自己限制在只遵循 React 文档上。通过涵盖这些构件的在线课程,观看教程或阅读其他博客文章也是打下坚实基础的一部分。所以,测试一下什么对你最有效。 如果我不得不选择最重要的概念来了解,我会建议这些: @@ -112,8 +112,8 @@ React开发者出现问题的另一个根本原因是对React在工作底层原 - 什么是组件重新渲染,它们是如何工作的? - 如何触发重新渲染 - 不同组件的生命周期以及如何交互 -- 虚拟DOM -- CSR(客户端渲染)和SSR(服务器端渲染)在一般情况下和React中的好处 +- 虚拟 DOM +- CSR(客户端渲染)和 SSR(服务器端渲染)在一般情况下和 React 中的好处 - 受控组件 VS 非受控组件 - State 提升 - 至少一种全局状态(state)管理技术(Context API, Redux/Toolkit, Recoil)。 @@ -144,7 +144,7 @@ React开发者出现问题的另一个根本原因是对React在工作底层原 ### 📁 创建一个良好的文件夹结构 -在你的React应用程序中组织你的文件和文件夹对于可维护性和可扩展性是必须的。 +在你的 React 应用程序中组织你的文件和文件夹对于可维护性和可扩展性是必须的。 一个**好的**文件夹结构取决于你的应用程序的大小和你的团队。所以,这没有一个普遍的答案。特别是因为这是一个非常有讨论性的话题,也取决于个人的喜好。 @@ -156,7 +156,7 @@ React开发者出现问题的另一个根本原因是对React在工作底层原 ### 👇 保持结构化的导入顺序 -如果你已经有了一些React的经验,你可能已经看到了有很多导入语句的臃肿文件。它们可能还混杂着来自第三方包的外部导入和内部导入,如其他组件、util函数、styles等等。 +如果你已经有了一些 React 的经验,你可能已经看到了有很多导入语句的臃肿文件。它们可能还混杂着来自第三方包的外部导入和内部导入,如其他组件、util 函数、styles 等等。 真实世界的例子(截取部分): @@ -180,7 +180,7 @@ import CustomButton from "../components/CustomButton"; ... ``` -实际上,这些导入语句跨越了55行。 +实际上,这些导入语句跨越了 55 行。 你可能认识到这里的问题。很难区分什么是所有的第三方和本地(内部)导入。它们没有被分组,似乎到处都是。 @@ -211,13 +211,13 @@ import Paragraph from "../components/Paragraph"; 我见过其他开发者喜欢把导入结构分成三个不同的部分: -内置的(如'react')-->外部(第三方node模块)-->内部。 +内置的(如'react')-->外部(第三方 node 模块)-->内部。 -你可以每次都自己管理,或者让**linter**做这个工作。[这里是](https://dev.to/otamnitram/sorting-your-imports-correctly-in-react-213m)一篇关于如何为你的React应用配置linter以保持正确的导入结构的好文章。 +你可以每次都自己管理,或者让**linter**做这个工作。[这里是](https://dev.to/otamnitram/sorting-your-imports-correctly-in-react-213m)一篇关于如何为你的 React 应用配置 linter 以保持正确的导入结构的好文章。 ### 📔 学习不同的组件模式 -为了确保你不会以不可维护和不可扩展的意大利面条代码而告终,随着你在React中的经验增加,学习不同的组件模式是必不可少的。 +为了确保你不会以不可维护和不可扩展的意大利面条代码而告终,随着你在 React 中的经验增加,学习不同的组件模式是必不可少的。 但这并不是全部。了解不同的模式是一个良好的基础。但关于它最重要的一点是,你知道**什么时候**使用哪种模式来解决你的问题。 @@ -225,19 +225,19 @@ import Paragraph from "../components/Paragraph"; 这里有一个关于`props-drilling`的简短的附带说明,因为我过去曾有过许多关于它的讨论。外面有很多关于它是否不好的意见。就我而言,如果我开始通过两个以上的组件级别来传递`props`,我喜欢尝试思考不同的方式/模式。 -这个事实让你作为一个开发者更有效率,让你写的组件更可维护或可扩展。在你的工具包中拥有这些模式,也让你从其他React开发者中脱颖而出。我非常鼓励你做你自己的研究,但 [这](https://www.udemy.com/course/the-complete-guide-to-advanced-react-patterns/)Udemy课程对我帮助非常大。 +这个事实让你作为一个开发者更有效率,让你写的组件更可维护或可扩展。在你的工具包中拥有这些模式,也让你从其他 React 开发者中脱颖而出。我非常鼓励你做你自己的研究,但 [这](https://www.udemy.com/course/the-complete-guide-to-advanced-react-patterns/)Udemy 课程对我帮助非常大。 -### 🔒用linter并遵守其规则 +### 🔒用 linter 并遵守其规则 -linter不仅可以帮助你保持你的依赖关系的可区分的导入顺序。它可以帮助你写出更好的代码。 +linter 不仅可以帮助你保持你的依赖关系的可区分的导入顺序。它可以帮助你写出更好的代码。 -当你使用_create-react-app_时,已经配置了ESLint,但你也可以完全自己设置,或者扩展预先配置的规则集的规则。 +当你使用_create-react-app_时,已经配置了 ESLint,但你也可以完全自己设置,或者扩展预先配置的规则集的规则。 -linter观察你正在编写的JavaScript代码,并提醒你在执行代码时更有可能发现的错误。我花了一段时间才真正重视linter的使用,但今天我无法想象没有它怎么工作。 +linter 观察你正在编写的 JavaScript 代码,并提醒你在执行代码时更有可能发现的错误。我花了一段时间才真正重视 linter 的使用,但今天我无法想象没有它怎么工作。 -使用linter是一回事,但遵守其规则是另一回事。当然,你可以禁用它。无论是对某一行代码还是对整个文件本身。但根据我的经验,这是很罕见的。 +使用 linter 是一回事,但遵守其规则是另一回事。当然,你可以禁用它。无论是对某一行代码还是对整个文件本身。但根据我的经验,这是很罕见的。 -另一个很大的好处是,你也可以调整样式检查。这对团队来说特别有帮助。一旦你接受了如何写你的代码以及它应该如何格式化的某些约定,你就可以很容易地将ESLint与JSPrettify这样的东西结合起来。 +另一个很大的好处是,你也可以调整样式检查。这对团队来说特别有帮助。一旦你接受了如何写你的代码以及它应该如何格式化的某些约定,你就可以很容易地将 ESLint 与 JSPrettify 这样的东西结合起来。 ### 🧪 测试你的代码 @@ -259,55 +259,55 @@ linter观察你正在编写的JavaScript代码,并提醒你在执行代码时 所以,不要因为测试似乎是`额外的工作`而回避它。现实是,当你正确地设置它时,它可以在未来为你节省额外的工作。 -看看[React文档中的 "测试 "章节](https://reactjs.org/docs/testing.html),通过一些关于React测试的教程,然后开始编写你的第一个小型TDD应用,或者在你目前正在开发的应用中实施测试。 +看看[React 文档中的 "测试 "章节](https://reactjs.org/docs/testing.html),通过一些关于 React 测试的教程,然后开始编写你的第一个小型 TDD 应用,或者在你目前正在开发的应用中实施测试。 -### 🧰 整合Typescript(或至少使用默认的props和prop类型) +### 🧰 整合 Typescript(或至少使用默认的 props 和 prop 类型) -我记得我作为软件开发者的第一个React项目,我们的团队收到了一个基本上已经由其他公司写好的项目。然后我们不得不在此基础上建立客户的项目,而Typescript已经被集成了。 +我记得我作为软件开发者的第一个 React 项目,我们的团队收到了一个基本上已经由其他公司写好的项目。然后我们不得不在此基础上建立客户的项目,而 Typescript 已经被集成了。 -在那之前,我和我的队友们对TypeScript并没有什么经验,因为我们都是来自于普通的JavaScript背景。 +在那之前,我和我的队友们对 TypeScript 并没有什么经验,因为我们都是来自于普通的 JavaScript 背景。 -在与该项目合作的几周后,我们觉得TypeScript并不是一个好处,而是一个阻碍我们工作流程的障碍。我们也没有真正使用它的好处,因为我们用`any`类型定义了所有东西,以抑制Typescript的警告。 +在与该项目合作的几周后,我们觉得 TypeScript 并不是一个好处,而是一个阻碍我们工作流程的障碍。我们也没有真正使用它的好处,因为我们用`any`类型定义了所有东西,以抑制 Typescript 的警告。 -这导致我们决定将TypeScript从项目中移除,用vanilla JavaScript来处理我们已知的地形。起初这很顺利,但我们的项目变得越复杂,出现的类型错误就越多。所以我们很怀疑自己完全摆脱TypeScript的决定。但这些事情可能会发生,并为我们的未来提供了宝贵的经验。 +这导致我们决定将 TypeScript 从项目中移除,用 vanilla JavaScript 来处理我们已知的地形。起初这很顺利,但我们的项目变得越复杂,出现的类型错误就越多。所以我们很怀疑自己完全摆脱 TypeScript 的决定。但这些事情可能会发生,并为我们的未来提供了宝贵的经验。 -这种情况使我又给了TypeScript一个机会,我在业余时间学习了它。在用它构建了一些业余项目后,我再也无法想象没有它的生活了。 +这种情况使我又给了 TypeScript 一个机会,我在业余时间学习了它。在用它构建了一些业余项目后,我再也无法想象没有它的生活了。 -使用TypeScript有很多好处,比如静态类型检查,在你的IDE中更好地完成代码(intellisense),改善开发者体验,以及在你写代码时捕捉类型错误--仅举几例。 +使用 TypeScript 有很多好处,比如静态类型检查,在你的 IDE 中更好地完成代码(intellisense),改善开发者体验,以及在你写代码时捕捉类型错误--仅举几例。 -另一方面,它当然也有一些挑战,因为如果你不是来自强类型语言(如Java或C#)的背景,在开始时可能更难掌握它。 +另一方面,它当然也有一些挑战,因为如果你不是来自强类型语言(如 Java 或 C#)的背景,在开始时可能更难掌握它。 -但我可以说,它真的值得你去学习和整合。[这里有](https://blog.bitsrc.io/5-strong-reasons-to-use-typescript-with-react-bc987da5d907)一篇不错的文章,可以帮助你了解在React应用中使用Typescript的历程。还有[这里有一个教程](https://www.freecodecamp.org/news/how-to-code-your-react-app-with-typescript/)是关于如何在TypeScript中编写你的React应用。 +但我可以说,它真的值得你去学习和整合。[这里有](https://blog.bitsrc.io/5-strong-reasons-to-use-typescript-with-react-bc987da5d907)一篇不错的文章,可以帮助你了解在 React 应用中使用 Typescript 的历程。还有[这里有一个教程](https://www.freecodecamp.org/news/how-to-code-your-react-app-with-typescript/)是关于如何在 TypeScript 中编写你的 React 应用。 -你可能有理由不想在你的React应用程序中使用TypeScript。这很好。但至少我建议你为你的组件使用**prop-types**和**default-props**,以确保你不会弄乱你的props。 +你可能有理由不想在你的 React 应用程序中使用 TypeScript。这很好。但至少我建议你为你的组件使用**prop-types**和**default-props**,以确保你不会弄乱你的 props。 ### 💎 使用懒加载/代码拆分 -如果你在JavaScript和React领域花了一些时间,你很可能已经偶然发现了**bundling**。对于那些第一次听到这个术语的人,让我们看看React官方文档是怎么说的: +如果你在 JavaScript 和 React 领域花了一些时间,你很可能已经偶然发现了**bundling**。对于那些第一次听到这个术语的人,让我们看看 React 官方文档是怎么说的: -大多数React应用会使用Webpack、Rollup或Browserify等工具对其文件进行 "bundling"。"bundling"是跟随导入的文件并将它们合并成一个文件的过程:一个 "bundling"。然后,这个捆绑文件可以包含在一个网页上,以便一次性加载整个应用程序。 +大多数 React 应用会使用 Webpack、Rollup 或 Browserify 等工具对其文件进行 "bundling"。"bundling"是跟随导入的文件并将它们合并成一个文件的过程:一个 "bundling"。然后,这个捆绑文件可以包含在一个网页上,以便一次性加载整个应用程序。 -基本上这是个很好的技术,但随着你的应用程序的增长,就会出现一个挑战。你的"bundling"程序也开始增长。特别是当你使用像three.js这样的大型第三方库时。 +基本上这是个很好的技术,但随着你的应用程序的增长,就会出现一个挑战。你的"bundling"程序也开始增长。特别是当你使用像 three.js 这样的大型第三方库时。 ->隐患在于,即使用户只需要一小部分的代码,这个"bundling"也需要一直完全加载。这导致了性能问题,因为它可能需要不必要的时间来加载你的应用程序。 +> 隐患在于,即使用户只需要一小部分的代码,这个"bundling"也需要一直完全加载。这导致了性能问题,因为它可能需要不必要的时间来加载你的应用程序。 -为了避免这种情况,有一种叫做代码拆分(code splitting)的技术,你把你的捆绑程序分割成用户需要的代码片段。最常见的捆绑器如Webpack、Rollup和Browserify都支持这种做法。它的最大好处是,你可以创建多个捆绑包并动态加载它们。 +为了避免这种情况,有一种叫做代码拆分(code splitting)的技术,你把你的捆绑程序分割成用户需要的代码片段。最常见的捆绑器如 Webpack、Rollup 和 Browserify 都支持这种做法。它的最大好处是,你可以创建多个捆绑包并动态加载它们。 拆分你的"bundling"程序可以帮助你通过懒加载(lazy load)用户需要的东西。 为了说明这一点,想象一下你进入一家杂货店,只想拿一些香蕉、苹果和面包。在这种情况下,你并不是买下整个商店的范围,然后从里面抓出你的香蕉、苹果和面包。你只是对其中的一小部分感兴趣。那么,你为什么要买所有的东西呢?这将花费更长的时间,当然也更昂贵。 -我认为重要的是要意识到在你的应用程序成长过程中可能出现的潜在挑战,而且有一些技术在手,可以摆脱这些问题。进一步阅读请查看[有关code splitting的React文档.](https://reactjs.org/docs/code-splitting.html) +我认为重要的是要意识到在你的应用程序成长过程中可能出现的潜在挑战,而且有一些技术在手,可以摆脱这些问题。进一步阅读请查看[有关 code splitting 的 React 文档.](https://reactjs.org/docs/code-splitting.html) -### 🗄️ 将可重复使用的逻辑提取到自定义hooks +### 🗄️ 将可重复使用的逻辑提取到自定义 hooks -根据React的文档, +根据 React 的文档, ->_Hook允许我们在不改变组件层次结构的情况下重用有状态的逻辑。_ +> _Hook 允许我们在不改变组件层次结构的情况下重用有状态的逻辑。_ 基本上,它们是以前与类组件结合使用的技术的一个更好的解决方案。如果你已经写了一段时间的代码,你可能还记得**高级组件(Higher Order Components)**或**render props**的使用。 -每当你发现自己必须重复使用已经在另一个功能组件中使用的相同的有状态逻辑时,这就是创建一个自定义Hook的好时机。在它里面,你封装了逻辑,只需要在你的组件中作为一个函数调用这个Hook。 +每当你发现自己必须重复使用已经在另一个功能组件中使用的相同的有状态逻辑时,这就是创建一个自定义 Hook 的好时机。在它里面,你封装了逻辑,只需要在你的组件中作为一个函数调用这个 Hook。 让我们来看一个简单的例子,我们需要根据屏幕的大小来更新我们的用户界面,并希望在手动调整浏览器窗口的大小时跟踪当前窗口的大小。 @@ -345,11 +345,11 @@ const ScreenDimensions = () => { 现在,棘手的部分来了。想象一下,我们想在另一个组件中使用确切的逻辑,在那里我们将根据当前的屏幕尺寸渲染一个不同的用户界面(一个用于智能手机,一个用于台式机)。 -当然,我们可以直接复制这个逻辑,把它粘贴进去就可以了。但这并不是一个好的做法,正如你可能从DRY原则中知道的那样。 +当然,我们可以直接复制这个逻辑,把它粘贴进去就可以了。但这并不是一个好的做法,正如你可能从 DRY 原则中知道的那样。 如果我们想调整我们的逻辑,我们必须在两个组件中进行调整。而当我们把我们的逻辑粘贴到更多的组件中时,它的可维护性就会降低,而且更容易出错。 -那么,在一个普通的JavaScript项目中,你通常会怎么做?你很可能会定义一个封装了逻辑的函数,可以在许多不同的地方使用。这正是我们要用Hook实现的。它们只不过是JavaScript函数,但有一些React的特点,因为它们使用了React Hook。 +那么,在一个普通的 JavaScript 项目中,你通常会怎么做?你很可能会定义一个封装了逻辑的函数,可以在许多不同的地方使用。这正是我们要用 Hook 实现的。它们只不过是 JavaScript 函数,但有一些 React 的特点,因为它们使用了 React Hook。 Let's see how our custom hook would look: @@ -391,7 +391,7 @@ const ScreenDimensions = () => { } ``` -这使我们能够在任何其他组件中调用自定义Hook,并将返回值(即当前窗口大小)保存在一个变量中,以便我们在组件中使用。 +这使我们能够在任何其他组件中调用自定义 Hook,并将返回值(即当前窗口大小)保存在一个变量中,以便我们在组件中使用。 ```jsx const ResponsiveView = () => { @@ -415,17 +415,17 @@ const ResponsiveView = () => { 但是,一旦你变得更有经验,并且在一些讨厌的情况下,更好的错误处理可以为你节省大量的精力(当然还有宝贵的时间),你就会意识到,从长远来看,在你的应用程序中拥有一个可靠的错误处理是必须的。特别是当应用程序被部署到生产中时。 -但在React世界里,`错误处理`到底是什么意思?有一些不同的部分在起作用。一个是**捕获错误**,另一个是处理相应的UI,最后一个是正确地 **记录** 错误。 +但在 React 世界里,`错误处理`到底是什么意思?有一些不同的部分在起作用。一个是**捕获错误**,另一个是处理相应的 UI,最后一个是正确地 **记录** 错误。 #### React 错误边界 -这是一个自定义的类组件,被用作你整个应用程序的包装器。当然,你也可以将ErrorBoundary(错误边界)组件包裹在组件树中更深的组件里,以呈现一个更具体的用户界面,例如。基本上,将ErrorBoundary(错误边界)包在容易出错的组件里也是一种最佳做法。 +这是一个自定义的类组件,被用作你整个应用程序的包装器。当然,你也可以将 ErrorBoundary(错误边界)组件包裹在组件树中更深的组件里,以呈现一个更具体的用户界面,例如。基本上,将 ErrorBoundary(错误边界)包在容易出错的组件里也是一种最佳做法。 -通过生命周期方法`componentDidCatch()`,你能够在渲染阶段或子组件的任何其他生命周期中捕获错误。因此,当该阶段出现错误时,它就会冒出来,被ErrorBoundary(错误边界)组件捕捉。 +通过生命周期方法`componentDidCatch()`,你能够在渲染阶段或子组件的任何其他生命周期中捕获错误。因此,当该阶段出现错误时,它就会冒出来,被 ErrorBoundary(错误边界)组件捕捉。 如果你正在使用一个日志服务(我也强烈推荐),这是一个连接它的好地方。 -静态函数`getDerivedStateFromError()`在渲染阶段被调用,用于更新ErrorBoundary组件的状态。基于你的状态,你可以有条件地渲染一个错误的用户界面。 +静态函数`getDerivedStateFromError()`在渲染阶段被调用,用于更新 ErrorBoundary 组件的状态。基于你的状态,你可以有条件地渲染一个错误的用户界面。 ```jsx class ErrorBoundary extends React.Component { @@ -454,9 +454,9 @@ class ErrorBoundary extends React.Component { 这种方法的最大缺点是,它不能处理异步回调、服务器端渲染或事件处理程序中的错误,因为它们在边界之外。 -#### 使用try-catch来处理超出边界的错误 +#### 使用 try-catch 来处理超出边界的错误 -这种技术对于捕捉异步回调中可能出现的错误非常有效。让我们想象一下,我们正在从API中获取用户的个人资料数据,并希望在个人资料组件中显示它。 +这种技术对于捕捉异步回调中可能出现的错误非常有效。让我们想象一下,我们正在从 API 中获取用户的个人资料数据,并希望在个人资料组件中显示它。 ```jsx const UserProfile = ({ userId }) => { @@ -505,11 +505,11 @@ const UserProfile = ({ userId }) => { } ``` -当组件被加载后,它开始向我们的API发出GET请求,以接收我们将从道具中获得的相应用户ID的用户数据。 +当组件被加载后,它开始向我们的 API 发出 GET 请求,以接收我们将从道具中获得的相应用户 ID 的用户数据。 -使用 try-catch 可以帮助我们捕捉在 API 调用过程中可能发生的任何错误。例如,这可能是一个来自API的404或500响应。 +使用 try-catch 可以帮助我们捕捉在 API 调用过程中可能发生的任何错误。例如,这可能是一个来自 API 的 404 或 500 响应。 -一旦错误被捕捉到,我们就会在catch块中接收错误作为一个参数。现在我们能够在我们的日志服务中记录它,并相应地更新状态以显示一个自定义的错误用户界面。 +一旦错误被捕捉到,我们就会在 catch 块中接收错误作为一个参数。现在我们能够在我们的日志服务中记录它,并相应地更新状态以显示一个自定义的错误用户界面。 #### Use the react-error-boundary library (personal recommendation) @@ -545,13 +545,13 @@ const App = () => { } ``` -该库导出了一个由我们已经知道的ErrorBoundary功能组成的组件,并在其中添加了一些细微的差别。它允许你传递一个 "FallbackComponent "作为prop,一旦发现错误,就应该呈现出来。 +该库导出了一个由我们已经知道的 ErrorBoundary 功能组成的组件,并在其中添加了一些细微的差别。它允许你传递一个 "FallbackComponent "作为 prop,一旦发现错误,就应该呈现出来。 -它还公开了一个prop `onError`,在出现错误时提供一个回调函数。这对于使用它将错误记录到日志服务中是非常好的。 +它还公开了一个 prop `onError`,在出现错误时提供一个回调函数。这对于使用它将错误记录到日志服务中是非常好的。 -还有一些其他的prop是相当有用的。如果你想了解更多,请随时查看[这个文档。](https://www.npmjs.com/package/react-error-boundary?activeTab=readme) +还有一些其他的 prop 是相当有用的。如果你想了解更多,请随时查看[这个文档。](https://www.npmjs.com/package/react-error-boundary?activeTab=readme) -这个库还提供了一个名为`useErrorHandler()`的Hook,旨在捕捉任何在事件处理程序等边界之外的错误,在异步代码和服务器端的渲染中。 +这个库还提供了一个名为`useErrorHandler()`的 Hook,旨在捕捉任何在事件处理程序等边界之外的错误,在异步代码和服务器端的渲染中。 #### 记录错误 @@ -565,11 +565,11 @@ const App = () => { 当使用第三方日志服务时,我个人推荐的肯定是**Sentry**,所以我非常鼓励你去看看。 -### ☝️ 在你的整个应用程序中保持你的关键prop的唯一性 +### ☝️ 在你的整个应用程序中保持你的关键 prop 的唯一性 -当对数组进行映射以渲染其数据时,你总是要为每个元素定义一个**key**属性。我见过的一个常见的做法,也是我自己使用的,就是简单地使用每个元素的**index**作为key道具。 +当对数组进行映射以渲染其数据时,你总是要为每个元素定义一个**key**属性。我见过的一个常见的做法,也是我自己使用的,就是简单地使用每个元素的**index**作为 key 道具。 -使用key prop是很重要的,因为它可以帮助React识别已经改变的、被添加或被移除的确切元素。想象一下,你的组件的状态改变了,用户界面需要用新的状态重新渲染。React需要弄清楚以前的UI和新的UI之间的差异,以便更新它。 +使用 key prop 是很重要的,因为它可以帮助 React 识别已经改变的、被添加或被移除的确切元素。想象一下,你的组件的状态改变了,用户界面需要用新的状态重新渲染。React 需要弄清楚以前的 UI 和新的 UI 之间的差异,以便更新它。 "哪些元素被添加/删除或发生了变化?" @@ -594,9 +594,9 @@ const SeasonScores = ({ seasonScoresData }) => { } ``` -虽然这只是在这里的地图函数中是唯一的,但这可能导致潜在的问题。在你的React应用程序中,甚至在一个组件中,有一个以上的map函数是很常见的。 +虽然这只是在这里的地图函数中是唯一的,但这可能导致潜在的问题。在你的 React 应用程序中,甚至在一个组件中,有一个以上的 map 函数是很常见的。 -让我们假设我们的组件中有另一个map函数来显示当前的名册: +让我们假设我们的组件中有另一个 map 函数来显示当前的名册: ```jsx const SeasonScores = ({ seasonScoresData, currentRoster }) => { @@ -625,13 +625,13 @@ const SeasonScores = ({ seasonScoresData, currentRoster }) => { } ``` -现在我们的情况是,我们在组件中使用了许多keys两次。让我们假设我们在 `seasonScoresData` 里有**14**个元素,在 `currentRoaster`里有**30**个。我们已经两次使用数字0-13作为key props。现在我们没有达到拥有唯一key props目的了。 +现在我们的情况是,我们在组件中使用了许多 keys 两次。让我们假设我们在 `seasonScoresData` 里有**14**个元素,在 `currentRoaster`里有**30**个。我们已经两次使用数字 0-13 作为 key props。现在我们没有达到拥有唯一 key props 目的了。 -这可能导致潜在的问题,因为React可能只重新渲染一个item而省略另一个item。或者它可能导致更新UI树的效率低下。请看本提示末尾的推荐博文,以获得更深入的例子。 +这可能导致潜在的问题,因为 React 可能只重新渲染一个 item 而省略另一个 item。或者它可能导致更新 UI 树的效率低下。请看本提示末尾的推荐博文,以获得更深入的例子。 -为了避免这种不必要的行为,请确保在你的整个应用程序中始终使用**唯一的key**,理想情况下,数组中的每个item都有自己的唯一id。但这并不总是如此,所以你可以使用一个外部库,如**uuidv4**来生成唯一的ID。 +为了避免这种不必要的行为,请确保在你的整个应用程序中始终使用**唯一的 key**,理想情况下,数组中的每个 item 都有自己的唯一 id。但这并不总是如此,所以你可以使用一个外部库,如**uuidv4**来生成唯一的 ID。 -考虑到这一点,并假设两个数组中的每个项目都有一个id属性,该组件将看起来像这样: +考虑到这一点,并假设两个数组中的每个项目都有一个 id 属性,该组件将看起来像这样: ```jsx const SeasonScores = ({ seasonScoresData, currentRoster }) => { @@ -666,15 +666,15 @@ const SeasonScores = ({ seasonScoresData, currentRoster }) => { ![joanna-kosinska-_xN7UbcZ33I-unsplash](https://www.freecodecamp.org/news/content/images/2022/01/joanna-kosinska-_xN7UbcZ33I-unsplash.jpg) -我想把这个指南比作建造房子的过程。第一部分,_学习React的构件_,是你建立应用程序的坚实基础。第二部分,_如何构建干净的、可执行的和可维护的React组件_,是用来建墙。 +我想把这个指南比作建造房子的过程。第一部分,_学习 React 的构件_,是你建立应用程序的坚实基础。第二部分,_如何构建干净的、可执行的和可维护的 React 组件_,是用来建墙。 这一节基本上是顶部的屋顶,用来完成房子。这就是我想把它称为 _Cherries on Top_ 的原因。这里的这些提示更加细化。 这些做法大多比之前的那些更可有可无,但如果你使用得当,也会有所作为。 -### 提前实施useReducer Hook +### 提前实施 useReducer Hook -React中最常使用的Hook之一是**useState**。在过去的时间里,我创建和看到的组件都有很多不同的状态。所以很自然地,它们会被大量的useState Hook所淹没。 +React 中最常使用的 Hook 之一是**useState**。在过去的时间里,我创建和看到的组件都有很多不同的状态。所以很自然地,它们会被大量的 useState Hook 所淹没。 ```jsx const CustomersMap = () => { @@ -693,17 +693,17 @@ const CustomersMap = () => { } ``` -有很多不同的useState Hook总是一个很好的信号,说明你的组件的规模和复杂性正在增长。 +有很多不同的 useState Hook 总是一个很好的信号,说明你的组件的规模和复杂性正在增长。 -如果你能创建一些较小的子组件,在那里你可以转换一些state和JSX,那么这是一个很好的方法。这样你就可以一步到位地清理你的useState Hook和你的JSX。 +如果你能创建一些较小的子组件,在那里你可以转换一些 state 和 JSX,那么这是一个很好的方法。这样你就可以一步到位地清理你的 useState Hook 和你的 JSX。 -在我们上面的例子中,我们可以把最后两个状态(states)放到一个单独的组件中,这个组件处理所有与表单有关的状态(state)和JSX。 +在我们上面的例子中,我们可以把最后两个状态(states)放到一个单独的组件中,这个组件处理所有与表单有关的状态(state)和 JSX。 但在有些情况下,这样做是没有意义的,你必须把这些不同的状态(states)放在一个组件里。为了提高你的组件的可读性,有一个**useReducer**钩。 -React的官方文档是这样说的: +React 的官方文档是这样说的: -> `useReducer` 当你有复杂的状态逻辑(state logic),涉及到多个子值,或者下一个状态(state)取决于上一个状态(state)时,通常比`useState`更可取。useReducer还可以让你优化触发深度更新的组件的性能,因为你可以把调度(dispatch)传递下去而不是回调(callbacks)。 +> `useReducer` 当你有复杂的状态逻辑(state logic),涉及到多个子值,或者下一个状态(state)取决于上一个状态(state)时,通常比`useState`更可取。useReducer 还可以让你优化触发深度更新的组件的性能,因为你可以把调度(dispatch)传递下去而不是回调(callbacks)。 考虑到这一点,该组件在使用`useReducer`时就会变成这样: @@ -752,18 +752,18 @@ const CustomersMap = () => { } ``` -该组件本身看起来更干净,并伴随着一些巨大的好处,你可以在文档中看到。如果你已经习惯了Redux,减速器的概念和它的构建方式对你来说并不陌生。 +该组件本身看起来更干净,并伴随着一些巨大的好处,你可以在文档中看到。如果你已经习惯了 Redux,减速器的概念和它的构建方式对你来说并不陌生。 -我个人的规则是,如果我的组件超过了四个useState Hook,或者状态(state)本身比布尔值更复杂,例如,就使用useReducer Hook。它可能是一个表单的对象,里面有一些更深的层次。 -### 🔌 使用布尔型props的速记 +我个人的规则是,如果我的组件超过了四个 useState Hook,或者状态(state)本身比布尔值更复杂,例如,就使用 useReducer Hook。它可能是一个表单的对象,里面有一些更深的层次。 +### 🔌 使用布尔型 props 的速记 -经常会有这样的情况,你把布尔props传递给一个组件。我见过很多开发者是这样做的。: +经常会有这样的情况,你把布尔 props 传递给一个组件。我见过很多开发者是这样做的。: ```jsx ``` -但你不需要一定要这样做,因为prop本身的场合要么是truthy的(如果prop被传递),要么是falsy的(如果prop丢失)。 +但你不需要一定要这样做,因为 prop 本身的场合要么是 truthy 的(如果 prop 被传递),要么是 falsy 的(如果 prop 丢失)。 一个更简洁的方法是: @@ -771,17 +771,17 @@ const CustomersMap = () => { ``` -### 👎 避免用大括号来表示字符串props +### 👎 避免用大括号来表示字符串 props -像我们之前在提示中看到的一个类似的用例是使用字符串props: +像我们之前在提示中看到的一个类似的用例是使用字符串 props: ```jsx ``` -在这种情况下,你不需要大括号,因为你被允许在你的props中直接使用字符串。当你想把className附加到一个JSX元素时,你很可能也是直接使用字符串。 +在这种情况下,你不需要大括号,因为你被允许在你的 props 中直接使用字符串。当你想把 className 附加到一个 JSX 元素时,你很可能也是直接使用字符串。 -当你想使用一个不同于字符串的JavaScript表达式时,你需要使用大括号。例如,如果你想使用一个数字或一个对象。这对于模板字符串也是如此(不要像我一样被抓到很多次,哈哈)。 +当你想使用一个不同于字符串的 JavaScript 表达式时,你需要使用大括号。例如,如果你想使用一个数字或一个对象。这对于模板字符串也是如此(不要像我一样被抓到很多次,哈哈)。 对于普通的字符串,就像例子中的那样,它看起来是这样的: @@ -789,7 +789,7 @@ const CustomersMap = () => { ``` -### 🧹 在传递props时擦除非html属性 +### 🧹 在传递 props 时擦除非 html 属性 让我们来看看一个简单的例子: @@ -807,9 +807,9 @@ const MainTitle = ({ isBold, children, ...restProps }) => { } ``` -我们刚刚创建了一个组件,它将渲染一个h1标签,提取一些props,并将所有其他潜在的props(potential props)传递到h1标签上。到目前为止,一切都很好。 +我们刚刚创建了一个组件,它将渲染一个 h1 标签,提取一些 props,并将所有其他潜在的 props(potential props)传递到 h1 标签上。到目前为止,一切都很好。 -现在,我们能够在其他组件中使用它,并且可以手动触发h1是否应该加粗: +现在,我们能够在其他组件中使用它,并且可以手动触发 h1 是否应该加粗: ```jsx // WITH BOLD TITLE @@ -841,9 +841,9 @@ const AboutPage = () => { } ``` -到现在为止,一切工作都很完美,没有任何错误或警告。有趣的部分从现在开始,当我们使用其他的props直接传递到h1标签上的时候。 +到现在为止,一切工作都很完美,没有任何错误或警告。有趣的部分从现在开始,当我们使用其他的 props 直接传递到 h1 标签上的时候。 -当你使用有效的HTML属性如id或class时,一切都能正常工作,没有任何错误(记住-->"className "将变成 "class"): +当你使用有效的 HTML 属性如 id 或 class 时,一切都能正常工作,没有任何错误(记住-->"className "将变成 "class"): ```jsx const IndexPage = () => { @@ -859,7 +859,7 @@ const IndexPage = () => { } ``` -所以上面所有的props都会作为属性添加到h1中,因为我们在上面使用了**{...restProps}**。无论怎样,我们正在添加而不是提取的props都会被添加到h1标签中。 +所以上面所有的 props 都会作为属性添加到 h1 中,因为我们在上面使用了**{...restProps}**。无论怎样,我们正在添加而不是提取的 props 都会被添加到 h1 标签中。 这对很多用例来说是很好的,但同时也是一个问题: @@ -895,11 +895,11 @@ const MainTitle = ({ isBold, children, ...restProps }) => { } ``` -在上面的代码中,我们为`MainTitle`组件添加了一个新的prop `hasPadding`,这是可选的。在组件内部,我们没有从prop中获取它,而是通过`restProps.hasPadding`调用它。 +在上面的代码中,我们为`MainTitle`组件添加了一个新的 prop `hasPadding`,这是可选的。在组件内部,我们没有从 prop 中获取它,而是通过`restProps.hasPadding`调用它。 -这段代码是有效的,但是当你打开浏览器时,你会收到一个警告:`hasPadding`是一个非HTML属性,你试图在h1标签上应用。这是因为h1标签上的`{...restProps}`没有像`isBold`那样获取`hasPadding`。 +这段代码是有效的,但是当你打开浏览器时,你会收到一个警告:`hasPadding`是一个非 HTML 属性,你试图在 h1 标签上应用。这是因为 h1 标签上的`{...restProps}`没有像`isBold`那样获取`hasPadding`。 -为了避免这种情况,总是先从props中提取所有非HTML属性,以确保在`restProps`中只有有效的HTML属性被你传播到JSX元素中。 +为了避免这种情况,总是先从 props 中提取所有非 HTML 属性,以确保在`restProps`中只有有效的 HTML 属性被你传播到 JSX 元素中。 在我们的例子中,它看起来像这样: @@ -936,11 +936,11 @@ const MainTitle = ({ isBold, children, hasPadding, ...restProps }) => { 许多警告会不必要地充斥你的浏览器的控制台,这可能是非常讨厌的。特别是在你调试的时候。 -要获得关于这个话题的更多信息和其他一些解决方法,请查看[React有关unknown-prop的文档](https://reactjs.org/warnings/unknown-prop.html). +要获得关于这个话题的更多信息和其他一些解决方法,请查看[React 有关 unknown-prop 的文档](https://reactjs.org/warnings/unknown-prop.html). ### 🔥 使用片段扩展 -例如,在Visual Studio Code中,有一些可用的扩展,可以极大地提高你的工作效率。这些扩展的一种类型是**片段扩展**。 +例如,在 Visual Studio Code 中,有一些可用的扩展,可以极大地提高你的工作效率。这些扩展的一种类型是**片段扩展**。 它们的最大好处是,你不必再写那些模板代码了。想象一下,你在构建许多新的组件时,不得不再次输入所有的代码。: @@ -954,7 +954,7 @@ const GoogleMap = () => { export default GoogleMap ``` -有了这些片段,你只需输入 **`rafce`**,按下tab,你就能得到同样的模板代码。节省时间,使开发更快。 +有了这些片段,你只需输入 **`rafce`**,按下 tab,你就能得到同样的模板代码。节省时间,使开发更快。 **但要谨慎使用它们!**我不会向所有的开发者推荐使用片段。在我看来,初学者不应该使用任何代码段,应该用手打出模板。当你这样做的时候,你会得到肌肉记忆,表现出你所学的东西。 @@ -968,9 +968,9 @@ export default GoogleMap ![Bildschirmfoto-2022-02-01-um-15.06.59](https://www.freecodecamp.org/news/content/images/2022/02/Bildschirmfoto-2022-02-01-um-15.06.59.png) -### ❌ 在不需要div的情况下写一个片段 +### ❌ 在不需要 div 的情况下写一个片段 -一个React组件在其根部只能渲染一个单一的HTML标签。因此,如果你想渲染两个相邻的元素,你会得到一个著名的错误,即**Adjacent JSX elements must be wrapped in an enclosing tag(相邻的JSX元素必须被包裹在一个封闭的标签中)**。 +一个 React 组件在其根部只能渲染一个单一的 HTML 标签。因此,如果你想渲染两个相邻的元素,你会得到一个著名的错误,即**Adjacent JSX elements must be wrapped in an enclosing tag(相邻的 JSX 元素必须被包裹在一个封闭的标签中)**。 ```jsx const InfoText = () => { @@ -983,7 +983,7 @@ const InfoText = () => { } ``` -那么,你能做什么呢?你只需将渲染后的输出包成一个片段,这样既满足了React的要求,又不会在浏览器中渲染一个额外的HTML元素。 +那么,你能做什么呢?你只需将渲染后的输出包成一个片段,这样既满足了 React 的要求,又不会在浏览器中渲染一个额外的 HTML 元素。 ```jsx const InfoText = () => { @@ -997,15 +997,15 @@ const InfoText = () => { } ``` -当然,你也可以用一个div标签来解决这个问题。但是,在浏览器中使用一个又一个的div会产生我称之为**div地狱**的东西,在那里你会得到许多没有任何意义的深度嵌套div标签。 +当然,你也可以用一个 div 标签来解决这个问题。但是,在浏览器中使用一个又一个的 div 会产生我称之为**div 地狱**的东西,在那里你会得到许多没有任何意义的深度嵌套 div 标签。 -所以只要你在React中必须使用包装标签,但不一定需要HTML标签,那么就简单地使用片段。 +所以只要你在 React 中必须使用包装标签,但不一定需要 HTML 标签,那么就简单地使用片段。 -### 👈 在不需要children(子级)的时候,整合自我关闭的标签 +### 👈 在不需要 children(子级)的时候,整合自我关闭的标签 根据我的经验,这个小窍门经常被忽视,但可以让你的代码变得更干净,而且不费吹灰之力。 -在React中,你有机会将子元素传递给一个组件,然后通过其子属性提供给该组件。这些组件通常被称为**复合组件(composite components)**。 +在 React 中,你有机会将子元素传递给一个组件,然后通过其子属性提供给该组件。这些组件通常被称为**复合组件(composite components)**。 在这种情况下,你当然要使用一个开头标签和一个结尾标签: @@ -1018,13 +1018,13 @@ const InfoText = () => { ``` -但是,当不需要有children(子级)的时候,使用开头和结尾标签就没有任何意义了,对吗? +但是,当不需要有 children(子级)的时候,使用开头和结尾标签就没有任何意义了,对吗? ```jsx ``` -我建议你不要这样做,而是把这个组件作为一个自我封闭的元素,就像HTML中的输入标签一样,它也不接受children(子级)。 +我建议你不要这样做,而是把这个组件作为一个自我封闭的元素,就像 HTML 中的输入标签一样,它也不接受 children(子级)。 ```jsx @@ -1036,9 +1036,9 @@ const InfoText = () => { 命名约定背后的意义是为了更容易识别你所处理的元素类型,并在你的代码中拥有一些在社区中常见的东西。 -从我的角度来看,在React和JavaScript中,有两个主要的命名惯例是你应该遵循的: +从我的角度来看,在 React 和 JavaScript 中,有两个主要的命名惯例是你应该遵循的: -#### 在组件、接口或类型别名中使用PascalCase(帕斯卡拼写法,将描述变量作用所有单词的首字母大写,然后直接连接起来,单词之间没有连接符) +#### 在组件、接口或类型别名中使用 PascalCase(帕斯卡拼写法,将描述变量作用所有单词的首字母大写,然后直接连接起来,单词之间没有连接符) ```jsx // React component @@ -1062,7 +1062,7 @@ type TodoList = { ``` -#### 对JavaScript数据类型如变量、数组、对象、函数等使用camelCase(驼峰写法,第一个单词首字母小写,后面的每个单词首字母大写) +#### 对 JavaScript 数据类型如变量、数组、对象、函数等使用 camelCase(驼峰写法,第一个单词首字母小写,后面的每个单词首字母大写) ```jsx const getLastDigit = () => { ... } @@ -1071,17 +1071,17 @@ const userTypes = [ ... ] ``` -用PascalCase来命名React组件是特别重要的。因为当你为React配置了一个linter,但你用camelCase命名组件,并在里面使用Hook时,你会一直收到一个警告信息:Hook只允许在组件中使用。这是因为linter会识别React组件是否用PascalCase书写。 +用 PascalCase 来命名 React 组件是特别重要的。因为当你为 React 配置了一个 linter,但你用 camelCase 命名组件,并在里面使用 Hook 时,你会一直收到一个警告信息:Hook 只允许在组件中使用。这是因为 linter 会识别 React 组件是否用 PascalCase 书写。 这可能很讨厌,但只要坚持既定的命名惯例就能很快解决。 ### 🧨 整理您的代码以防止 XSS 攻击 -也许你已经发现自己处于这样的情景中:你必须在React的一个元素上使用`dangerouslySetInnerHTML`属性。基本上,它相当于你在Javascript中可能知道的`innerHTML'的React属性。 +也许你已经发现自己处于这样的情景中:你必须在 React 的一个元素上使用`dangerouslySetInnerHTML`属性。基本上,它相当于你在 Javascript 中可能知道的`innerHTML'的 React 属性。 -所以使用它,你可以直接从React设置HTML。 +所以使用它,你可以直接从 React 设置 HTML。 -让我们考虑下面的例子,我们想在一个div里面渲染一个HTML字符串。这个字符串可能来自一个富文本编辑器,它已经被格式化为HTML。 +让我们考虑下面的例子,我们想在一个 div 里面渲染一个 HTML 字符串。这个字符串可能来自一个富文本编辑器,它已经被格式化为 HTML。 ```jsx const Markup = () => { @@ -1099,10 +1099,10 @@ const Markup = () => { ## 结语 -哇,这很有趣吧?我尽力把过去堆积在我脑子里的东西都整理出来。我做这个指南的动机是与你分享我的经验,这样你就可以在学习和开发React的过程中避免一些困难。 +哇,这很有趣吧?我尽力把过去堆积在我脑子里的东西都整理出来。我做这个指南的动机是与你分享我的经验,这样你就可以在学习和开发 React 的过程中避免一些困难。 当然,可能会有一些你认为更重要的最佳实践,而我在这里错过了。这很好。我很乐意听到你想在本指南中添加什么。 记住,这总是关于调整对你有用的东西。因此,不要认为这一切是理所当然的,想想哪些东西可能对你的情况有帮助。然后你就可以把它添加到你自己的最佳实践堆中。 -你也可以在我的[Instagram简介](https://www.instagram.com/jean_marc.dev/)上关注我的开发者之旅,并获得更多关于开发者生活的有用见解。我总是在那里给你提供帮助,并对我能得到的每一个反馈感到高兴。所以,请随时联系我。 \ No newline at end of file +你也可以在我的[Instagram 简介](https://www.instagram.com/jean_marc.dev/)上关注我的开发者之旅,并获得更多关于开发者生活的有用见解。我总是在那里给你提供帮助,并对我能得到的每一个反馈感到高兴。所以,请随时联系我。 \ No newline at end of file diff --git a/chinese/articles/big-o-notation.md b/chinese/articles/big-o-notation.md index cf8bdbe7a..80e28c65b 100644 --- a/chinese/articles/big-o-notation.md +++ b/chinese/articles/big-o-notation.md @@ -26,7 +26,7 @@ 第一个到的人独享蛋糕,接下来到的两个人分一个蛋糕,在接着到的四个人分一个蛋糕,以此类推。 -因此一个人聚会需要一个蛋糕。两人或者三人聚会需要两个蛋糕。4 - 7人聚会需要 3 个蛋糕。8 - 15 人聚会需要四个蛋糕。 ‘n’ 个人聚会需要 log_2_(n) 个蛋糕。 +因此一个人聚会需要一个蛋糕。两人或者三人聚会需要两个蛋糕。4 - 7 人聚会需要 3 个蛋糕。8 - 15 人聚会需要四个蛋糕。 ‘n’ 个人聚会需要 log_2_(n) 个蛋糕。 ![O(log n) Logarithmic Time Illustration](https://www.freecodecamp.org/news/content/images/2020/12/o-log-n--logarithmic-time.png) @@ -56,7 +56,7 @@ 对于二次时间,每个参会者都有自己的蛋糕,另外,每个蛋糕上的奶油都有所有人的签名。 -在这种情况下一人参会需要 1 个蛋糕和 1 个签名。两人参会需要 2 个蛋糕,每个蛋糕都需要 2 个名字(一共 4 个名字)。三人参会需要 3个蛋糕,每个蛋糕都有 3 个名字,一共 9 个名字。 +在这种情况下一人参会需要 1 个蛋糕和 1 个签名。两人参会需要 2 个蛋糕,每个蛋糕都需要 2 个名字(一共 4 个名字)。三人参会需要 3 个蛋糕,每个蛋糕都有 3 个名字,一共 9 个名字。 ![O(n^2) Quadratic Time Illustration](https://www.freecodecamp.org/news/content/images/2020/12/o-n-2--quadratic-time.png) diff --git a/chinese/articles/buffer-overflow-attacks.md b/chinese/articles/buffer-overflow-attacks.md index 1592123bc..fff6f5a3e 100644 --- a/chinese/articles/buffer-overflow-attacks.md +++ b/chinese/articles/buffer-overflow-attacks.md @@ -7,13 +7,13 @@ 当写到内存的内容超过分配给它的大小时,就会发生缓冲区溢出。这种行为可能会导致数据损坏、程序崩溃,甚至是恶意代码的执行。 -C、C++和Objective-C是容易出现缓冲区溢出漏洞的主要语言(因为它们比许多解释型语言更直接地处理内存),而且它们是互联网的大部分基础。 +C、C++和 Objective-C 是容易出现缓冲区溢出漏洞的主要语言(因为它们比许多解释型语言更直接地处理内存),而且它们是互联网的大部分基础。 -即使代码是用"安全"语言(如Python)编写的,如果它调用任何用C、C++或Objective C编写的库,它仍然有可能受到缓冲区溢出的影响。 +即使代码是用"安全"语言(如 Python)编写的,如果它调用任何用 C、C++或 Objective C 编写的库,它仍然有可能受到缓冲区溢出的影响。 ## 内存分配 -为了理解缓冲区溢出,我们有必要了解一下程序如何分配内存。在C语言中,你可以在编译时在堆栈中分配内存,也可以在运行时在堆中分配内存。 +为了理解缓冲区溢出,我们有必要了解一下程序如何分配内存。在 C 语言中,你可以在编译时在堆栈中分配内存,也可以在运行时在堆中分配内存。 在堆栈上声明一个变量:`int numberPoints = 10;` @@ -44,7 +44,7 @@ https://en.wikipedia.org/wiki/Stack\_(abstract\_data\_type) 为了持续的追踪栈帧,计算机在内存中保留了以下几个指针: - **栈指针:** 指向进程调用栈的顶部(或压入栈的最后一个指针)。 -- **指令指针:** 指向下一条要执行的CPU指令的地址。 +- **指令指针:** 指向下一条要执行的 CPU 指令的地址。 - **基准指针(BP):** (也称为帧指针) 指向当前栈帧的基点。只要程序在执行当前栈帧,它就保持不变(尽管栈指针会改变)。 例如,请看以下程序: @@ -85,13 +85,13 @@ int main() { } ``` -这个简单的例子读入了任意数量的数据(gets会读入到文件的末尾或换行符)。想一想我们上面看过的调栈,你就会明白为什么这很危险。如果用户输入的数据多于变量被分配的数量,用户输入的字符串将覆盖调用栈的下一个内存位置。如果它足够长,它甚至可能覆盖调用函数的返回地址。 +这个简单的例子读入了任意数量的数据(gets 会读入到文件的末尾或换行符)。想一想我们上面看过的调栈,你就会明白为什么这很危险。如果用户输入的数据多于变量被分配的数量,用户输入的字符串将覆盖调用栈的下一个内存位置。如果它足够长,它甚至可能覆盖调用函数的返回地址。 计算机对此的反应取决于栈的实现方式和特定系统中内存的分配方式。对缓冲区溢出的反应是不可预测的,从程序故障,到崩溃,再到执行恶意代码。 ## 为什么会发生缓冲区溢出? -缓冲区溢出之所以成为如此重大的问题,是因为C和C++中的许多内存操作函数不执行任何边界检查。缓冲区溢出现在相当有名,它们也非常普遍地被利用 (例如,[WannaCry](https://en.wikipedia.org/wiki/WannaCry_ransomware_attack) 利用了缓冲区溢出)。 +缓冲区溢出之所以成为如此重大的问题,是因为 C 和 C++中的许多内存操作函数不执行任何边界检查。缓冲区溢出现在相当有名,它们也非常普遍地被利用 (例如,[WannaCry](https://en.wikipedia.org/wiki/WannaCry_ransomware_attack) 利用了缓冲区溢出)。 缓冲区溢出最常见的情况是,代码依赖于外部输入数据,这种方式过于复杂,程序员不容易理解其行为,或者它有代码直接范围之外的依赖关系。 @@ -102,20 +102,20 @@ int main() { ## 如何减轻缓冲区溢出的影响 - **使用解释性的语言** 这并不容易受缓冲区溢出的影响。 -- **避免使用不进行缓冲区检查的函数** (例如,在C语言中,用 fgets() 代替 gets()。) +- **避免使用不进行缓冲区检查的函数** (例如,在 C 语言中,用 fgets() 代替 gets()。) - **使用能够帮助识别不安全函数或错误的编译器。** - **[使用](https://ritcsec.wordpress.com/2017/05/18/buffer-overflows-aslr-and-stack-canaries/)[金丝雀](http://www.cbi.umn.edu/securitywiki/CBI_ComputerSecurity/MechanismCanary.html),** 这是一种 "防护值",可以帮助防止缓冲区溢出。它们被插入到堆栈中的返回地址之前,并在返回地址被访问之前被检查。如果程序检测到金丝雀值的变化,它将中止进程,防止攻击者得逞。警戒值要么是随机的(所以,攻击者很难猜到),要么是一串字符,由于技术原因,不可能被覆盖。 - **重新安排局部变量** 所以普通变量(单个固定大小的数据对象)高于数组变量(包含多个值)。这意味着,如果数组变量真的溢出,它们不会影响普通变量。这种技术,当与警戒值相结合时,它可以帮助抵御缓冲区溢出攻击。 -- **将一个栈变为不可执行** 通过设置NX(No-eXecute)位,防止攻击者将shellcode直接插入栈中并在那里执行它。这并不是一个完美的解决方案,因为即使是不可执行的栈也会成为缓冲区溢出攻击的受害者,如返回到libc攻击。当栈框架的返回地址被替换成已经在进程地址空间的库的地址时,这种攻击就会发生。此外,并非所有的CPU都允许设置NX位。 -- **[ASLR(地址空间布局随机化)](https://en.wikipedia.org/wiki/Address_space_layout_randomization)**,它可以作为一种一般的防御措施(以及对返回到libc攻击的特殊防御)。这意味着,无论何时一个库文件或其他函数被运行中的进程调用,其地址都会被一个随机数移位。这使得几乎不可能将一个固定的进程内存地址与函数联系起来,这意味着攻击者很难,甚至不可能知道从哪里调用特定的函数。在许多版本的Linux、OS X和Android中,ASLR是默认开启的(可以在命令行中切换关闭)。 +- **将一个栈变为不可执行** 通过设置 NX(No-eXecute)位,防止攻击者将 shellcode 直接插入栈中并在那里执行它。这并不是一个完美的解决方案,因为即使是不可执行的栈也会成为缓冲区溢出攻击的受害者,如返回到 libc 攻击。当栈框架的返回地址被替换成已经在进程地址空间的库的地址时,这种攻击就会发生。此外,并非所有的 CPU 都允许设置 NX 位。 +- **[ASLR(地址空间布局随机化)](https://en.wikipedia.org/wiki/Address_space_layout_randomization)**,它可以作为一种一般的防御措施(以及对返回到 libc 攻击的特殊防御)。这意味着,无论何时一个库文件或其他函数被运行中的进程调用,其地址都会被一个随机数移位。这使得几乎不可能将一个固定的进程内存地址与函数联系起来,这意味着攻击者很难,甚至不可能知道从哪里调用特定的函数。在许多版本的 Linux、OS X 和 Android 中,ASLR 是默认开启的(可以在命令行中切换关闭)。 -### 关于Stack Underflow的说明: +### 关于 Stack Underflow 的说明 -当同一个程序的两个部分以不同的方式处理同一个内存块时,也有可能出现缓冲区溢出的漏洞。例如,如果你分配了一个大小为X的数组,但用大小为x> | RecursiveReadonlyArray>'.`` -TypeScript将你的`IntentOptions`数组推断为一个字符串,但`Client`构造函数期望的是更具体的类型。 +TypeScript 将你的`IntentOptions`数组推断为一个字符串,但`Client`构造函数期望的是更具体的类型。 回到你的`config/IntentOptions.ts`文件,添加另一个导入。`import { IntentsString } from "discord.js"`. 然后用新的类型定义更新你的变量: `export const IntentOptions: IntentsString[] = ["GUILDS"];`。 -现在`npm run build`应该成功了。如果你已经把你的新机器人(bot)添加到一个Discord服务器,运行`npm start`将显示你的机器人在该服务器中上线。然而,机器人还不会对任何事情做出反应,因为你还没有开始监听事件。 +现在`npm run build`应该成功了。如果你已经把你的新机器人(bot)添加到一个 Discord 服务器,运行`npm start`将显示你的机器人在该服务器中上线。然而,机器人还不会对任何事情做出反应,因为你还没有开始监听事件。 -## Discord中的网关事件(Gateway Events) +## Discord 中的网关事件(Gateway Events) 网关事件是在 Discord 上发生动作时产生的,通常以 JSON payloads(有效载荷)的形式发送到客户端(包括你的机器人)。你可以用`.on()`方法监听这些事件,允许你为你的机器人编写逻辑,以便在特定事件发生时执行。 @@ -246,9 +246,9 @@ TypeScript将你的`IntentOptions`数组推断为一个字符串,但`Client` ## 连接到数据库 -你将使用`mongoose`包来连接到MongoDB实例。如果你愿意,你可以在本地运行MongoDB,或者你可以使用MongoDB Atlas免费层来实现基于云的解决方案。 +你将使用`mongoose`包来连接到 MongoDB 实例。如果你愿意,你可以在本地运行 MongoDB,或者你可以使用 MongoDB Atlas 免费层来实现基于云的解决方案。 -如果你没有MongoDB Atlas账户,freeCodeCamp有一个 [关于设置一个账户的好教程](https://www.freecodecamp.org/news/get-started-with-mongodb-atlas/)。 +如果你没有 MongoDB Atlas 账户,freeCodeCamp 有一个 [关于设置一个账户的好教程](https://www.freecodecamp.org/news/get-started-with-mongodb-atlas/)。 获得你的数据库的连接字符串,并将其添加到你的`.env`文件中,作为`MONGO_URI=""`,连接字符串要在引号之间。对于数据库的名称,使用`oneHundredDays`。 @@ -279,7 +279,7 @@ export const connectDatabase = async () => { } ``` -现在,在你的 `index.ts` 文件中,用 `import { connectDatabase } from "./database/connectDatabase"` 导入这个函数,并在你的IIFE中添加 `await connectDatabase()`,就在 `.login()` 方法之前。继续并再次运行 `npm run build`。 +现在,在你的 `index.ts` 文件中,用 `import { connectDatabase } from "./database/connectDatabase"` 导入这个函数,并在你的 IIFE 中添加 `await connectDatabase()`,就在 `.login()` 方法之前。继续并再次运行 `npm run build`。 ![image-157](https://www.freecodecamp.org/news/content/images/2021/06/image-157.png) @@ -290,7 +290,7 @@ export const connectDatabase = async () => { 环境变量的问题是,它们都可能是 `undefined`。如果你在环境变量名称中打错了字,或者把名称和其他名称混在一起,就会经常发生这种情况(我在写这个教程时犯了一个错误,在一些地方用`TOKEN`而不是`BOT_TOKEN`)。 -TypeScript警告你,`connect` 方法需要一个字符串,而 `undefined` 值会破坏事情。你可以解决这个问题,但首先你要写一个函数来处理验证你的环境变量。 +TypeScript 警告你,`connect` 方法需要一个字符串,而 `undefined` 值会破坏事情。你可以解决这个问题,但首先你要写一个函数来处理验证你的环境变量。 在你的 `src` 目录下,创建一个 `utils` 目录,包含你的实用函数。在那里添加一个 `validateEnv.ts` 文件。 @@ -314,7 +314,7 @@ export const validateEnv = () => { ``` -回到你的 `index.ts` 文件,用 `import { validateEnv } from "./utils/validateEnv"` 导入这个验证函数。然后在你的IIFE的开头,使用一个if语句,如果函数返回false,就提前返回。你的 `index.ts` 应该看起来像: +回到你的 `index.ts` 文件,用 `import { validateEnv } from "./utils/validateEnv"` 导入这个验证函数。然后在你的 IIFE 的开头,使用一个 if 语句,如果函数返回 false,就提前返回。你的 `index.ts` 应该看起来像: ```ts import { Client } from "discord.js"; @@ -334,17 +334,17 @@ import { validateEnv } from "./utils/validateEnv"; })(); ``` -如果你再次尝试 `npm run build`,你会看到和之前一样的错误信息。这是因为虽然我们知道环境变量存在,但TypeScript仍然无法推断出它。验证函数被设置为在环境变量丢失时退出进程,所以我们要告诉TypeScript它肯定是一个字符串。 +如果你再次尝试 `npm run build`,你会看到和之前一样的错误信息。这是因为虽然我们知道环境变量存在,但 TypeScript 仍然无法推断出它。验证函数被设置为在环境变量丢失时退出进程,所以我们要告诉 TypeScript 它肯定是一个字符串。 回到你的 `connectDatabase.ts` 文件中,在 `connect` 函数中使用 `process.env.MONGO_URI as string`来强迫类型为 `string`。错误应该消失了,你现在可以运行 `npm run build` 和 `npm start`。 -你应该看到你为Discord和MongoDB连接写的信息在终端打印出来。 +你应该看到你为 Discord 和 MongoDB 连接写的信息在终端打印出来。 ## 交互事件 虽然你的机器人取得了很大的进展,但它仍然 _没有_ 做任何事情。为了接收命令,你将需要创建另一个事件监听器。 -Discord推出了 `slash` 命令,具有一个新的用户界面和一个新的网关事件。当有人用你的机器人使用 `slash`命令时,`interactionCreate` 事件被触发。这是你想要监听的事件。因为逻辑比 `ready`事件更复杂,你将需要创建一个单独的文件。 +Discord 推出了 `slash` 命令,具有一个新的用户界面和一个新的网关事件。当有人用你的机器人使用 `slash`命令时,`interactionCreate` 事件被触发。这是你想要监听的事件。因为逻辑比 `ready`事件更复杂,你将需要创建一个单独的文件。 在你的 `src` 目录下,创建一个 `events` 目录,并在其中创建 `onInteraction.ts` 文件。首先定义一个导出的函数 `onInteraction`。这应该是一个异步函数,有一个名为 `interaction` 的单一参数。 @@ -366,7 +366,7 @@ export const onInteraction = async (interaction: Interaction) => { `interaction`事件实际上是在任何命令交互上触发的,这包括像按钮点击和选择菜单,以及我们想要的 `slash` 命令。 -因为你将只为这个机器人编写 `slash` 命令,你可以过滤掉任何其他的交互类型,帮助TypeScript理解你正在处理的数据。 +因为你将只为这个机器人编写 `slash` 命令,你可以过滤掉任何其他的交互类型,帮助 TypeScript 理解你正在处理的数据。 在你的新函数中,添加一个条件来检查`interaction.isCommand()`。稍后你将在这个块中编写逻辑。 @@ -390,17 +390,17 @@ export const onInteraction = async (interaction: Interaction) => { 记住,你将需要导入你的 `onInteraction` 函数。 -很好! 你可以运行 `npm run build` 来确认TypeScript没有抛出任何错误,但如果没有实际的命令来使用,你还不能完全测试这段代码。 +很好! 你可以运行 `npm run build` 来确认 TypeScript 没有抛出任何错误,但如果没有实际的命令来使用,你还不能完全测试这段代码。 ## 准备命令 -我维护了一些Discord机器人,我发现有一件事有助于保持代码的可维护性和可读性,那就是使组件模块化。 +我维护了一些 Discord 机器人,我发现有一件事有助于保持代码的可维护性和可读性,那就是使组件模块化。 ### 定义一个接口 你将首先需要为你的命令定义一个共同的结构。在`src`中创建一个`interfaces`文件夹。然后在`interfaces`中创建一个名为`Command.ts`的文件。 -现在你要创建一个接口。在TypeScript中,接口经常被用来定义对象的结构,也是众多用于声明变量类型的工具之一。 +现在你要创建一个接口。在 TypeScript 中,接口经常被用来定义对象的结构,也是众多用于声明变量类型的工具之一。 在你的`Command.ts`文件中,创建一个名为`Command`的导出接口。: @@ -410,7 +410,7 @@ export interface Command { } ``` -你的接口将有两个属性 - `data`,它将保存要发送给Discord的命令数据,以及 `run`,它将保存回调函数和命令逻辑。 +你的接口将有两个属性 - `data`,它将保存要发送给 Discord 的命令数据,以及 `run`,它将保存回调函数和命令逻辑。 对于 `data` 属性,从 `@discordjs/builders` 导入 `SlashCommandBuilder` 和`SlashCommandSubcommandsOnlyBuilder`。将 `data` 属性定义为这两种类型中的一种。 @@ -455,7 +455,7 @@ for (const Command of CommandList) { } ``` -从Discord收到的交互 payload (有效载荷)包括一个 `commandName` 属性,你可以用它来查找用户选择的命令。要检查这一点,将 `interaction.commandName` 与 `Command.data.name` 属性进行比较。 +从 Discord 收到的交互 payload (有效载荷)包括一个 `commandName` 属性,你可以用它来查找用户选择的命令。要检查这一点,将 `interaction.commandName` 与 `Command.data.name` 属性进行比较。 ```ts if (interaction.commandName === Command.data.name) { @@ -487,15 +487,15 @@ export const onInteraction = async (interaction: Interaction) => { ## 数据库模型 -在你准备开始编写命令之前,还有一个步骤。这个机器人将跟踪你的社区成员的100天代码的进展。而你需要将该进度存储在数据库中。 +在你准备开始编写命令之前,还有一个步骤。这个机器人将跟踪你的社区成员的 100 天代码的进展。而你需要将该进度存储在数据库中。 -`mongoose` 可以帮助你结构化你的MongoDB记录,以防止你将畸形或不完整的数据传入数据库。 +`mongoose` 可以帮助你结构化你的 MongoDB 记录,以防止你将畸形或不完整的数据传入数据库。 首先,在你的 `database` 目录下创建一个 `models` 文件夹。在这个 `models` 文件夹中,创建一个 `CamperModel.ts` 文件。这将是你的用户对象的结构。 你首先需要从 `mongoose` 库中导入必要的值。在文件的顶部添加 `import { Document, model, Schema } from "mongoose";`。 -因为你正在使用TypeScript,你需要为你的数据库对象创建一个类型定义。创建另一个接口,就像你为你的命令所做的那样,名为`CamperInt`。 +因为你正在使用 TypeScript,你需要为你的数据库对象创建一个类型定义。创建另一个接口,就像你为你的命令所做的那样,名为`CamperInt`。 ```ts export interface CamperInt extends Document { @@ -503,16 +503,16 @@ export interface CamperInt extends Document { } ``` -`extends` 关键字告诉TypeScript我们要在 `Document` 类型的基础上添加属性。 +`extends` 关键字告诉 TypeScript 我们要在 `Document` 类型的基础上添加属性。 你的数据库模型将有四个属性。把这些添加到你的接口中: -- `discordId: string;` - Discord中的每个用户对象都有一个唯一的标识符,称为Snowflake,用于区分他们与其他用户。与用户名或判别符(用户名后的四位数)不同,`id`值不能被改变。这使得它成为将你的存储数据与 Discord 用户联系起来的理想值。 -- `round: number;` - 这将代表用户在挑战中所处的 "回合"。当某人完成了100天的挑战,他们可以选择再次进行挑战。当他们这样做的时候,他们通常称其为 "第二轮",例如。 +- `discordId: string;` - Discord 中的每个用户对象都有一个唯一的标识符,称为 Snowflake,用于区分他们与其他用户。与用户名或判别符(用户名后的四位数)不同,`id`值不能被改变。这使得它成为将你的存储数据与 Discord 用户联系起来的理想值。 +- `round: number;` - 这将代表用户在挑战中所处的 "回合"。当某人完成了 100 天的挑战,他们可以选择再次进行挑战。当他们这样做的时候,他们通常称其为 "第二轮",例如。 - `day: number;` - 这代表用户在挑战中的日期。 -- `timestamp: number;` - 你将使用这个值来跟踪用户最后一次提交100天代码帖子的时间。 +- `timestamp: number;` - 你将使用这个值来跟踪用户最后一次提交 100 天代码帖子的时间。 -很好! 现在你需要为你的数据库条目定义模式。`mongoose` 使用一个Schema对象来定义进入数据库集合的文件的形状。`Schema` 导入有一个构造函数,你将把它分配给一个变量。 +很好! 现在你需要为你的数据库条目定义模式。`mongoose` 使用一个 Schema 对象来定义进入数据库集合的文件的形状。`Schema` 导入有一个构造函数,你将把它分配给一个变量。 ```ts export const Camper = new Schema(); @@ -529,21 +529,21 @@ export const Camper = new Schema({ }) ``` -注意,我们使用的是`String`而不是`string`。`String`是指JavaScript的原始类型,而`string`是TypeScript的类型定义。 +注意,我们使用的是`String`而不是`string`。`String`是指 JavaScript 的原始类型,而`string`是 TypeScript 的类型定义。 -接下来你需要创建`model`。在 `mongoose` 中,`model` 对象的作用是在MongoDB数据库中创建、读取和更新你的文档。在你文件的底部添加 `export default model();`。 +接下来你需要创建`model`。在 `mongoose` 中,`model` 对象的作用是在 MongoDB 数据库中创建、读取和更新你的文档。在你文件的底部添加 `export default model();`。 `model` 函数需要几个参数。第一个是一个字符串,是你数据库中的文档(documents)的名称。对于这个集合(collection),使用 `"camper"`。第二个参数是用于数据的模式(schema)--使用你的 `Camper` 模式(schema)。 默认情况下,`mongoose` 将使用你的 `model` 名称的复数版本作为集合。在我们的例子中,这将是 "campers"。如果你想改变它,你可以传入第三个参数 `{集合: "name" }` 来设置集合为 `name`。 -如果你使用的是 JavaScript,这就足以让你的数据库模型设置好。然而,由于你使用的是TypeScript,你应该利用类型安全的优势。`model()` 默认返回一个 `Document` 类型的 `any`。 +如果你使用的是 JavaScript,这就足以让你的数据库模型设置好。然而,由于你使用的是 TypeScript,你应该利用类型安全的优势。`model()` 默认返回一个 `Document` 类型的 `any`。 为了解决这个问题,你可以在 `model` 函数中传递一个泛型。从某种意义上说,泛型可以作为类型定义的变量。你需要为你的 `model` 设置泛型以使用你的接口。通过将 `model` 改为 `model`,来添加泛型。 这里只有一个步骤了。你的 `CamperInt` 接口只定义了你在 MongoDB 文档中设置的属性,但并不包括标准属性。 -将你的 `export interface CamperInt` 改为 `export interface CamperInt extends Document`。这告诉TypeScript,你的类型定义是现有 `Document` 类型定义的扩展--你基本上是在向该结构添加属性。 +将你的 `export interface CamperInt` 改为 `export interface CamperInt extends Document`。这告诉 TypeScript,你的类型定义是现有 `Document` 类型定义的扩展--你基本上是在向该结构添加属性。 你的最终文件应该看起来像这样: @@ -571,11 +571,11 @@ export default model("camper", Camper); ## 编写机器人命令 -你终于准备好开始编写一些命令了 由于这是一个100天代码机器人,你应该从创建100天代码更新的命令开始。 +你终于准备好开始编写一些命令了 由于这是一个 100 天代码机器人,你应该从创建 100 天代码更新的命令开始。 ### 100 Command -在你的 `commands` 文件夹中,创建一个 `oneHundred.ts` 文件。这将保存你的100天代码命令。用 `import { Command } from ".../interfaces/Command;`导入你的命令接口。 +在你的 `commands` 文件夹中,创建一个 `oneHundred.ts` 文件。这将保存你的 100 天代码命令。用 `import { Command } from ".../interfaces/Command;`导入你的命令接口。 现在声明一个导出的变量`oneHundred`,并赋予它`Command`类型: @@ -591,9 +591,9 @@ export const oneHundred: Command = { 首先从 `@discordjs/builders` 包中导入 `SlashCommandBuilder()`。然后,用 `new SlashCommandBuilder()` 在`data` 属性中构建一个新实例。你将在这里使用一些方法来传递你想要的信息到构建器中。 -`.setName()` 方法允许你设置斜线命令的名称。设置名称为 `"100"`。`setDescription()` 选项允许你在Discord的用户界面中显示命令的描述。将描述设为 `"Check in for the 100 Days of Code challenge"`。 +`.setName()` 方法允许你设置斜线命令的名称。设置名称为 `"100"`。`setDescription()` 选项允许你在 Discord 的用户界面中显示命令的描述。将描述设为 `"Check in for the 100 Days of Code challenge"`。 -Slash命令也可以接受 `option` 值。这些是用来接受用户的参数的,有各种类型。对于这个命令,你需要一个字符串选项,使用 `addStringOption()` 方法。选项方法需要一个回调函数,有一个 `option` 参数。 +Slash 命令也可以接受 `option` 值。这些是用来接受用户的参数的,有各种类型。对于这个命令,你需要一个字符串选项,使用 `addStringOption()` 方法。选项方法需要一个回调函数,有一个 `option` 参数。 然后你可以在 `option` 参数上使用连锁方法来配置参数的信息。使用 `.setName()` 方法给选项取名为`"message"`,使用`.setDescription()`方法给它取名为`"The message to go in your 100 Days of Code update"`。最后,使用`.setRequired()`方法将该选项设置为必填。 @@ -616,9 +616,9 @@ export const oneHundred: Command = { }; ``` -如果你在IDE中编码启用了智能提示,你可能已经注意到,这将在 `data` 属性上抛出一个类型错误(type error)。 这是因为 `SlashCommandBuilder` 实际上返回了一个 `Omit` 类型! `Omit` 类型是用来告诉TypeScript,该属性是由TypeScript中定义的。_almost_ 是另一个类型几乎相同,但删除了特定属性。 +如果你在 IDE 中编码启用了智能提示,你可能已经注意到,这将在 `data` 属性上抛出一个类型错误(type error)。 这是因为 `SlashCommandBuilder` 实际上返回了一个 `Omit` 类型! `Omit` 类型是用来告诉 TypeScript,该属性是由 TypeScript 中定义的。_almost_ 是另一个类型几乎相同,但删除了特定属性。 -前往你的 `interfaces/Command.ts` 文件,更新类型。用 `Omit` 替换 `SlashCommandBuilder` 类型。这将告诉TypeScript,`data` 应该是一个`SlashCommandBuilder`,但没有那两个特定的属性。 +前往你的 `interfaces/Command.ts` 文件,更新类型。用 `Omit` 替换 `SlashCommandBuilder` 类型。这将告诉 TypeScript,`data` 应该是一个`SlashCommandBuilder`,但没有那两个特定的属性。 ```ts import { @@ -637,13 +637,13 @@ export interface Command { 很好! 现在你的类型错误已经解决了,回到你的 `oneHundred.ts` 命令文件--是时候编写命令逻辑了。 -你的机器人响应命令的所有逻辑将被放在 `run` 属性中。就像你在界面中做的那样,首先创建一个接受 `interaction` 参数的async函数。然后,让你的函数的第一行是 `await interaction.deferReply();`。 +你的机器人响应命令的所有逻辑将被放在 `run` 属性中。就像你在界面中做的那样,首先创建一个接受 `interaction` 参数的 async 函数。然后,让你的函数的第一行是 `await interaction.deferReply();`。 -Discord期望机器人在三秒内对一个命令做出反应。因为这个命令可能需要更长的时间来处理,使用 `.deferReply()` 方法会发送一个确认响应,让你有整整15分钟的时间来发送实际响应。 +Discord 期望机器人在三秒内对一个命令做出反应。因为这个命令可能需要更长的时间来处理,使用 `.deferReply()` 方法会发送一个确认响应,让你有整整 15 分钟的时间来发送实际响应。 -接下来,你需要从该命令中提取一些数据。首先,用 `const { user } = interaction;`将 `user` 对象从交互的有效载荷中解构出来。`user` 对象代表调用该命令的Discord用户。 +接下来,你需要从该命令中提取一些数据。首先,用 `const { user } = interaction;`将 `user` 对象从交互的有效载荷中解构出来。`user` 对象代表调用该命令的 Discord 用户。 -然后用 `const text = interaction.options.getString("message", true);` 获得你发送的 `message` 选项。通过这一行,你正在访问交互的 `options` 属性。`.getString()` 方法专门抓取一个字符串选项(记得你在 `data` 中创建了这个选项),`"message"`是这个选项的**name**。`true`参数表示这是一个必选项,所以TypeScript不会认为它是空的。 +然后用 `const text = interaction.options.getString("message", true);` 获得你发送的 `message` 选项。通过这一行,你正在访问交互的 `options` 属性。`.getString()` 方法专门抓取一个字符串选项(记得你在 `data` 中创建了这个选项),`"message"`是这个选项的**name**。`true`参数表示这是一个必选项,所以 TypeScript 不会认为它是空的。 你的文件应该看起来像这样: @@ -702,7 +702,7 @@ export const getCamperData = async (id: string) => { }; ``` -在这里,我们在第一轮第0天开始一个新的 `camper`,如果他们使用 `100 command`,这允许我们更新他们的状态。 +在这里,我们在第一轮第 0 天开始一个新的 `camper`,如果他们使用 `100 command`,这允许我们更新他们的状态。 最后,你需要 `返回(return)`你的数据。在函数的末尾添加 `return camperData`。为了额外的类型安全,将你的函数的返回类型定义为 `Promise`。 @@ -736,7 +736,7 @@ export const updateCamperData = async (Camper: CamperInt) => { 你唯一要更新数据的时候是在 `/100` 命令中--在那里你要增加营员的日计数,检查他们是否开始了新的一轮(round),并更新时间戳。 -首先,用 `Camper.day++;` 来增加日计数。根据100天代码挑战的工作方式,如果 `camper` 已经过了第100天,那么他们就开始了新的 "一轮(round)"。你需要一个条件来检查 `Camper.day > 100`,如果是的话,就把日子重置为1,并增加一轮(round)。 +首先,用 `Camper.day++;` 来增加日计数。根据 100 天代码挑战的工作方式,如果 `camper` 已经过了第 100 天,那么他们就开始了新的 "一轮(round)"。你需要一个条件来检查 `Camper.day > 100`,如果是的话,就把日子重置为 1,并增加一轮(round)。 在这个条件之后,用 `Camper.timestamp = Date.now();` 更新时间戳,用 `await Camper.save();` 保存数据。最后,返回修改后的数据对象,这样你就可以在命令中使用它。 @@ -814,7 +814,7 @@ export const oneHundred: Command = { 现在你需要构建响应,以便在 `camper` 使用该命令时将其送回。 -为此,你将使用Discord的消息嵌入功能。首先从 discord.js 导入 `MessageEmbed` 构造函数,然后用 `const oneHundredEmbed = new MessageEmbed();` 创建一个新的嵌入。`MessageEmbed` 类有几个方法可以用来建立嵌入的内容。 +为此,你将使用 Discord 的消息嵌入功能。首先从 discord.js 导入 `MessageEmbed` 构造函数,然后用 `const oneHundredEmbed = new MessageEmbed();` 创建一个新的嵌入。`MessageEmbed` 类有几个方法可以用来建立嵌入的内容。 使用 `.setTitle()` 方法来设置嵌入的标题为`"100 Days of Code"`。 @@ -832,7 +832,7 @@ export const oneHundred: Command = { 使用`await interaction.editReply()`来编辑响应。`.editReply()`方法接收一个具有各种属性的对象,在这种情况下,你正在发送一个嵌入(embed)。传递一个对象,其 `embeds` 属性设置为`[oneHundredEmbed]`。 -注意,这是一个包含你的嵌入的数组。Discord消息最多可以包含10个嵌入物(embeds),API希望有一个嵌入对象(embed objects)的数组来匹配。 +注意,这是一个包含你的嵌入的数组。Discord 消息最多可以包含 10 个嵌入物(embeds),API 希望有一个嵌入对象(embed objects)的数组来匹配。 你的最终命令文件应该是这样的: @@ -883,7 +883,7 @@ export const oneHundred: Command = { ### Registering Commands(注册命令) -如果你运行`npm run build`和`npm start`,一切都会启动 - 但你没有办法实际使用你的新命令。这是因为Discord要求命令被注册,以便它们在应用程序的用户界面上可用。要做到这一点,我们需要采取几个步骤。 +如果你运行`npm run build`和`npm start`,一切都会启动 - 但你没有办法实际使用你的新命令。这是因为 Discord 要求命令被注册,以便它们在应用程序的用户界面上可用。要做到这一点,我们需要采取几个步骤。 首先,前往你的`_CommandList.ts`文件,导入你的`oneHundred`命令。把它添加到你的`CommandList`数组中,这样它就可以在其他地方使用。 @@ -894,9 +894,9 @@ import { oneHundred } from "./oneHundred"; export const CommandList: Command[] = [oneHundred]; ``` -现在是时候添加逻辑来发送命令信息给Discord了。在你的`src/events`目录下,添加一个`onReady.ts`文件。我们将在`"ready"`事件中使用它。 +现在是时候添加逻辑来发送命令信息给 Discord 了。在你的`src/events`目录下,添加一个`onReady.ts`文件。我们将在`"ready"`事件中使用它。 -创建一个名为`onReady`的出口异步函数,并给它一个名为`BOT`的参数。从discord.js导入`Client`类型,并将`BOT`类型定义设为`Client`。 +创建一个名为`onReady`的出口异步函数,并给它一个名为`BOT`的参数。从 discord.js 导入`Client`类型,并将`BOT`类型定义设为`Client`。 ```ts import { Client } from "discord.js"; @@ -904,11 +904,11 @@ import { Client } from "discord.js"; export const onReady = async (BOT: Client) => {}; ``` -现在从`@discordjs/rest`导入`REST`模块。这将允许你实例化一个API客户端,你将用它来发送命令。用`const rest = new REST();`构建一个新的实例。 +现在从`@discordjs/rest`导入`REST`模块。这将允许你实例化一个 API 客户端,你将用它来发送命令。用`const rest = new REST();`构建一个新的实例。 -你需要对你的REST客户端进行一些配置。首先,向`REST()`构造函数传递一个对象,并将`version`属性设置为`"9"`。这告诉客户端使用Discord的API版本9,这是目前最新的版本。 +你需要对你的 REST 客户端进行一些配置。首先,向`REST()`构造函数传递一个对象,并将`version`属性设置为`"9"`。这告诉客户端使用 Discord 的 API 版本 9,这是目前最新的版本。 -然后,在构造函数上链接一个`.setToken()`调用,将API token(口令)设置为`process.env.BOT_TOKEN`, 你必须将其强制为一个`字符串`。 +然后,在构造函数上链接一个`.setToken()`调用,将 API token(口令)设置为`process.env.BOT_TOKEN`, 你必须将其强制为一个`字符串`。 ```ts import { REST } from "@discordjs/rest"; @@ -921,7 +921,7 @@ export const onReady = async (BOT: Client) => { }; ``` -API希望命令数据以特定的JSON格式发送,但值得庆幸的是,我们使用的slash命令生成器有一个方法专门用于此。导入你的`CommandList`,然后创建一个新的数组并映射你的命令数据。 +API 希望命令数据以特定的 JSON 格式发送,但值得庆幸的是,我们使用的 slash 命令生成器有一个方法专门用于此。导入你的`CommandList`,然后创建一个新的数组并映射你的命令数据。 ```ts const commandData = CommandList.map((command) => command.data.toJSON()); @@ -929,15 +929,15 @@ const commandData = CommandList.map((command) => command.data.toJSON()); 在你向 Discord 发送命令之前,有必要注意有两种类型的命令。"全球命令(Global Commands)" 在你的机器人被使用的所有地方都可用,但需要一个小时左右的时间来更新。"公会命令(Guild Commands)"只在单个服务器中可用,但会立即更新。因为这个机器人被设计为在单个服务器中运行,所以我们要使用公会命令。 -你需要获得你使用该机器人的服务器的ID。要做到这一点,确保你在你的Discord应用程序中启用了开发者模式,然后右击你的服务器图标并选择 "Copy ID"。在你的 `.env` 文件中,添加一个 `GUILD_ID` 变量,并将你复制的ID分配给它。它应该看起来像 `GUILD_ID="778130114772598785"`。 +你需要获得你使用该机器人的服务器的 ID。要做到这一点,确保你在你的 Discord 应用程序中启用了开发者模式,然后右击你的服务器图标并选择 "Copy ID"。在你的 `.env` 文件中,添加一个 `GUILD_ID` 变量,并将你复制的 ID 分配给它。它应该看起来像 `GUILD_ID="778130114772598785"`。 -回到你的 `onReady.ts` 文件中,用 `await rest.put()` 开始你的API调用。发送一个 `PUT` 请求将更新任何现有的命令,而 `POST` 将试图创建新的命令,如果命令共享一个名字就会出错。从`discord-api-types/v9`导入`Routes`,并在`rest.put()` 调用中传递 `Routes.applicationGuildCommands()` 调用。这将被用来构建API端点以发送命令。 +回到你的 `onReady.ts` 文件中,用 `await rest.put()` 开始你的 API 调用。发送一个 `PUT` 请求将更新任何现有的命令,而 `POST` 将试图创建新的命令,如果命令共享一个名字就会出错。从`discord-api-types/v9`导入`Routes`,并在`rest.put()` 调用中传递 `Routes.applicationGuildCommands()` 调用。这将被用来构建 API 端点以发送命令。 调用 `applicationGuildCommands()` 时,将接受两个参数。 -首先是应用程序的ID,以便将这些命令与之联系起来。你可以从 `BOT.user.id` 的值中得到它, 但 `user` 有可能是未定义的,所以你需要选择性地把它连起来。使用 `BOT.user?.id || "missing id"` 来添加一个会出错的后备值--这将允许我们知道机器人的ID是否丢失。 +首先是应用程序的 ID,以便将这些命令与之联系起来。你可以从 `BOT.user.id` 的值中得到它, 但 `user` 有可能是未定义的,所以你需要选择性地把它连起来。使用 `BOT.user?.id || "missing id"` 来添加一个会出错的后备值--这将允许我们知道机器人的 ID 是否丢失。 -第二个参数是服务器ID,你把它设置为`process.env.GUILD_ID`(记得要强制使用这个类型!)。 +第二个参数是服务器 ID,你把它设置为`process.env.GUILD_ID`(记得要强制使用这个类型!)。 `.put()`调用也需要第二个参数,这是你要发送的数据。以`{ body: commandData }`的形式传递,以符合预期格式。 @@ -995,7 +995,7 @@ import { validateEnv } from "./utils/validateEnv"; })(); ``` -现在运行`npm run build`和`npm start`,并在Discord中连接你的服务器。如果你输入`/`,你应该看到你的新`/100`命令显示出来。尝试使用该命令并检查响应。 +现在运行`npm run build`和`npm start`,并在 Discord 中连接你的服务器。如果你输入`/`,你应该看到你的新`/100`命令显示出来。尝试使用该命令并检查响应。 ![image-122](https://www.freecodecamp.org/news/content/images/2022/01/image-122.png) @@ -1005,7 +1005,7 @@ import { validateEnv } from "./utils/validateEnv"; ### Edit Command -如果营员在他们的 `/100` 信息中出现了错误,会发生什么?因为机器人会发送回复,camper 无法编辑它(Discord不允许你编辑你没有发送的信息)。你应该创建一个命令,允许 camper 这样做。 +如果营员在他们的 `/100` 信息中出现了错误,会发生什么?因为机器人会发送回复,camper 无法编辑它(Discord 不允许你编辑你没有发送的信息)。你应该创建一个命令,允许 camper 这样做。 在你的 `src/commands` 目录下创建一个 `edit.ts` 文件。就像你对 `/100` 命令所做的那样,导入你的`SlashCommandBuilder` 和 `Command` 接口,并导出一个 `edit` 对象,其类型为 `Command`。 @@ -1047,7 +1047,7 @@ export const edit: Command = { } ``` -`channle`属性是nullable(例如,在通过DM发送互动的情况下),所以你要检查它是否存在。如果它不存在,则回应一个命令缺少参数的消息。 +`channle`属性是 nullable(例如,在通过 DM 发送互动的情况下),所以你要检查它是否存在。如果它不存在,则回应一个命令缺少参数的消息。 ```ts if (!channel) { @@ -1058,7 +1058,7 @@ export const edit: Command = { } ``` -现在你知道了这个 `channel` 的存在,你可以根据 `camper` 提供的ID来获取他们想要编辑的信息。使用`channel.messages.fetch()` 来做这件事,把 `targetId` 作为参数传入。 +现在你知道了这个 `channel` 的存在,你可以根据 `camper` 提供的 ID 来获取他们想要编辑的信息。使用`channel.messages.fetch()` 来做这件事,把 `targetId` 作为参数传入。 因为目标 `message` 有可能不存在,你需要在你的代码中考虑到这一点。添加一个条件来检查这一点,如果没有找到消息,则回应一个解释。 @@ -1163,7 +1163,7 @@ export const edit: Command = { 在你的命令目录下创建一个`view.ts`文件,并设置好你的命令变量。在`data`属性中创建一个命令,其名称为`view`,描述为 `Shows your latest 100 days of code check in`。这个命令不需要任何选项。 -在 `run` 属性中设置你的异步函数,并推迟交互响应。从交互中提取 `user` 对象。使用你的 `getCamperData` 模块从数据库中获取 `camper` 的数据。然后,检查数据的 `day` 属性是否有一个非零值。如果没有,让 `camper` 知道他们还没有开始100天的代码挑战,可以用 `/100` 命令来做。 +在 `run` 属性中设置你的异步函数,并推迟交互响应。从交互中提取 `user` 对象。使用你的 `getCamperData` 模块从数据库中获取 `camper` 的数据。然后,检查数据的 `day` 属性是否有一个非零值。如果没有,让 `camper` 知道他们还没有开始 100 天的代码挑战,可以用 `/100` 命令来做。 创建一个嵌入,标题设置为 `My 100DoC Progress`。将描述设置为`Here is my 100 Days of Code progress. I last reported an update on:`, 并添加 `camper` 的时间戳。添加一个 `Round` 和 `Day` 字段,并设置嵌入的作者。然后在交互响应中发送嵌入的内容。 @@ -1221,7 +1221,7 @@ export const view: Command = { 在 `command` 目录下创建你的 `help.ts` 文件,并创建你的 `data` 属性。给该命令起名为 `help`,并说明`Provides information on using this bot(提供关于使用该机器人的信息)`。 -用async函数设置你的`run`属性,并记住推迟回复的时间。创建一个嵌入,并使用描述和字段来提供你想与 `camper` 分享的信息。在交互响应中发送嵌入的信息。 +用 async 函数设置你的`run`属性,并记住推迟回复的时间。创建一个嵌入,并使用描述和字段来提供你想与 `camper` 分享的信息。在交互响应中发送嵌入的信息。 将你的新帮助命令加载到 `CommandList` 中,并建立启动你的机器人来测试它。你应该看到一个带有你创建的嵌入的响应。 diff --git a/chinese/articles/build-a-full-stack-application-with-nextjs.md b/chinese/articles/build-a-full-stack-application-with-nextjs.md index b4d031467..caff9d2ef 100644 --- a/chinese/articles/build-a-full-stack-application-with-nextjs.md +++ b/chinese/articles/build-a-full-stack-application-with-nextjs.md @@ -11,7 +11,7 @@ In this tutorial, I will take you through the basics of Next.js and guide you in So let's jump right in and unlock the power of Next.js together. -## Here's what we'll cover: +## Here's what we'll cover - [What Are We Going to Build?](#what-are-we-going-to-build) - [Getting Started](#getting-started) diff --git a/chinese/articles/build-a-logging-web-app-with-server-sent-events-rxjs-and-express.md b/chinese/articles/build-a-logging-web-app-with-server-sent-events-rxjs-and-express.md index 71a84fd25..4af7007b3 100644 --- a/chinese/articles/build-a-logging-web-app-with-server-sent-events-rxjs-and-express.md +++ b/chinese/articles/build-a-logging-web-app-with-server-sent-events-rxjs-and-express.md @@ -7,25 +7,25 @@ 假设你正在研究你的新的伟大想法--一个网络或移动应用程序,以及一个后端服务器。到目前为止,没有什么太复杂的东西。直到你意识到你需要将数据从你的服务器流向这些客户端。 -通常,在处理这个问题时,首先想到的是使用社区里的一个很酷的工具,如WebSockets、SocketIO,甚至是一个付费服务,为你处理这个问题。 +通常,在处理这个问题时,首先想到的是使用社区里的一个很酷的工具,如 WebSockets、SocketIO,甚至是一个付费服务,为你处理这个问题。 -但是,还有一种方法通常被遗漏了,你可能还没有听说过它。它叫做SSE,是服务器发送事件的简称。 +但是,还有一种方法通常被遗漏了,你可能还没有听说过它。它叫做 SSE,是服务器发送事件的简称。 -SSE在我心中有一个特殊的位置,因为它很简单。它是轻量级的,高效的,而且非常强大。 +SSE 在我心中有一个特殊的位置,因为它很简单。它是轻量级的,高效的,而且非常强大。 -为了详细解释SSE以及我如何使用它,我将介绍我的一个小项目,我认为这是SSE的一个很好的展示。我将使用Typescript、Express和RxJS,所以请准备好你的环境并系好安全带,因为我们即将进入一些代码 +为了详细解释 SSE 以及我如何使用它,我将介绍我的一个小项目,我认为这是 SSE 的一个很好的展示。我将使用 Typescript、Express 和 RxJS,所以请准备好你的环境并系好安全带,因为我们即将进入一些代码 -在我们开始之前,你应该知道一些关于SSE的事情。顾名思义,服务器发送的事件是单向的,从你的服务器到客户端。如果你的客户需要把数据流回给服务器,这可能是个大问题。但在很多情况下不是这样的,我们可以直接依靠REST来向服务器发送数据。 +在我们开始之前,你应该知道一些关于 SSE 的事情。顾名思义,服务器发送的事件是单向的,从你的服务器到客户端。如果你的客户需要把数据流回给服务器,这可能是个大问题。但在很多情况下不是这样的,我们可以直接依靠 REST 来向服务器发送数据。 ## 项目是怎样的? -这个项目的想法很简单。我有一堆脚本在树莓派上运行,还有部署在Digital Ocean上的,以及其他一些我不容易访问的地方。因此,我希望有一种方法可以打印出日志,并从任何地方查看它们。 +这个项目的想法很简单。我有一堆脚本在树莓派上运行,还有部署在 Digital Ocean 上的,以及其他一些我不容易访问的地方。因此,我希望有一种方法可以打印出日志,并从任何地方查看它们。 作为一个解决方案,我希望有一个基本的网络应用程序来推送我的日志,并有一个直接链接到我的会话,我可以在任何设备上打开,甚至与其他人分享。 在我们开始之前,有几件事情需要记住。 -首先,来自我的脚本的日志不是那么频繁,而且对于我的使用情况来说,使用HTTP的开销可以忽略不计。正因为如此,我决定通过一个基本的REST API发布我的日志,并在客户端使用SSE来订阅传入的日志。 +首先,来自我的脚本的日志不是那么频繁,而且对于我的使用情况来说,使用 HTTP 的开销可以忽略不计。正因为如此,我决定通过一个基本的 REST API 发布我的日志,并在客户端使用 SSE 来订阅传入的日志。 ![Frame-8-1](https://www.freecodecamp.org/news/content/images/2022/02/Frame-8-1.png) @@ -43,7 +43,7 @@ SSE在我心中有一个特殊的位置,因为它很简单。它是轻量级 如果我们把我们的后端服务器看作是一个管道,在一端我们有一系列的发布者--在我们的例子中,是发布日志的脚本。在另一端,我们有一些客户在订阅这些日志。 -为了连接这两端,我将使用RxJS。它将允许我通过REST发布来自发布者的任何信息,然后订阅这些事件并通过SSE将消息转发给客户端 +为了连接这两端,我将使用 RxJS。它将允许我通过 REST 发布来自发布者的任何信息,然后订阅这些事件并通过 SSE 将消息转发给客户端 为了开始,让我们定义我们的日志接口。为了保持简单,我将只定义一个内容字段,用来保存我们的日志信息。 @@ -53,11 +53,11 @@ interface Log { } ``` -### 如何设置RxJS +### 如何设置 RxJS -让我们导入RxJS,为我们的日志创建一个新的Subject,并定义一个函数来发布我们的日志到这个Subject。 +让我们导入 RxJS,为我们的日志创建一个新的 Subject,并定义一个函数来发布我们的日志到这个 Subject。 -当然,我们可以导出我们的Subject并直接从我们的路由中调用它,但我更喜欢抽象实现,只向我的代码的其他部分提供函数调用。 +当然,我们可以导出我们的 Subject 并直接从我们的路由中调用它,但我更喜欢抽象实现,只向我的代码的其他部分提供函数调用。 ```ts import { Subject } from 'rxjs'; @@ -74,7 +74,7 @@ export function emitNewLog(log: Log): void { } ``` -最后,让我们在我们的Express服务器上定义一个新的路由,它将接受来自客户端的新日志,并将它们发布到我们刚刚创建的emitNewLog方法。 +最后,让我们在我们的 Express 服务器上定义一个新的路由,它将接受来自客户端的新日志,并将它们发布到我们刚刚创建的 emitNewLog 方法。 ```ts app.post('/', (req: Request, res: Response) => { @@ -85,15 +85,15 @@ app.post('/', (req: Request, res: Response) => { }); ``` -我们现在已经完成了发布方面的工作。剩下的就是定义我们的SSE路由,订阅RxJS Subject,并将日志交付给我们的客户端。 +我们现在已经完成了发布方面的工作。剩下的就是定义我们的 SSE 路由,订阅 RxJS Subject,并将日志交付给我们的客户端。 -### 如何设置SSE路由 +### 如何设置 SSE 路由 -让我们为我们的SSE连接定义一个新的路由。为了启用SSE,我们需要将一些头信息(headers)返回给我们的客户。 +让我们为我们的 SSE 连接定义一个新的路由。为了启用 SSE,我们需要将一些头信息(headers)返回给我们的客户。 -我们希望**'Connection'**设置为**'keep-alive'**,**'Cache-Control'**设置为'**no-cache**',并且**'Content-Type'**设置为**'text/event-stream'**。这样,我们的客户就会明白这是一个SSE路由。 +我们希望**'Connection'**设置为**'keep-alive'**,**'Cache-Control'**设置为'**no-cache**',并且**'Content-Type'**设置为**'text/event-stream'**。这样,我们的客户就会明白这是一个 SSE 路由。 -此外,我还为CORS添加了**'Access-Control-Allow-Origin'**,并将**'X-Accel-Buffering'**设置为**'no'**,以防止[Nginx](https://www.nginx.com/)干扰这个路由。最后,我们可以将头信息冲回给我们的客户端,以启动事件流。 +此外,我还为 CORS 添加了**'Access-Control-Allow-Origin'**,并将**'X-Accel-Buffering'**设置为**'no'**,以防止[Nginx](https://www.nginx.com/)干扰这个路由。最后,我们可以将头信息冲回给我们的客户端,以启动事件流。 ```ts app.get('/', (req: Request, res: Response) => { @@ -108,7 +108,7 @@ app.get('/', (req: Request, res: Response) => { 我们现在可以通过在我们的响应(response)中写一些东西来开始流式数据(streaming data)。 -SSE提供了一个基于文本的协议,我们可以用它来帮助我们的客户区分事件的类型。我们的每一个事件都应该看起来像下面这样。 +SSE 提供了一个基于文本的协议,我们可以用它来帮助我们的客户区分事件的类型。我们的每一个事件都应该看起来像下面这样。 ```ts event: ${event name}\n @@ -129,7 +129,7 @@ function serializeEvent(event: string, data: any): string { } ``` -我们现在可以订阅我们先前创建的RxJS Subject,序列化每个新的日志,并将其作为一个**NEW/_LOG**事件写入我们的连接。 +我们现在可以订阅我们先前创建的 RxJS Subject,序列化每个新的日志,并将其作为一个**NEW/_LOG**事件写入我们的连接。 ```ts app.get('/', (req: Request, res: Response) => { @@ -147,7 +147,7 @@ app.get('/', (req: Request, res: Response) => { } ``` -最后,我们必须确保在SSE连接关闭时取消对观察者的订阅。把所有这些放在一起,我们应该有这样的东西。 +最后,我们必须确保在 SSE 连接关闭时取消对观察者的订阅。把所有这些放在一起,我们应该有这样的东西。 ```ts app.get('/', (req: Request, res: Response) => { @@ -172,7 +172,7 @@ app.get('/', (req: Request, res: Response) => { ## 编写客户端代码 -在浏览器上订阅我们的SSE路由是非常简单的。首先,让我们移动到我们的客户端代码,创建一个**EventSource**接口的新实例,并将我们的端点传递给构造函数。 +在浏览器上订阅我们的 SSE 路由是非常简单的。首先,让我们移动到我们的客户端代码,创建一个**EventSource**接口的新实例,并将我们的端点传递给构造函数。 ```js const eventSource = new EventSource("/"); @@ -199,9 +199,9 @@ eventSource.close(); 正如你所看到的,服务器发送事件使得从服务器到客户端的内容流变得非常容易。它们特别有帮助,因为我们在大多数现代浏览器中得到了一个内置的接口,对于那些不提供接口的浏览器,我们可以很容易地进行降级处理。 -此外,在客户端与服务器失去连接的情况下,SSE自动为我们处理重新连接。因此,它是SocketIO和WebSockets的有效替代方案,在各种情况下,我们需要从服务器获得单向的事件流。 +此外,在客户端与服务器失去连接的情况下,SSE 自动为我们处理重新连接。因此,它是 SocketIO 和 WebSockets 的有效替代方案,在各种情况下,我们需要从服务器获得单向的事件流。 -如果你对这个项目进一步感兴趣,我在刚才的代码中增加了一些额外的功能,还有一个web GUI,你可以在这里查看。[LogSnag Console](https://logsnag.com/console)。 +如果你对这个项目进一步感兴趣,我在刚才的代码中增加了一些额外的功能,还有一个 web GUI,你可以在这里查看。[LogSnag Console](https://logsnag.com/console)。 ![Frame-9-1](https://www.freecodecamp.org/news/content/images/2022/02/Frame-9-1.png) diff --git a/chinese/articles/build-portfolio-website-react.md b/chinese/articles/build-portfolio-website-react.md index 4702f2342..87f649752 100644 --- a/chinese/articles/build-portfolio-website-react.md +++ b/chinese/articles/build-portfolio-website-react.md @@ -7,9 +7,9 @@ 今天,你将为自己创建一个最重要的应用程序,你的作品集。 -每个React开发者或者Web开发者通常需要向潜在的客户或者雇主,展示他们能做什么。 +每个 React 开发者或者 Web 开发者通常需要向潜在的客户或者雇主,展示他们能做什么。 -这是我们现在要做的,在一些行业标准工具帮助下,包括React,Tailwind CSS,和Netlify。 +这是我们现在要做的,在一些行业标准工具帮助下,包括 React,Tailwind CSS,和 Netlify。 开始吧! @@ -23,9 +23,9 @@ ## 我们将使用什么工具? -- 我们将用React来创建应用程序的用户界面。它将允许我们通过可重复使用的组件来组成登录页面的每一部分,和添加我们想要的功能,如果博客。 -- 为了设计我们的应用程序, 我们将使用Tailwind CSS。为了给我们的应用程序一个专业的外观,Tailwind允许我们通过在React元素上组合类名(classnames)。 -- 为了把我们的应用程序部署收到网络上,我们将使用免费的Netlify。通过CDN的帮助下,用户可以通过我们自己的的域名,快速访问到我们的项目。 +- 我们将用 React 来创建应用程序的用户界面。它将允许我们通过可重复使用的组件来组成登录页面的每一部分,和添加我们想要的功能,如果博客。 +- 为了设计我们的应用程序, 我们将使用 Tailwind CSS。为了给我们的应用程序一个专业的外观,Tailwind 允许我们通过在 React 元素上组合类名(classnames)。 +- 为了把我们的应用程序部署收到网络上,我们将使用免费的 Netlify。通过 CDN 的帮助下,用户可以通过我们自己的的域名,快速访问到我们的项目。 ## 怎样开始 @@ -43,21 +43,21 @@ npm install 创建我们的应用程序从开始到部署,需要以下的条件。 -1. 你的电脑安装Node.js。 你可以在nodejs.org下载安装程序。 -2. 在你的电脑安装Git,你可以在git-scm.com下载。 -3. 我建议使用VS Code作为你的代码编辑器。你可以在code.visualstudio.com下载它。 -4. 一个在Netlify.com上的免费的Netlify账户。 -5. 一个免费的 GitHub账号。 +1. 你的电脑安装 Node.js。 你可以在 nodejs.org 下载安装程序。 +2. 在你的电脑安装 Git,你可以在 git-scm.com 下载。 +3. 我建议使用 VS Code 作为你的代码编辑器。你可以在 code.visualstudio.com 下载它。 +4. 一个在 Netlify.com 上的免费的 Netlify 账户。 +5. 一个免费的 GitHub 账号。 ## 如何建立作品集的结构 -使用React的好处是,我们可以将我们的应用程序扩展到任意多的页面,并添加大量的内容,这是非常容易的。 +使用 React 的好处是,我们可以将我们的应用程序扩展到任意多的页面,并添加大量的内容,这是非常容易的。 在我们只是在处理一个页面,我们可以在我们的应用程序组件中非常迅速找到需要的不同组件。我们将在顶部有一个导航栏,上面有所有的链接,可以跳转到我们作品集的不同部分。 在此之后,我们将包含一个部分,这是关于我们的项目,推荐书,最后是我们的联系表格。 -这种快速的规划使我们能够弄清楚我们的组件怎样命名,以什么顺序命名。下一步,把它们全部添加到我们的App.js文件中(在src文件夹): +这种快速的规划使我们能够弄清楚我们的组件怎样命名,以什么顺序命名。下一步,把它们全部添加到我们的 App.js 文件中(在 src 文件夹): ```js // src/App.js @@ -107,7 +107,7 @@ my-portfolio └── Testimonials.js ``` - 然后我们将创建每个React组件的基本结构,并从该文件导出,通过使用`export default`: + 然后我们将创建每个 React 组件的基本结构,并从该文件导出,通过使用`export default`: ```js // src/components/About.js @@ -117,7 +117,7 @@ export default function About() {} // repeat the same basic structure for all 6 components (在所有的6个组件中重复相同的结构) ``` -最后在App.js中导入它: +最后在 App.js 中导入它: ```js // src/App.js @@ -144,13 +144,13 @@ export default function App() { } ``` -_请注意,总共应该有个6个组件_ +_请注意,总共应该有个 6 个组件_ -## Tailwind CSS介绍 +## Tailwind CSS 介绍 -做完上面的,我们可以开始使用Tailwind CSS,给我们的应用程序一个基本的外观。 +做完上面的,我们可以开始使用 Tailwind CSS,给我们的应用程序一个基本的外观。 -使用Tailwind CSS的好处,我们不必在CSS样式中手工编写任何样式。我们所做的就是组合多个类(class)来创建我们想要的外观。 +使用 Tailwind CSS 的好处,我们不必在 CSS 样式中手工编写任何样式。我们所做的就是组合多个类(class)来创建我们想要的外观。 ```js // src/App.js @@ -231,13 +231,13 @@ export default function About() { } ``` -对于本节右侧的图片,我使用的`public`文件夹中的一个svg文件(coding.svg)。 +对于本节右侧的图片,我使用的`public`文件夹中的一个 svg 文件(coding.svg)。 这个图片只是作为一个临时的占位符,我强烈建议使用你自己的图片。 -## 如何构建`projects`组件。 +## 如何构建`projects`组件 -我们的项目部分是由一个`section`元素组成,id为`prpjects`。这将是包含所有项目的图片组成的画廊。 +我们的项目部分是由一个`section`元素组成,id 为`prpjects`。这将是包含所有项目的图片组成的画廊。 ```js // src/components/Projects.js @@ -292,7 +292,7 @@ export default function Projects() { } ``` -注意,我们还将使用库`@heroicons/react`,以便将SVG图片写成React组件。 +注意,我们还将使用库`@heroicons/react`,以便将 SVG 图片写成 React 组件。 我们从同一个文件夹中的`data.js`文件导入一个项目数组。在那里,我们导出一个对象数组,每个对象包含项目的数据。 @@ -341,7 +341,7 @@ export const projects = [ 这将包含一个简单的清单,列出在我们的雇主或客户的项目中,使用的主要工具。 -再一次,我们将从`data`文件夹导入一个数组。但是这个数组是由字符串组成,是我们所知道的技能,如JavaScript,React和Node。 +再一次,我们将从`data`文件夹导入一个数组。但是这个数组是由字符串组成,是我们所知道的技能,如 JavaScript,React 和 Node。 ```js // src/components/Skills.js @@ -440,9 +440,9 @@ export default function Testimonials() { 在登录页的尾部,我们将加入我们的联系表格,以便潜在的雇主能联系到我们。 -这个表格包含3个输入:姓名、电子邮件和输入信息。 +这个表格包含 3 个输入:姓名、电子邮件和输入信息。 -为了接收这些表格所提交的信息,我们将使用Netlify表格工具,非常容易保存处理这些信息。 +为了接收这些表格所提交的信息,我们将使用 Netlify 表格工具,非常容易保存处理这些信息。 ```js // src/components/Contact.js @@ -552,10 +552,10 @@ export default function Contact() { 我们可以在一个在线工具(embed-map.com)的帮助下这样做。你所要做的事只是输入你的位置并点击`Generate HTML code` -在给我们生成的代码中,不要复制所有的代码,只要复制ifame中的`src`属性,然后替换掉`src`的默认值。 +在给我们生成的代码中,不要复制所有的代码,只要复制 ifame 中的`src`属性,然后替换掉`src`的默认值。 ![](https://www.freecodecamp.org/news/content/images/2021/06/portfolio-2.png) -向Netlify发送任何提交的表单数据,Netlify Forms需要将从静态HTML中识别表单。因为的我们的React应用是由Javascript控制的,而不是普通的HTML组成,所以我们需要在`public`文件夹下的index.html文件中添加一个隐藏的表单。 +向 Netlify 发送任何提交的表单数据,Netlify Forms 需要将从静态 HTML 中识别表单。因为的我们的 React 应用是由 Javascript 控制的,而不是普通的 HTML 组成,所以我们需要在`public`文件夹下的 index.html 文件中添加一个隐藏的表单。 ```html @@ -579,11 +579,11 @@ export default function Contact() { ``` -我们需要隐藏这个表单,因为它不需要被用户看到,它只需要被Netlify看到。 +我们需要隐藏这个表单,因为它不需要被用户看到,它只需要被 Netlify 看到。 ## 如何从联系表单提交 -完成上面这些,我们将回到Contact.js。我们将使用Javascript提交这个表单。 +完成上面这些,我们将回到 Contact.js。我们将使用 Javascript 提交这个表单。 ```js const [name, setName] = React.useState(""); @@ -770,11 +770,11 @@ export default function Navbar() { ## 如何部署的你作品集 -现在为了使我们的作品集上线,我们需要把我们的应用程序推送到Github。 +现在为了使我们的作品集上线,我们需要把我们的应用程序推送到 Github。 -一旦你熟悉了这个流程,我们可以首先创建一个新的Github仓库。之后,我们将运行`git add .`,`git commit -m "Deploy"`,创建我们的git 远程,然后`git push -u orgin master`。 +一旦你熟悉了这个流程,我们可以首先创建一个新的 Github 仓库。之后,我们将运行`git add .`,`git commit -m "Deploy"`,创建我们的 git 远程,然后`git push -u orgin master`。 -一旦我们的项目建立在Github上,我们就可以去Netlify,选择`Choose Site from Git`。然后选择Github作为我们的持续部署,并选择我们刚刚推送代码的Github仓库。 +一旦我们的项目建立在 Github 上,我们就可以去 Netlify,选择`Choose Site from Git`。然后选择 Github 作为我们的持续部署,并选择我们刚刚推送代码的 Github 仓库。 ![](https://www.freecodecamp.org/news/content/images/2021/06/portfolio-3-min.gif) @@ -784,18 +784,18 @@ export default function Navbar() { 祝贺你!你现在在网上由一个作品集的应用程序,向潜在雇主展示你的所有的项目和技能。 -下一步要做的事设置一个自己的域名,最好用你的名字(i.e. [reedbarger.com](https://reedbarger.com/)). -由于Netlify包含一个DNS,你可以很容易在他们那里设置一个自己的域名。 +下一步要做的事设置一个自己的域名,最好用你的名字(i.e. [reedbarger.com](https://reedbarger.com/)). +由于 Netlify 包含一个 DNS,你可以很容易在他们那里设置一个自己的域名。 -可以考虑在你的React应用程序中,添加一个博客,向潜在的雇主展示你更多的开发知识。 +可以考虑在你的 React 应用程序中,添加一个博客,向潜在的雇主展示你更多的开发知识。 个人作品集是作为一个开发者所热衷表达自己的方式,你会从中获得成就。 -## 想获得学习React的方法? +## 想获得学习 React 的方法? -**[The React Bootcamp](http://bit.ly/join-react-bootcamp)** 本书有你应该学习React的所有知识,里面有视频、手册,还有特别的奖金。 +**[The React Bootcamp](http://bit.ly/join-react-bootcamp)** 本书有你应该学习 React 的所有知识,里面有视频、手册,还有特别的奖金。 - 获得内幕信息 **100s** ,开发人员通过已经掌握React,找到他们梦想的工作,并掌握他们的未来 + 获得内幕信息 **100s** ,开发人员通过已经掌握 React,找到他们梦想的工作,并掌握他们的未来 [![The React Bootcamp](https://reedbarger.nyc3.digitaloceanspaces.com/react-bootcamp-banner.png)](http://bit.ly/join-react-bootcamp) _点击这里,将得到通知,当它开放时_ \ No newline at end of file diff --git a/chinese/articles/chose-a-cms-wordpress-vs-shopify-vs-ghost.md b/chinese/articles/chose-a-cms-wordpress-vs-shopify-vs-ghost.md index e0bd04801..afbea3d7d 100644 --- a/chinese/articles/chose-a-cms-wordpress-vs-shopify-vs-ghost.md +++ b/chinese/articles/chose-a-cms-wordpress-vs-shopify-vs-ghost.md @@ -5,11 +5,11 @@ ![How to Choose a CMS – WordPress vs Ghost vs Shopify](https://www.freecodecamp.org/news/content/images/size/w2000/2022/01/WordPress-vs-Ghost-vs-Shopify.jpg) -WordPress、Shopify和Ghost是最强大和最流行的CMS平台之一。 +WordPress、Shopify 和 Ghost 是最强大和最流行的 CMS 平台之一。 你可能想知道为什么这么多人使用它们。嗯,这是因为它们易于使用,成本效益高,而且效率高。 -使用WordPress、Shopify和Ghost,你可以在几分钟内从头开始创建一个网站。 +使用 WordPress、Shopify 和 Ghost,你可以在几分钟内从头开始创建一个网站。 根据你的需要,这些[强大的网站建设工具](https://www.weblime.com/stories/top-website-builders)可以帮助你实现从建立电商网站到开始你自己的博客或展示你的投资组合的任何目标。 @@ -19,7 +19,7 @@ WordPress、Shopify和Ghost是最强大和最流行的CMS平台之一。 因此,在这篇文章中,我们将介绍这三个平台,并看看它们的优点和缺点,以便你能弄清楚什么会对你的特定情况最有效。 -## 如果你想从头开始建立一个博客--使用Ghost +## 如果你想从头开始建立一个博客--使用 Ghost 如果你是一个正在寻找开源平台的博主,[Ghost](https://ghost.org/)可能是你的完美选择。 @@ -27,50 +27,50 @@ WordPress、Shopify和Ghost是最强大和最流行的CMS平台之一。 即使你不是一个程序员,你也可以从其他用户那里得到帮助,他们愿意通过修复错误或增加新功能来为社区做出贡献。 -网上有关于如何在自己的服务器上安装Ghost的教程。你也可以选择使用托管计划,这允许你使用其他用户创建的主题,而不是依赖默认主题。 +网上有关于如何在自己的服务器上安装 Ghost 的教程。你也可以选择使用托管计划,这允许你使用其他用户创建的主题,而不是依赖默认主题。 -Ghost是一个基于Node.js的平台,它使用Node.js服务器运行。它有一个丰富的插件系统,允许你定制你的博客以满足你自己的需求。 +Ghost 是一个基于 Node.js 的平台,它使用 Node.js 服务器运行。它有一个丰富的插件系统,允许你定制你的博客以满足你自己的需求。 -Ghost让你使用Markdown而不是HTML进行写作。这使你很容易直接在浏览器窗口中写作。 +Ghost 让你使用 Markdown 而不是 HTML 进行写作。这使你很容易直接在浏览器窗口中写作。 -如果你不了解Markdown,它是一种轻量级的标记语言,允许你用富文本写作,但代码更简单,与HTML相对应。 +如果你不了解 Markdown,它是一种轻量级的标记语言,允许你用富文本写作,但代码更简单,与 HTML 相对应。 -事实上,与其他平台相比,Ghost的好处之一是你的文章是用Markdown写的,而不是HTML或文本文件,这导致代码更简洁。 +事实上,与其他平台相比,Ghost 的好处之一是你的文章是用 Markdown 写的,而不是 HTML 或文本文件,这导致代码更简洁。 -### 你可以将Ghost作为一个无头CMS使用 +### 你可以将 Ghost 作为一个无头 CMS 使用 -Ghost有一个灵活的架构,这意味着它可以作为一个无头CMS使用。事实上,它是JAMStack中最受欢迎的无头内容管理系统之一。 +Ghost 有一个灵活的架构,这意味着它可以作为一个无头 CMS 使用。事实上,它是 JAMStack 中最受欢迎的无头内容管理系统之一。 -一个[无头CMS](https://www.freecodecamp.org/news/what-is-headless-cms-explained/)允许你建立一个前端网站或应用程序,如一个移动应用程序,有一个调用CMS的数据的API。你可以将API和你的网站或应用程序部署到不同的服务器或环境中,而不必改变你内容中的任何东西。 +一个[无头 CMS](https://www.freecodecamp.org/news/what-is-headless-cms-explained/)允许你建立一个前端网站或应用程序,如一个移动应用程序,有一个调用 CMS 的数据的 API。你可以将 API 和你的网站或应用程序部署到不同的服务器或环境中,而不必改变你内容中的任何东西。 -由于API从CMS提供数据,你将完全控制这些数据,包括权限、标签和类别。如果你需要对你的内容进行修改,你将只需要在一端进行修改,而不是在两端。 +由于 API 从 CMS 提供数据,你将完全控制这些数据,包括权限、标签和类别。如果你需要对你的内容进行修改,你将只需要在一端进行修改,而不是在两端。 -无头CMS通常用于使用AngularJS和React.js等JavaScript框架构建网站和应用程序。然而,如果你需要用一个API调用来渲染一个有多个页面的动态网站,这种类型的CMS也是有益的。 +无头 CMS 通常用于使用 AngularJS 和 React.js 等 JavaScript 框架构建网站和应用程序。然而,如果你需要用一个 API 调用来渲染一个有多个页面的动态网站,这种类型的 CMS 也是有益的。 -要使用无头CMS,你需要学习它的工作原理,以及如何通过其API来利用它。这可能需要额外的开发资源,这取决于你的技能水平。 +要使用无头 CMS,你需要学习它的工作原理,以及如何通过其 API 来利用它。这可能需要额外的开发资源,这取决于你的技能水平。 这种方法在初创企业和机构中越来越受欢迎,他们希望对内容的显示有更多的控制,同时仍然保留通过中央界面(central interface)进行更新的能力。 ![Screenshot of Ghost admin interface](https://www.freecodecamp.org/news/content/images/2022/01/ghost-screenshot-admin.png) -### 使用Ghost的优势 +### 使用 Ghost 的优势 -Ghost是一个免费的、易于使用的CMS,不需要任何种类的编码技能。 +Ghost 是一个免费的、易于使用的 CMS,不需要任何种类的编码技能。 -除了简单之外,Ghost CMS还具有很多开箱即用的功能,这使它对博主和小企业非常有吸引力。 +除了简单之外,Ghost CMS 还具有很多开箱即用的功能,这使它对博主和小企业非常有吸引力。 你可以添加多个作者,设置合作写作,跟踪分析,创建民意调查,客户支持论坛,管理模板,以及更多使用拖放功能的功能。 -当你开始在Ghost上使用第三方应用程序时,事情变得很有趣。有许多集成可用于安装谷歌分析和Discuz评论等应用程序。 +当你开始在 Ghost 上使用第三方应用程序时,事情变得很有趣。有许多集成可用于安装谷歌分析和 Discuz 评论等应用程序。 其他一些优点包括: -- **它有一个干净、简约的设计**。如果你想让你的博客变得漂亮,那么Ghost CMS就很适合。 +- **它有一个干净、简约的设计**。如果你想让你的博客变得漂亮,那么 Ghost CMS 就很适合。 - **完整的工具**。你可以得到创建博客所需的所有工具,从安装开始到用拖放功能设置主题。 - **可定制的主题**。主题可以使用内置的主题编辑器进行定制,这使得任何人都可以根据自己的需要修改它们。 -- **Ghost社区**。如果你被卡住了,你可以通过翻阅Ghost的论坛和提问,从其他Ghost用户那里找到帮助。 +- **Ghost 社区**。如果你被卡住了,你可以通过翻阅 Ghost 的论坛和提问,从其他 Ghost 用户那里找到帮助。 -## 如果你想让你的网站更上一层楼--使用WordPress +## 如果你想让你的网站更上一层楼--使用 WordPress 你可以使用[WordPress](https://wordpress.org/)作为一个简单的博客平台,但它也是一个功能齐全的内容管理系统(CMS)。如果你想让你的业务更上一层楼,它允许你建立定制网站和博客。 @@ -78,62 +78,62 @@ Ghost是一个免费的、易于使用的CMS,不需要任何种类的编码技 基本功能和更高级功能的教程在网上广泛提供。 -WordPress是免费的,但如果你需要帮助安装它或根据你的需要定制它,你可以从一个有信誉的公司购买虚拟主机。托管费用因你选择的托管服务类型而异。 +WordPress 是免费的,但如果你需要帮助安装它或根据你的需要定制它,你可以从一个有信誉的公司购买虚拟主机。托管费用因你选择的托管服务类型而异。 -[你可以自己安装WordPress](https://www.freecodecamp.org/news/how-to-get-started-with-wordpress/)或付钱给别人帮你做。WordPress网站提供了如何在不同的服务器平台上安装该软件的说明。 +[你可以自己安装 WordPress](https://www.freecodecamp.org/news/how-to-get-started-with-wordpress/)或付钱给别人帮你做。WordPress 网站提供了如何在不同的服务器平台上安装该软件的说明。 -一旦安装在你的服务器上,WordPress将允许你创建你自己的博客页面。通过使用主题,你可以完全控制你的网站的外观和感觉,这些主题是为你的页面布局预先建立的设计。 +一旦安装在你的服务器上,WordPress 将允许你创建你自己的博客页面。通过使用主题,你可以完全控制你的网站的外观和感觉,这些主题是为你的页面布局预先建立的设计。 -主题可以从WordPress网站购买或免费下载。如果你愿意挑战自己,你也可以[设计你自己的自定义主题](https://www.freecodecamp.org/news/learn-how-to-create-your-own-wordpress-theme-from-scratch/)。 +主题可以从 WordPress 网站购买或免费下载。如果你愿意挑战自己,你也可以[设计你自己的自定义主题](https://www.freecodecamp.org/news/learn-how-to-create-your-own-wordpress-theme-from-scratch/)。 -你可能想要一个比基本WordPress安装所提供的更复杂的网站。在这种情况下,你可以雇用一个设计师为你建立一个定制的主题,或者使用一个现有的主题作为框架,并通过插件添加你自己的定制功能。插件通过特殊的设计元素和编程功能来扩展主题的功能。 +你可能想要一个比基本 WordPress 安装所提供的更复杂的网站。在这种情况下,你可以雇用一个设计师为你建立一个定制的主题,或者使用一个现有的主题作为框架,并通过插件添加你自己的定制功能。插件通过特殊的设计元素和编程功能来扩展主题的功能。 ![Screenshot of WordPress themes in the admin UI](https://www.freecodecamp.org/news/content/images/2022/01/WordPress-screen-themes.png) -### 使用WordPress的优势 +### 使用 WordPress 的优势 -到目前为止,WordPress是这个列表中最可定制的选项。它具有最全面的主题和插件库,允许你很容易地编辑源代码,甚至可以编写你自己的自定义功能。 +到目前为止,WordPress 是这个列表中最可定制的选项。它具有最全面的主题和插件库,允许你很容易地编辑源代码,甚至可以编写你自己的自定义功能。 -你可以把WordPress看作是介于Ghost和Shopify之间的CMS,因为它允许你通过简单地安装一个兼容的主题来同时运行一个电子商务商店和一个博客。 +你可以把 WordPress 看作是介于 Ghost 和 Shopify 之间的 CMS,因为它允许你通过简单地安装一个兼容的主题来同时运行一个电子商务商店和一个博客。 -WordPress让你的博客更上一个水平,并随着你的业务增长而扩展你的电子商务商店。 +WordPress 让你的博客更上一个水平,并随着你的业务增长而扩展你的电子商务商店。 缺点是,你迟早会需要一个开发人员的帮助。这是因为你安装的插件越多,你的网站就越慢,而用户讨厌慢的网站(更不用说搜索引擎)。 其他一些优点包括: -- **极度便宜,可以开始使用**。你可以免费建立和运行一个WordPress网站,基本的托管计划每年低至18.96美元。 -- **能够分配用户角色**。在你的业务增长周期的某个时候,你可能需要引入额外的人帮助你创建、编辑和发布内容。WordPress带有内置的用户角色,这将使你的生活变得更加容易。 -- **市场上最好的营销插件**。你可以安装插件目录中的许多SEO插件之一。一些例子包括Yoast或Rank Math。这些插件帮助你优化你的内容(如文章,页面,甚至产品页面),并增加你超过竞争对手的机会。 -- *大量的集成**。你有可能会使用第三方SaaS产品,如Mailchimp或ActiveCampaign。不管是哪种服务,你会发现将它们与你的网站整合起来非常容易,因为几乎所有的服务都创建了一个WordPress插件。 -- **最大的在线资源库**。到目前为止,WordPress社区是最大的,这使得你很容易找到关于你安装的所有问题的答案。有数以万计的网站和论坛只专注于WordPress网站,有大量的信息。 +- **极度便宜,可以开始使用**。你可以免费建立和运行一个 WordPress 网站,基本的托管计划每年低至 18.96 美元。 +- **能够分配用户角色**。在你的业务增长周期的某个时候,你可能需要引入额外的人帮助你创建、编辑和发布内容。WordPress 带有内置的用户角色,这将使你的生活变得更加容易。 +- **市场上最好的营销插件**。你可以安装插件目录中的许多 SEO 插件之一。一些例子包括 Yoast 或 Rank Math。这些插件帮助你优化你的内容(如文章,页面,甚至产品页面),并增加你超过竞争对手的机会。 +- *大量的集成**。你有可能会使用第三方 SaaS 产品,如 Mailchimp 或 ActiveCampaign。不管是哪种服务,你会发现将它们与你的网站整合起来非常容易,因为几乎所有的服务都创建了一个 WordPress 插件。 +- **最大的在线资源库**。到目前为止,WordPress 社区是最大的,这使得你很容易找到关于你安装的所有问题的答案。有数以万计的网站和论坛只专注于 WordPress 网站,有大量的信息。 -## 如果你想以最快的方式开展电子商务业务--使用Shopify +## 如果你想以最快的方式开展电子商务业务--使用 Shopify -[Shopify](https://www.shopify.com/)于2006年作为一个滑雪板设备的网上商店开始。那时,CMS生态系统缺乏一个强大的、易于使用的解决方案,所以Shopify的创建者们决定自己编程实现。 +[Shopify](https://www.shopify.com/)于 2006 年作为一个滑雪板设备的网上商店开始。那时,CMS 生态系统缺乏一个强大的、易于使用的解决方案,所以 Shopify 的创建者们决定自己编程实现。 -不用说,这很受欢迎。在随后的几年里,越来越多的商店开始使用Shopify CMS。截至2021年5月,来自约175个国家的170多万企业使用Shopify来经营他们的业务。 +不用说,这很受欢迎。在随后的几年里,越来越多的商店开始使用 Shopify CMS。截至 2021 年 5 月,来自约 175 个国家的 170 多万企业使用 Shopify 来经营他们的业务。 -到目前为止,Shopify是启动电子商务业务的最简单和最快速的方式,主要用于非技术人员,他们想把他们的实体店带到网上。在不超过几分钟的时间内,你就可以建立和运行自己的网站,并开始接受在线支付。 +到目前为止,Shopify 是启动电子商务业务的最简单和最快速的方式,主要用于非技术人员,他们想把他们的实体店带到网上。在不超过几分钟的时间内,你就可以建立和运行自己的网站,并开始接受在线支付。 -但Shopify也被互联网上一些最大的网店所使用,这使得它成为一个很好的选择,如果你想将你的业务扩展到一个新的水平,并有资金这样做。 +但 Shopify 也被互联网上一些最大的网店所使用,这使得它成为一个很好的选择,如果你想将你的业务扩展到一个新的水平,并有资金这样做。 ![Screenshot of Shopify UI](https://www.freecodecamp.org/news/content/images/2022/01/Shopify-screenshot.png) -Shopify的一些优势: +Shopify 的一些优势: -- **极易上手**。该内容管理系统是在考虑到易于使用的情况下建立的。任何人都可以在短时间内建立和运行一个商店。你不需要知道如何编码,甚至不需要知道如何将域名连接到你的网站,因为Shopify为你做这一切。 -- **与大多数主要的第三方服务兼容**。Shopify开箱即与最大的第三方服务连接,如Klaviyo的电子邮件营销或Facebook Ads的付费广告。 +- **极易上手**。该内容管理系统是在考虑到易于使用的情况下建立的。任何人都可以在短时间内建立和运行一个商店。你不需要知道如何编码,甚至不需要知道如何将域名连接到你的网站,因为 Shopify 为你做这一切。 +- **与大多数主要的第三方服务兼容**。Shopify 开箱即与最大的第三方服务连接,如 Klaviyo 的电子邮件营销或 Facebook Ads 的付费广告。 - **应用商店里有大量的应用程序**。如果你觉得你的商店需要额外的功能,例如捆绑折扣,你会很高兴听到有大量的应用程序可以帮助你实现这个目标。只是要小心,看看他们的成本,因为他们可能会上升得很快。 -Shopify的一些劣势: +Shopify 的一些劣势: -- **可以变得相当昂贵**。Shopify非常容易上手,但这也是有代价的。最便宜的计划(基本)每月29美元起,而最贵的计划(高级)每月达到299美元。主要的区别是Shopify在付款时应用的佣金。 -- **高支付费用**。你必须考虑到,Shopify对你的交易收取高达2%的费用(在基本计划中)。这种佣金随着计划的减少而减少,最低为0.5%(在高级计划中)。相比之下,WooCommerce不会对你的销售额收取一定比例的费用。 -- **Shopify的开发人员是昂贵的**。如果你想在基本功能之外定制你的商店,你将需要一个开发人员的帮助,而他们并不便宜。然而,有大量的应用程序可以帮助你实现你的目标。 -- **大多数Shopify应用程序都有月费**。你可以在应用商店里找到免费的应用程序,但它们中的大多数都有月费(相比之下,绝大多数WordPress的插件都是一次性收取固定费用)。 -- **博客的功能不是很好**。Shopify是一个以电子商务为中心的平台,所以难怪他们没有花太多的时间来优化他们的博客功能。大商店选择在WordPress上托管他们的博客,而将他们的店面保留在Shopify上。 +- **可以变得相当昂贵**。Shopify 非常容易上手,但这也是有代价的。最便宜的计划(基本)每月 29 美元起,而最贵的计划(高级)每月达到 299 美元。主要的区别是 Shopify 在付款时应用的佣金。 +- **高支付费用**。你必须考虑到,Shopify 对你的交易收取高达 2%的费用(在基本计划中)。这种佣金随着计划的减少而减少,最低为 0.5%(在高级计划中)。相比之下,WooCommerce 不会对你的销售额收取一定比例的费用。 +- **Shopify 的开发人员是昂贵的**。如果你想在基本功能之外定制你的商店,你将需要一个开发人员的帮助,而他们并不便宜。然而,有大量的应用程序可以帮助你实现你的目标。 +- **大多数 Shopify 应用程序都有月费**。你可以在应用商店里找到免费的应用程序,但它们中的大多数都有月费(相比之下,绝大多数 WordPress 的插件都是一次性收取固定费用)。 +- **博客的功能不是很好**。Shopify 是一个以电子商务为中心的平台,所以难怪他们没有花太多的时间来优化他们的博客功能。大商店选择在 WordPress 上托管他们的博客,而将他们的店面保留在 Shopify 上。 -总之,你可以用Ghost几乎立即开始你的博客,或者用Shopify配置一个快速和安全的网上商店。而且,如果你想获得两个世界的最佳效果,你可能想用WordPress。 +总之,你可以用 Ghost 几乎立即开始你的博客,或者用 Shopify 配置一个快速和安全的网上商店。而且,如果你想获得两个世界的最佳效果,你可能想用 WordPress。 -[终极WordPress指南](https://www.weblime.com/stories/the-ultimate-wordpress-guide)是一个免费资源,将帮助你了解基础知识,并帮助你在短时间内启动你的新网站,所以在你做出选择特定平台的重大决定之前,一定要阅读它。 +[终极 WordPress 指南](https://www.weblime.com/stories/the-ultimate-wordpress-guide)是一个免费资源,将帮助你了解基础知识,并帮助你在短时间内启动你的新网站,所以在你做出选择特定平台的重大决定之前,一定要阅读它。 diff --git a/chinese/articles/coding-interview-prep-for-big-tech.md b/chinese/articles/coding-interview-prep-for-big-tech.md index 54e6bb2bc..936446000 100644 --- a/chinese/articles/coding-interview-prep-for-big-tech.md +++ b/chinese/articles/coding-interview-prep-for-big-tech.md @@ -5,15 +5,15 @@ ![Coding Interview Prep for Big Tech (FAANG) – And How I Became A Google Engineer](https://www.freecodecamp.org/news/content/images/size/w2000/2022/07/Google-Mel.jpeg) -当我成功从一名律师转行成为google软件工程师的时候,我发表了帮助我成功转型的[10个想法](https://www.freecodecamp.org/news/from-lawyer-to-google-engineer/)这篇文章,之后我收到许多提问: +当我成功从一名律师转行成为 google 软件工程师的时候,我发表了帮助我成功转型的[10 个想法](https://www.freecodecamp.org/news/from-lawyer-to-google-engineer/)这篇文章,之后我收到许多提问: - 如何自学新技能 -- 我怎么知道37岁学代码“不晚” +- 我怎么知道 37 岁学代码“不晚” - 我是怎么准备大科技公司的面试 - 我是如何分析和最小化转行的风险 - 我是如何发现软件工程师这个职业适合我 - 我专注于哪个语言 -- 成为FANG/FAMGA(Facebook/Amazon/Netflix/Google/Microsoft/Apple)的软件工程师是否适合每一个人 (提示:这些公司听上去很诱人,但事实证明并适合一些人) +- 成为 FANG/FAMGA(Facebook/Amazon/Netflix/Google/Microsoft/Apple)的软件工程师是否适合每一个人 (提示:这些公司听上去很诱人,但事实证明并适合一些人) 注意: 我认为“FAANG/FAMGA”不足以概括所有好公司,所以我倾向于使用“大科技公司”,来指代包括这四五个令人着迷的公司在内的所有颇具盛名的公司。 @@ -23,8 +23,8 @@ 所以在这篇文章中我将回答其中一个问题: -1. 当38岁时,我从律师转行为软件工程师 -2. 以及39岁时,我准备大科技公司面试,仅有不到两年的相关经验 +1. 当 38 岁时,我从律师转行为软件工程师 +2. 以及 39 岁时,我准备大科技公司面试,仅有不到两年的相关经验 我所采用的方法。 @@ -34,27 +34,27 @@ 我持以下观点:获得面试机会比学习代码难;在面试中表现出色和获得面试机会一样难;如果你不具备竞争对手同样的扎实经验,对于你来说,行为面试很困难。 -当我零基础转行时,以及当我将目标设定在大科技公司的时候,我知道我将和博士生、从青少年时期就开始写代码(通常写了20多年)以及在技术上有相当成就的人同台竞技,而当时的我只有一年的相关经验。 +当我零基础转行时,以及当我将目标设定在大科技公司的时候,我知道我将和博士生、从青少年时期就开始写代码(通常写了 20 多年)以及在技术上有相当成就的人同台竞技,而当时的我只有一年的相关经验。 同时我不在美国,给挑战又增加了难度。 所以我制定了一个不仅仅是“学习写代码”的计划。 -首先,让我详细介绍一下为什么我认为“学习写代码”是准备环节中最简单的部分。虽然从2012到2018年,我反复尝试失败了4次。 +首先,让我详细介绍一下为什么我认为“学习写代码”是准备环节中最简单的部分。虽然从 2012 到 2018 年,我反复尝试失败了 4 次。 -这个想法是2018年产生的。当时我的创业公司正在垂死挣扎,为此我损失了上万元的资金,2年没有任何收入。 +这个想法是 2018 年产生的。当时我的创业公司正在垂死挣扎,为此我损失了上万元的资金,2 年没有任何收入。 -但是我决定从我的信贷中取出4万多美元,为什么?去参加一个旧金山的训练营(bootcamp)。 +但是我决定从我的信贷中取出 4 万多美元,为什么?去参加一个旧金山的训练营(bootcamp)。 -我离开了家人,只身前往旧金山,本来要参加为期14周的课程,但是在加入这个顶级训练营的第一周我就放弃了,返回澳大利亚。 +我离开了家人,只身前往旧金山,本来要参加为期 14 周的课程,但是在加入这个顶级训练营的第一周我就放弃了,返回澳大利亚。 我曾十分期待这个训练营(也为贷款感到焦虑),但是对训练营的教学策略产生怀疑。我注意到老师和课程设置是帮助学员“学习写代码”而不是“成为一个程序员”。 -根据我在4个国家以及其他3个行业的工作经验来判断,这种策略是一个错误。 +根据我在 4 个国家以及其他 3 个行业的工作经验来判断,这种策略是一个错误。 学习写代码也是一种形式的“识字能力”,识字能力并不是技能。 -我自己就是一个活生生的例子:这4次尝试中,每当我专注于“学习写代码”,我都会在一个非常小的领域里获得成功,要么是学会了HTML或者Java,要么就是跟着书本编写了一个安卓应用,我总是能够学会读写一些基本的内容。但是我不知道怎么自己搭建一个有用的东西,当需要真正应用技能的时候我感觉绝望——我没有真正的“技能”。 +我自己就是一个活生生的例子:这 4 次尝试中,每当我专注于“学习写代码”,我都会在一个非常小的领域里获得成功,要么是学会了 HTML 或者 Java,要么就是跟着书本编写了一个安卓应用,我总是能够学会读写一些基本的内容。但是我不知道怎么自己搭建一个有用的东西,当需要真正应用技能的时候我感觉绝望——我没有真正的“技能”。 在这个时代,我们并不因为我们知道什么而被雇佣,我们因为我们的技能被雇佣。 @@ -66,7 +66,7 @@ 另外,训练营默认将每一位学员打造符合“初级程序员”的标准。 -我当时已经37岁了,不满足于“入门级别工作”的思维方式。另外即便是行业新人,我不相信任何有三年以上工作经验的人是“初级”员工。 +我当时已经 37 岁了,不满足于“入门级别工作”的思维方式。另外即便是行业新人,我不相信任何有三年以上工作经验的人是“初级”员工。 然后我发现训练营的一些老师和助教是没有找到工作的一些前学员。他们没有转行的经验,有一些甚至没有过“职业经历”,职业辅导员甚至从来没有过科技行业面试别人的经验。 @@ -84,25 +84,25 @@ 在学习期间有收入会让我有信心做出更好的判断。更好的判断对于需要通过长期努力来实现的职业很重要。 -毫无疑问,如果我在训练营中学习3-4个月,我会“学习写代码”。 +毫无疑问,如果我在训练营中学习 3-4 个月,我会“学习写代码”。 但是我学到的东西足以让一个优秀的团队买单吗?我不相信训练营和在线代码学习网站会帮助我实现这一目标。 学写代码不会让我走得太远。我必须足够优秀才能击败拥有相关学位、经验和人脉的竞争对手。我想在代码领域获得一份职业。 -所以我离开了训练营,浪费了大约9000美元,回到澳大利亚。当然,我已经掌握了基本的代码知识,可以通过训练营的入学考试。但我远非能胜任工作。 +所以我离开了训练营,浪费了大约 9000 美元,回到澳大利亚。当然,我已经掌握了基本的代码知识,可以通过训练营的入学考试。但我远非能胜任工作。 如果你是职场新手,可能很难理解这里的分析。但你是否注意到许多人都玩音乐,但不是所有人都能胜任乐队成员的职位。正如我的导师所说: -> _"迈克尔·乔丹不想学打篮球,他想进入NBA。这两者差异巨大。"_ +> _"迈克尔·乔丹不想学打篮球,他想进入 NBA。这两者差异巨大。"_ ## 如何扬长避短 -这一想法改变了一切。在2019年的8个月内,我获得了我申请的所有4个开发岗位的offer。而在这个过程中,我仅遵从了我和我的教练(非技术)制定的发展计划。 +这一想法改变了一切。在 2019 年的 8 个月内,我获得了我申请的所有 4 个开发岗位的 offer。而在这个过程中,我仅遵从了我和我的教练(非技术)制定的发展计划。 不过不要被误导。我有意想不到的优势。使我受益的两个主要优势,一开始看上去是我的劣势。 -这不是我的第一次转行,在曾经的职业生涯中我有近10年的招聘经验。今天我也做技术方面的招聘,这些招聘模式非常类似。 +这不是我的第一次转行,在曾经的职业生涯中我有近 10 年的招聘经验。今天我也做技术方面的招聘,这些招聘模式非常类似。 我最大的优势是我不以候选人的角度考虑招聘,而是从招聘经理的角度考虑招聘。这对我的计划产生了重大影响,我了解招聘经理的想法——他们的限制、优先级、价值观、业务需求、不喜欢的点、危险信号…… @@ -118,7 +118,7 @@ - 没有正式的计算机科学资格认证 - 零技术基础 - 抵押贷款和财务责任 -- 我的 “年龄” – 40岁时学习新技能比25岁时更难 +- 我的 “年龄” – 40 岁时学习新技能比 25 岁时更难 - 来自文化和社会的期望、判断、消极情绪 - 招聘人员和老板比我年轻,不确定如何与我打交道 - 与其他候选人相比,人们认为转行非常,非常“有风险” @@ -149,7 +149,7 @@ “搞定难题”或舒适不舒适不是首要任务。我的首要任务是成功地改变职业。 -接受一份垃圾代码工作(这种工作有很多......)对我来说并不是一个“成功的”职业转变。但同样,我转行“成功”与否不取决于是否加入了大科技公司。成功的标准非常个人——对我来说,成功意味着热爱我所做的工作并从中学到很多东西。句号。 +接受一份垃圾代码工作(这种工作有很多……)对我来说并不是一个“成功的”职业转变。但同样,我转行“成功”与否不取决于是否加入了大科技公司。成功的标准非常个人——对我来说,成功意味着热爱我所做的工作并从中学到很多东西。句号。 ### 我如何做定制化计划 @@ -181,29 +181,29 @@ - 愿意改变自己、我的习惯和消极信念,这样我就可以改变我周围的世界。 - 准备好专注于建立一个有回报和有成就感的职业,而不仅仅是“获得下一份工作”。 - 准备好专注于为我未来的团队创造价值,而不是“对我有什么好处”的心态。 -- 愿意打持久战——具备5-10-25年的视野,而只是接下来的几周。 +- 愿意打持久战——具备 5-10-25 年的视野,而只是接下来的几周。 我必须承认,坚持这些原则比我预期的要难得多。我失败了很多次,尤其是前三点。但既然我已经写下了定制计划,它就是我的行为指导和真理的唯一来源。 -我的计划要求我专注于基本的编程技能,然后将其缩小到我认为与我的长期目标和技能相匹配的部分。对我来说,就是web开发。所以我要完全且无情地避免所有“光鲜亮丽的新玩意儿”,比如Python或Java。 +我的计划要求我专注于基本的编程技能,然后将其缩小到我认为与我的长期目标和技能相匹配的部分。对我来说,就是 web 开发。所以我要完全且无情地避免所有“光鲜亮丽的新玩意儿”,比如 Python 或 Java。 -教程和没完没了的视频不会让我击败竞争对手。我计算出达成我所在城市开发岗位所需最低技能,需要900-1100小时专注学习代码,以正确的顺序练习正确的内容。 +教程和没完没了的视频不会让我击败竞争对手。我计算出达成我所在城市开发岗位所需最低技能,需要 900-1100 小时专注学习代码,以正确的顺序练习正确的内容。 准备计划花费了数周时间。我不断地修改和强化它,不慌不忙。亚伯拉罕·林肯(另一位转行的律师!)给我很大启发,他曾经说过“给我六个小时砍一棵树,我会用前四个小时磨斧头”。 我很想直接进入我的计划并变得“忙碌”起来,但忙碌并不等同于高效。一旦在我的认知范围内,我确定计划完善了,我就转向全神贯注地执行该计划。 -执行意味着很多牺牲,很多很多天的自我怀疑,与改计划的诱惑作斗争,学会管理精力。在此期间,我养成了一些惊人的习惯,但这是事后诸葛。在这6个月的执行中,我不断受到不确定性、恐惧和偶尔失去希望的打击。 +执行意味着很多牺牲,很多很多天的自我怀疑,与改计划的诱惑作斗争,学会管理精力。在此期间,我养成了一些惊人的习惯,但这是事后诸葛。在这 6 个月的执行中,我不断受到不确定性、恐惧和偶尔失去希望的打击。 -后来,我在这个计划的基础上做了调整,为大型科技公司,尤其是Google制定了一个计划,花费了500-600小时的刻意学习,这与我成为开发人员的计划完全不同。稍后再谈。 +后来,我在这个计划的基础上做了调整,为大型科技公司,尤其是 Google 制定了一个计划,花费了 500-600 小时的刻意学习,这与我成为开发人员的计划完全不同。稍后再谈。 -## 早期成果,以及……Google工程师 +## 早期成果,以及……Google 工程师 -我从4年的试验、错误和失败中学到的另一个教训是,我容易在中途改变计划,转换资源、课程或重点。 +我从 4 年的试验、错误和失败中学到的另一个教训是,我容易在中途改变计划,转换资源、课程或重点。 这是一个非常严重的问题,因为每次我们切换焦点或计划时,我们都会放弃我们已经完成的辛勤工作,回到起点并……重新开始……全部……重新开始。 -想象一下,如果你从A开车到B并不断掉头并返回并重新启动。你永远不会到达任何地方。 +想象一下,如果你从 A 开车到 B 并不断掉头并返回并重新启动。你永远不会到达任何地方。 但我给自己做了一个承诺(信守一个承诺比信守一大堆更容易!):我要完成我的计划,然后再决定是否继续。这一次直到完成我的计划,我都不会停下来。 @@ -211,13 +211,13 @@ 我的计划有一个特定的时间,即使我还没有准备好面试,我也会开始面试。但要达到那个阶段,我必须善于创造面试机会。 -我过去在其他职业中的经历再一次帮助了我。我应用了我过去18年所学的知识,在几周内获得了4次面试机会,并获得了全部4份offer,尽管有很多候选人比我拥有更多的经验、技能和资格。 +我过去在其他职业中的经历再一次帮助了我。我应用了我过去 18 年所学的知识,在几周内获得了 4 次面试机会,并获得了全部 4 份 offer,尽管有很多候选人比我拥有更多的经验、技能和资格。 这不是因为我更擅长写代码。怎么可能是呢?我只有几个月的经验。 -我相信拿到四个offer是因为 _在面试过程中_ , _在招聘官面前_ 我表现出了自己是更好的候选人的样子。这是展示自己极为重要的一种方法。 +我相信拿到四个 offer 是因为 _在面试过程中_ , _在招聘官面前_ 我表现出了自己是更好的候选人的样子。这是展示自己极为重要的一种方法。 -收到offer很棒,但我遇到了一个意想不到的问题。由于我的计划要求我对工作职位有清晰的认识,所以我相信这4个职位都是我新职业生涯的绝佳开始。我将如何选择? +收到 offer 很棒,但我遇到了一个意想不到的问题。由于我的计划要求我对工作职位有清晰的认识,所以我相信这 4 个职位都是我新职业生涯的绝佳开始。我将如何选择? 是的,这是一个甜蜜的烦恼,但这个决定不容易做! @@ -229,7 +229,7 @@ 有意识地要求我专注于我所知道的,而不是我希望的,或者仅仅是想的。我必须要么找到证据来支持我的想法,要么根据我的了解无视一些想法。 -这个框架培养了我的分析能力,并帮助我在2019年初正确地选择了第一份工作,那时我已经39岁了。 +这个框架培养了我的分析能力,并帮助我在 2019 年初正确地选择了第一份工作,那时我已经 39 岁了。 直到今天我还在使用 _知道 vs 想_ 框架来思考日常决策,我发现它是分析复杂决策权衡取舍的绝佳框架。 @@ -241,7 +241,7 @@ 他们说“不可能”的时候,是 _他们 **知道** 这一点还是_ _他们只是这么**想**_ _?_ -我的目标没有改变,对我来说,学习、成长和团队仍然比品牌或金钱更重要。但我快40岁了,我还想以一种我在职业生涯的前半段从未有过的勇气去探索生活。 +我的目标没有改变,对我来说,学习、成长和团队仍然比品牌或金钱更重要。但我快 40 岁了,我还想以一种我在职业生涯的前半段从未有过的勇气去探索生活。 所以我决定给自己设定一个新目标:我想了解在大科技公司做一名软件工程师是什么样的。我以前在大公司工作过,我知道这并不适合所有人——这就是我一开始决定进入初创公司和小公司的原因。 @@ -253,47 +253,47 @@ 我重新设计了我的计划。有几个步骤是相同的​​,但必须对代码课程进行大调整。我还需要了解美国大型科技公司的招聘情况,并使自己值得被推荐。 -大约7个月后,我开始接受面试。在这7个月里,我非常努力地证明自己值得被推荐,人们根据我的努力和被验证的决心给我推荐工作。 +大约 7 个月后,我开始接受面试。在这 7 个月里,我非常努力地证明自己值得被推荐,人们根据我的努力和被验证的决心给我推荐工作。 -我被引荐到了Meta(当时它被称为 Facebook),但我没有得到面试机会,因为我的技能不匹配。这对我来说是一个宝贵的经验,我一直认为自己小心翼翼地只申请与技能相匹配的职位——我错了。 +我被引荐到了 Meta(当时它被称为 Facebook),但我没有得到面试机会,因为我的技能不匹配。这对我来说是一个宝贵的经验,我一直认为自己小心翼翼地只申请与技能相匹配的职位——我错了。 我意识到,职位描述对招聘公司来说一件事,对 _公司之外_ 的人来说是完全不同的另一件事。这是因为不同公司使用相同的语言来描述不同的职位。招聘方和应聘方可能都不知道这一点! -汇总这些经验之后,在3个月内,我收到了2家大型科技公司的offer,还有一家没有通过终面,因为我不知道如何从头开始编写文件系统(我对Linux世界一点也不了解!)。 +汇总这些经验之后,在 3 个月内,我收到了 2 家大型科技公司的 offer,还有一家没有通过终面,因为我不知道如何从头开始编写文件系统(我对 Linux 世界一点也不了解!)。 -然后我收到了Google的offer。 +然后我收到了 Google 的 offer。 -再一次,我面临着一个非常艰难的决定。Google的名声响亮,我很难客观权衡,但我真的很想忠于我的目标、我的计划和我的意图。 +再一次,我面临着一个非常艰难的决定。Google 的名声响亮,我很难客观权衡,但我真的很想忠于我的目标、我的计划和我的意图。 -当涉及到Google时,试图将我所知道的与我想的区分开来非常困难。但我十分确定:面试我的团队都是很棒的人。 +当涉及到 Google 时,试图将我所知道的与我想的区分开来非常困难。但我十分确定:面试我的团队都是很棒的人。 这是我认为运气很重要的地方。无论人们如何强调技能、大脑、智慧等,运气和“魔法”在生活中都扮演着重要的角色。 -我在Google遇到的面试官是友好、善良、开朗且高度专注的工程师。在面试中,他们不想证明我不行,而是帮助我证明我可以。他们热情地回答了我的问题,让我从一开始就感受到相互合作。 +我在 Google 遇到的面试官是友好、善良、开朗且高度专注的工程师。在面试中,他们不想证明我不行,而是帮助我证明我可以。他们热情地回答了我的问题,让我从一开始就感受到相互合作。 -这是Google特有的吗?也许。但后来,当我在Google接受技术面试培训时,我见识到了多种多样的面试官/招聘经理的风格和信念,我看到技能高超的候选人控制不了情绪,沟通不了解答过程等等。所以我很感恩机遇在我找工作中起到的作用。 +这是 Google 特有的吗?也许。但后来,当我在 Google 接受技术面试培训时,我见识到了多种多样的面试官/招聘经理的风格和信念,我看到技能高超的候选人控制不了情绪,沟通不了解答过程等等。所以我很感恩机遇在我找工作中起到的作用。 所以是的——我很幸运,我遇到我的面试官,并且在面试那天我碰巧知道如何回答那些代码题。 -这也是我对 _工作类型_ 和 _相关技能_ 的超专注准备得到回报的地方。 大科技公司有很多不适合我的职位(比如Meta的那个)。即使我之前有其他职业经验,我也不知道软件工程师的世界有多大,有多少种职业类型和偏好,区分它们有多难。 +这也是我对 _工作类型_ 和 _相关技能_ 的超专注准备得到回报的地方。 大科技公司有很多不适合我的职位(比如 Meta 的那个)。即使我之前有其他职业经验,我也不知道软件工程师的世界有多大,有多少种职业类型和偏好,区分它们有多难。 -通过强迫自己有超强的目标感,而不是随机、盲目地申请大科技公司职位,我抓住了小但重要的机会。我深入挖掘了每个职位,并通过与业内朋友交谈来仔细研究它们(再次强调,我的年龄和经验是一项资产,因为我已经建立了超过15年的关系,从没想过它们以后会如此有用!)。 +通过强迫自己有超强的目标感,而不是随机、盲目地申请大科技公司职位,我抓住了小但重要的机会。我深入挖掘了每个职位,并通过与业内朋友交谈来仔细研究它们(再次强调,我的年龄和经验是一项资产,因为我已经建立了超过 15 年的关系,从没想过它们以后会如此有用!)。 -对于我得到的每一个offer,我都进行了深入的研究,并为技术面试做好了充分的准备。在面试当天,就往事俱备,只欠东风。虽然我不认为我“完美搞定了”我的面试,但我做得很好,传达出我是 _满足团队需求_ 的合适人选的信息。 +对于我得到的每一个 offer,我都进行了深入的研究,并为技术面试做好了充分的准备。在面试当天,就往事俱备,只欠东风。虽然我不认为我“完美搞定了”我的面试,但我做得很好,传达出我是 _满足团队需求_ 的合适人选的信息。 这就来到下一个问题:我是如何为大科技公司的面试做准备的? ## 如何为大科技面试做准备 -答:分两个阶段,我花了500多个小时执行。 +答:分两个阶段,我花了 500 多个小时执行。 -### 第1阶段:了解现实和竞争格局 +### 第 1 阶段:了解现实和竞争格局 -如果我想斩获另一个国家的大型科技公司的职位,而我拥有不到一年的行业经验,有15年以上不相关工作经验,没有计算机科学学位。我需要对现实有一个非常清晰的认识,尤其是竞争格局。 +如果我想斩获另一个国家的大型科技公司的职位,而我拥有不到一年的行业经验,有 15 年以上不相关工作经验,没有计算机科学学位。我需要对现实有一个非常清晰的认识,尤其是竞争格局。 这意味着有准备的希望可能降临,但白日梦,天上掉馅饼这类事情不会发生。 -艰难的道理(可以查看[我的的YouTube视频](https://www.youtube.com/playlist?list=PLAPuklwJx5V3XZS19AlJQayZFpiZyDT9C))、艰难的现实、艰难的工作。 +艰难的道理(可以查看[我的的 YouTube 视频](https://www.youtube.com/playlist?list=PLAPuklwJx5V3XZS19AlJQayZFpiZyDT9C))、艰难的现实、艰难的工作。 我必须完全接受并且内化以下内容: @@ -302,17 +302,17 @@ - 我的竞争对手可能不是转行人员,如果他们是,他们[将来自密切相关的领域,如计算机工程、机械工程或电子工程](https://www.linkedin.com/posts/zubinpratap_software-engineering-computer-engineering-activity-6946411823759810560-VvA-?utm_source=linkedin_share&utm_medium=member_desktop_web)。绝大多数人拥有技术资格,甚至可能拥有博士学位(事实证明这是真的!),以及数年的行业经验。 - 作为一个“异常值”和“通配符”,最难的部分是获得面试机会。学习算法和数据结构更容易,“破解代码面试”(无论这意味着什么……)也会更容易。为什么?代码是确定性的——相同的代码通常会产生相同的结果。但生活不是确定的,是否能获得面试机会非常主观。在就业市场,相同的行为不会产生相同的结果。 - 我需要把自己塑造成经验丰富的工程师愿意与之共事的 _那种人_。 -- 我假设我的大多数竞争对手至少有3-5年的经验。我追不上他们,更别说 _超过他们_ 了。相反,我需要在非技术技能上超越他们,并在技术方面有得比(如果不超越他们)。 +- 我假设我的大多数竞争对手至少有 3-5 年的经验。我追不上他们,更别说 _超过他们_ 了。相反,我需要在非技术技能上超越他们,并在技术方面有得比(如果不超越他们)。 - 我必须比其他人更善于沟通。如果我不知道某事,我需要说出来,然后说明如果有合适的时间和机会,我将如何解决它。我还必须与面试官沟通,以表明我了解他们的 _业务需求_,而不仅仅是专注于我自私的梦想。 - 这意味着我必须真正努力了解招聘团队看中的、正在寻找、想要和需要的东西。 - 我无法控制我的竞争对手(他们的技能、他们的表现、他们知道多少等等),或者我的面试官在想什么、想要什么、他们看重什么,或者他们是否喜欢转行的候选人。我无法控制 _大部分_ 事情。我只能优化我的努力、我的注意力、我的心态,以及我从每次经历有所收获,无论好坏。我只能控制自己的选择和行动。因此,专注于任何外部的事情都是对宝贵精力的浪费。 - 我得意识到运气的存在。乔丹、[坦都卡](https://zh.wikipedia.org/zh-cn/%E6%B2%99%E5%A5%87%C2%B7%E5%BE%B7%E9%B2%81%E5%8D%A1)、费德勒——他们都有运气不好的时候。我也会的。或者,也许我会做得很好,但别人做得更好。其他人会更适合团队的需要,这是不要觉得伤心也不要嫉妒,我自己做面试官时也做过无数次艰难的决定,所以我知道这常常发生。 -- 如果我斩获超过1个offer,我需要 _预先_ 考虑并 _预先_ 与自己达成一致,以决定我将使用哪些信号和因素来做决定(从我之前4个offer中学到的!)。 - 如果你好奇我是这么做成的……其实我不知道。并不是一蹴而就的。这些都是事后总结。这些经验大部分都来自于“实时”应用[第一原则思维](https://fs.blog/first-principles/)。我的导师帮助我一起,缩小范围确定目标,这花了很多时间,我急不可耐地想“开始写代码”。但是……我知道亚伯拉罕·林肯会给我什么建议…… +- 如果我斩获超过 1 个 offer,我需要 _预先_ 考虑并 _预先_ 与自己达成一致,以决定我将使用哪些信号和因素来做决定(从我之前 4 个 offer 中学到的!)。 +如果你好奇我是这么做成的……其实我不知道。并不是一蹴而就的。这些都是事后总结。这些经验大部分都来自于“实时”应用[第一原则思维](https://fs.blog/first-principles/)。我的导师帮助我一起,缩小范围确定目标,这花了很多时间,我急不可耐地想“开始写代码”。但是……我知道亚伯拉罕·林肯会给我什么建议…… -### 第2阶段:我如何选择学习资源 +### 第 2 阶段:我如何选择学习资源 -我知道每个人都期望有一份“灵丹妙药”。或许是一些博客、视频、资源、教程、播客、PDF清单……揭开代码的所有“秘密”,学习效果立竿见影。 +我知道每个人都期望有一份“灵丹妙药”。或许是一些博客、视频、资源、教程、播客、PDF 清单……揭开代码的所有“秘密”,学习效果立竿见影。 没有。 @@ -326,15 +326,15 @@ 它们 _必须_ 如此,因为这就是计算机的工作方式。 -如果你和我用JavaScript、Python或Java编写相同的函数,我们将得到相同的结果。这就是计算机的工作方式——它们是[确切的](https://en.wikipedia.org/wiki/Deterministic_algorithm) [算法](https://en.wikipedia.org/wiki/Deterministic_algorithm). +如果你和我用 JavaScript、Python 或 Java 编写相同的函数,我们将得到相同的结果。这就是计算机的工作方式——它们是[确切的](https://en.wikipedia.org/wiki/Deterministic_algorithm) [算法](https://en.wikipedia.org/wiki/Deterministic_algorithm). 但是生活(和面试)**绝对不是**确切的。相同的努力、成绩、技能、智力不会产生相同的结果。 再次强调,我必须 _自学_ 。我必须将注意力从资源/博客/网站/课程上转移开,放在建立坚实的思考模型、识别相关技能、深入研究概念而不是代码实现、以新的方式应用我已经知道的东西,锻炼推理、解决问题的能力,_并_ 在推理时沟通出来我的推理过程。 -你会惊讶于我在准备Google和其他大科技公司面试时使用的资源。 +你会惊讶于我在准备 Google 和其他大科技公司面试时使用的资源。 -是的,我使用过[Leetcode](http://leetcode.com), [Algoexpert](http://algoepxert.io), [InterviewCake](http://interviewcake.com)和[Jenny的CS讲座](https://www.youtube.com/c/JennyslecturesCSITNETJRF)可能还有一些其他的资源,但我没有完整完成一个资源。 +是的,我使用过[Leetcode](http://leetcode.com), [Algoexpert](http://algoepxert.io), [InterviewCake](http://interviewcake.com)和[Jenny 的 CS 讲座](https://www.youtube.com/c/JennyslecturesCSITNETJRF)可能还有一些其他的资源,但我没有完整完成一个资源。 这不是因为我突然转移了重点,我是故意的。我意识到他们都教同样的东西,只是风格和内容略有不同。所以我使用这些资源来学习概念,并根据我对面试 _模式_ 的分析混合和搭配这些资源。 @@ -342,7 +342,7 @@ 但世界有自己的运行规律,因此所有人都涌向大公司。竞争的增强使招聘经理更难评估候选人。 -招聘经理处理这个问题的唯一方法是提高标准,候选人的处境就更难。候选人的总数不断增加,但被邀请参加面试的候选人“人才库”保持不变,依旧很狭小——通常是2-10人。不管有几百人申请,参与面试的人数永远这么多。 +招聘经理处理这个问题的唯一方法是提高标准,候选人的处境就更难。候选人的总数不断增加,但被邀请参加面试的候选人“人才库”保持不变,依旧很狭小——通常是 2-10 人。不管有几百人申请,参与面试的人数永远这么多。 因此,没有收到回复或遭到拒绝的候选人就越来越多,尤其是在牛市中。 @@ -350,31 +350,31 @@ 我还意识到大科技公司会有面试问题清单(这是“高效的”,因为面试— _非常_ 耗时,因此通过拥有一个面试官可以使用的问题库来节省时间是有意义的)。自然,如果这些问题被“泄露”,他们就不会使用这些问题——这会破坏面试过程。 -因此,从逻辑上讲,招聘经理不会问Leetcode或Algoexpert或其他网站上提供的问题。这就产生了一种“军备竞赛”——公开的问题越多,题库中的问题就越多。这导致问题和招聘策略的更多创新和变化。 +因此,从逻辑上讲,招聘经理不会问 Leetcode 或 Algoexpert 或其他网站上提供的问题。这就产生了一种“军备竞赛”——公开的问题越多,题库中的问题就越多。这导致问题和招聘策略的更多创新和变化。 这让我只有一个选择。我必须学会使用思维模型和对问题的归类总结。我可能永远不会被要求对链表进行排序或实现戴克斯特拉的最短路径算法。相反,我需要知道如何将这些算法应用于“现实世界”的实际问题。 通常,现实世界的问题看起来、听起来或闻起来都不像我们研究的练习题。练习题和竞赛代码题往往被“整齐”地打包好,有明确的限制。 -但作为一名面试官,我想知道候选人是如何思考、推理、分析、解释信息和合作的。解决问题的能力是一个亮点。候选人如果采用正确的解决方案,但时间不够用——但会提出很好的问题,并且清楚地知道如何解决问题。就仍然可以获得offer。 +但作为一名面试官,我想知道候选人是如何思考、推理、分析、解释信息和合作的。解决问题的能力是一个亮点。候选人如果采用正确的解决方案,但时间不够用——但会提出很好的问题,并且清楚地知道如何解决问题。就仍然可以获得 offer。 -后来,作为Google的一名工程师,我总能判断出有人是否 _知道_ 如何解决问题,即使他们无法及时解决。同样,当应聘者不知道如何解决某事时(这没关系——我们都在学习),这一点也很明显。 +后来,作为 Google 的一名工程师,我总能判断出有人是否 _知道_ 如何解决问题,即使他们无法及时解决。同样,当应聘者不知道如何解决某事时(这没关系——我们都在学习),这一点也很明显。 通过采用我理解问题类型和解决方案,而不是特定代码实现的方法,我可以专注于学习推理而不是学习编写特定算法。 -这种方法意味着我完成了不到40%的Algoexpert(当时它的问题是现在的一半)。我还完成了Leetcode上大约50-60个问题,其中大多数都不是“难”的问题。 +这种方法意味着我完成了不到 40%的 Algoexpert(当时它的问题是现在的一半)。我还完成了 Leetcode 上大约 50-60 个问题,其中大多数都不是“难”的问题。 -我认为“难”问题可能会出现在45分钟的面试中,大约有20%的时间出现,这意味着80%的时间它们会是简单问题或中等问题。所以优化80%更有意义,因为我还是个新人,专注于困难的问题会妨碍理解简单和中等的问题。 +我认为“难”问题可能会出现在 45 分钟的面试中,大约有 20%的时间出现,这意味着 80%的时间它们会是简单问题或中等问题。所以优化 80%更有意义,因为我还是个新人,专注于困难的问题会妨碍理解简单和中等的问题。 我使用这些资源来识别问题模式,而不仅仅是“做完“,并获得认证。这就是为什么我没有完成任何一个题库。我也没有使用“[Cracking the coding interview](https://www.amazon.com/Cracking-Coding-Interview-Programming-Questions/dp/0984782850)”这本书。 在此过程中,我还开始了理解系统设计问题。并将此记录在了[系统设计面试问题这个长博客中](https://www.freecodecamp.org/news/systems-design-for-interviews/)。 -我还决定专注于一种语言:JavaScript。这不是面试的最佳选择(经验丰富的工程师在Quora和其他地方不鼓励使用它),但我觉得不重要。面试的目的不是为了测试我对语言的选择——而是为了测试我抽象思考和解决复杂的计算机科学问题的能力。 +我还决定专注于一种语言:JavaScript。这不是面试的最佳选择(经验丰富的工程师在 Quora 和其他地方不鼓励使用它),但我觉得不重要。面试的目的不是为了测试我对语言的选择——而是为了测试我抽象思考和解决复杂的计算机科学问题的能力。 语言只是一种工具(我持有的另一个核心信念)。事实上,使用像 JS 这样的无类型语言会让我有机会谈论它的局限性或优势,证明我理解如何权衡语言选择的利弊。这样,我就可以展示更广泛的知识和洞察力,而无需实际在代码中体现。 -但是我从使用Java和C++学习了很多资源。这些语言是 Google 的主要语言。因此,被迫阅读这些语言并理解背后的原则使我不要过多关注“写出代码”,而更多地关注代码背后的推理,使我能够真正写出来。 +但是我从使用 Java 和 C++学习了很多资源。这些语言是 Google 的主要语言。因此,被迫阅读这些语言并理解背后的原则使我不要过多关注“写出代码”,而更多地关注代码背后的推理,使我能够真正写出来。 那是我的全部计划。实践、模式识别、思维模型/第一性原理思维、系统设计、做好更少的事,专注于获得面试机会,而不仅仅是学习更多的代码。 @@ -398,7 +398,7 @@ 所有这些事情也表明我的目的性很强。我对团队、公司、产品和未来真的很感兴趣。这不仅仅是我申请的另一份工作。这是主动的和个人的……而不是被动的和机会主义的。 -我相信这帮助我脱颖而出。虽然不是我面试的所有职位,但大部分面试我都获得了offer。 +我相信这帮助我脱颖而出。虽然不是我面试的所有职位,但大部分面试我都获得了 offer。 当我在招聘方面,我总是更喜欢那些真正对职位、人员、产品和公司感兴趣的候选人。那些只是为了“找到工作”的人并没有我想要的能量和动力。 @@ -408,15 +408,15 @@ 这包括技术和非技术面试、面试的形式、公司组织、运行、计划、配备、评估和权衡面试的方式。但这也要求我了解自己的长处和短处。 -在美国寻找大科技公司时,我的目标是每月进行2-3次面试——这是一个巨大的挑战,因为我不在美国,而且时区比西海岸早17小时。 +在美国寻找大科技公司时,我的目标是每月进行 2-3 次面试——这是一个巨大的挑战,因为我不在美国,而且时区比西海岸早 17 小时。 -我必须在奇怪的时间计划和安排面试,这样我才能配合我作为开发人员的日常工作和学习时间。有的面试需要6个小时,有的需要10个小时或更多。有些是“一整天结对编程”类型的面试。 +我必须在奇怪的时间计划和安排面试,这样我才能配合我作为开发人员的日常工作和学习时间。有的面试需要 6 个小时,有的需要 10 个小时或更多。有些是“一整天结对编程”类型的面试。 所有这一切都需要大量的计划和心理训练。我必须有足够的睡眠,足够的锻炼,保持我的心态和信心,完成我的日常工作,陪伴我的家人,学习并专注于我的目标。 为此,我必须对自己擅长的事情诚实。例如,我不是一个早起的人。但我可以忍受深夜。所以我相应地安排了面试、工作、睡眠甚至锻炼。 -有一些面试在凌晨2点或更晚,我之前不会睡觉(因为我真的不擅长按时起床!)。因此,我会在凌晨1点锻炼以获取能量并集中注意力,然后进行面试,然后睡到上午10点,去上班并管理我的日程安排。 +有一些面试在凌晨 2 点或更晚,我之前不会睡觉(因为我真的不擅长按时起床!)。因此,我会在凌晨 1 点锻炼以获取能量并集中注意力,然后进行面试,然后睡到上午 10 点,去上班并管理我的日程安排。 我也会小心地计划面试,这样我就不会背靠背地做两个,除非它们非常相似且有时间限制。例如,在同一周内进行带回家的测试和定时测试需要与带回家和实时代码面试不同的计划——同时还要管理工作和家庭。 @@ -424,7 +424,7 @@ ## 总结 -我相信你们中的许多人都期待这篇文章提供“内幕”技巧和某种特定的语言,以及要学习的DSA问题。我相信我给了你更好的东西。授人以鱼不如授人以渔。 +我相信你们中的许多人都期待这篇文章提供“内幕”技巧和某种特定的语言,以及要学习的 DSA 问题。我相信我给了你更好的东西。授人以鱼不如授人以渔。 除了道德原因之外,内幕消息的价值本身有限,尤其是在大科技公司。在大型公司中,团队与团队、城市与城市的情况可能大不相同。你需要了解招聘和职业发展的原则,而不仅仅是特定的语言和算法。假设所有的面试都是一样的,是一个很大的错误。 @@ -444,4 +444,4 @@ 如果你想要了解更多我从律师转行成为软件工程师的过程,可以收听播客 [episode 53](http://podcast.freecodecamp.org/53-zubin-pratap-from-lawyer-to-developer) 来自 [freeCodeCamp podcast](http://podcast.freecodecamp.org/) 以及 [Episode 207](https://lessonsfromaquitter.com/episode207/) 来自 "Lessons from a Quitter"。这两期播客大概介绍了我的成长蓝图。 -如果你对自学代码、转行和 [成为自己的代码合伙人](https://www.freecodecamp.org/news/non-technical-and-looking-for-a-technical-co-founder-2c212c01d6da/)感兴趣, 可以查阅[这里](http://linktree.com/zubinpratap)。如果转行是你梦寐以求的事情,也可以查看我在free webinar上的[转码文章](http://futurecoderstraining.com/)。 \ No newline at end of file +如果你对自学代码、转行和 [成为自己的代码合伙人](https://www.freecodecamp.org/news/non-technical-and-looking-for-a-technical-co-founder-2c212c01d6da/)感兴趣, 可以查阅[这里](http://linktree.com/zubinpratap)。如果转行是你梦寐以求的事情,也可以查看我在 free webinar 上的[转码文章](http://futurecoderstraining.com/)。 \ No newline at end of file diff --git a/chinese/articles/command-line-for-beginners.md b/chinese/articles/command-line-for-beginners.md index 09cb983d8..8b8a98a3f 100644 --- a/chinese/articles/command-line-for-beginners.md +++ b/chinese/articles/command-line-for-beginners.md @@ -5,7 +5,7 @@ ![Command Line for Beginners – How to Use the Terminal Like a Pro](https://www.freecodecamp.org/news/content/images/size/w2000/2022/03/pexels-pixabay-207580.jpg) -大家好! 在这篇文章中,我们将好好看看命令行(也被称为CLI、控制台、终端或shell)。 +大家好! 在这篇文章中,我们将好好看看命令行(也被称为 CLI、控制台、终端或 shell)。 命令行是我们作为开发者和一般的计算机用户所拥有的最有用和最有效的工具之一。但是,当你开始使用它时,可能会感到有点不知所措和复杂。 @@ -35,7 +35,7 @@ 我认为一个好的开始是准确了解什么是命令行。 -在提到这一点时,你可能听说过终端、控制台、命令行、CLI和shell这些术语。人们经常交替使用这些词,但事实是它们实际上是不同的东西。 +在提到这一点时,你可能听说过终端、控制台、命令行、CLI 和 shell 这些术语。人们经常交替使用这些词,但事实是它们实际上是不同的东西。 区分它们并不一定是很重要的知识,但它将有助于澄清事情。因此,让我们简单地解释一下每一个。 @@ -61,23 +61,23 @@ ## Shell -shell是一个**程序**,作为命令行解释器。它**处理命令**并**输出结果**。它解释和处理用户输入的命令。 +shell 是一个**程序**,作为命令行解释器。它**处理命令**并**输出结果**。它解释和处理用户输入的命令。 -与终端(terminal)一样,shell是所有操作系统中默认的程序,但也可以由用户自己安装和卸载。 +与终端(terminal)一样,shell 是所有操作系统中默认的程序,但也可以由用户自己安装和卸载。 -不同的shell也有不同的语法和特点。也可以在你的电脑上安装许多shell,并在你想的时候运行每个shell。 +不同的 shell 也有不同的语法和特点。也可以在你的电脑上安装许多 shell,并在你想的时候运行每个 shell。 -在大多数Linux和Mac操作系统中,默认的shell是Bash。而在Windows中则是Powershell。其他一些常见的shell的例子是Zsh和Fish。 +在大多数 Linux 和 Mac 操作系统中,默认的 shell 是 Bash。而在 Windows 中则是 Powershell。其他一些常见的 shell 的例子是 Zsh 和 Fish。 - shells也可以作为 **编程语言** 来工作,在这个意义上,我们可以用它们建立 **脚本(scripts)**,使我们的计算机执行某种任务。脚本只不过是一系列指令(命令),我们可以将其保存在文件中,然后在我们想执行的时候执行。 + shells 也可以作为 **编程语言** 来工作,在这个意义上,我们可以用它们建立 **脚本(scripts)**,使我们的计算机执行某种任务。脚本只不过是一系列指令(命令),我们可以将其保存在文件中,然后在我们想执行的时候执行。 -我们将在本文的后面看一下脚本的内容。现在只要记住,shell是你的计算机用来 **理解** 和执行你的命令的程序,你也可以用它来编写任务。 +我们将在本文的后面看一下脚本的内容。现在只要记住,shell 是你的计算机用来 **理解** 和执行你的命令的程序,你也可以用它来编写任务。 -还要记住,终端是shell运行的程序。但这两个程序是独立的。这意味着,我可以让任何shell在任何终端上运行。在这个意义上,两个程序之间没有依赖性。 +还要记住,终端是 shell 运行的程序。但这两个程序是独立的。这意味着,我可以让任何 shell 在任何终端上运行。在这个意义上,两个程序之间没有依赖性。 ## Command line or CLI (command line interface) -CLI是一个界面,我们在其中输入命令供计算机处理。用简单来说,它是一个空间,你在其中输入计算机将处理的命令。 +CLI 是一个界面,我们在其中输入命令供计算机处理。用简单来说,它是一个空间,你在其中输入计算机将处理的命令。 ![cli](https://www.freecodecamp.org/news/content/images/2022/03/cli.png) @@ -90,88 +90,88 @@ CLI是一个界面,我们在其中输入命令供计算机处理。用简单 # Why should I even care about using the terminal? -我们刚刚提到,大多数操作系统都有一个图形用户界面。因此,如果我们可以在屏幕上看到东西,并点击来做我们想做的事情,你可能会想,为什么你要学习这个复杂的终端(terminal)/cli/shell东西呢? +我们刚刚提到,大多数操作系统都有一个图形用户界面。因此,如果我们可以在屏幕上看到东西,并点击来做我们想做的事情,你可能会想,为什么你要学习这个复杂的终端(terminal)/cli/shell 东西呢? -第一个原因是,对于许多任务来说,它只是**更有效率**。我们稍后会看到一些例子,但是有很多任务在GUI中需要在不同的窗口中进行多次点击。但在CLI上,这些任务可以用一个命令来执行。 +第一个原因是,对于许多任务来说,它只是**更有效率**。我们稍后会看到一些例子,但是有很多任务在 GUI 中需要在不同的窗口中进行多次点击。但在 CLI 上,这些任务可以用一个命令来执行。 从这个意义上说,熟悉命令行将帮助你节省时间,能够更快地执行你的任务。 -第二个原因是,通过使用命令,你可以轻松地**自动化任务**。如前所述,我们可以用我们的shell建立脚本,然后在我们想要的时候执行这些脚本。在处理那些我们不想重复做的重复性任务时,这非常有用。 +第二个原因是,通过使用命令,你可以轻松地**自动化任务**。如前所述,我们可以用我们的 shell 建立脚本,然后在我们想要的时候执行这些脚本。在处理那些我们不想重复做的重复性任务时,这非常有用。 仅举一些例子,我们可以建立一个脚本,为我们创建一个新的在线 repo,或者为我们在云提供商上创建一个特定的基础设施,或者执行一个更简单的任务,如每小时改变我们的屏幕墙纸。 脚本是一种节省重复性任务时间的好方法。 -第三个原因是,有时CLI将是我们能够与计算机互动的***唯一方式。例如,当你需要与云平台服务器互动时。在大多数情况下,你不会有一个GUI,只有一个CLI来运行命令。 +第三个原因是,有时 CLI 将是我们能够与计算机互动的***唯一方式。例如,当你需要与云平台服务器互动时。在大多数情况下,你不会有一个 GUI,只有一个 CLI 来运行命令。 -因此,熟练掌握CLI将使你能够在所有情况下与计算机互动。 +因此,熟练掌握 CLI 将使你能够在所有情况下与计算机互动。 最后一个原因是它看起来很酷,很有趣。你不会看到电影中的黑客在他们的电脑周围点击,对吗?) # Different kinds of shells -在深入研究你可以在终端运行的实际命令之前,我认为认识不同类型的shell以及如何识别你目前正在运行的shell很重要。 +在深入研究你可以在终端运行的实际命令之前,我认为认识不同类型的 shell 以及如何识别你目前正在运行的 shell 很重要。 -不同的shell有不同的语法和不同的功能,所以要知道到底要输入什么命令,你首先要知道你在运行什么shell。 +不同的 shell 有不同的语法和不同的功能,所以要知道到底要输入什么命令,你首先要知道你在运行什么 shell。 ## A bit of history – Posix -对于shells,有一个通用的标准,叫做 **[Posix](https://en.wikipedia.org/wiki/POSIX)**。 +对于 shells,有一个通用的标准,叫做 **[Posix](https://en.wikipedia.org/wiki/POSIX)**。 -Posix对shell的作用与ECMAScript对JavaScript的作用非常相似。它是一个标准,规定了所有shell应该遵守的某些特性和功能。 +Posix 对 shell 的作用与 ECMAScript 对 JavaScript 的作用非常相似。它是一个标准,规定了所有 shell 应该遵守的某些特性和功能。 -这个标准是在20世纪80年代建立的,目前大多数shell都是根据这个标准开发的。这就是为什么大多数shell共享类似的语法和类似的特征。 +这个标准是在 20 世纪 80 年代建立的,目前大多数 shell 都是根据这个标准开发的。这就是为什么大多数 shell 共享类似的语法和类似的特征。 ## How do I know what shell I'm running? -要知道你当前运行的是什么shell,只需打开你的终端并输入`echo $0`。这将打印出当前运行的程序名称,在这种情况下,它就是实际的shell。 +要知道你当前运行的是什么 shell,只需打开你的终端并输入`echo $0`。这将打印出当前运行的程序名称,在这种情况下,它就是实际的 shell。 ![screenshot-1](https://www.freecodecamp.org/news/content/images/2022/04/screenshot-1.png) ## What shell is better? -大多数shell之间没有很大的区别。由于它们大多符合相同的标准,你会发现它们中的大多数工作都是类似的。 +大多数 shell 之间没有很大的区别。由于它们大多符合相同的标准,你会发现它们中的大多数工作都是类似的。 不过,你可能想知道一些细微的差别: -- 如前所述,**Bash**是使用最广泛的,在Mac和Linux上默认安装。 -- *Zsh**与Bash非常相似,但它是在Bash之后创建的,并且比它有一些不错的改进。如果你想更详细地了解它的区别,[这里有一篇很酷的文章](https://linuxhint.com/differences_between_bash_zsh/#:~:text=It%20has%20many%20features%20like,by%20default%20with%20Linux%20distribution.) 关于 zsh。 -- **Fish**是另一个常用的shell,它有一些不错的内置功能和配置,如自动完成和语法高亮。关于Fish的问题是,它不是Posix兼容,而Bash和Zsh是。这意味着你能在Bash和Zsh上运行的一些命令不能在Fish上运行,反之亦然。这使得Fish脚本与Bash和Zsh相比对大多数计算机的兼容性较差。 -- 还有一些其他的shell,比如**Ash**或**Dash**(命名只是让一切变得更加混乱,我知道......),它们是Posix shell的精简版本。这意味着它们只提供Posix所需要的功能,而没有其他的。而Bash和Zsh **增加** 了比Posix要求更多的功能。 +- 如前所述,**Bash**是使用最广泛的,在 Mac 和 Linux 上默认安装。 +- *Zsh**与 Bash 非常相似,但它是在 Bash 之后创建的,并且比它有一些不错的改进。如果你想更详细地了解它的区别,[这里有一篇很酷的文章](https://linuxhint.com/differences_between_bash_zsh/#:~:text=It%20has%20many%20features%20like,by%20default%20with%20Linux%20distribution.) 关于 zsh。 +- **Fish**是另一个常用的 shell,它有一些不错的内置功能和配置,如自动完成和语法高亮。关于 Fish 的问题是,它不是 Posix 兼容,而 Bash 和 Zsh 是。这意味着你能在 Bash 和 Zsh 上运行的一些命令不能在 Fish 上运行,反之亦然。这使得 Fish 脚本与 Bash 和 Zsh 相比对大多数计算机的兼容性较差。 +- 还有一些其他的 shell,比如**Ash**或**Dash**(命名只是让一切变得更加混乱,我知道……),它们是 Posix shell 的精简版本。这意味着它们只提供 Posix 所需要的功能,而没有其他的。而 Bash 和 Zsh **增加** 了比 Posix 要求更多的功能。 -事实上,shells增加了更多的功能,使它们更容易、更友好地进行交互,但执行脚本和命令的速度较慢。 +事实上,shells 增加了更多的功能,使它们更容易、更友好地进行交互,但执行脚本和命令的速度较慢。 -因此,一种常见的做法是使用Bash或Zsh这样的 **增强型** shell进行一般的交互,而使用Ash或Dash这样的 **剥离型** shell来执行脚本。 +因此,一种常见的做法是使用 Bash 或 Zsh 这样的 **增强型** shell 进行一般的交互,而使用 Ash 或 Dash 这样的 **剥离型** shell 来执行脚本。 -当我们在后面讨论脚本时,我们将看到如何定义什么shell来执行一个给定的脚本。 +当我们在后面讨论脚本时,我们将看到如何定义什么 shell 来执行一个给定的脚本。 -如果你对这些shell之间更详细的比较感兴趣,[这里有一个视频,解释得非常清楚](https://www.youtube.com/watch?v=dRdGq8khTJc): +如果你对这些 shell 之间更详细的比较感兴趣,[这里有一个视频,解释得非常清楚](https://www.youtube.com/watch?v=dRdGq8khTJc): -如果必须推荐一个shell,我会推荐bash,因为它是最标准和最常用的一个。这意味着你能够将你的知识转化为大多数环境。 +如果必须推荐一个 shell,我会推荐 bash,因为它是最标准和最常用的一个。这意味着你能够将你的知识转化为大多数环境。 -但是,事实上,大多数shell之间并没有很大的区别。所以在任何情况下,你都可以尝试几种,看看你最喜欢哪一种。:) +但是,事实上,大多数 shell 之间并没有很大的区别。所以在任何情况下,你都可以尝试几种,看看你最喜欢哪一种。:) ### A comment about customization -我刚刚提到,Fish有内置的配置,比如自动完成和语法高亮。这在Fish中是内置的,但在Bash或Zsh中你也可以配置这些功能。 +我刚刚提到,Fish 有内置的配置,比如自动完成和语法高亮。这在 Fish 中是内置的,但在 Bash 或 Zsh 中你也可以配置这些功能。 -关键是shells是可定制的。你可以编辑程序如何工作,你有哪些命令,你的提示符显示什么信息,等等。 +关键是 shells 是可定制的。你可以编辑程序如何工作,你有哪些命令,你的提示符显示什么信息,等等。 -我们不会在这里看到详细的定制选项,但要知道,当你在你的计算机上安装shell时,你的系统上将会创建某些文件。以后你可以编辑这些文件来定制你的程序。 +我们不会在这里看到详细的定制选项,但要知道,当你在你的计算机上安装 shell 时,你的系统上将会创建某些文件。以后你可以编辑这些文件来定制你的程序。 此外,网上有许多插件,允许你以更容易的方式定制你的外壳。你只需安装它们并获得该插件提供的功能。一些例子是 [OhMyZsh](https://ohmyz.sh/) 和 [Starship](https://starship.rs/)。 这些定制选项对终端也是如此。 -因此,你不仅有许多shell和终端(terminal)选项可供选择--你还为每个shell和终端有许多配置选项。 +因此,你不仅有许多 shell 和终端(terminal)选项可供选择--你还为每个 shell 和终端有许多配置选项。 如果你刚开始使用,所有这些信息可能会让你感到有点不知所措。但只要知道有许多选项可用,而且每个选项也可以被定制。 # Most common and useful commands to use -现在我们对CLI的工作方式有了一个基础,让我们深入了解一下最有用的命令,你可以开始在你的日常工作中使用。 +现在我们对 CLI 的工作方式有了一个基础,让我们深入了解一下最有用的命令,你可以开始在你的日常工作中使用。 -请记住,这些例子将基于我目前的配置(Linux操作系统上的Bash)。但无论如何,大多数命令应该适用于大多数配置。 +请记住,这些例子将基于我目前的配置(Linux 操作系统上的 Bash)。但无论如何,大多数命令应该适用于大多数配置。 - **Echo**在终端打印出我们传递给它的任何参数。 @@ -187,7 +187,7 @@ pwd // Output: /home/German - **ls** 向你展示你当前所在的目录的内容。它将向你展示你当前目录中的文件和其他目录。 -例如,这里我在最近工作的一个React项目目录上: +例如,这里我在最近工作的一个 React 项目目录上: ```shell ls // Output: @@ -202,7 +202,7 @@ ls -a // Output: .. .git node_modules package-lock.json README.md ``` -- **cd** 是Change directory的缩写,它将把你从你的当前目录带到另一个目录。 +- **cd** 是 Change directory 的缩写,它将把你从你的当前目录带到另一个目录。 在我的 `home` 目录下,我可以输入`cd Desktop`,它将把我带到桌面目录。 @@ -222,7 +222,7 @@ ls -a // Output: - **cp** 允许你复制文件或目录。这个命令需要两个参数:第一个是你想复制的文件或目录,第二个是你复制的目的地(你想把文件/目录复制到哪里)。 -如果我想在同一目录下复制我的txt文件,我可以输入以下内容: +如果我想在同一目录下复制我的 txt 文件,我可以输入以下内容: ```shell cp test.txt testCopy.txt @@ -279,11 +279,11 @@ cd: cd [-L|[-P [-e]] [-@]] [dir] Change the shell working directory. ``` -将当前目录改为DIR。默认的DIR是HOME shell 变量的值。 +将当前目录改为 DIR。默认的 DIR 是 HOME shell 变量的值。 -变量CDPATH定义了包含DIR的目录的搜索路径。CDPATH中的备选目录名由冒号`:`分隔。 +变量 CDPATH 定义了包含 DIR 的目录的搜索路径。CDPATH 中的备选目录名由冒号`:`分隔。 -如果DIR以`...`开头,则空目录名与当前目录相同。 +如果 DIR 以`...`开头,则空目录名与当前目录相同。 - 以类似的方式,**man** 命令将返回任何特定命令的信息。 @@ -314,7 +314,7 @@ Change the shell working directory. ... ``` -你甚至可以输入 "man bash",这将返回一个关于这个shell的所有知识的详细手册。 ;) +你甚至可以输入 "man bash",这将返回一个关于这个 shell 的所有知识的详细手册。 ;) - **code**将打开你的默认代码编辑器。如果你单独输入这个命令,它只是打开你最近打开的文件/目录的编辑器。 @@ -322,17 +322,17 @@ Change the shell working directory. 或者通过传递新的文件名来打开一个新的文件: `code thisIsAJsFile.js`. -- **edit**将在你的默认命令行文本编辑器上打开文本文件(如果你是在Mac或Linux上,可能是Nano或Vim)。 +- **edit**将在你的默认命令行文本编辑器上打开文本文件(如果你是在 Mac 或 Linux 上,可能是 Nano 或 Vim)。 如果你打开了你的文件,然后无法退出你的编辑器,首先看一下这个: ![vimExit](https://www.freecodecamp.org/news/content/images/2022/03/vimExit.png) -然后输入 `:q!` 并回车。(译者注: q是quit的缩写) +然后输入 `:q!` 并回车。(译者注: q 是 quit 的缩写) -这个备忘录很有趣,因为每个人一开始都在为使用CLI文本编辑器而挣扎,因为大多数操作(比如退出编辑器)都是通过键盘快捷键完成的。使用这些编辑器是另外一个话题,如果你有兴趣了解更多,就去找教程吧。 ;) +这个备忘录很有趣,因为每个人一开始都在为使用 CLI 文本编辑器而挣扎,因为大多数操作(比如退出编辑器)都是通过键盘快捷键完成的。使用这些编辑器是另外一个话题,如果你有兴趣了解更多,就去找教程吧。 ;) -- **ctrl+c** 允许你退出终端正在运行的当前进程。例如,如果你正在用`npx create-react-app`创建一个react应用,并想在某个时候取消构建,只要按下组合键 **ctrl+c**,它就会停止。 +- **ctrl+c** 允许你退出终端正在运行的当前进程。例如,如果你正在用`npx create-react-app`创建一个 react 应用,并想在某个时候取消构建,只要按下组合键 **ctrl+c**,它就会停止。 - 从终端复制文本可以用**ctrl+shift+c**来完成,粘贴可以用**ctrl+shift+v**来完成。 @@ -342,15 +342,15 @@ Change the shell working directory. - 通过按**上 下方向键**你可以浏览你之前输入的命令。 -- 按下 **tab**,你将得到基于你到目前为止所写的命令的自动补全。按下 **两次tab**,你会得到基于你到目前为止所写命令的建议。 +- 按下 **tab**,你将得到基于你到目前为止所写的命令的自动补全。按下 **两次 tab**,你会得到基于你到目前为止所写命令的建议。 例如,如果我输入 **edit test** 并按下两次 tab,我就会得到 `testFolder/ test.txt`。如果我写`edit test.`并按下 **tab**,我的文本会自动补全为`edit test.txt`。 ## Git commands -除了在文件系统中工作和安装/卸载东西,作为一个开发者在终端最常使用可能是Git和线上仓库的交互。 +除了在文件系统中工作和安装/卸载东西,作为一个开发者在终端最常使用可能是 Git 和线上仓库的交互。 -从终端做这些事情比到处点击更有效率,所以让我们看看最有用的git命令吧。 +从终端做这些事情比到处点击更有效率,所以让我们看看最有用的 git 命令吧。 - **git init**将为你创建一个新的本地版本库。 @@ -378,7 +378,7 @@ On branch master nothing to commit, working tree clean ``` -- **git clone**允许你克隆(复制)一个仓库到你当前所在的目录。请记住,你既可以克隆远程仓库(GitHub、GitLab等),也可以克隆本地仓库(那些存储在你电脑中的仓库)。 +- **git clone**允许你克隆(复制)一个仓库到你当前所在的目录。请记住,你既可以克隆远程仓库(GitHub、GitLab 等),也可以克隆本地仓库(那些存储在你电脑中的仓库)。 ```shell git clone https://github.com/coccagerman/MazeGenerator.git // output: @@ -390,13 +390,13 @@ remote: Total 15 (delta 1), reused 11 (delta 0), pack-reused 0 Unpacking objects: 100% (15/15), done. ``` -- **git remote add origin**是用来详细说明你的项目要使用的远程仓库的URL。如果你想在某个时候改变它,你可以通过使用`git remote set-url origin`命令来实现。 +- **git remote add origin**是用来详细说明你的项目要使用的远程仓库的 URL。如果你想在某个时候改变它,你可以通过使用`git remote set-url origin`命令来实现。 ```shell git remote add origin https://github.com/coccagerman/testRepo.git ``` -> 请记住,你需要先创建你的远程版本,以便获得其URL。稍后我们将看到你如何通过一个小脚本从命令行中做到这一点。 ;) +> 请记住,你需要先创建你的远程版本,以便获得其 URL。稍后我们将看到你如何通过一个小脚本从命令行中做到这一点。 ;) - **git remote -v**可以让你列出当前正在使用的远程仓库。 @@ -477,7 +477,7 @@ Author: German Cocca ... ``` -- **--help/**标志会显示关于某个命令的信息,与bash的工作方式完全相同。 +- **--help/**标志会显示关于某个命令的信息,与 bash 的工作方式完全相同。 ```shell git diff --help // output: @@ -496,7 +496,7 @@ SYNOPSIS 现在我们准备进入命令行中真正有趣和很棒的部分,即脚本! -正如我之前提到的,一个脚本只不过是一系列我们可以在任何时候执行的命令或指令。为了解释我们如何编写一个脚本,我们将使用一个简单的例子,让我们通过运行一个命令来创建一个github repo。 ;) +正如我之前提到的,一个脚本只不过是一系列我们可以在任何时候执行的命令或指令。为了解释我们如何编写一个脚本,我们将使用一个简单的例子,让我们通过运行一个命令来创建一个 github repo。 ;) - 首先要做的是创建一个`.sh`文件。你可以把它放在任何地方。我把我的文件称为 `newGhRepo.sh`。 @@ -504,13 +504,13 @@ SYNOPSIS - 在我们的第一行,我们将写下以下内容。`#! /bin/sh`。 -这被称为**shebang**,它的作用是声明什么shell来运行这个脚本。 +这被称为**shebang**,它的作用是声明什么 shell 来运行这个脚本。 -还记得我们以前提到过,我们可以用一个给定的shell来进行一般的交互,而用另一个给定的shell来执行一个脚本吗?那么,shebang就是决定由哪个shell来运行脚本的指令。 +还记得我们以前提到过,我们可以用一个给定的 shell 来进行一般的交互,而用另一个给定的 shell 来执行一个脚本吗?那么,shebang 就是决定由哪个 shell 来运行脚本的指令。 -如前所述,我们使用一个 "精简的 "shell(也称为sh shells)来运行脚本,因为它们更有效率(尽管说实话,这种差别可能是难以察觉的,这只是一种个人偏好)。在我的电脑中,我把dash作为我的sh shell。 +如前所述,我们使用一个 "精简的 "shell(也称为 sh shells)来运行脚本,因为它们更有效率(尽管说实话,这种差别可能是难以察觉的,这只是一种个人偏好)。在我的电脑中,我把 dash 作为我的 sh shell。 -如果我们想让这个脚本用bash运行,那么shebang应该是`#! /bin/bash`。 +如果我们想让这个脚本用 bash 运行,那么 shebang 应该是`#! /bin/bash`。 - 我们的下一行将是`repoName=$1`。 @@ -542,7 +542,7 @@ done 我们可以这样做: -1. 当repoName变量未被分配时(`while [ -z "$repoName" ]`) +1. 当 repoName 变量未被分配时(`while [ -z "$repoName" ]`) 2. 将此信息打印到控制台(`echo '提供一个版本库名称'`) 3. 然后读取用户提供的任何输入,并将输入分配给 `repoName` 变量 (`read -r -p $'Repository name:' repoName`) @@ -555,7 +555,7 @@ git add . git commit -m "First commit" ``` -这就是创建一个readme文件,并在第一行写上 repo名称(`echo "# $repoName" >> README.md`),然后初始化git repo并做第一次提交。 +这就是创建一个 readme 文件,并在第一行写上 repo 名称(`echo "# $repoName" >> README.md`),然后初始化 git repo 并做第一次提交。 - 然后,是时候把我们的 repo 上传到 github 了。要做到这一点,我们将利用[github API](https://docs.github.com/en/rest/reference/repos)的优势,使用以下命令: @@ -565,15 +565,15 @@ git commit -m "First commit" 接下来我们使用`-u`标志来声明我们为之创建的用户(`-u coccagerman`)。 -接下来是GitHub API提供的 endpoint(`https://api.github.com/user/repos`) +接下来是 GitHub API 提供的 endpoint(`https://api.github.com/user/repos`) 最后,我们使用`-d`标志来传递参数给这个命令。在这种情况下,我们要指出版本库的名称(为此我们使用`repoName`变量),并将`private`选项设置为`false`,因为我们希望我们的版本库是公开的。 -在API中还有很多其他的配置选项,所以[查看文档](https://docs.github.com/en/rest/reference/repos#create-a-repository-for-the-authenticated-user)了解更多信息。 +在 API 中还有很多其他的配置选项,所以[查看文档](https://docs.github.com/en/rest/reference/repos#create-a-repository-for-the-authenticated-user)了解更多信息。 - 运行此命令后,GitHub 会提示我们输入 **(private token)私人令牌** 进行认证。 -如果你还没有私人令牌,你可以在GitHub的 **Settings > Developer settings > Personal access tokens** +如果你还没有私人令牌,你可以在 GitHub 的 **Settings > Developer settings > Personal access tokens** ![screenshot](https://www.freecodecamp.org/news/content/images/2022/04/screenshot.png) @@ -581,7 +581,7 @@ git commit -m "First commit" ![screenshot_2](https://www.freecodecamp.org/news/content/images/2022/04/screenshot_2.png) -- 酷,我们现在几乎已经完成了! 现在我们需要的是我们新创建的GitHub repo的 **remote URL**。 +- 酷,我们现在几乎已经完成了! 现在我们需要的是我们新创建的 GitHub repo 的 **remote URL**。 为了得到它,我们将再次使用 curl 和 GitHub API,像这样: @@ -593,13 +593,13 @@ GIT_URL=$(curl -H "Accept: application/vnd.github.v3+json" https://api.github.co `-H`标志设置了我们请求的头。 -然后我们传递GitHub的API endpoint,其中应包含我们的用户名(user name)和 repo名称 (`https://api.github.com/repos/coccagerman/"$repoName"`). +然后我们传递 GitHub 的 API endpoint,其中应包含我们的用户名(user name)和 repo 名称 (`https://api.github.com/repos/coccagerman/"$repoName"`). 然后,我们就 **piping** 了我们请求的返回值。管道化(piping)只是意味着将一个进程的返回值作为另一个进程的输入值。我们可以用`|`符号来做,比如` | `。 -最后我们运行 `jq` 命令,这是一个处理JSON输入的工具。在这里,我们告诉它获得 `.clone_url` 的值,根据GitHub API提供的数据格式,这是我们的远程git URL的位置。 +最后我们运行 `jq` 命令,这是一个处理 JSON 输入的工具。在这里,我们告诉它获得 `.clone_url` 的值,根据 GitHub API 提供的数据格式,这是我们的远程 git URL 的位置。 -- 最后一步,我们将主分支重命名为main,添加我们刚刚获得的远程源码,然后将我们的代码推送到GitHub上! =D +- 最后一步,我们将主分支重命名为 main,添加我们刚刚获得的远程源码,然后将我们的代码推送到 GitHub 上! =D ```shell git branch -M main @@ -635,7 +635,7 @@ git push -u origin main - 现在是测试我们的脚本的时候了! 要 **执行** 它,我们可以做两件事。 -一种方法是输入shell名称,并将文件作为参数传入,比如。`dash ../ger/code/projects/scripts/newGhRepo.sh`。 +一种方法是输入 shell 名称,并将文件作为参数传入,比如。`dash ../ger/code/projects/scripts/newGhRepo.sh`。 另一种是通过运行`chmod u+x ../ger/code/projects/scripts/newGhRepo.sh`,使文件有可执行权限。 @@ -647,13 +647,13 @@ git push -u origin main 在这里, **bash aliases(别名)** 来解决我们的问题。 -别名是bash提供的一种方法,用于为我们想要运行的确切命令命名。 +别名是 bash 提供的一种方法,用于为我们想要运行的确切命令命名。 -要创建一个新的别名,我们需要编辑我们系统中的bash配置文件。这些文件通常位于主目录中。别名可以在不同的文件中定义(主要是`.bashrc`或`.bash_aliases`)。 +要创建一个新的别名,我们需要编辑我们系统中的 bash 配置文件。这些文件通常位于主目录中。别名可以在不同的文件中定义(主要是`.bashrc`或`.bash_aliases`)。 我的系统上有一个`.bash_aliases`文件,所以我们来编辑它。 -- 在我们的CLI中,我们输入`cd ~`来查看主目录。 +- 在我们的 CLI 中,我们输入`cd ~`来查看主目录。 - 然后我们可以输入`ls -a`列出所有文件(包括隐藏的),并检查我们的系统中是否有`.bashrc`或`.bash_aliases`文件。 @@ -678,8 +678,8 @@ git push -u origin main 当你开始工作时,终端可能感觉是一个令人生畏和复杂的地方。但是,花时间和精力去学习它的内在和延展,肯定是值得的。极大提高效率。 -> 如果你有兴趣学习更多关于终端和Bash的知识,Zach Gollwitzer在Youtube上有 [一个很棒的速成系列课程](https://www.youtube.com/playlist?list=PLYQSCk-qyTW0d88jNocdi_YIFMA5Fnpug). -> 他在其他主题上也有很好的教程,如Node和Javascript,所以我推荐你关注他;) +> 如果你有兴趣学习更多关于终端和 Bash 的知识,Zach Gollwitzer 在 Youtube 上有 [一个很棒的速成系列课程](https://www.youtube.com/playlist?list=PLYQSCk-qyTW0d88jNocdi_YIFMA5Fnpug). +> 他在其他主题上也有很好的教程,如 Node 和 Javascript,所以我推荐你关注他;) 像往常一样,我希望你喜欢这篇文章,并学到一些新东西。如果你愿意,你也可以在 [linkedin](https://www.linkedin.com/in/germancocca/) 或 [twitter](https://twitter.com/CoccaGerman) 上关注我。 diff --git a/chinese/articles/complete-introduction-to-the-most-useful-javascript-array-methods.md b/chinese/articles/complete-introduction-to-the-most-useful-javascript-array-methods.md index 6e0cb4acd..13122daa3 100644 --- a/chinese/articles/complete-introduction-to-the-most-useful-javascript-array-methods.md +++ b/chinese/articles/complete-introduction-to-the-most-useful-javascript-array-methods.md @@ -374,7 +374,7 @@ console.log(user); // { name: "John Cena", age: 34 } 可以看到的是,使用普通的 for 循环会使代码量更大并且更难理解。但是使用 `find` 方法,我们可以通过容易理解的方式写出同样的代码。 -### find 方法的优点: +### find 方法的优点 - 我们可以不用写大量代码就快速找到任意一个元素 - 找到匹配元素后会立即停止循环,因此不用写额外的 break 语句 @@ -411,7 +411,7 @@ console.log(index); // 1 这里是 [Code Pen 演示](https://codepen.io/myogeshchavan97/pen/JjbWebQ?editors=0012)。 -这里我们得到的输出 **1** 就是名为 `John` 的第一个对象的索引。注意索引是从0开始的。 +这里我们得到的输出 **1** 就是名为 `John` 的第一个对象的索引。注意索引是从 0 开始的。 与上面代码相同的 for 循环代码如下: @@ -482,7 +482,7 @@ console.log(employee); // [ { name: "John Cena", age: 34 }, { name: "John Carte" 因此使用 `filter` ,在找到数组中符合条件的元素时也不会停止搜索其他满足条件的元素,之后会返回所有满足条件的元素。 -> `find` 与 `filter` 的主要区别在于 `find` 只返回数组中匹配的第一个元素,而 `filter` 返回数组中所有匹配元素。 +> `find` 与 `filter` 的主要区别在于 `find` 只返回数组中匹配的第一个元素,而 `filter` 返回数组中所有匹配元素。 注意 `filter` 方法返回的是一个数组,如果没有元素通过检测条件,将会返回一个空数组。 @@ -509,7 +509,7 @@ console.log(filtered); // [ { name: "John Cena", age: 34 }, { name: "John Carte" 这里是 [Code Pen 演示](https://codepen.io/myogeshchavan97/pen/qBqrwaq?editors=0011). -### 使用 filter 方法的优点: +### 使用 filter 方法的优点 - 可以让我们快速找到数组中所有匹配的元素 - 不管有没有匹配到,都会返回一个数组,因此可以避免写额外的 `if` 条件 @@ -578,11 +578,11 @@ window.onload = function () { 在上面的代码中,如果 callback 函数中, `fields` 数组中的每一项都返回 `true` ,那 `every` 方法就返回 `true` 。 如果 `fields` 数组中任意一个 callback 函数返回 `false` 值, `every` 方法就会返回 `false` 结果。 -### 使用 every 方法的优点: +### 使用 every 方法的优点 - 能让我们不用写大量代码就快速检测出是否所有元素都满足特定的标准 -### 浏览器支持: +### 浏览器支持 - IE9 及以上和所有现代浏览器 - Edge 12 及以上 @@ -619,7 +619,7 @@ console.log(containsPositive); // false 这里有一些使用 `some` 方法的有用场景: -### `Some` 方法例 1: +### `Some` 方法例 1 假设有一个应聘者的列表,我们想检查某个特定的应聘者是否在这个数组中,如果有的话,同时要得到在数组总的索引位置。 @@ -647,7 +647,7 @@ console.log(employee, indexValue); // true 1 这里是 [Code Pen 演示](https://codepen.io/myogeshchavan97/pen/ExNWOvz?editors=0011)。 -### `Some` 方法示例 2: +### `Some` 方法示例 2 `forEach`, `map`, 和 `filter` 方法会将数组中的所有元素从头到尾执行一次。没有终止循环的方法,除非找到特定的元素。 @@ -660,12 +660,12 @@ console.log(employee, indexValue); // true 1 如上面例 1 中看到的,一旦找到特定的匹配值, `some` 方法就会停止循环。 -### 使用 some 方法的优点: +### 使用 some 方法的优点 - 能让我们不用写大量代码就快速检测是否有匹配一定标准的元素 - 能快速终止循环,这是上面其他循环方法没有的 -### 浏览器支持: +### 浏览器支持 - IE9 及以上和所有现代浏览器 - Edge 12 及以上 @@ -769,7 +769,7 @@ console.log(sum); // 6 这里是 [Code Pen 演示](https://codepen.io/myogeshchavan97/pen/OJbpaOg?editors=0012). -### 使用 reduce 方法的优点: +### 使用 reduce 方法的优点 - 使用 `reduce` 可以基于数组生成任何简单或者复杂类型的数据 - 它能记住循环中之前的返回值,因此可以避免创建一个保存历史值的全局变量 diff --git a/chinese/articles/computer-skills-for-resume-software-skill-proficiency-guide.md b/chinese/articles/computer-skills-for-resume-software-skill-proficiency-guide.md index ac73b589d..96716c57a 100644 --- a/chinese/articles/computer-skills-for-resume-software-skill-proficiency-guide.md +++ b/chinese/articles/computer-skills-for-resume-software-skill-proficiency-guide.md @@ -19,7 +19,7 @@ 1. [为什么计算机知识很重要](./#why-computer-literacy-is-important) 2. [软件技能指南](./#software-skills-guide) - - [掌握操作系统和基本的IT故障排除知识](./#knowledge-of-operating-systems-and-of-basic-IT-troubleshooting) + - [掌握操作系统和基本的 IT 故障排除知识](./#knowledge-of-operating-systems-and-of-basic-IT-troubleshooting) - [熟悉网络浏览器和网络搜索技能](./#knowledge-of-web-browsers-and-web-searching-skills) - [项目管理软件的知识](./#knowledge-of-project-management-software) - [通信软件知识](./#knowledge-of-communication-software) @@ -65,7 +65,7 @@ 例如,如果你是一名行政助理,你可能不会在日常工作中使用命令行。 -如果你是一名软件开发人员,你不一定会写Excel公式。 +如果你是一名软件开发人员,你不一定会写 Excel 公式。 也就是说,计算机软件的知识将使你比缺乏这种知识的人更进一步。 @@ -81,45 +81,45 @@ 实现数字扫盲的第一步是对操作系统有一个全面的了解。 -如果这不是你的目标,就没有必要成为一个IT专家,而是要了解它们的基本功能。 +如果这不是你的目标,就没有必要成为一个 IT 专家,而是要了解它们的基本功能。 -[操作系统(简称OS)](https://www.freecodecamp.org/news/what-is-an-os-operating-system-definition-for-beginners/)管理计算机上的所有软件和硬件,并允许你首先与计算机互动。 +[操作系统(简称 OS)](https://www.freecodecamp.org/news/what-is-an-os-operating-system-definition-for-beginners/)管理计算机上的所有软件和硬件,并允许你首先与计算机互动。 三个最广泛使用的操作系统是: -- Windows操作系统,由微软拥有。 +- Windows 操作系统,由微软拥有。 - macOS,由苹果公司拥有。 -- Linux,这是一个开源的操作系统。与Windows和macOS相比,Linux不是专有软件。没有一家公司拥有它。任何有知识的人都可以作出贡献,进行修改和改进,并帮助维护它。 +- Linux,这是一个开源的操作系统。与 Windows 和 macOS 相比,Linux 不是专有软件。没有一家公司拥有它。任何有知识的人都可以作出贡献,进行修改和改进,并帮助维护它。 -此外,至少拥有一些基本的IT故障排除技能是有帮助的。这些知识将帮助你在遇到困难时找到解决方案,并帮助你自己解决问题。 +此外,至少拥有一些基本的 IT 故障排除技能是有帮助的。这些知识将帮助你在遇到困难时找到解决方案,并帮助你自己解决问题。 例如,一些基本的计算机故障排除技能是。: -- 识别网络图标,知道WiFi是开还是关 +- 识别网络图标,知道 WiFi 是开还是关 - 知道当计算机程序卡顿时该怎么做。 - 知道如何处理死机。 - 知道何时以及如何重启系统。 - 知道如何重启计算机或与使用任务管理器处理无反应的程序。 - 知道当电脑不开机时该怎么做。 -- 知道如何解决USB记忆棒和外部硬盘驱动器的问题。 +- 知道如何解决 USB 记忆棒和外部硬盘驱动器的问题。 - 知道如何加快缓慢的电脑速度。 - 知道如何进行基本的网络诊断。 - 知道何时更新系统,何时需要进行常规维护。 - 知道用户账户管理。 -你将使用GUI(图形用户界面)在电脑上进行操作。 +你将使用 GUI(图形用户界面)在电脑上进行操作。 -不同的按钮和下拉菜单,指向和鼠标点击,以及你在屏幕上看到的不同图标使GUI易于使用。 +不同的按钮和下拉菜单,指向和鼠标点击,以及你在屏幕上看到的不同图标使 GUI 易于使用。 -然而,有时你可能需要使用命令行界面(或简称CLI)。 +然而,有时你可能需要使用命令行界面(或简称 CLI)。 -CLI是一个基于文本的程序。你只用键盘导航在提示中输入命令,在执行重复性任务时可以节省大量时间。 +CLI 是一个基于文本的程序。你只用键盘导航在提示中输入命令,在执行重复性任务时可以节省大量时间。 -在Windows上,该软件程序被称为命令提示符,在macOS上,它被称为终端。 +在 Windows 上,该软件程序被称为命令提示符,在 macOS 上,它被称为终端。 -shell(如Bash或Zsh shell)是你(用户)和计算机操作系统之间的接口。 +shell(如 Bash 或 Zsh shell)是你(用户)和计算机操作系统之间的接口。 -你在一个CLI应用程序中输入命令。shell读取、处理和解释这些命令,并指示操作系统执行任务。 +你在一个 CLI 应用程序中输入命令。shell 读取、处理和解释这些命令,并指示操作系统执行任务。 掌握命令行的方法将帮助你节省大量时间。一些需要掌握的基本命令行技能是: @@ -127,7 +127,7 @@ shell(如Bash或Zsh shell)是你(用户)和计算机操作系统之间 - 如何创建和删除文件和文件夹。 - 如何查看文件和文件夹的内容。 - 如何打开程序。 -- 如何使用命令行文本编辑器,如Vim、Emacs或Nano,来写入文件。 +- 如何使用命令行文本编辑器,如 Vim、Emacs 或 Nano,来写入文件。 - 如何管理当前的工作进程。

熟悉网络浏览器和网络搜索技能

@@ -140,25 +140,25 @@ shell(如Bash或Zsh shell)是你(用户)和计算机操作系统之间 一些最流行的网络浏览器是: -- 谷歌浏览器Chrome,由谷歌开发。 -- 火狐Mozilla,由火狐开发。 -- 微软Edge,由Mircosoft开发。 +- 谷歌浏览器 Chrome,由谷歌开发。 +- 火狐 Mozilla,由火狐开发。 +- 微软 Edge,由 Mircosoft 开发。 - Safari,由苹果公司开发。 要有效地使用网络浏览器,以下是你需要的一些技能: - 确保你使用现代和更新的网络浏览器,而不是旧的和不支持的浏览器。 - 了解导航按钮。回到上一页,刷新当前页面,创建一个文件并在新的标签或窗口中打开该页面,了解你在浏览器菜单中的额外功能,仅举几例。 -- 输入URL(Uniform Resource Locator的缩写)。URL是一个独特的网络地址,你把它放在浏览器的地址栏里。 -- 通过了解URL的不同部分,知道如何阅读它,并识别出一个网站可能是草率的。 +- 输入 URL(Uniform Resource Locator 的缩写)。URL 是一个独特的网络地址,你把它放在浏览器的地址栏里。 +- 通过了解 URL 的不同部分,知道如何阅读它,并识别出一个网站可能是草率的。 - 通过使用关键词进行搜索,并进行基本的互联网探索。 - 知道如何应用过滤器进行高级搜索技术。 - 利用标签式浏览。利用现代浏览器,你可以在同一个浏览器窗口中同时打开、管理和切换许多网页。 - 知道如何恢复标签。 - 利用浏览器的内置书签管理器创建和管理书签。书签是 "你的最爱 "的另一个名称--你想保存和保留的网站,以供将来参考。你可以创建文件夹和子文件夹,以建立一个更有组织的书签系统。 - 知道如何管理和删除你系统中的浏览历史。 -- 了解浏览器的安全和隐私问题。第一步是了解 [HTTP和HTTPS的区别](https://www.freecodecamp.org/news/what-is-https-http-vs-https-meaning-and-how-it-works/),特别是在互联网上进行金融交易时。其次是在与他人共享电脑或使用公共电脑时,使用隐身模式来保护敏感数据。 -- 知道如何清除浏览器的缓存和cookies,以改善加载时间,节省空间,并减少带宽。 +- 了解浏览器的安全和隐私问题。第一步是了解 [HTTP 和 HTTPS 的区别](https://www.freecodecamp.org/news/what-is-https-http-vs-https-meaning-and-how-it-works/),特别是在互联网上进行金融交易时。其次是在与他人共享电脑或使用公共电脑时,使用隐身模式来保护敏感数据。 +- 知道如何清除浏览器的缓存和 cookies,以改善加载时间,节省空间,并减少带宽。 - 知道如何安装具有额外浏览器功能的扩展程序。

项目管理软件的知识

@@ -265,9 +265,9 @@ shell(如Bash或Zsh shell)是你(用户)和计算机操作系统之间 你的电子邮件账户将与一个数字日历程序同步。 -例如,Gmail使用谷歌日历,Outlook使用Outlook日历。 +例如,Gmail 使用谷歌日历,Outlook 使用 Outlook 日历。 -对Calendly(一种日程安排应用程序)的了解也很有帮助。 +对 Calendly(一种日程安排应用程序)的了解也很有帮助。 你可以把它与你的电子邮件帐户连接起来,用于安排约会、会议、和预约。 @@ -312,12 +312,12 @@ shell(如Bash或Zsh shell)是你(用户)和计算机操作系统之间 - 利用查找和替换功能。 - 通过突出显示文本并在文档的侧边栏中留下评论和建议来与其他团队成员协作。 - 知道如何通过设置密码保护来分享和保护文件。 -- 知道如何以不同的文件格式保存文档(如将文档保存为PDF格式)。 +- 知道如何以不同的文件格式保存文档(如将文档保存为 PDF 格式)。 - 知道如何设置和打印文档。 - 知道如何插入列。 - 知道如何在文本文件中插入和格式化表格、文本框、图表、图形和视觉元素,使数据可视化。 - 知道如何创建、编辑和格式化带有数据的复杂表格。 -- 知道如何导入图形和嵌入其他文件(如Excel工作表)。 +- 知道如何导入图形和嵌入其他文件(如 Excel 工作表)。 - 知道如何插入图像和书签。 - 知道如何创建、分类和过滤邮件合并。 @@ -344,9 +344,9 @@ shell(如Bash或Zsh shell)是你(用户)和计算机操作系统之间 - 知道如何根据你设置的条件,用条件格式化来突出显示列。 - 知道如何过滤列。 - 知道如何创建图形和图表 -- 知道如何使用函数进行算术计算。例如,知道如何使用SUM将单个数值相加并找出总和,或者熟悉使用AVERAGE找出一系列数字的平均值。或者,知道如何使用MIN和MAX分别找到范围内的最高和最低数字。 -- 知道如何使用高级函数 [如VLOOKUP](https://www.freecodecamp.org/news/vlookup-in-excel/)(或垂直查询)来搜索列。 -- [知道基本的VBA](https://www.freecodecamp.org/news/excel-vba-tutorial/)(这是Visual Basic Application的缩写)来创建Excel宏。知道如何使用宏命令来创建自定义函数,使重复的、耗时的手工任务自动化。 +- 知道如何使用函数进行算术计算。例如,知道如何使用 SUM 将单个数值相加并找出总和,或者熟悉使用 AVERAGE 找出一系列数字的平均值。或者,知道如何使用 MIN 和 MAX 分别找到范围内的最高和最低数字。 +- 知道如何使用高级函数 [如 VLOOKUP](https://www.freecodecamp.org/news/vlookup-in-excel/)(或垂直查询)来搜索列。 +- [知道基本的 VBA](https://www.freecodecamp.org/news/excel-vba-tutorial/)(这是 Visual Basic Application 的缩写)来创建 Excel 宏。知道如何使用宏命令来创建自定义函数,使重复的、耗时的手工任务自动化。 - 知道如何创建数据透视表和图表,以便利用可视化的方式对大量的数据进行可视化、比较和展示。知道如何用你的发现生成报告。

掌握演示软件的知识

@@ -374,7 +374,7 @@ shell(如Bash或Zsh shell)是你(用户)和计算机操作系统之间 - 知道如何插入剪贴画。 - 为每张幻灯片添加注释 - 通过插入超链接使幻灯片具有互动性。 -- 知道如何插入媒体,如图片、音乐片段、音频和音效、GIF、短视频片段,或从电脑上传或从Youtube嵌入的完整视频。 +- 知道如何插入媒体,如图片、音乐片段、音频和音效、GIF、短视频片段,或从电脑上传或从 Youtube 嵌入的完整视频。

熟悉图形设计软件

@@ -404,7 +404,7 @@ shell(如Bash或Zsh shell)是你(用户)和计算机操作系统之间 - 知道如何使文件准备好在网络上使用。 - 知道如何创建和处理互动媒体。 - 知道如何进行基本的照片编辑、修饰和增强,并通过剪裁和移动来处理图像。 -- 知道如何创建图形、图表、信息图表和交互式PDF。 +- 知道如何创建图形、图表、信息图表和交互式 PDF。 - 知道如何为网页设计项目创建线框,原型和模拟,以及用户界面。 - 知道如何创建标识、图标和艺术品。 @@ -414,35 +414,35 @@ shell(如Bash或Zsh shell)是你(用户)和计算机操作系统之间 即使你不是作为一个开发人员工作,[了解编码的基本知识](https://www.freecodecamp.org/news/what-is-coding/) 可以帮助你的工作的重复性方面更容易。 -除此之外,编码是21世纪最基本的技术技能之一,更不用说对懂得编码的申请人有很高的需求。 +除此之外,编码是 21 世纪最基本的技术技能之一,更不用说对懂得编码的申请人有很高的需求。 下面是一些编码和与数据库互动的基本技能: -- 熟悉文本编辑器并使用可用于编写源代码的智能功能。 - \-熟悉具有类似IDE功能的代码编辑器--这里的IDE是集成开发环境的简称--如Visual Studio Code,用于编写和编辑源代码,编译和运行源代码,使用内置终端和调试,都在同一个屋檐下。 -- [Git版本控制软件知识](https://www.freecodecamp.org/news/git-and-github-for-beginners/),用于跟踪项目的变化和进行协作。创建、推送到、拉出和克隆一个仓库。 -- 掌握更高级的Git命令,如合并和分支。 -- 根据手头的任务,掌握一种编程语言的知识。如果你需要在网站的视觉部分与用户互动(也称为客户端),学习前端技术,如HTML、CSS和JavaScript。如果任务涉及到事情的逻辑方面(或服务器端),分析数据,并使重复性和耗时的任务自动化,那么学习服务器端脚本编程语言,如Python。 -- 了解 [Linux命令和在Linux环境中工作](https://www.freecodecamp.org/news/the-linux-commands-handbook/)。知道如何设置虚拟机并在本地计算机上运行Linux。 -- [了解SQL](https://www.freecodecamp.org/news/learn-sql-free-relational-database-courses-for-beginners/) (结构化查询语言的简称),与关系型数据库(如Oracle数据库、MySQL和PostgreSQL)进行交互。知道如何编写基本的SQL命令来执行CRUD(创建读取更新删除的缩写)操作,以查询数据库和操作存储的数据。 +- 熟悉文本编辑器并使用可用于编写源代码的智能功能。 +-熟悉具有类似 IDE 功能的代码编辑器--这里的 IDE 是集成开发环境的简称--如 Visual Studio Code,用于编写和编辑源代码,编译和运行源代码,使用内置终端和调试,都在同一个屋檐下。 +- [Git 版本控制软件知识](https://www.freecodecamp.org/news/git-and-github-for-beginners/),用于跟踪项目的变化和进行协作。创建、推送到、拉出和克隆一个仓库。 +- 掌握更高级的 Git 命令,如合并和分支。 +- 根据手头的任务,掌握一种编程语言的知识。如果你需要在网站的视觉部分与用户互动(也称为客户端),学习前端技术,如 HTML、CSS 和 JavaScript。如果任务涉及到事情的逻辑方面(或服务器端),分析数据,并使重复性和耗时的任务自动化,那么学习服务器端脚本编程语言,如 Python。 +- 了解 [Linux 命令和在 Linux 环境中工作](https://www.freecodecamp.org/news/the-linux-commands-handbook/)。知道如何设置虚拟机并在本地计算机上运行 Linux。 +- [了解 SQL](https://www.freecodecamp.org/news/learn-sql-free-relational-database-courses-for-beginners/) (结构化查询语言的简称),与关系型数据库(如 Oracle 数据库、MySQL 和 PostgreSQL)进行交互。知道如何编写基本的 SQL 命令来执行 CRUD(创建读取更新删除的缩写)操作,以查询数据库和操作存储的数据。

如何在简历展示计算机技能

以下是将计算机技能列入简历时需要考虑的一些要点。 - 你可以建立一个单独的专用部分,比如在简历的侧边栏,只为列出你的计算机技能而保留,包括一个带圆点的列表。 -- 列入使你有别于其他申请人的计算机技能,并且只与该职位相关。 - 请注意:如果你申请的是软件开发人员的职位,不要把Microsoft Word列为计算机技能。 - Microsoft Word可能是你在日常工作中使用的工具,但雇主认为你已经有了这方面的知识,而这不是他们想从你的简历中看到的。 +- 列入使你有别于其他申请人的计算机技能,并且只与该职位相关。 +请注意:如果你申请的是软件开发人员的职位,不要把 Microsoft Word 列为计算机技能。 +Microsoft Word 可能是你在日常工作中使用的工具,但雇主认为你已经有了这方面的知识,而这不是他们想从你的简历中看到的。 - 列出你所熟悉并能自如使用的技能。雇主可能会根据你添加的技能和技术来提问,所以不要误导,要直接说明你确切的熟练程度。 - 列出你为提高技能所学的相关课程和文凭/证书。 - 不要在列出你的技能的同时,用一个指标条来说明你的熟练程度。 - 例如,不要写上 "我对JavaScript的熟练程度是78%"。这样的内容很容易导致混淆。 -- 要具体。例如,不要只写 "Microsoft Excel的高级知识"。相反,提供一些例子,说明你能用Excel做什么,比如说出你精通的高级函数和公式的名称。 +例如,不要写上 "我对 JavaScript 的熟练程度是 78%"。这样的内容很容易导致混淆。 +- 要具体。例如,不要只写 "Microsoft Excel 的高级知识"。相反,提供一些例子,说明你能用 Excel 做什么,比如说出你精通的高级函数和公式的名称。 - 与其只列举技能,不如重点提及成就、里程碑、指标,以及你的计算机技能如何帮助公司。雇主们有兴趣看到你的计算机技能在以前的工作中是如何帮助提高团队的生产力,增加销售和收入的。 - 例如,你可以提到你通过编写Python脚本自动完成的一项任务,为你的团队节省了大量时间。 +例如,你可以提到你通过编写 Python 脚本自动完成的一项任务,为你的团队节省了大量时间。 - 请确保简短,并以简明的方式表达你的观点。招聘人员和雇主不会花太多时间浏览简历,所以相关的信息需要以一种总结的方式轻松突出。 -- 你可以包括文章的链接,你在会议上的演讲,你主持的研讨会,或你拥有的YouTube频道,你在那里解释你所在领域的技术话题。这样,你就向未来的雇主表明,你参与了你的社区,而且你拥有他们正在寻找的知识和专长。 +- 你可以包括文章的链接,你在会议上的演讲,你主持的研讨会,或你拥有的 YouTube 频道,你在那里解释你所在领域的技术话题。这样,你就向未来的雇主表明,你参与了你的社区,而且你拥有他们正在寻找的知识和专长。 [这里有一份撰写简历的指南](https://www.freecodecamp.org/news/how-to-write-a-developer-resume-recruiters-will-read/),提高招聘经理查看你的简历的可能性。 diff --git a/chinese/articles/configure-metadata-in-single-page-applications.md b/chinese/articles/configure-metadata-in-single-page-applications.md index c9dc90fbe..4d3f0dba1 100644 --- a/chinese/articles/configure-metadata-in-single-page-applications.md +++ b/chinese/articles/configure-metadata-in-single-page-applications.md @@ -7,31 +7,31 @@ ## Why Metadata Matters -元数据是任何现代Web应用程序不可或缺的一部分,它与搜索引擎优化(SEO)有着不可分割的联系。 +元数据是任何现代 Web 应用程序不可或缺的一部分,它与搜索引擎优化(SEO)有着不可分割的联系。 通过元数据,搜索引擎及其各自的结果页面 (SERPS) 才能正确索引和显示每个站点的相关信息。 此外,社交媒体平台展示的内容也是依赖元标记的,例如文章或者待售的商品。 -因此,了解现代web应用是如何配置元数据尤为重要。 +因此,了解现代 web 应用是如何配置元数据尤为重要。 -单页应用程序(SPA)是一种非常流行的现代Web应用程序实现。今天的大多数框架使用这种实现形式。在当今最流行的SPA框架中配置元数据将是本教程的重点。 +单页应用程序(SPA)是一种非常流行的现代 Web 应用程序实现。今天的大多数框架使用这种实现形式。在当今最流行的 SPA 框架中配置元数据将是本教程的重点。 ## 单页应用和元数据 -SPA的天然属性导致与传统多页应用相比,配置元数据变得不那么直观。我将通过以下几个关键点带你来理清这部分内容: +SPA 的天然属性导致与传统多页应用相比,配置元数据变得不那么直观。我将通过以下几个关键点带你来理清这部分内容: -1. SPA的结构 -2. 在SPA中修改元数据面临的问题 -3. 针对当前最流行的SPA框架:React、Svelte和Vue的元数据解决方案 +1. SPA 的结构 +2. 在 SPA 中修改元数据面临的问题 +3. 针对当前最流行的 SPA 框架:React、Svelte 和 Vue 的元数据解决方案 -你必须对HTML、元数据以及上述一个SPA框架有基本的了解,才能理解接下来的内容。但是我会讲解得通俗易懂,所以不必担心。 +你必须对 HTML、元数据以及上述一个 SPA 框架有基本的了解,才能理解接下来的内容。但是我会讲解得通俗易懂,所以不必担心。 ## 单页应用是如何运作的? -在深入话题之前,让我们分析一下SPA是由什么组成的。 +在深入话题之前,让我们分析一下 SPA 是由什么组成的。 -顾名思义,单页应用程序是由从服务器发送的单个HTML页面组成。这个页面只是一个HTML外壳,看起来像这样: +顾名思义,单页应用程序是由从服务器发送的单个 HTML 页面组成。这个页面只是一个 HTML 外壳,看起来像这样: ```html @@ -47,33 +47,33 @@ SPA的天然属性导致与传统多页应用相比,配置元数据变得不 ``` -你可能会好奇一整个网站是如何从一个HTML外壳而来的。 +你可能会好奇一整个网站是如何从一个 HTML 外壳而来的。 -这主要归功于客户端的JavaScript代码,为每一个页面生成内容,HTML页面是这些代码的延伸。这些代码包含在` ``` -每个Vue组件都会导出一个供Vue应用程序使用的对象,并且由于我们在**main.js**文件中导入了VueMeta插件,因此Vue将寻找我们从**登陆**组件中导出的`metaInfo`属性。 +每个 Vue 组件都会导出一个供 Vue 应用程序使用的对象,并且由于我们在**main.js**文件中导入了 VueMeta 插件,因此 Vue 将寻找我们从**登陆**组件中导出的`metaInfo`属性。 我们只需要提供一些数据,原标签就会自动生成。 -## 总结——SPA、SEO和元数据 +## 总结——SPA、SEO 和元数据 -我们分别展示了三大框架的代码示例,每一个都能够得到本教程一开始HTML标记语言示例的结果。 +我们分别展示了三大框架的代码示例,每一个都能够得到本教程一开始 HTML 标记语言示例的结果。 -在SPA应用程序中修改元数据并不像多页应用程序那样简单,即使这些SPA框架是动态的。理解这个概念帮助你使用当今层出不穷的SPA框架。 +在 SPA 应用程序中修改元数据并不像多页应用程序那样简单,即使这些 SPA 框架是动态的。理解这个概念帮助你使用当今层出不穷的 SPA 框架。 -元数据是任何现代Web应用程序不可或缺的一部分。希望在阅读本教程后,你可以自信地在下一个SPA项目中使用它。 +元数据是任何现代 Web 应用程序不可或缺的一部分。希望在阅读本教程后,你可以自信地在下一个 SPA 项目中使用它。 对单页应用程序元数据和搜索引擎优化的更多信息感兴趣吗?阅读[SEO for SPA](https://www.ohmycrawl.com/spa-seo/) 完整指南,可以提升你对这个概念的认知水平。 diff --git a/chinese/articles/cracking-google-season-of-docs-2020.md b/chinese/articles/cracking-google-season-of-docs-2020.md index 61866c56e..2e4f3991e 100644 --- a/chinese/articles/cracking-google-season-of-docs-2020.md +++ b/chinese/articles/cracking-google-season-of-docs-2020.md @@ -7,17 +7,17 @@ 近年,有[调查](https://cdn2.hubspot.net/hubfs/4008838/Resources/The-2019-Tidelift-managed-open-source-survey-results.pdf)表明良好的文档对开发者如何选择和使用开源非常重要。 -2019年首届Google Season of Docs(GSoD,即谷歌文档之季)项目,共收到了近450份技术写作者的申请,最终有50多名技术写作者被录取。 +2019 年首届 Google Season of Docs(GSoD,即谷歌文档之季)项目,共收到了近 450 份技术写作者的申请,最终有 50 多名技术写作者被录取。 -作为入选参加GSoD项目的技术写作者之一,我想分享一些关于如何为GSoD项目做准备并制作有竞争力申请书的想法。 +作为入选参加 GSoD 项目的技术写作者之一,我想分享一些关于如何为 GSoD 项目做准备并制作有竞争力申请书的想法。 # 目录 -- [Google Season of Docs是什么?](#what-is-google-season-of-docs) +- [Google Season of Docs 是什么?](#what-is-google-season-of-docs) - [哪些人有资格参与该项目?](#who-is-eligible-to-participate-in-the-program) - [参与该项目的好处](#benefits-of-participating) -- [让你的GSoD申请脱颖而出的5个建议](#5-tips-to-ace-your-gSoD-application) -- [我的GSoD项目经验](#my-gSoD-experience) +- [让你的 GSoD 申请脱颖而出的 5 个建议](#5-tips-to-ace-your-gSoD-application) +- [我的 GSoD 项目经验](#my-gSoD-experience) - [有用的参考资源](#useful-resources)

Google Season of Docs是什么?

@@ -26,13 +26,13 @@ 这项活动非常重要,因为通过开源项目的文档,用户不仅可以理解该项目,还可以为项目做出贡献。 -在项目期间,入选的技术写作者将花费3-5个月的时间来构建一套新的文档、改进现有文档的结构、开发一个急需的教程或改进一个开源组织的贡献流程和指南。 +在项目期间,入选的技术写作者将花费 3-5 个月的时间来构建一套新的文档、改进现有文档的结构、开发一个急需的教程或改进一个开源组织的贡献流程和指南。

哪些人有资格参与该项目?

-根据[谷歌的规则](https://developers.google.com/season-of-docs/docs),在注册时你必须已年满18岁。你还必须有技术写作者的实践经验,并且有资格在你的居住国工作。 +根据[谷歌的规则](https://developers.google.com/season-of-docs/docs),在注册时你必须已年满 18 岁。你还必须有技术写作者的实践经验,并且有资格在你的居住国工作。 -如果你符合这些资格,就可以申请参加GSoD项目。 +如果你符合这些资格,就可以申请参加 GSoD 项目。

参与该项目的好处

@@ -40,19 +40,19 @@ ### 你可以获得一笔津贴 -在完成GSoD项目后,你会获得由谷歌支付的一笔津贴。值得注意的是,这项津贴是可选的,因此你必须在申请阶段表明你希望获得津贴。 +在完成 GSoD 项目后,你会获得由谷歌支付的一笔津贴。值得注意的是,这项津贴是可选的,因此你必须在申请阶段表明你希望获得津贴。 津贴金额是按照申请人所在国家的购买力平价计算的。有关津贴的完整详细信息,请参考[此处](https://developers.google.com/season-of-docs/docs/tech-writer-stipends)。 ### 你可以提高自己的技能 -参与GSoD项目的另一个明显好处是,可以提高你的技术写作技能。 +参与 GSoD 项目的另一个明显好处是,可以提高你的技术写作技能。 ### 你可以扩大自己的人际圈子 除了提高自己在技术写作方面的技能,你还有机会与在开源领域做着了不起事情的人一起工作。 -就我而言,我有机会与[VideoLAN组织](https://www.videolan.org/index.html)合作,这意味着我必须与该组织的社长和项目的主要贡献者密切合作。 +就我而言,我有机会与[VideoLAN 组织](https://www.videolan.org/index.html)合作,这意味着我必须与该组织的社长和项目的主要贡献者密切合作。 ### 你可以获得推荐和介绍 @@ -70,11 +70,11 @@

让你的GSoD申请脱颖而出的5个建议

-以下5个建议,帮助你为GSoD项目做准备和制作有竞争力申请书。 +以下 5 个建议,帮助你为 GSoD 项目做准备和制作有竞争力申请书。 ### 在你的博客上创建和发表文章 -在GSoD项目中被录取为技术写作者的最重要标准之一是能够证明你以前有写作经验。 +在 GSoD 项目中被录取为技术写作者的最重要标准之一是能够证明你以前有写作经验。 谷歌和你想从事项目的拟派导师需要确信你是从事该项目工作的合适人选。这意味着他们希望能看到你创作的大量优质文档和文章。 @@ -88,8 +88,8 @@ 以下是我对选择合适项目的想法: -- 认真查看所有[已录取的项目](https://developers.google.com/season-of-docs/docs/participants),并尝试选出至少5个你感兴趣的项目。 -- 选出至少5个项目后,再次浏览,将项目数量缩减至2到3个。 +- 认真查看所有[已录取的项目](https://developers.google.com/season-of-docs/docs/participants),并尝试选出至少 5 个你感兴趣的项目。 +- 选出至少 5 个项目后,再次浏览,将项目数量缩减至 2 到 3 个。 - 虽然可能在申请阶段只专注于一个项目是合理的,但我鼓励你至少申请两个项目,这样你至少被一个项目录取的机会就会更大。 - 对于你选择的每个项目,加入其所在组织的沟通渠道,并让项目的维护者知道你对该项目的兴趣。这样,你将能够与导师建立关系。另外,请注意,导师选择人时并不仅仅因为申请人拥有必要的技能,还因为他们认为申请人能够与他们合作。因此,当你向导师发送信息时,要明确说明你的需求。最重要的是不要因为他们没有立即回复而感到沮丧,因为大多数导师都是有其他全职工作的志愿者。 - 不要被动等待。对项目进行大量研究,去发现并理解你需要了解的所有内容,如果你有任何疑问,请向该项目的拟派导师寻求帮助,或询问该社区的其他成员。 @@ -101,11 +101,11 @@ 除此之外,写提案还可以向你的导师展示你对该项目的目标、为什么你认为你是这个角色的合适人选、你的经验、项目时间线等等。 -写完提案后,将其发送给项目的导师进行审核。他们的审核将帮助你从导师的角度了解项目的要求。我在Resources部分添加了我的GSoD提案的链接。 +写完提案后,将其发送给项目的导师进行审核。他们的审核将帮助你从导师的角度了解项目的要求。我在 Resources 部分添加了我的 GSoD 提案的链接。 ### 至少为开源组织做出一项贡献 -文档之季项目并不像Google Summer of Code(GSoC,即谷歌编程之夏)项目那样要求你对感兴趣的开源项目至少做出一项贡献。但是做出贡献绝对可以提高你被录取的机会,所以试试吧。 +文档之季项目并不像 Google Summer of Code(GSoC,即谷歌编程之夏)项目那样要求你对感兴趣的开源项目至少做出一项贡献。但是做出贡献绝对可以提高你被录取的机会,所以试试吧。 ### 在申请截止日期后仍在社区中保持活跃 @@ -113,22 +113,22 @@

我的GSoD项目经验

-在去年的GSoD项目中,我有机会与[VideoLAN组织](https://www.videolan.org/index.html)合作开展VLC用户文档现代化(重写)项目。 +在去年的 GSoD 项目中,我有机会与[VideoLAN 组织](https://www.videolan.org/index.html)合作开展 VLC 用户文档现代化(重写)项目。 -在我的提案被VideoLAN组织接受后,我的导师和我就商定了我在GSoD项目中要实现的目标。 +在我的提案被 VideoLAN 组织接受后,我的导师和我就商定了我在 GSoD 项目中要实现的目标。 这些就是我的目标: 1. 重构文档。 -2. 更新文档以适应VLC的现代版本。 -3. 使用Sphinx和ReadtheDocs将用户文档迁移到Gitlab。 +2. 更新文档以适应 VLC 的现代版本。 +3. 使用 Sphinx 和 ReadtheDocs 将用户文档迁移到 Gitlab。 4. 删除过时的图像和信息。 5. 重写用户文档,使其易于理解。 -6. 使用国际化版Sphinx进行翻译。 +6. 使用国际化版 Sphinx 进行翻译。 -我对VideoLAN组织和[VLC](https://www.videolan.org/vlc)有了更多的了解,我发现了很多我从未了解的VLC功能,并且我学会了如何使用Sphinx文档平台并重构文本。 +我对 VideoLAN 组织和[VLC](https://www.videolan.org/vlc)有了更多的了解,我发现了很多我从未了解的 VLC 功能,并且我学会了如何使用 Sphinx 文档平台并重构文本。 -我的技术写作技巧也得到了极大的提高。总的来说,参加GSoD项目是2019年我经历的最好的事情之一。 +我的技术写作技巧也得到了极大的提高。总的来说,参加 GSoD 项目是 2019 年我经历的最好的事情之一。

有用的参考资源

@@ -141,4 +141,4 @@ - [Season of Docs Slack channel](https://seasonofdocs.slack.com/) - [Awesome Technical Writing](https://github.com/BolajiAyodeji/awesome-technical-writing) -喜欢这篇文章?Twitter上关注我[@Didicodes](https://twitter.com/Didicodes)。 +喜欢这篇文章?Twitter 上关注我[@Didicodes](https://twitter.com/Didicodes)。 diff --git a/chinese/articles/create-a-data-driven-component-with-wordpress-and-nextjs.md b/chinese/articles/create-a-data-driven-component-with-wordpress-and-nextjs.md index 3ade0d9f2..477aff72c 100644 --- a/chinese/articles/create-a-data-driven-component-with-wordpress-and-nextjs.md +++ b/chinese/articles/create-a-data-driven-component-with-wordpress-and-nextjs.md @@ -374,7 +374,7 @@ There are three distinct things I'd like to draw attention to in this code block 2. I'm also using Next Image component. This handles image optimization and lazy loading. It works like a normal image tag but you have to provide the dimensions of the final image. 3. I'm using a function called `getMetaImage`. This function constructs a Cloudinary link that overlays the title of the post on a base image I use for all my posts. -## And we're there... +## And we're there There is a lot of code in this post, well done for sticking with it to this point. You can see the final product of the FrontPanels on my homepage (https://kevincunningham.co.uk). diff --git a/chinese/articles/create-a-habit-system-and-stay-motivated-as-a-developer.md b/chinese/articles/create-a-habit-system-and-stay-motivated-as-a-developer.md index 497b0c22c..357a41074 100644 --- a/chinese/articles/create-a-habit-system-and-stay-motivated-as-a-developer.md +++ b/chinese/articles/create-a-habit-system-and-stay-motivated-as-a-developer.md @@ -17,11 +17,11 @@ 必须记住动力会以各种形式或样貌到来。有些时候,动力来源于想要创作艺术;而另一些时候是激发你写一篇博客文章。 -有时动力是想要远足7英里;另外一些时候,动力源于照顾好自己或者拼命工作。 +有时动力是想要远足 7 英里;另外一些时候,动力源于照顾好自己或者拼命工作。 请承认并且欣赏各种形式的动力。让动力带领你,感受新的经历、喜悦和圆满。不论动力以何种形式造访,请让你自己感受到它,因为一种形式的动力都会渗透到其他的动力中去。 -当我觉得沮丧的时候,我会拿出画笔,打开Bob Ross的绘画教程,把我最喜欢的节目作为背景,给自己泡杯茶,点一支香氛蜡烛,然后开始作画。 +当我觉得沮丧的时候,我会拿出画笔,打开 Bob Ross 的绘画教程,把我最喜欢的节目作为背景,给自己泡杯茶,点一支香氛蜡烛,然后开始作画。 利用这段时间放松,让我感受良好。我并不计较这段时间是否高效,我在意的是当下我的感受。我感到放松、受到启发、备受鼓舞,我感受到我在照顾自己,并且充满创造力。 @@ -29,7 +29,7 @@ 然后我把这个感受延续到第二天,要么继续运用我的创造力作画,要么创造一些别的东西。 -但需要注意的是动力是转瞬即逝的。灵感的爆发往往只持续1-3周。靠动力来实现目标和梦想的话,并不是长久之计。 +但需要注意的是动力是转瞬即逝的。灵感的爆发往往只持续 1-3 周。靠动力来实现目标和梦想的话,并不是长久之计。 你需要纪律,[你需要习惯体系](https://www.freecodecamp.org/news/how-to-be-more-consistent-when-learning-to-code/)。一个你日复一日重复坚持的习惯,即便你丧失动力,它早已成为你的日常。 @@ -41,9 +41,9 @@ 比如作为一个开发人员,你会给自己制定什么目标?这些目标或许是: 1. 本周:创建项目功能 -2. 本月:学习TypeScript +2. 本月:学习 TypeScript 3. 本季度:发布项目 -4. 今年: 熟练掌握React和TypeScript,在会议演讲,每周创作一篇博文 +4. 今年: 熟练掌握 React 和 TypeScript,在会议演讲,每周创作一篇博文 现在将这些目标拆解成任务,在什么时候你希望完成这些任务。再从截止时间反向推导,看看短期内你需要达成哪些目标。 @@ -64,8 +64,8 @@ 第一天:调研 第二到三天:开发和测试 -第四天:结束测试,修复bug -第五天:部署到预发布环境,准备好demo +第四天:结束测试,修复 bug +第五天:部署到预发布环境,准备好 demo 接下来就是挤出时间去做这些事,在你的日历上添加这些任务。 @@ -81,9 +81,9 @@ 我感到没有动力去做任何事的时候常常是因为我的盘子里没有东西。我可能积压了一堆可以去做的事,但对这些事并没有什么想法,这是因为我没有把它们“放入我的盘子里”,所以这些事并不会让我感到兴奋。 -所以我会过一遍这些积压已久的事情,来看看是否能找到一些灵感。如果没有,我会浏览dev.to、freeCodeCamp或者Medium上最近的文章来激发灵感。 +所以我会过一遍这些积压已久的事情,来看看是否能找到一些灵感。如果没有,我会浏览 dev.to、freeCodeCamp 或者 Medium 上最近的文章来激发灵感。 -如果还没有,我会在Twitter上找有趣的话题。如果这也行不通,我在Udemy或者YouTube找点东西来学习。 +如果还没有,我会在 Twitter 上找有趣的话题。如果这也行不通,我在 Udemy 或者 YouTube 找点东西来学习。 如果这些都不奏效,就意味着我需要放松休息一下了。我会给自己放一个“创意”假,去散散步、听听播客。途中如果有什么激发我创造力的事情发生,我会记录下来。 @@ -91,7 +91,7 @@ 假如我的脑袋里已经有一些想法了,我会挑选最让我兴奋的那个去做,然后设置一个宽松的截止时间——通常是两周。根据截至时间来反向推算我需要针对这个想法所做的事情,并且标记到日历上。 -如果日程很满,我会把拆分出来的任务限定到一个小时内,再安插到日历里;如果不忙,会给更多时间。有些时候,可能我一周只能腾出来2个小时,这也没关系。 +如果日程很满,我会把拆分出来的任务限定到一个小时内,再安插到日历里;如果不忙,会给更多时间。有些时候,可能我一周只能腾出来 2 个小时,这也没关系。 一旦在日历设置好时间,我会强迫自己坐下来,专注干活儿。 @@ -99,21 +99,21 @@ 调整心态、拥有一个良好我的氛围对于我来说很重要,能够激发我的创造力。我的激发创造力氛围包括声音、气温和视觉。 -**声音**: 我会听lofi音乐或者播放专注音乐清单。我推荐[这个视频](https://www.youtube.com/watch?utm_campaign=JSByte&utm_medium=email&utm_source=Revue%20newsletter&v=csCp0Wd2-40.)。 +**声音**: 我会听 lofi 音乐或者播放专注音乐清单。我推荐[这个视频](https://www.youtube.com/watch?utm_campaign=JSByte&utm_medium=email&utm_source=Revue%20newsletter&v=csCp0Wd2-40.)。 **气味**: 我在办公室放了香氛蜡烛和扩香器来创造“专注氛围“,如果需要刺激的话,我会使用滚珠精油。 **视觉**: 当和别人一起或者在大自然中工作的时候我最有动力。所以我的桌子面朝一扇窗户,透过窗户我可以看见大树、风吹动树叶沙沙作响,小鸟在叽叽喳喳地叫。 -我上文推荐的youtube视频里有一个正在工作的男人,这位让我有动力去工作,我喜欢有一个工作伙伴。 +我上文推荐的 youtube 视频里有一个正在工作的男人,这位让我有动力去工作,我喜欢有一个工作伙伴。 -我为此创建了一个[Twitch直播](http://twitch.tv/shrutikapoor?utm_campaign=JSByte&utm_medium=blog&utm_source=devto)和一个[discord社群](https://discord.gg/rgqyH75Saf?utm_campaign=JSByte&utm_medium=blog&utm_source=devto)。当我独自居家办公觉得动力不足,需要外部刺激的时候,我会去咖啡馆。最近我尝尝去附近的公园写作,这对我的创造力非常有帮助。 +我为此创建了一个[Twitch 直播](http://twitch.tv/shrutikapoor?utm_campaign=JSByte&utm_medium=blog&utm_source=devto)和一个[discord 社群](https://discord.gg/rgqyH75Saf?utm_campaign=JSByte&utm_medium=blog&utm_source=devto)。当我独自居家办公觉得动力不足,需要外部刺激的时候,我会去咖啡馆。最近我尝尝去附近的公园写作,这对我的创造力非常有帮助。 ### 如何避免分心 -这一点非常重要,因为我的专注力像金鱼一样薄弱。一旦逮到机会,我就会快乐地在Twitter上冲浪,或者不断点击消息提醒,下滑Instagram。 +这一点非常重要,因为我的专注力像金鱼一样薄弱。一旦逮到机会,我就会快乐地在 Twitter 上冲浪,或者不断点击消息提醒,下滑 Instagram。 -所以当需要专注的时候,我会把手机调整成为静音模式,退出邮件应用,把slack调整为免打扰模式。我告诉自己,接下来的45分钟,不可以看手机,回邮件,查看Twitter或者打开Slack。 +所以当需要专注的时候,我会把手机调整成为静音模式,退出邮件应用,把 slack 调整为免打扰模式。我告诉自己,接下来的 45 分钟,不可以看手机,回邮件,查看 Twitter 或者打开 Slack。 如果要杜绝分心,我会退出这些应用。眼不见,心不烦。 😄 @@ -121,7 +121,7 @@ 现在我已经构思好了想法、布置好了氛围,饮料就在手边,音乐正在播放,唯一需要我做的事就是打字。 -我打开番茄时钟。我的番茄时钟是工作45分钟休息15分钟。我使用上面的视频作为番茄时钟。我的目标是一整天都播放这个视频辅助工作。 +我打开番茄时钟。我的番茄时钟是工作 45 分钟休息 15 分钟。我使用上面的视频作为番茄时钟。我的目标是一整天都播放这个视频辅助工作。 一旦视频的铃铛“叮”地一声响起,我就准备好了。我开始敲打键盘输出我脑海中的想法,我不会在第一遍就去计较内容是否修饰到位,或者代码够不够完善。这仅只是初稿,之后我会一遍又一遍修改我的内容或者代码。 @@ -129,9 +129,9 @@ ### 别忘了休息 -一旦时钟响起,就可以去休息了。我会强迫自己站起来,出去走走,或者泡一杯新的茶,做一些杂事——任何无目的刷Twitter或者Instagram以外的事。 +一旦时钟响起,就可以去休息了。我会强迫自己站起来,出去走走,或者泡一杯新的茶,做一些杂事——任何无目的刷 Twitter 或者 Instagram 以外的事。 -这是休息的时间,有时候我会在休息之后再给自己10分钟,检查Slack和email,看看手机上是否有些紧急的消息。有时候,根本就没人找我,我会继续回到我的45分钟循环。 +这是休息的时间,有时候我会在休息之后再给自己 10 分钟,检查 Slack 和 email,看看手机上是否有些紧急的消息。有时候,根本就没人找我,我会继续回到我的 45 分钟循环。 ### 总结 @@ -145,9 +145,9 @@ 希望这篇文章能够激发你去创建一个自己的习惯体系,帮助你保持状态。 -如果你想参与我的Twitch或者Discord的话, [欢迎直接在Twitter上找我](http://twitter.com/shrutikapoor08?utm_campaign=JSByte&utm_medium=blog&utm_source=Devto),告诉我你习惯的工作时间段: +如果你想参与我的 Twitch 或者 Discord 的话, [欢迎直接在 Twitter 上找我](http://twitter.com/shrutikapoor08?utm_campaign=JSByte&utm_medium=blog&utm_source=Devto),告诉我你习惯的工作时间段: -1. 周中9 AM - 11 AM PST -2. 周中9 PM - 11 PM PST +1. 周中 9 AM - 11 AM PST +2. 周中 9 PM - 11 PM PST 下篇文章我讲说说当你没有动力、没有思路的,想要给自己一些灵感的时候该怎么办。保持关注![你也可以点击订阅我的文章提醒推送](http://bit.ly/shrutinewsletter?utm_campaign=JSByte&utm_medium=blog&utm_source=Devto)。 diff --git a/chinese/articles/css-box-model-explained-with-examples.md b/chinese/articles/css-box-model-explained-with-examples.md index e5296cc3f..a58669273 100644 --- a/chinese/articles/css-box-model-explained-with-examples.md +++ b/chinese/articles/css-box-model-explained-with-examples.md @@ -23,7 +23,7 @@ We're also going to see some practical use cases for these properties. Let's get **Topics covered** -### You can watch this tutorial on YouTube as well if you like: +### You can watch this tutorial on YouTube as well if you like # Why learn CSS Box Model? @@ -384,7 +384,7 @@ This property defines how our margin, padding, and borders will be calculated. T - padding-box - content box -### Note: +### Note We're not gonna discus **box-sizing: padding-box,** as only Firefox supports it and it isn't used very often. diff --git a/chinese/articles/css-flexbox-tutorial-with-cheatsheet.md b/chinese/articles/css-flexbox-tutorial-with-cheatsheet.md index 028c976c8..d920d7502 100644 --- a/chinese/articles/css-flexbox-tutorial-with-cheatsheet.md +++ b/chinese/articles/css-flexbox-tutorial-with-cheatsheet.md @@ -19,7 +19,7 @@ In this article, I'll teach you **CSS Flexbox** basics so you can make your own - Short Hands - Conclusion -### You can watch this tutorial on YouTube as well if you like: +### You can watch this tutorial on YouTube as well if you like # First, What is Flexbox? @@ -95,7 +95,7 @@ Target the `.container` class and all the boxes. Then style the boxes so that al } ``` -## But Wait.... +## But Wait ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cq8exwor5aiciu2j6jwu.png) diff --git a/chinese/articles/css-grid-tutorial-with-cheatsheet.md b/chinese/articles/css-grid-tutorial-with-cheatsheet.md index 6280d6dd2..b76f4073d 100644 --- a/chinese/articles/css-grid-tutorial-with-cheatsheet.md +++ b/chinese/articles/css-grid-tutorial-with-cheatsheet.md @@ -7,7 +7,7 @@ Today we're going to learn **CSS Grid** properties so that you can make your own responsive websites. I'll explain how each of Grid's properties work along with a CheatSheet that covers everything you can do with Grid. Let's go. 🎖️ -# Table of Contents: +# Table of Contents - [CSS Grid Architecture](#css-grid-architecture) - [CSS Grid Chart](#css-grid-chart) @@ -26,7 +26,7 @@ Today we're going to learn **CSS Grid** properties so that you can make your own - [Shorthand for Grid](#shorthand-for-css-grid-properties) - [Conclusion](#conclusion) -## You can watch this tutorial on YouTube as well if you like: +## You can watch this tutorial on YouTube as well if you like # First, What is CSS Grid? @@ -159,7 +159,7 @@ Now, place some gaps between our boxes like this 👇 } ``` -## But Wait.... +## But Wait ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cq8exwor5aiciu2j6jwu.png) diff --git a/chinese/articles/css-position-property-explained.md b/chinese/articles/css-position-property-explained.md index fe80d09eb..d97bc2892 100644 --- a/chinese/articles/css-position-property-explained.md +++ b/chinese/articles/css-position-property-explained.md @@ -15,7 +15,7 @@ - [CSS 中的 **固定定位**](#what-is-the-fixed-position-in-css) - [CSS 中的 **粘性定位**](#what-is-the-sticky-position-in-css) -## **你可以在 YouTube 上观看本教程:** +## **你可以在 YouTube 上观看本教程** # CSS 中的 position 属性 @@ -27,7 +27,7 @@ ![](https://www.freecodecamp.org/news/content/images/2021/06/Frame-35--2-.png) -**Flexbox制作的网站** +**Flexbox 制作的网站** 通过 **position 属性**, 你可以制作一个 **非对称的网站**,像这样: @@ -35,11 +35,11 @@ ** Grid 和 position 属性 制作的网站** -你不能使用Flexbox和Grid把你的内容放在所有你想要的地方。你会受限于 X 轴和 Y 轴。看看这幅图你就会明白我的意思了:👇 +你不能使用 Flexbox 和 Grid 把你的内容放在所有你想要的地方。你会受限于 X 轴和 Y 轴。看看这幅图你就会明白我的意思了:👇 ![](https://www.freecodecamp.org/news/content/images/2021/06/Frame-1--6-.png) -**对称的内容布局显示元素分别放置在x轴和y轴** +**对称的内容布局显示元素分别放置在 x 轴和 y 轴** 你的盒子会遵循这些衡量因素。👆 @@ -48,11 +48,11 @@ But, using the **position property**, you can place your content anywhere you wi ![](https://www.freecodecamp.org/news/content/images/2021/06/Frame-2--4-.png) -**不对称的内容布局,不需要考虑x轴和y轴的位置。** +**不对称的内容布局,不需要考虑 x 轴和 y 轴的位置。** 你可以用这种布局把你的盒子放在任何你想要的地方。👆换句话说,你可以在屏幕上将元素 **自由移动**。 -下面是另一个使用position属性的例子: +下面是另一个使用 position 属性的例子: ![](https://www.freecodecamp.org/news/content/images/2021/06/Frame-3--8-.png) @@ -64,7 +64,7 @@ But, using the **position property**, you can place your content anywhere you wi ![](https://www.freecodecamp.org/news/content/images/2021/06/Frame-4--7-.png) -对于这个项目,你可以使用任何安装了 **emmet插件** 的代码编辑器。我将使用 [CodePen.io](https://codepen.io). +对于这个项目,你可以使用任何安装了 **emmet 插件** 的代码编辑器。我将使用 [CodePen.io](https://codepen.io). ### HTML @@ -97,7 +97,7 @@ body 标签内部,写如下代码:👇 } ``` -position 属性有5个值: +position 属性有 5 个值: 1. relative 2. absolute @@ -128,7 +128,7 @@ position 属性有5个值: **相对定位和绝对定位** -### 看如下2个例子 👇 +### 看如下 2 个例子 👇 我们先尝试一下 **`relative`** 值。写如下代码: @@ -235,7 +235,7 @@ body{ ![](https://www.freecodecamp.org/news/content/images/2021/06/Frame-14.png) -**结果是粉色盒子右移了100px** +**结果是粉色盒子右移了 100px** 注意 .box-2 相对于 .box-1 移动了 100px。 @@ -264,7 +264,7 @@ body{ ![](https://www.freecodecamp.org/news/content/images/2021/06/Frame-15.png) -**结果是粉色盒子相对于 body 移动了100px** +**结果是粉色盒子相对于 body 移动了 100px** 注意是 .box-2 相对于 **body** 标签移动了 **100px** 。 @@ -335,7 +335,7 @@ CSS 如下: ### 粘性定位示例 -无需改变你当前的HTML和CSS中的任何东西,只需改变这个值: +无需改变你当前的 HTML 和 CSS 中的任何东西,只需改变这个值: ```CSS .box-1{ diff --git a/chinese/articles/data-analytics-roadmap.md b/chinese/articles/data-analytics-roadmap.md index 3d06fb6a9..f9f8ac191 100644 --- a/chinese/articles/data-analytics-roadmap.md +++ b/chinese/articles/data-analytics-roadmap.md @@ -41,7 +41,7 @@ 那如何才能了解更多数据分析师的工作内容呢?您可以从众多线上免费资源入手。比如说,[这篇文章讨论了数据分析师的实际工作](https://www.freecodecamp.org/news/what-does-a-data-analyst-do-data-analyst-job-description/);[这篇文章讨论了数据分析和数据科学](https://www.freecodecamp.org/news/data-analyst-vs-data-scientist-whats-the-difference/). -许多博客、播客以及YouTube频道都提供数据分析相关的娱乐和资讯内容。我将会在下方列出一些多年来对我有帮助的YouTube频道。 +许多博客、播客以及 YouTube 频道都提供数据分析相关的娱乐和资讯内容。我将会在下方列出一些多年来对我有帮助的 YouTube 频道。 要了解所需技能和素质,您还可以与其他数据分析师联系、参加会议或研讨会,并研究职位描述。 @@ -59,8 +59,8 @@ 下面列出了一些职位要求示例: -- 熟练掌握SQL并有大型数据集的工作经验 -- 熟悉Python,熟悉 Tableau、Power BI 等数据可视化工具 +- 熟练掌握 SQL 并有大型数据集的工作经验 +- 熟悉 Python,熟悉 Tableau、Power BI 等数据可视化工具 - 拥有强大的分析能力并能从复杂数据中洞察信息 - 拥有统计分析和建模技术的经验 - 拥有卓越的沟通能力,以及向技术和非技术受众解释复杂发现的能力 @@ -113,7 +113,7 @@ Excel 是数据分析师装备库中的重要工具。几乎每个组织都在 使用 Excel,您可以创建公式和函数以进行计算、创建数据透视表和图表以可视化数据,以及使用数据分析工具预测并识别模式。 Excel 对于回归分析、预测和情景分析尤其有用。 -如果您真的想成为一名数据分析师,那么掌握 Excel 是必要的。幸运的是,有大量可用的在线资源能帮助您学习。您可以在YouTube上观看教程 [ExcelIsFun](https://www.youtube.com/@excelisfun)、[Excel Chandoo](https://www.youtube.com/playlist?list=PLmejDGrsgFyCZ4YC5s8mgdQztj7zt5to5)、[Tutorials Point](https://www.youtube.com/playlist?list=PLWPirh4EWFpEpO6NjjWLbKSCb-wx3hMql)、[Ashutosh Kumar](https://www.youtube.com/@AshutoshKumaryt) 和 [MyOnlineTrainingHub](https://www.youtube.com/@MyOnlineTrainingHub)。此外,以下课程将指导您如何充分利用 Excel。 +如果您真的想成为一名数据分析师,那么掌握 Excel 是必要的。幸运的是,有大量可用的在线资源能帮助您学习。您可以在 YouTube 上观看教程 [ExcelIsFun](https://www.youtube.com/@excelisfun)、[Excel Chandoo](https://www.youtube.com/playlist?list=PLmejDGrsgFyCZ4YC5s8mgdQztj7zt5to5)、[Tutorials Point](https://www.youtube.com/playlist?list=PLWPirh4EWFpEpO6NjjWLbKSCb-wx3hMql)、[Ashutosh Kumar](https://www.youtube.com/@AshutoshKumaryt) 和 [MyOnlineTrainingHub](https://www.youtube.com/@MyOnlineTrainingHub)。此外,以下课程将指导您如何充分利用 Excel。 1. [使用 Excel 进行数据分析简介](https://www.coursera.org/learn/excel-data-analysis?irclickid=WskXxw2EKxyNRBjSCewfUQQZUkARwUz2LzeJ2A0&irgwc=1&utm_medium=partners&utm_source=impact&utm_campaign=1359419&utm_content=b2c) Coursera 2. [Excel 用户使用 Python 数据分析](https://www.freecodecamp.org/news/data-analysis-with-python-for-excel-users-course/) freeCodeCamp 的 YouTube 频道 @@ -126,21 +126,21 @@ Excel 是数据分析师装备库中的重要工具。几乎每个组织都在 ## 第五步:熟练掌握用于提取数据的 SQL -SQL (结构化查询语言Structured Query Language)是一个在数据分析中重要的工具。作为一个数据分析师,您的主要责任包括从数据库中提取数据。SQL是帮助您实现该目的的编程语言。 +SQL (结构化查询语言 Structured Query Language)是一个在数据分析中重要的工具。作为一个数据分析师,您的主要责任包括从数据库中提取数据。SQL 是帮助您实现该目的的编程语言。 -SQL不仅限于执行像SELECT,FROM,和WHERE这样的基本查询。它是一个复杂的编程语言——您能用它以多种方式操纵和转化数据。SQL被用于从多个表格中合并数据,筛选提炼数据,和生成新的表格和视图。 +SQL 不仅限于执行像 SELECT,FROM,和 WHERE 这样的基本查询。它是一个复杂的编程语言——您能用它以多种方式操纵和转化数据。SQL 被用于从多个表格中合并数据,筛选提炼数据,和生成新的表格和视图。 -为了成为一个高效的数据分析师,熟练掌握SQL非常重要。您需要能得心应手地查询数据,生成表格,并且理解如何使您的查询高效运行。 +为了成为一个高效的数据分析师,熟练掌握 SQL 非常重要。您需要能得心应手地查询数据,生成表格,并且理解如何使您的查询高效运行。 -幸运的是,有很多帮助您学习SQL的资源。您能从以下平台开始您的学习:[可汗学院 SQL](https://www.khanacademy.org/computing/computer-programming/sql/)、W3Schools、[SQLZoo](https://sqlzoo.net/wiki/SQL_Tutorial)、[SQLbolt](https://www.freecodecamp.org/news/p/98971712-334e-40fb-8401-3e78a822dde8/SQLbolt.com)、[Luke Barousse](https://www.youtube.com/@LukeBarousse)、[Alex the Analyst](https://www.youtube.com/@AlexTheAnalyst)、[Microsoft Power Tools](https://www.youtube.com/@TheOyinbooke), 最后,一些像 SQL island 和 SQL murder 这样的SQL游戏也能帮助您学习。 +幸运的是,有很多帮助您学习 SQL 的资源。您能从以下平台开始您的学习:[可汗学院 SQL](https://www.khanacademy.org/computing/computer-programming/sql/)、W3Schools、[SQLZoo](https://sqlzoo.net/wiki/SQL_Tutorial)、[SQLbolt](https://www.freecodecamp.org/news/p/98971712-334e-40fb-8401-3e78a822dde8/SQLbolt.com)、[Luke Barousse](https://www.youtube.com/@LukeBarousse)、[Alex the Analyst](https://www.youtube.com/@AlexTheAnalyst)、[Microsoft Power Tools](https://www.youtube.com/@TheOyinbooke), 最后,一些像 SQL island 和 SQL murder 这样的 SQL 游戏也能帮助您学习。 -此外,还有很多现有的网课和书籍能够深入地覆盖SQL。以下是一些可以帮助您入门的例子: +此外,还有很多现有的网课和书籍能够深入地覆盖 SQL。以下是一些可以帮助您入门的例子: -1. [学习SQL和数据库-初学者完整课程](https://www.freecodecamp.org/news/sql-and-databases-full-course/) -2. [来自freeCodeCamp的关系数据库课程](https://www.freecodecamp.org/news/learn-sql-free-relational-database-courses-for-beginners/#relational-database-freecodecamp-curriculum) +1. [学习 SQL 和数据库-初学者完整课程](https://www.freecodecamp.org/news/sql-and-databases-full-course/) +2. [来自 freeCodeCamp 的关系数据库课程](https://www.freecodecamp.org/news/learn-sql-free-relational-database-courses-for-beginners/#relational-database-freecodecamp-curriculum) 3. [免费的数据库课程系列](https://www.freecodecamp.org/news/learn-sql-free-relational-database-courses-for-beginners/) -通过熟练掌握SQL,您将能够从数据库中提取有价值的见解,并能操作数据以提供有意义的业务见解。 +通过熟练掌握 SQL,您将能够从数据库中提取有价值的见解,并能操作数据以提供有意义的业务见解。 ## 第六步:针对数据分析学习 Python @@ -150,7 +150,7 @@ SQL不仅限于执行像SELECT,FROM,和WHERE这样的基本查询。它是 Python 拥有着[极为广泛的工具包和函数库](https://www.freecodecamp.org/news/python-data-science-course-matplotlib-pandas-numpy/)。 这些工具使得数据分析变得容易,比如说用于操纵和分析数据的 Pandas,和用于可视化数据的 Matplotlib。Python 也有着[自动化任务的能力](https://www.freecodecamp.org/news/python-automation-scripts/), 这使数据分析变得更加简洁高效。 -[为数据分析学习Python](https://www.freecodecamp.org/news/learn-data-analysis-with-python-course/) 是一个您在数据分析职业道路上的不错的投资。它不仅让您能使用强大的函数库工作,而且它还能为您提供不少机会,来分析更大的数据集和进行更复杂的分析。 +[为数据分析学习 Python](https://www.freecodecamp.org/news/learn-data-analysis-with-python-course/) 是一个您在数据分析职业道路上的不错的投资。它不仅让您能使用强大的函数库工作,而且它还能为您提供不少机会,来分析更大的数据集和进行更复杂的分析。 从免费网课到付费的网络和纸质教程,有很多帮助您学习 Python 的资源。这些资源包括[freeCodeCamp](https://www.youtube.com/@freecodecamp)、DataCamp、[CodeBasis](https://www.youtube.com/@codebasics)、[Programming with Mosh](https://www.youtube.com/@programmingwithmosh) 和 [Microsoft Learn](https://learn.microsoft.com?wt.mc_id=studentamb_207021)。 @@ -198,6 +198,6 @@ PowerBI 是一个对于数据可视化的极佳选择,因为它学习过程简 成为一个数据分析师,我们需要投入、努力、和对数据分析的热爱。通过在这份指南里列出的步骤,我们可以得到成为一个成功的数据分析师所需的技能和知识。 -从理解数据分析师的工作,到熟练掌握SQL和Python,再到和其他的开发者建立联系,其中每一步对于在数据分析领域取得成功而言都至关重要。 +从理解数据分析师的工作,到熟练掌握 SQL 和 Python,再到和其他的开发者建立联系,其中每一步对于在数据分析领域取得成功而言都至关重要。 要记得保持好奇心、从不停止学习,也要记得一直愿意去接受新的科技和方法。带着决心和坚持,您能成为一个熟练的数据分析师,并解锁更多令人激动的职业机会。 diff --git a/chinese/articles/data-structures-in-javascript-with-examples.md b/chinese/articles/data-structures-in-javascript-with-examples.md index 6a3943f4b..18fd243ce 100644 --- a/chinese/articles/data-structures-in-javascript-with-examples.md +++ b/chinese/articles/data-structures-in-javascript-with-examples.md @@ -9,7 +9,7 @@ 数据结构是任何一个软件开发从业人员必须知道的内容,但当你刚开始学习的时候,可能觉得这个话题难以理解,甚至有些吓人, -在这篇文章中,我会简单介绍什么是数据结构,它们在什么时候有用,以及如何用JavaScript来实现这鞋数据结构。 +在这篇文章中,我会简单介绍什么是数据结构,它们在什么时候有用,以及如何用 JavaScript 来实现这鞋数据结构。 让我们开始吧! @@ -56,26 +56,26 @@ arr.push(4) console.log(arr) // [1,2,3,4] ``` -JavaScript包含**原始(内置)**和**非原始(非内置)** 两种数据结构。 +JavaScript 包含**原始(内置)**和**非原始(非内置)** 两种数据结构。 原始数据结构是编程语言默认的、可以拿来就用(如数组和对象)的;而非原始数据结构不是默认的、如果需要使用的话,你必须先编写出来。 不同的数据结构对应不同的操作场景。你或许可以使用内置数据结构处理大部分编程任务,但当遇到特殊任务的时候,非原始数据机构可以派上用场。 -让我们一起来看一看最流行的数据结构,它们是怎么运作的,在哪些场合适用,以及如何使用JavaScript编写这些数据结构。 +让我们一起来看一看最流行的数据结构,它们是怎么运作的,在哪些场合适用,以及如何使用 JavaScript 编写这些数据结构。

数组

**数组**是存储在连续内存位置的项目合集。 -数组内的每一个元素都可以通过其**索引**(位置)访问。数组的索引通常从0开始,所以在一个包含4个元素的数组中,第三个元素的索引为2. +数组内的每一个元素都可以通过其**索引**(位置)访问。数组的索引通常从 0 开始,所以在一个包含 4 个元素的数组中,第三个元素的索引为 2. ```javascript const arr = ['a', 'b', 'c', 'd'] console.log(arr[2]) // c ``` -数组的**长度**属性定义了数组包含的元素数量。如果一个数组包含4个元素,我们就可以说这个数组的长度为4. +数组的**长度**属性定义了数组包含的元素数量。如果一个数组包含 4 个元素,我们就可以说这个数组的长度为 4. ```javascript const arr = ['a', 'b', 'c', 'd'] @@ -84,13 +84,13 @@ console.log(arr.length) // 4 在一些编程语言中,一个数组中只能存储同一种数据类型的元素,在数组被创建的时候就必须定义数组的长度,并且不可以修改。 -但JavaScript的数组并不是这样,在JavaScript中,同一数组可以存储**任何数据类型**的元素,数组**长度**是**动态**的(也就是说可以按需更改数组长度)。 +但 JavaScript 的数组并不是这样,在 JavaScript 中,同一数组可以存储**任何数据类型**的元素,数组**长度**是**动态**的(也就是说可以按需更改数组长度)。 ```javascript const arr = ['store', 1, 'whatever', 2, 'you want', 3] ``` -JavaScript数组可以存储任何数据类型的值,也就意味着可以存储数组。一个包含其他数组的数组被称为**多维数组**。 +JavaScript 数组可以存储任何数据类型的值,也就意味着可以存储数组。一个包含其他数组的数组被称为**多维数组**。 ```javascript const arr = [ @@ -100,7 +100,7 @@ const arr = [ ] ``` -JavaScript数组有许多内置的属性和方法,可以针对不同目的来使用,如从数组添加或者删除元素、给数组排序、过滤数组,以及我们知道的数组长度等,数组的完全属性和方法列表可以在[这里](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)找到。 😉 +JavaScript 数组有许多内置的属性和方法,可以针对不同目的来使用,如从数组添加或者删除元素、给数组排序、过滤数组,以及我们知道的数组长度等,数组的完全属性和方法列表可以在[这里](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)找到。 😉 在数组中每一个元素都对应一个索引,索引跟元素位于数组位置相关。如果我们在数组末尾添加一个新的元素,则这个元素的索引为之前数组最后一位索引加一。 @@ -110,9 +110,9 @@ JavaScript数组有许多内置的属性和方法,可以针对不同目的来

对象(哈希表)

-在JavaScript中,**对象**是**键值对**的集合。在其他编程语言中,这种数据结构也被称作**映射**、**字典**和**哈希表**。 +在 JavaScript 中,**对象**是**键值对**的集合。在其他编程语言中,这种数据结构也被称作**映射**、**字典**和**哈希表**。 -一个典型的JS对象如下: +一个典型的 JS 对象如下: ```javascript const obj = { @@ -155,7 +155,7 @@ console.log(obj["prop5"]) // "The new prop on the block" obj.prop6() // "yet another example" ``` -和数组一样,JavaScript对象也有内置的方法供我们进行不同的操作,或者获取特定对象的信息,完整内容可以查看[这里](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)。 +和数组一样,JavaScript 对象也有内置的方法供我们进行不同的操作,或者获取特定对象的信息,完整内容可以查看[这里](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)。 对象是将有相同之处或者相互关联的数据放在一起的好办法,同时,因为对象的属性是独一无二的,当想要根据特定条件来区分数据的时候,对象可以派上用场。 @@ -171,21 +171,21 @@ const obj = {

-栈是一种以列表的方式来存储信息的数据结构,添加和删除栈的元素遵循**LIFO模式(后进先出)**。在栈中,不允许按照元素顺序来添加或删除元素,只能遵循LIFO模式。 +栈是一种以列表的方式来存储信息的数据结构,添加和删除栈的元素遵循**LIFO 模式(后进先出)**。在栈中,不允许按照元素顺序来添加或删除元素,只能遵循 LIFO 模式。 -你可以想象桌面有一叠纸,来思考栈是如何运作的。你只能在这叠纸上方添加更多纸张,也只能在最上方取出纸张。这就是LIFO,后进先出。 😉 +你可以想象桌面有一叠纸,来思考栈是如何运作的。你只能在这叠纸上方添加更多纸张,也只能在最上方取出纸张。这就是 LIFO,后进先出。 😉 ![istockphoto-178580846-170667a](https://www.freecodecamp.org/news/content/images/2022/05/istockphoto-178580846-170667a.jpg) 一叠纸 -只要确认元素遵循**LIFO模式**,那么栈结构就可以派上用场。下面是栈的使用场景: +只要确认元素遵循**LIFO 模式**,那么栈结构就可以派上用场。下面是栈的使用场景: -- JavaScript的调用栈 +- JavaScript 的调用栈 - 在各种编程语言中管理函数调用 - 许多程序提供的撤销/重做功能 -有不止一种实现栈的方法,但是最简单的或许是**在数组中使用push和pop方法**。如果你仅通过pop和push的方法来添加和删除元素,你就遵循了LIFO模式,用栈的方法操作了数组。 +有不止一种实现栈的方法,但是最简单的或许是**在数组中使用 push 和 pop 方法**。如果你仅通过 pop 和 push 的方法来添加和删除元素,你就遵循了 LIFO 模式,用栈的方法操作了数组。 另一个方法是列表,实现如下: @@ -252,7 +252,7 @@ stck.push("value4") console.log(stck.pop()) // value4 ``` -栈方法的大O为: +栈方法的大 O 为: - 插入 - O(1) - 删除 - O(1) @@ -261,7 +261,7 @@ console.log(stck.pop()) // value4

队列

-队列和栈的运作方式类似,但是元素遵循另一个添加和删除的模式。队列值遵循**FIFO先进先出模式**。在队列中,元素不按照顺序添加或删除,仅遵循FIFO模式。 +队列和栈的运作方式类似,但是元素遵循另一个添加和删除的模式。队列值遵循**FIFO 先进先出模式**。在队列中,元素不按照顺序添加或删除,仅遵循 FIFO 模式。 下面这张排队购买食物的图可以帮助你思考这个概念。这里的逻辑是如果你先加入到队伍中,你就会先被服务。如果你是队伍的第一个,你就第一个离开队伍。FIFO。😉 @@ -274,9 +274,9 @@ console.log(stck.pop()) // value4 - 后台任务 - 打印/任务处理 -和栈一样,有不止一种实现队列的方式。但是最简单的是在数组中使用push和shift方法。 +和栈一样,有不止一种实现队列的方式。但是最简单的是在数组中使用 push 和 shift 方法。 -如果我们仅使用push和shift方法来添加和删除元素,我们就在数组中遵循了FIFO模式,将数组按照队列来操作。 +如果我们仅使用 push 和 shift 方法来添加和删除元素,我们就在数组中遵循了 FIFO 模式,将数组按照队列来操作。 另一个实现办法是列表,如下: @@ -343,7 +343,7 @@ quickQueue.enqueue("value4") console.log(quickQueue.dequeue()) // value1 ``` -队列方法的大O: +队列方法的大 O: - 插入 - O(1) - 删除 - O(1) @@ -352,7 +352,7 @@ console.log(quickQueue.dequeue()) // value1

链表

-**链表**是一种以**列表**存储值的数据结构,在列表中每一个值都被当作为一个**节点**,每一个节点都通过**指针**与列表的下一个值关联(若该节点是列表最后一个元素则下一个值为null)。 +**链表**是一种以**列表**存储值的数据结构,在列表中每一个值都被当作为一个**节点**,每一个节点都通过**指针**与列表的下一个值关联(若该节点是列表最后一个元素则下一个值为 null)。 有两种链表:**单链表**和**双链表**。两种链表的运作方式类似,但是在单链表中每一个节点有**单指针**指向**下一个节点**,在双链表中,每一个节点有**双指针**,一个指向**下一个节点**,一个指向**上一个节点**。 ![linked-list](https://www.freecodecamp.org/news/content/images/2022/05/linked-list.png) @@ -371,7 +371,7 @@ console.log(quickQueue.dequeue()) // value1 - 因为列表没有索引,所以我们**不能随机访问列表中的元素**。当我们想要访问一个值,必须通过从头到尾遍历整个列表的方法 - 没有索引的好处是**添加或删除**列表中任意部分比在数组中更高效。我们只需要重新分配指针指向的“相邻”值,但是在数组中,我们需要重新分配余下所有值的索引 -和其他所有数据结构一样,可以采用不同的**方法**来操作以链表存储的数据。通常会使用:push(在尾部添加)、pop(在尾部删除)、unshift(在头部添加)、shift(在头部删除)、get(获取)、set(设置)、remove(删除)和reverse(反转)。 +和其他所有数据结构一样,可以采用不同的**方法**来操作以链表存储的数据。通常会使用:push(在尾部添加)、pop(在尾部删除)、unshift(在头部添加)、shift(在头部删除)、get(获取)、set(设置)、remove(删除)和 reverse(反转)。 我们先来看看如何实现单链表,再来看看如何实现双链表。 @@ -654,7 +654,7 @@ class DoublyLinkedList { } ``` -双链表的大O为: +双链表的大 O 为: - 插入 - O(1) - 删除 - O(1) @@ -679,7 +679,7 @@ class DoublyLinkedList { 程序中使用树的场景有: -- DOM模型 +- DOM 模型 - 人工智能中的情景分析 - 操作系统中的文件夹 @@ -695,9 +695,9 @@ class DoublyLinkedList { 二叉树的一个重要使用场景是搜索。用于搜索的二叉树被称为**二叉查找树(BST)**。 -BST和普通二叉树类似,只是内部的数据结构被排列成易于搜索的结构。 +BST 和普通二叉树类似,只是内部的数据结构被排列成易于搜索的结构。 -在BST中的值是排过序的,所有节点的左子节点的值要小于父节点,所有节点的右子节点的值要大于父节点。 +在 BST 中的值是排过序的,所有节点的左子节点的值要小于父节点,所有节点的右子节点的值要大于父节点。 ![download--1-](https://www.freecodecamp.org/news/content/images/2022/05/download--1-.png) @@ -712,11 +712,11 @@ BST和普通二叉树类似,只是内部的数据结构被排列成易于搜 - 如果比根节点小,则检查左边是否有节点,并重复上面的步骤;如果左边没有节点,则将这个节点在当下位置添加或者删除 - 如果比根节点大,则检查右边有没有节点,并重复上述步骤;如果有变没有节点,则将这个节点在当下位置添加或者删除 -在BST中查找与上述方法类似,但是没有添加或者删除值,取而代之的是与节点比较我们搜寻的值的大小。 +在 BST 中查找与上述方法类似,但是没有添加或者删除值,取而代之的是与节点比较我们搜寻的值的大小。 -树的**大O**复杂度呈**对数(log(n))**。但是需要注意的是,想要实现这样的时间复杂度,必须保证树结构的每一步都是左右对称的,这样我们才可以在搜索的过程中“丢弃”一半的数据。如果在任意一边存储的值更多,树结构的搜索效率就会打折扣。 +树的**大 O**复杂度呈**对数(log(n))**。但是需要注意的是,想要实现这样的时间复杂度,必须保证树结构的每一步都是左右对称的,这样我们才可以在搜索的过程中“丢弃”一半的数据。如果在任意一边存储的值更多,树结构的搜索效率就会打折扣。 -实现BST的方法如下: +实现 BST 的方法如下: ```javascript // 我们创建树的节点 @@ -836,7 +836,7 @@ class BinarySearchTree { 如果节点之间没有的关联没有定义方向,我们就称这个图为无向图。 -在下图中我们可以看到节点2和节点3之间的关联没有方向性,我们可以从节点2到节点3,也可以从节点3到节点2。无定向意味着节点间的连接是双向的。 +在下图中我们可以看到节点 2 和节点 3 之间的关联没有方向性,我们可以从节点 2 到节点 3,也可以从节点 3 到节点 2。无定向意味着节点间的连接是双向的。 ![Undirected_graph.svg](https://www.freecodecamp.org/news/content/images/2022/05/Undirected_graph.svg.png) @@ -844,7 +844,7 @@ class BinarySearchTree { 你可能已经猜出来了,有向图就是完全相反的。让我们再次使用上面的图,这时节点之间的连接是有固定方向的。 -在这幅图中,你可以由节点A到节点B,但是不能从节点B到节点A。 +在这幅图中,你可以由节点 A 到节点 B,但是不能从节点 B 到节点 A。 ![images-1](https://www.freecodecamp.org/news/content/images/2022/05/images-1.png) @@ -854,7 +854,7 @@ class BinarySearchTree { 如果节点之间的连接被分配了权重,我们就称其为加权图。权重仅分配给了节点之间的连接,仅和连接相关,不和节点相关。 -在下面的例子中我们可以看到,节点0和节点4之间连接的权重时7;而节点3和节点1之间的权重是4. +在下面的例子中我们可以看到,节点 0 和节点 4 之间连接的权重时 7;而节点 3 和节点 1 之间的权重是 4. ![1_P68VQPcnFXBo9XlA4pDNrg](https://www.freecodecamp.org/news/content/images/2022/05/1_P68VQPcnFXBo9XlA4pDNrg.png) @@ -885,7 +885,7 @@ class BinarySearchTree {
-ABCD
A0110
B1001
C1001
D0110
-矩阵可以用表格来表示,列和行来代表图里的节点,单元格内的值表示节点之间连接,如果单元格的值为1,则表示该位置的行和列是相关联的,如果是0,则表示没有联系。 +矩阵可以用表格来表示,列和行来代表图里的节点,单元格内的值表示节点之间连接,如果单元格的值为 1,则表示该位置的行和列是相关联的,如果是 0,则表示没有联系。 这个表格可以用简单的二维数组来表示: @@ -923,7 +923,7 @@ class BinarySearchTree {
-ABCDE
A01100
B10011
C10010
D01100
E01000
-但是在列表中,我们只需要在B的连接数组中添加一个值,以及再添加一个代表E的键值对就够了: +但是在列表中,我们只需要在 B 的连接数组中添加一个值,以及再添加一个代表 E 的键值对就够了: ```javascript { @@ -935,11 +935,11 @@ class BinarySearchTree { } ``` -现在假设我们需要验证B和E之间是否存在连接,在矩阵中检查就非常简单,因为我们知道节点间关联的位置位于哪个单元格。 +现在假设我们需要验证 B 和 E 之间是否存在连接,在矩阵中检查就非常简单,因为我们知道节点间关联的位置位于哪个单元格。
-ABCDE
A01100
B10011
C10010
D01100
E01000
-但如果是在列表中,我们不能马上得出结论,必须先遍历所有和B的连接相关的数组,来查看是否有E。通过这个例子你就了解了两种形式的优劣了。 +但如果是在列表中,我们不能马上得出结论,必须先遍历所有和 B 的连接相关的数组,来查看是否有 E。通过这个例子你就了解了两种形式的优劣了。 邻接列表的完全实现如下,我们把图限定在无向和无权重,来简化代码: diff --git a/chinese/articles/debounce-and-throttle-in-react-with-hooks.md b/chinese/articles/debounce-and-throttle-in-react-with-hooks.md index 1e6b1dac1..3e809a195 100644 --- a/chinese/articles/debounce-and-throttle-in-react-with-hooks.md +++ b/chinese/articles/debounce-and-throttle-in-react-with-hooks.md @@ -5,7 +5,7 @@ ![How to Use Debounce and Throttle in React and Abstract them into Hooks](https://www.freecodecamp.org/news/content/images/size/w2000/2020/07/og-image.png) -[钩子](https://reactjs.org/docs/hooks-intro.html)是对React绝妙的补充。在使用`class`组件时需要被分配到不同生命周期的逻辑,因为钩子而简化了不少。 +[钩子](https://reactjs.org/docs/hooks-intro.html)是对 React 绝妙的补充。在使用`class`组件时需要被分配到不同生命周期的逻辑,因为钩子而简化了不少。 当然使用钩子需要具备 _不一样的_ 思考方式, [特别是对于初次使用者来说](https://overreacted.io/making-setinterval-declarative-with-react-hooks/)。 @@ -13,7 +13,7 @@ ## 防抖和节流 -已经有许多介绍如何编写防抖和节流的博文,这里我就不赘述了。简单起见,你可以参考Lodash的[`debounce`](https://lodash.com/docs/4.17.15#debounce)和[`throttle`](https://lodash.com/docs/4.17.15#throttle)。 +已经有许多介绍如何编写防抖和节流的博文,这里我就不赘述了。简单起见,你可以参考 Lodash 的[`debounce`](https://lodash.com/docs/4.17.15#debounce)和[`throttle`](https://lodash.com/docs/4.17.15#throttle)。 我们快速回顾一下,两种函数都接受一个(回调)函数, 一个以毫秒为单位的 _延迟_ (如 `x`)并且返回另一个具有特殊行为的函数: @@ -22,7 +22,7 @@ ## 用例 -有一个迷你博客编辑器项目(这里是该项目的[GitHub仓库](https://github.com/wtjs/react-debounce-throttle-hooks/)) ,在这个编辑器中,我们希望用户停止输出后1秒钟将博文添加到数据库中。 +有一个迷你博客编辑器项目(这里是该项目的[GitHub 仓库](https://github.com/wtjs/react-debounce-throttle-hooks/)) ,在这个编辑器中,我们希望用户停止输出后 1 秒钟将博文添加到数据库中。 > 你可以通过查看[Codesandbox](https://codesandbox.io/s/github/wtjs/react-debounce-throttle-hooks)获取最终代码效果。 @@ -59,9 +59,9 @@ function App() { } ``` -在这段代码中`saveToDb`在真实场景应该用于向后端发起API调用。在这里我们做简化处理,将数据存入状态(state)然后作为 `dbValue`渲染。 +在这段代码中`saveToDb`在真实场景应该用于向后端发起 API 调用。在这里我们做简化处理,将数据存入状态(state)然后作为 `dbValue`渲染。 -因为我们仅想在用户停止输入后(1秒后),执行存储行为,所以我们使用的是 _防抖_。 +因为我们仅想在用户停止输入后(1 秒后),执行存储行为,所以我们使用的是 _防抖_。 [这里](https://github.com/wtjs/react-debounce-throttle-hooks/tree/starter)是起始代码库和分支。 @@ -96,7 +96,7 @@ function App() { 当给子组件传入调用时,[`useCallback`](https://reactjs.org/docs/hooks-reference.html#usecallback)可优化性能。我们可以借助他对回调函数的记忆化,来确保每次渲染`debouncedSave`都指向同一个防抖函数。 -> 我在freeCodeCamp也写过这篇[文章](https://www.freecodecamp.org/news/understanding-memoize-in-javascript-51d07d19430e/),帮助你理解记忆化的基本知识。 +> 我在 freeCodeCamp 也写过这篇[文章](https://www.freecodecamp.org/news/understanding-memoize-in-javascript-51d07d19430e/),帮助你理解记忆化的基本知识。 这样代码就奏效了: diff --git a/chinese/articles/definitive-guide-to-snippets-visual-studio-code.md b/chinese/articles/definitive-guide-to-snippets-visual-studio-code.md index 9967d5eff..ba59f0788 100644 --- a/chinese/articles/definitive-guide-to-snippets-visual-studio-code.md +++ b/chinese/articles/definitive-guide-to-snippets-visual-studio-code.md @@ -38,7 +38,7 @@ - 我避免用代码片段,因为我不喜欢依赖任何现成的工具。 - 我从不使用代码片段,因为我宁愿将时间花在怎样避免重复上,而不是让重复变得更简单。 -- 有些时候,离开了代码片段的帮助,我甚至不记得代码应该怎么写了。对于那些我已经熟透的内容,使用代码片段是OK的,但我不想在那些我还不熟悉的内容上使用代码片段。 +- 有些时候,离开了代码片段的帮助,我甚至不记得代码应该怎么写了。对于那些我已经熟透的内容,使用代码片段是 OK 的,但我不想在那些我还不熟悉的内容上使用代码片段。 - 网上的代码片段都有这样或那样的错误。我一直无法找到不存在浮点数错误的数值算法。我想,在网上是找不到不存在错误的代码片段的。 ## 什么时候你需要使用代码片段? @@ -97,9 +97,9 @@ ![insert-snippet-list](https://www.freecodecamp.org/news/content/images/2020/09/insert-snippet-list.png) -[Emmet][1] 已集成到VS Code中,并且具有自己的CSS选择器启发式语法,可用于插入HTML和CSS代码段。 +[Emmet][1] 已集成到 VS Code 中,并且具有自己的 CSS 选择器启发式语法,可用于插入 HTML 和 CSS 代码段。 -Emmet是独立的功能,但是机制和代码片段是一样的。你可以从片文章 [Emmet in Visual Studio Code guide][2] 里了解相关信息。 +Emmet 是独立的功能,但是机制和代码片段是一样的。你可以从片文章 [Emmet in Visual Studio Code guide][2] 里了解相关信息。 ### 用户相关设置 @@ -111,7 +111,7 @@ Emmet是独立的功能,但是机制和代码片段是一样的。你可以从 可选值: -- `on`: 打开tab-completion 功能。 +- `on`: 打开 tab-completion 功能。 - `off`: 关闭 tab-completion 功能,这是 _默认值_。 - `onlySnippets`: 只对代码片段打开 tab-completion。 @@ -144,7 +144,7 @@ Yes! 那么,怎么才能找出你使用的语言的的所有内置的代码片段呢? -简单说,我对上面的情况表示不满,所以我写了一个 VS Code 插件 [**Snippets Ranger**][4],提供了一个漂亮的UI界面来展示所有的内置代码片段。Think of it as a _Marauder's Map_ for snippets! +简单说,我对上面的情况表示不满,所以我写了一个 VS Code 插件 [**Snippets Ranger**][4],提供了一个漂亮的 UI 界面来展示所有的内置代码片段。Think of it as a _Marauder's Map_ for snippets! ![snippets-ranger](https://www.freecodecamp.org/news/content/images/2020/09/snippets-ranger.png) @@ -168,7 +168,7 @@ Visual Studio 市场 有专门的 [snippets category][5] 分类,你可以在 ### 自己如何写代码片段? -代码片段文件是 JSON 格式的。 如果你想,你还可以添加C语言风格的注释(技术上讲,它是微软的"JSONC"格式)。 +代码片段文件是 JSON 格式的。 如果你想,你还可以添加 C 语言风格的注释(技术上讲,它是微软的"JSONC"格式)。 你可以创建针对不同作用域的代码片段:全局的,工作区的,以及针对特定语言类型的。 @@ -176,7 +176,7 @@ Visual Studio 市场 有专门的 [snippets category][5] 分类,你可以在 ![user snippets](https://www.freecodecamp.org/news/content/images/2020/09/user-snippets.png) -如果你更喜欢使用GUI界面来编写代码片段,你可以尝试以这个 [snippet generator web app][6] 网站。 +如果你更喜欢使用 GUI 界面来编写代码片段,你可以尝试以这个 [snippet generator web app][6] 网站。 ![snippet generator](https://www.freecodecamp.org/news/content/images/2020/09/snippet-generator.png) @@ -207,7 +207,7 @@ Visual Studio 市场 有专门的 [snippets category][5] 分类,你可以在 4. `description`属性提供了代码片段的更多描述。它是可选的。 5. `scope`属性允许你指定特定的语言类型,你可以使用逗号来分割多种语言。它也是可选的。当然,对于特定于语言的代码片段文件来说是多余的。 -该代码片段的内容具有2个 tab stop ,并使用了`$ {TM_SELECTED_TEXT}`变量。 +该代码片段的内容具有 2 个 tab stop ,并使用了`$ {TM_SELECTED_TEXT}`变量。 接下来,让我们详细了解语法相关的细节。 @@ -219,7 +219,7 @@ VS Code 的代码片段语法和 [TextMate snippet syntax][7] 是相同的。然 #### 1\. Tab Stops -Tab stops由 **$** 和 **序号** 组成,像 `$1`。`$1`代表了第一个位置,`$2`代表了第二个位置,以此类推。`$0`代表退出代码片段,以及最后光标停留的位置, +Tab stops 由 **$** 和 **序号** 组成,像 `$1`。`$1`代表了第一个位置,`$2`代表了第二个位置,以此类推。`$0`代表退出代码片段,以及最后光标停留的位置, 看一个例子,我们希望写一个 _div_ 的代码片段。它的第一个 tab stop 在标签内,同时希望用户按下 tab 的时候能退出代码片段,并让光标停留在标签外。 @@ -239,7 +239,7 @@ Tab stops由 **$** 和 **序号** 组成,像 `$1`。`$1`代表了第一个位 有时候你想在插入的模板中的多个位置输入相同的值。这种时候你可以使用相同序号的 tab stops 来标识这几个位置,当你编辑其中一个的时候,就会自动同步输入的值到其它位置。 -一个典型的例子就是 _for_ 循环中的 _index_ 变量会被多次使用,下面是一个JavaScript中的 _for_ 循环代码片段。 +一个典型的例子就是 _for_ 循环中的 _index_ 变量会被多次使用,下面是一个 JavaScript 中的 _for_ 循环代码片段。 ```json { @@ -302,8 +302,8 @@ VS Code 提供了大量变量供你使用。你可以简单的在变量名前加 - `TM_SELECTED_TEXT`: 当前选中的文字或者空字符串, - `TM_CURRENT_LINE`: 当前行的文字, - `TM_CURRENT_WORD`: 光标下的单词或者空字符串, -- `TM_LINE_INDEX`: 以0为第一行的当前行序号, -- `TM_LINE_NUMBER`: 以1为第一行的当前行序号, +- `TM_LINE_INDEX`: 以 0 为第一行的当前行序号, +- `TM_LINE_NUMBER`: 以 1 为第一行的当前行序号, - `TM_FILENAME`: 当前文档的文件名, - `TM_FILENAME_BASE`: 当前文档的文件名,不带扩展名, - `TM_DIRECTORY`: 当前文档所在的文件夹, @@ -321,26 +321,26 @@ VS Code 提供了大量变量供你使用。你可以简单的在变量名前加 - `CURRENT_DATE`: 当前月份中的日期, - `CURRENT_DAY_NAME`: 当前日期的名字 (例如 'Monday'), - `CURRENT_DAY_NAME_SHORT`: 当前日期的名字缩写 (例如 'Mon'), -- `CURRENT_HOUR`: 当前的时间(小时)以24小时制展示, +- `CURRENT_HOUR`: 当前的时间(小时)以 24 小时制展示, - `CURRENT_MINUTE`: 当前分钟数, - `CURRENT_SECOND`: 当前秒数, -- `CURRENT_SECONDS_UNIX`: 从UNIX起的秒数(时间戳). +- `CURRENT_SECONDS_UNIX`: 从 UNIX 起的秒数(时间戳). 下面是一些关于注释的变量,在不同的语言下会出现不同的注释字符串: -- `BLOCK_COMMENT_START`: 例如, `` 在HTML中, -- `LINE_COMMENT`: 例如, `//` 在JavaScript中. +- `BLOCK_COMMENT_START`: 例如, `` 在 HTML 中, +- `LINE_COMMENT`: 例如, `//` 在 JavaScript 中. #### 6\. Transformations 转换一般运用于 variable 或者 placeholder 。如果你熟悉正则表达式(regex),你会发现这很相似。 -转换的语法像下面这样:`${«variable or placeholder»/«regex»/«replacement string»/«flags»}`。它很像JavaScript中的 [String.protoype.replace()][8] 。其中参数的作用如下: +转换的语法像下面这样:`${«variable or placeholder»/«regex»/«replacement string»/«flags»}`。它很像 JavaScript 中的 [String.protoype.replace()][8] 。其中参数的作用如下: -- `«regex»`: 这是一个与变量或占位符的值匹配的正则表达式。支持JavaScript regex语法。 +- `«regex»`: 这是一个与变量或占位符的值匹配的正则表达式。支持 JavaScript regex 语法。 - `«replacement string»`: 这是用来替换匹配到的内容的字符串。它可以引用`«regex»`中的捕获组,执行大小写的格式化(使用特殊的标记函数:`/upcase`, `/downcase`, 和 `/capitalize`),以及执行条件插入。查看 [TextMate Replacement String Syntax][9] 了解更多信息。 -- `«flags»`: Flags是传递给正则表达式使用的。可以使用 [JavaScript regex flags][10] 中的标志: +- `«flags»`: Flags 是传递给正则表达式使用的。可以使用 [JavaScript regex flags][10] 中的标志: - `g` : 全局搜索, - `i` : 大小写敏感, - `m` : 多行搜索, @@ -380,7 +380,7 @@ VS Code 提供了大量变量供你使用。你可以简单的在变量名前加 } ``` -有一个令人困惑的特性,当你在第一个tab stop上设置了placeholder,但却在它的镜像tab stop上运用了转换,转换会发生在第一个tab stop上么?不会! +有一个令人困惑的特性,当你在第一个 tab stop 上设置了 placeholder,但却在它的镜像 tab stop 上运用了转换,转换会发生在第一个 tab stop 上么?不会! 你会选择使用这个特性嘛?我最开始对这个特性感到十分困惑,所以我觉得你们也可能会。下面时具体的例子: @@ -398,7 +398,7 @@ VS Code 提供了大量变量供你使用。你可以简单的在变量名前加 你可以通过向 `keybindings.json` 加入你的快捷键。这个文件可以通过这个命令 **'Preferences: Open Keyboard Shortcuts File (JSON)'** 打开。 -比如,为 markdown的 代码片段 "Insert heading level 1" 绑定一个快捷键: +比如,为 markdown 的 代码片段 "Insert heading level 1" 绑定一个快捷键: ```json { @@ -413,9 +413,9 @@ VS Code 提供了大量变量供你使用。你可以简单的在变量名前加 ``` -你可以定义你的快捷键组合,指定命令的ID,以及可选的 [when clause context][11] 来指定什么时候这个快捷键是可用的。 +你可以定义你的快捷键组合,指定命令的 ID,以及可选的 [when clause context][11] 来指定什么时候这个快捷键是可用的。 -通过 `args` 对象,你可以通过 `langId` 和 `name` 指定目标代码片段。其中 `langId` 参数是目标代码片段所属的语言ID [language ID][12] ,参数 `name` 是你编写代码片段时指定的名字。 +通过 `args` 对象,你可以通过 `langId` 和 `name` 指定目标代码片段。其中 `langId` 参数是目标代码片段所属的语言 ID [language ID][12] ,参数 `name` 是你编写代码片段时指定的名字。 如果你想,你也可以使用 `snippet` 属性指定行内的代码片段。 @@ -435,7 +435,7 @@ VS Code 提供了大量变量供你使用。你可以简单的在变量名前加 你也可以使用 _Keyboard Shortcuts UI_ 来编辑快捷键,但是它不具备添加新的快捷键的能力。 -使用UI的另一个缺点是,他不会显示 `args` 对象,这会让你查找或者编辑自定义快捷键时更加费劲。 +使用 UI 的另一个缺点是,他不会显示 `args` 对象,这会让你查找或者编辑自定义快捷键时更加费劲。 ![shortcuts-ui](https://www.freecodecamp.org/news/content/images/2020/09/shortcuts-ui.png) @@ -463,7 +463,7 @@ VS Code 提供了大量变量供你使用。你可以简单的在变量名前加 第二点, _fre_ 与内置的代码片段 _foreach_ **重复** 了。 -一些人可能把快速建议的功能关闭了,只使用tab键来触发代码片段。这种情况下你需要在没有提示的情况下输入前缀。这些人可能更喜欢使用缩写前缀来减少按键的次数。 +一些人可能把快速建议的功能关闭了,只使用 tab 键来触发代码片段。这种情况下你需要在没有提示的情况下输入前缀。这些人可能更喜欢使用缩写前缀来减少按键的次数。 然而这种方式,同样使用了模糊搜索,所以你按下 tab 键的时候,代表你默认选择了第一个匹配项。 @@ -473,7 +473,7 @@ VS Code 提供了大量变量供你使用。你可以简单的在变量名前加 因此,如果你想的话,你可以不输入完整的前缀!?但是想象一下,在某种语言下,拥有大量相似前缀的代码片段,你只输入前缀缩写是多么不切实际的。 -更实际的做法是,选择合适的前缀,在按下tab键之前,完整的把它们打出来。 +更实际的做法是,选择合适的前缀,在按下 tab 键之前,完整的把它们打出来。 以上观点,可能根据你个人存在某些取舍。 @@ -499,7 +499,7 @@ Some snippet authors have rigid patterns to overcome this, but that's just somet - 插入日期, - 格式化选中的文字或者粘贴文字, - 为你的搜索引擎或者应用加入常见的搜索词组, -- 在邮件客户端中使用HTML的代码片段, +- 在邮件客户端中使用 HTML 的代码片段, - 向文档添加不同的模板. 大多数针对代码片段的应用都称自己为 “文本扩展器” ,但是还是有很多用于任务管理或者生产力的应用同样包含了代码片段的功能。 @@ -510,7 +510,7 @@ Some snippet authors have rigid patterns to overcome this, but that's just somet #### Autohotkey (Windows) -[AutoHotkey][14] 是一个 **Windows下的免费,开源的脚本语言** ,用于执行所有类型任务. +[AutoHotkey][14] 是一个 **Windows 下的免费,开源的脚本语言** ,用于执行所有类型任务. 它拥有自己专属语法。你可以在 VS Code 中安装 [AutoHotKey extension][15] ,来获得更好的编辑体验。 @@ -534,7 +534,7 @@ Some snippet authors have rigid patterns to overcome this, but that's just somet [PhraseExpress][17] 是一个文本扩展器应用,它管理常用文本模板,并可以插入任何程序。 -这是一个 **基于GUI的免费增值应用**。它比 _AutoHotKey_ 的受众更广泛。 +这是一个 **基于 GUI 的免费增值应用**。它比 _AutoHotKey_ 的受众更广泛。 他十分简洁和易于使用。你可以设置它为启动时运行,他就会在后台被激活。 @@ -544,7 +544,7 @@ Some snippet authors have rigid patterns to overcome this, but that's just somet #### Espanso (Windows, Mac, Linux) -这是一个 **用Rust写的开源,跨平台的文本扩展器应用**。 +这是一个 **用 Rust 写的开源,跨平台的文本扩展器应用**。 它采用了 **基于文件配置的方法**,配置文件采用了 [YAML][18] 语法。 diff --git a/chinese/articles/developers-the-why-and-how-to-writing-technical-articles-54e824789ef6.md b/chinese/articles/developers-the-why-and-how-to-writing-technical-articles-54e824789ef6.md index 27095452d..278c894a3 100644 --- a/chinese/articles/developers-the-why-and-how-to-writing-technical-articles-54e824789ef6.md +++ b/chinese/articles/developers-the-why-and-how-to-writing-technical-articles-54e824789ef6.md @@ -11,7 +11,7 @@ by Goodness Kayode ![vBYM3clasrOG9A8bkLmF5RtcHUiOC0U2vmq6](https://cdn-media-1.freecodecamp.org/images/vBYM3clasrOG9A8bkLmF5RtcHUiOC0U2vmq6) -我在一个由200多名开发人员组成的Whatsapp群中发布了一个小广播,说多发表一些技术文章的必要性。群里的回复给了我写这篇短文的灵感。我发现很多开发人员不重视写作,我觉得这是不对的。 +我在一个由 200 多名开发人员组成的 Whatsapp 群中发布了一个小广播,说多发表一些技术文章的必要性。群里的回复给了我写这篇短文的灵感。我发现很多开发人员不重视写作,我觉得这是不对的。 就像我知道很多开发人员不会写技术文章一样,我决定在广播最后补充如下文字。令我惊讶的是,有几个开发人员给我发消息说对于如何开始写文章,他们还需要帮助。所以我决定写这篇快速上手文章。 @@ -49,7 +49,7 @@ by Goodness Kayode **增加浏览量** -文章有浏览量也是令我兴奋的一件事。我曾在\[[React、Babel、webpack和Webpack 3.0](https://www.codementor.io/goodnesskay/setting-up-react-with-webpack-3-0-yarn-and-babel-9ftd5phqz)\]写过文章,然后我的[Codementor](https://www.freecodecamp.org/news/developers-the-why-and-how-to-writing-technical-articles-54e824789ef6/undefined)在Twitter上提醒我,浏览量达2000了。我开始为自己感到骄傲。 +文章有浏览量也是令我兴奋的一件事。我曾在\[[React、Babel、webpack 和 Webpack 3.0](https://www.codementor.io/goodnesskay/setting-up-react-with-webpack-3-0-yarn-and-babel-9ftd5phqz)\]写过文章,然后我的[Codementor](https://www.freecodecamp.org/news/developers-the-why-and-how-to-writing-technical-articles-54e824789ef6/undefined)在 Twitter 上提醒我,浏览量达 2000 了。我开始为自己感到骄傲。 我认为有人阅读和学习你的内容,对你来说应该是个好事。 @@ -57,7 +57,7 @@ by Goodness Kayode 愿意投入时间来写技术文章的人很少。当有很多人申请某个东西时,有写作优势的人更有机会申请成功。 -[Auth0](https://www.freecodecamp.org/news/developers-the-why-and-how-to-writing-technical-articles-54e824789ef6/undefined)开放了他们的Web应用程序便于用户申请为Auth0大使。申请的条目之一是用户可以附上所写文章链接。**这你应该有印象吧**。 +[Auth0](https://www.freecodecamp.org/news/developers-the-why-and-how-to-writing-technical-articles-54e824789ef6/undefined)开放了他们的 Web 应用程序便于用户申请为 Auth0 大使。申请的条目之一是用户可以附上所写文章链接。**这你应该有印象吧**。 #### **我应该如何开始写作?** @@ -83,9 +83,9 @@ by Goodness Kayode 寻找写作素材的最佳方法是学习新技术。可以学习没人要求你学习的新框架,然后着手写作。 -最近,我看到一个新的JavaScript框架,却没看到关于这个框架的任何教程。在我翻阅这个框架的文档时,发现这个框架轻量又好用。 +最近,我看到一个新的 JavaScript 框架,却没看到关于这个框架的任何教程。在我翻阅这个框架的文档时,发现这个框架轻量又好用。 -所以,我做了相关教程。一些活跃的JavaScript Twitter用户看到我的文章后不断转发,文章就获得了更多的浏览量。 +所以,我做了相关教程。一些活跃的 JavaScript Twitter 用户看到我的文章后不断转发,文章就获得了更多的浏览量。 4\. **主题就在你身边** @@ -97,7 +97,7 @@ by Goodness Kayode 5. **知道你的利基(定位)** -要在技术文章写作方面取得成功,你必须找准自己的定位。如果你知道自己在构建Web应用程序方面很厉害,就坚持写这方面的文章。同样,如果是在移动开发、人工智能、机器学习等方面很厉害,就坚持写这些方面的文章。 +要在技术文章写作方面取得成功,你必须找准自己的定位。如果你知道自己在构建 Web 应用程序方面很厉害,就坚持写这方面的文章。同样,如果是在移动开发、人工智能、机器学习等方面很厉害,就坚持写这些方面的文章。 因为如果你在编写教程时犯了不该犯的错,读者会把你当成迷糊粗心的人。 @@ -105,13 +105,13 @@ by Goodness Kayode 在写作时,你必须尽可能写得简单且让人读着轻松。就像是在向另一个开发人员解释一样写作,你要用简单的术语就能让他们理解。 -你可以插入一些有趣的GIF图片、短视频、屏幕截图等等,吸引读者注意力。 +你可以插入一些有趣的 GIF 图片、短视频、屏幕截图等等,吸引读者注意力。 7. **获取反馈** 兄弟姐妹们,不要以为自己什么都懂!那样只会是搬起石头砸自己的脚。征求意见,对于批评意见持开放态度,因为批评在所难免。 -读者有一些不清楚的事情时,保证他们能够通过邮件或Twitter与你探讨。 +读者有一些不清楚的事情时,保证他们能够通过邮件或 Twitter 与你探讨。 8\. **别停止写作** @@ -121,6 +121,6 @@ by Goodness Kayode **总结** -我相信你一定从我这篇简短但真挚的文章中有所收获。欢迎你在Twitter[@goodnesskayode](https://twitter.com/goodnesskayode)上与我分享你的想法。 +我相信你一定从我这篇简短但真挚的文章中有所收获。欢迎你在 Twitter[@goodnesskayode](https://twitter.com/goodnesskayode)上与我分享你的想法。 如果你能在[Scotch.io](https://scotch.io/@goodnesskay)、[Codementor](https://www.codementor.io/goodnesskay/posts)、[LinkedIn](https://www.linkedin.com/in/goodness-toluwanimi-kayode/detail/recent-activity/posts/)和[Dev.to](https://dev.to/goodnesskay)阅读我的文章,我也会非常高兴。 diff --git a/chinese/articles/docker-mount-volume-guide-how-to-mount-a-local-directory.md b/chinese/articles/docker-mount-volume-guide-how-to-mount-a-local-directory.md index d98477506..149e342af 100644 --- a/chinese/articles/docker-mount-volume-guide-how-to-mount-a-local-directory.md +++ b/chinese/articles/docker-mount-volume-guide-how-to-mount-a-local-directory.md @@ -9,16 +9,16 @@ 容器对于本地开发来说是必不可少的。它们让你在本地环境中测试你的应用程序,并开始建立所需的基础设施。 -Docker容器在是不变的。这意味着重新启动一个容器会删除你在容器中存储的所有数据。但是Docker提供了卷和绑定挂载,这是两种在Docker容器中持久保存数据的机制。 +Docker 容器在是不变的。这意味着重新启动一个容器会删除你在容器中存储的所有数据。但是 Docker 提供了卷和绑定挂载,这是两种在 Docker 容器中持久保存数据的机制。 -本教程将教你如何将本地目录绑定到你的Docker容器上,并交替使用docker管理的卷。了解了这两点,你就能在更多的用例中使用Docker容器,从而提高你的工作效率。 +本教程将教你如何将本地目录绑定到你的 Docker 容器上,并交替使用 docker 管理的卷。了解了这两点,你就能在更多的用例中使用 Docker 容器,从而提高你的工作效率。 ## 使用 `docker run -v` 进行本地目录挂载 > `docker run`命令首先在指定的镜像上创建一个可写的容器层,然后使用指定的命令启动。(来源 [docker.com](https://www.bing.com/search?form=MOZLBR&ptag=MOZZ0000000011&pc=MOZD&q=docker+run+)) 使用参数`-v`允许你绑定一个本地目录。 -`-v`或`--volume`允许你挂载本地目录和文件到你的容器。例如,你可以启动一个MySQL数据库并挂载数据目录,将实际数据存储在你挂载的目录中。 +`-v`或`--volume`允许你挂载本地目录和文件到你的容器。例如,你可以启动一个 MySQL 数据库并挂载数据目录,将实际数据存储在你挂载的目录中。 ```shell # run mysql container in the background @@ -45,11 +45,11 @@ docker rm -f mysql-db 这种方法的优点是使用起来很直接,而且容易访问。你应该使用绑定的本地目录来存放你想在主机上改变或观察的文件,如配置文件和日志文件。 -## 如何使用Docker Volumes来保存变化 +## 如何使用 Docker Volumes 来保存变化 -你可以使用Docker卷,而不是绑定你的本地目录。Docker卷是你的Docker存储目录中的某个地方的一个目录,可以挂载到一个或许多容器上。它们是完全可管理的,不依赖于某些操作系统。 +你可以使用 Docker 卷,而不是绑定你的本地目录。Docker 卷是你的 Docker 存储目录中的某个地方的一个目录,可以挂载到一个或许多容器上。它们是完全可管理的,不依赖于某些操作系统。 -让我们创建一个Docker卷并挂载它来保存MySQL数据: +让我们创建一个 Docker 卷并挂载它来保存 MySQL 数据: ```shell # create volume @@ -65,31 +65,31 @@ docker rm -f mysql-db docker volume remove mysql-data ``` -在删除Docker卷之前,你可以打开你的Docker GUI,通过点击`data`标签来检查卷。 +在删除 Docker 卷之前,你可以打开你的 Docker GUI,通过点击`data`标签来检查卷。 ![docker-ui-volume](https://www.freecodecamp.org/news/content/images/2022/03/docker-ui-volume.png) -你可以看到这些文件,但它们被隔离在一个Docker卷中。建议使用它们来保存那些你不需要从主机系统观察或改变的文件。众所周知,这种方法比本地目录绑定有更好的性能。 +你可以看到这些文件,但它们被隔离在一个 Docker 卷中。建议使用它们来保存那些你不需要从主机系统观察或改变的文件。众所周知,这种方法比本地目录绑定有更好的性能。 # 总结 -当你知道如何持久化你的数据,并且在停止容器时不丢失它们时,Docker容器会变得更加强大。 +当你知道如何持久化你的数据,并且在停止容器时不丢失它们时,Docker 容器会变得更加强大。 -你通过提供Docker运行`-v`参数将本地目录和卷绑定到一个容器。你需要给出绝对的本地路径或卷的名称,并将其映射到容器内的目录`-v :`。 +你通过提供 Docker 运行`-v`参数将本地目录和卷绑定到一个容器。你需要给出绝对的本地路径或卷的名称,并将其映射到容器内的目录`-v :`。 我希望你喜欢这篇文章。 -如果你喜欢它,觉得有必要给我鼓掌,或者只是想联系我,[在Twitter上关注我](https://twitter.com/sesigl)。 +如果你喜欢它,觉得有必要给我鼓掌,或者只是想联系我,[在 Twitter 上关注我](https://twitter.com/sesigl)。 -我在eBay Kleinanzeigen工作,这是全球最大的分类公司之一。顺便说一下,[我们正在招聘](https://www.ebay-kleinanzeigen.de/careers)! +我在 eBay Kleinanzeigen 工作,这是全球最大的分类公司之一。顺便说一下,[我们正在招聘](https://www.ebay-kleinanzeigen.de/careers)! ## 参考资料 -- [如何在Docker容器内挂载一个目录](https://towardsdatascience.com/how-to-mount-a-directory-inside-a-docker-container-4cee379c298b) -- [使用MySql的Docker镜像](https://hub.docker.com/_/mysql/) -- [Docker-Compose卷或绑定挂载语法](https://maximorlov.com/docker-compose-syntax-volume-or-bind-mount/) -- [如何暂停和恢复Docker容器](https://www.thegeekdiary.com/how-to-pause-and-resume-docker-containers/) -- [Docker卷与绑定挂载](https://blog.logrocket.com/docker-volumes-vs-bind-mounts/) -- [Docker文档:卷创建的命令](https://docs.docker.com/engine/reference/commandline/volume_create/) -- [Docker文档:卷的备份和恢复](https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes) -- [Docker容器桌面管理端](https://blog.jessfraz.com/post/docker-containers-on-the-desktop/) +- [如何在 Docker 容器内挂载一个目录](https://towardsdatascience.com/how-to-mount-a-directory-inside-a-docker-container-4cee379c298b) +- [使用 MySql 的 Docker 镜像](https://hub.docker.com/_/mysql/) +- [Docker-Compose 卷或绑定挂载语法](https://maximorlov.com/docker-compose-syntax-volume-or-bind-mount/) +- [如何暂停和恢复 Docker 容器](https://www.thegeekdiary.com/how-to-pause-and-resume-docker-containers/) +- [Docker 卷与绑定挂载](https://blog.logrocket.com/docker-volumes-vs-bind-mounts/) +- [Docker 文档:卷创建的命令](https://docs.docker.com/engine/reference/commandline/volume_create/) +- [Docker 文档:卷的备份和恢复](https://docs.docker.com/storage/volumes/#backup-restore-or-migrate-data-volumes) +- [Docker 容器桌面管理端](https://blog.jessfraz.com/post/docker-containers-on-the-desktop/) diff --git "a/chinese/articles/es5-to-esnext\342\200\224every-feature-added-to-javascript-since-2015.md" "b/chinese/articles/es5-to-esnext\342\200\224every-feature-added-to-javascript-since-2015.md" index 7bd33d60a..003ac992b 100644 --- "a/chinese/articles/es5-to-esnext\342\200\224every-feature-added-to-javascript-since-2015.md" +++ "b/chinese/articles/es5-to-esnext\342\200\224every-feature-added-to-javascript-since-2015.md" @@ -1,4 +1,4 @@ -> - 原文地址:[ES5 to ESNext — here’s every feature added to JavaScript since 2015](https://www.freecodecamp.org/news/es5-to-esnext-heres-every-feature-added-to-javascript-since-2015-d0c255e13c6e/) +> - 原文地址:[ES5 to ESNext—here’s every feature added to JavaScript since 2015](https://www.freecodecamp.org/news/es5-to-esnext-heres-every-feature-added-to-javascript-since-2015-d0c255e13c6e/) > - 原文作者:[Flavio Copes](https://www.freecodecamp.org/news/author/flavio/) > - 译者: > - 校对者: diff --git a/chinese/articles/event-propagation-event-bubbling-event-catching-beginners-guide.md b/chinese/articles/event-propagation-event-bubbling-event-catching-beginners-guide.md index 58880244f..8e26c608c 100644 --- a/chinese/articles/event-propagation-event-bubbling-event-catching-beginners-guide.md +++ b/chinese/articles/event-propagation-event-bubbling-event-catching-beginners-guide.md @@ -5,7 +5,7 @@ ![Event Bubbling and Event Catching in JavaScript and React – A Beginner's Guide](https://www.freecodecamp.org/news/content/images/size/w2000/2021/09/pexels-anthony-132477.jpg) -这篇文章将帮助你像一位专业程序员一样理解事件冒泡和事件捕获。我将用简单、清晰的语言帮助你了解事件传播在JavaScript和React中的工作机制。❤ +这篇文章将帮助你像一位专业程序员一样理解事件冒泡和事件捕获。我将用简单、清晰的语言帮助你了解事件传播在 JavaScript 和 React 中的工作机制。❤ 阅读完从事件冒泡到事件捕获的完整介绍,你就可以在项目练习中学以致用。 @@ -13,17 +13,17 @@ 你将学习到: - ✨ [何为事件委托?](#何为事件委托?) - ✨ [何为事件冒泡?](#何为事件冒泡?) -- ✨ [在JavaScript中事件冒泡是如何产生的?](#在JavaScript中事件冒泡是如何产生的) -- ✨ [在React中事件冒泡是如何产生的?](#在React中事件冒泡是如何产生的) +- ✨ [在 JavaScript 中事件冒泡是如何产生的?](#在JavaScript中事件冒泡是如何产生的) +- ✨ [在 React 中事件冒泡是如何产生的?](#在React中事件冒泡是如何产生的) - ✨ [如何在你的组件中终止事件冒泡](#如何在你的组件中终止事件冒泡) -- ✨ [对比Event.target和Event.currentTarget](#对比Event.target和Event.currentTarget) -- ✨ [更新后的事件执行顺序以及JavaScript中的useCapture参数](#更新后的事件执行顺序以及JavaScript中的useCapture参数) +- ✨ [对比 Event.target 和 Event.currentTarget](#对比Event.target和Event.currentTarget) +- ✨ [更新后的事件执行顺序以及 JavaScript 中的 useCapture 参数](#更新后的事件执行顺序以及JavaScript中的useCapture参数) - ✨ [哪些事件不冒泡,如何处理这些事件?](#哪些事件不冒泡,如何处理这些事件) -- ✨ [React 16及过往版本中的事件监听器对比React 17及以上](#React 16及过往版本中的事件监听器对比React 17及以上]) +- ✨ [React 16 及过往版本中的事件监听器对比 React 17 及以上](#React 16 及过往版本中的事件监听器对比 React 17 及以上]) - ✨ [特殊情况:当需要执行父元素的时候怎么办?](#特殊情况:当需要执行父元素的时候怎么办) ## 何为事件委托? -长话短说,事件委托是一种强大的JavaScript技术,这种技术使得事件处理更加高效。 +长话短说,事件委托是一种强大的 JavaScript 技术,这种技术使得事件处理更加高效。 ### 👍 优点 (下文更多补充) @@ -37,7 +37,7 @@ ## 何为事件冒泡? -假设有一个女孩儿叫`Molly`,恰好她不是大活人,而是(🥁此处有掌声)一个React模块。害!可真巧! +假设有一个女孩儿叫`Molly`,恰好她不是大活人,而是(🥁此处有掌声)一个 React 模块。害!可真巧! ![shiba inu meme "wow such convenience. much impress. so wow"](https://www.freecodecamp.org/news/content/images/2021/09/image-19.png) 图片生成自 [https://memegenerator.net/](https://memegenerator.net/) @@ -84,7 +84,7 @@ export default Molly; 下面展示了点击每个按钮会发生什么: -如果需要更直观的体验,这里有一个codepen版本: +如果需要更直观的体验,这里有一个 codepen 版本: [**事件冒泡例子**](https://codepen.io/maariyadiminsky/pen/MWobvZd) @@ -95,10 +95,10 @@ export default Molly; 多数情况下,或许你只希望绑定在按钮上的事件处理器被激活,但例子里面的情况一样,父元素的事件处理器也被触发了,这就是****✨事件冒泡✨****。 在下文中,我将分析到底发生了什么,以及我们如何处理这个问题。 -## 在JavaScript中事件冒泡是如何产生的 +## 在 JavaScript 中事件冒泡是如何产生的 ### 事件冒泡为何存在? -JavaScript在设计事件传播模型的其中一个初衷是让事件捕获更加方便。即可以从单一源头(父元素)捕获,而非每一个子元素上添加事件处理器。 +JavaScript 在设计事件传播模型的其中一个初衷是让事件捕获更加方便。即可以从单一源头(父元素)捕获,而非每一个子元素上添加事件处理器。 ### 事件传播的触发顺序 @@ -107,8 +107,8 @@ JavaScript在设计事件传播模型的其中一个初衷是让事件捕获更 图片来源于[https://ehsankorhani.com/](https://ehsankorhani.com/) -1. 🟢**捕获阶段**  –  这是触发事件后的第一个阶段。事件首先在顶层被“捕获”或者说传播。顶层即`window`对象,然后是`document`对象,再就是`html`元素,之后抵达最内部的元素。事件传播由上到下一直抵达到`event.target`(即你点击触发事件的元素)。 -2. 🟢 **目标阶段**  –  当抵达`event.target`后便进入第二个阶段。当用户点击按钮,这个按钮便是`event.target`所指的元素。 +1. 🟢**捕获阶段** – 这是触发事件后的第一个阶段。事件首先在顶层被“捕获”或者说传播。顶层即`window`对象,然后是`document`对象,再就是`html`元素,之后抵达最内部的元素。事件传播由上到下一直抵达到`event.target`(即你点击触发事件的元素)。 +2. 🟢 **目标阶段** – 当抵达`event.target`后便进入第二个阶段。当用户点击按钮,这个按钮便是`event.target`所指的元素。 3. 🟢 **冒泡阶段** – 这是第三个阶段。该阶段起始于`event.target`,一路向上传播直到重新触达顶层元素(虽然顶层父元素此时不会被再次调用)。 值得注意的是,即便事件传播分为三个主要阶段,但是目标阶段并没有被独立出来。事件监听器在捕获和冒泡阶段都在此处触发。 @@ -152,19 +152,19 @@ JavaScript在设计事件传播模型的其中一个初衷是让事件捕获更 图片源于 [https://www.w3.org/](https://www.w3.org/) -## 在React中事件冒泡是如何产生的? +## 在 React 中事件冒泡是如何产生的? -在React中对应的概念是[合成事件](https://reactjs.org/docs/events.html). +在 React 中对应的概念是[合成事件](https://reactjs.org/docs/events.html). 这其实是把浏览器的事件对象封装在一起。基本的使用场景类似于`stopPropagation`和`preventDefault`(下文更多补充)。合成事件最大的优点是所有浏览器通用。 -React并没有讲事件处理器绑定在node上,而是documment的根元素(root)。当事件被触发,React首先调用的是触发的元素(即目标阶段中你点击的元素),然后开始冒泡。 +React 并没有讲事件处理器绑定在 node 上,而是 documment 的根元素(root)。当事件被触发,React 首先调用的是触发的元素(即目标阶段中你点击的元素),然后开始冒泡。 -为什么React要这样操作,而不是简单复制原生DOM处理事件的方式? +为什么 React 要这样操作,而不是简单复制原生 DOM 处理事件的方式? ### 浏览器的兼容 -事件能够在所有浏览器中实现同样的效果非常重要。React创造出合成事件是为了确保即便在不同的浏览器中使用,属性能够保持一致。 +事件能够在所有浏览器中实现同样的效果非常重要。React 创造出合成事件是为了确保即便在不同的浏览器中使用,属性能够保持一致。 你当然不希望自己创造出来的应用的事件在某个浏览器中可以相应,但是在另一个浏览器中就失效——这是糟糕的用户体验。 ### 触发你想触发的事件 @@ -180,9 +180,9 @@ React并没有讲事件处理器绑定在node上,而是documment的根元素 这个事件流顺序从未逆转过(即捕获阶段从未被触发)。 -这是因为React的合成事件仅利用了冒泡阶段(目标阶段也包含在内)。如果只是为了触发`event.target`所在的地方(如此处按钮的例子),这样的设置是合理的。 +这是因为 React 的合成事件仅利用了冒泡阶段(目标阶段也包含在内)。如果只是为了触发`event.target`所在的地方(如此处按钮的例子),这样的设置是合理的。 -记住React在合成事件中只是_仿造_JavaScript原生的冒泡和捕获阶段,所以在后文中你会发现两者之间的不同(下文更多补充)。 +记住 React 在合成事件中只是_仿造_JavaScript 原生的冒泡和捕获阶段,所以在后文中你会发现两者之间的不同(下文更多补充)。 **⚠️ 合成事件** 并不默认专注在捕获阶段,除非特意设置。若需要触发捕获阶段,可将父元素`div`的事件监听器由`onClick`修改成`onClickCapture`: ```javascript @@ -208,7 +208,7 @@ export default Molly; 注意在这个例子中捕获阶段代替了冒泡阶段被触发。 -**⚠️** 最后,我想强调在React 16及更低版本,若在事件合成中触发冒泡阶段,冒泡阶段的发现和JavaScript中原生的一样,事件会一直向上至`Document`。 +**⚠️** 最后,我想强调在 React 16 及更低版本,若在事件合成中触发冒泡阶段,冒泡阶段的发现和 JavaScript 中原生的一样,事件会一直向上至`Document`。 ![Image displaying React's bubbling phase ending at the root level in React Version 17 but it ends at Window/Document in React Version 16 or lower](https://www.freecodecamp.org/news/content/images/2021/09/image-22.png) @@ -380,12 +380,12 @@ export default Molly; 自此我们学到的内容可以总结为: - `event.target` 是事件流中最底部的元素。 - `event.currentTarget` 是监听事件的元素(事件监听器绑定的地方)。 -## 更新后的事件执行顺序以及JavaScript中的useCapture参数 +## 更新后的事件执行顺序以及 JavaScript 中的 useCapture 参数 -在JavaScrip中`EventTarget.addEventListener`被用作添加事件处理器。 +在 JavaScrip 中`EventTarget.addEventListener`被用作添加事件处理器。 -[MDN文档](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)中表示即可以选择性地使用`options`对象中的`capture`也可以使用`useCapture`参数(也是可选的),两者效果相同。 +[MDN 文档](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)中表示即可以选择性地使用`options`对象中的`capture`也可以使用`useCapture`参数(也是可选的),两者效果相同。 ```javascript // 你可以这样写: yourElement.addEventListener(type, listener, { capture: true }); @@ -394,7 +394,7 @@ yourElement.addEventListener(type, listener, { capture: true }); yourElement.addEventListener(type, listener, useCapture: true); ``` -⚠️ 之所以可以这样操作,是因为在JavaScript中除非有特别设置,捕获阶段会被忽略,仅有冒泡阶段会被触发(在目标阶段之后),MDN是这样解释的: +⚠️ 之所以可以这样操作,是因为在 JavaScript 中除非有特别设置,捕获阶段会被忽略,仅有冒泡阶段会被触发(在目标阶段之后),MDN 是这样解释的: > 绑定在事件目标的事件监听器,事件处在目标阶段,而非捕获或冒泡阶段。事件监听器的捕获阶段在其他任何非捕获阶段之间被调用。 @@ -403,7 +403,7 @@ yourElement.addEventListener(type, listener, useCapture: true); 即便大多数事件冒泡,但是你知道有些事件其实不冒泡? -以下是原生JavaScript中的一些例子: +以下是原生 JavaScript 中的一些例子: - [blur](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event) ([focusout](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusout_event) 区别在于后者冒泡). - [focus](https://developer.mozilla.org/en-US/docs/Web/API/Element/focus_event) ([focusin](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusin_event) 区别在于后者冒泡). - [mouseleave](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseleave_event) ([mouseout](https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseout_event) 区别在于后者冒泡). @@ -412,24 +412,24 @@ yourElement.addEventListener(type, listener, useCapture: true); ⚠️ 当[事件](https://developer.mozilla.org/en-US/docs/Web/API/Event/Event)被创造时,可以冒泡的事件可以通过设定`bubbles`选项为`true`,当然这些事件仍然会经历捕获阶段。 -## React 16及过往版本中的事件监听器对比React 17及以上 +## React 16 及过往版本中的事件监听器对比 React 17 及以上 -如上文所述,React中的事件合成并不总是和原生JavaScript对应的概念一样。 +如上文所述,React 中的事件合成并不总是和原生 JavaScript 对应的概念一样。 -我们将在下文中学习其中一些差异点,以及React不同版本之间的差异。 +我们将在下文中学习其中一些差异点,以及 React 不同版本之间的差异。 -### 你不希望在React中出现事件冒泡 +### 你不希望在 React 中出现事件冒泡 -比方说,你可能希望React中的`onBlur`和`onFocus`和原生JavaScript中一样,不冒泡。但在React这两个事件也冒泡。 +比方说,你可能希望 React 中的`onBlur`和`onFocus`和原生 JavaScript 中一样,不冒泡。但在 React 这两个事件也冒泡。 -⚠️ 在React版本17中已经对一些特定事件做了[调整](https://reactjs.org/blog/2020/08/10/react-v17-rc.html#aligning-with-browsers),如`onScroll`,但大多数事件仍会冒泡。 +⚠️ 在 React 版本 17 中已经对一些特定事件做了[调整](https://reactjs.org/blog/2020/08/10/react-v17-rc.html#aligning-with-browsers),如`onScroll`,但大多数事件仍会冒泡。 更多话题内容可以参考[这个答案](https://stackoverflow.com/questions/34926910/onfocus-bubble-in-react)和[这篇文章](https://www.quirksmode.org/blog/archives/2008/04/delegating_the.html) ### `event.target.value` 在异步函数中曾作为无效值(Nullfied) -在React17之前,如果你想在异步函数中获取一个事件,你会获得未定义。 +在 React17 之前,如果你想在异步函数中获取一个事件,你会获得未定义。 -这是因为React的合成事件被纳入的事件池,即事件处理器被调用后,你将无法再次获取事件,因为这些事件会被重置并放入事件池。 +这是因为 React 的合成事件被纳入的事件池,即事件处理器被调用后,你将无法再次获取事件,因为这些事件会被重置并放入事件池。 ![](https://www.freecodecamp.org/news/content/images/2021/09/image-25.png) 图片源于[React](https://reactjs.org/docs/legacy-event-pooling.html) @@ -443,36 +443,36 @@ yourElement.addEventListener(type, listener, useCapture: true); 图片源于 [React](https://reactjs.org/docs/legacy-event-pooling.html) -设定这样机制的初衷时为了提升性能,但是React团队通过进一步观察,发现这样做不仅没有提升性能,反而让程序员感到困惑,所以他们废置了这个机制。 +设定这样机制的初衷时为了提升性能,但是 React 团队通过进一步观察,发现这样做不仅没有提升性能,反而让程序员感到困惑,所以他们废置了这个机制。 -⚠️ 在React17之后,React不再将合成事件对象纳入事件池。所以你可以在不借助`event.persisit()`方法的前提下在异步函数中获取`event.target.value`的值。 +⚠️ 在 React17 之后,React 不再将合成事件对象纳入事件池。所以你可以在不借助`event.persisit()`方法的前提下在异步函数中获取`event.target.value`的值。 在使用之前确保你阅读[这篇文章](https://reactjs.org/blog/2020/08/10/react-v17-rc.html#no-event-pooling)。 ## 特殊情况:当需要执行父元素的时候怎么办? -让我们利用所学一起解决一个特殊情况,并将这一技巧运用到你下一个(或者现在的)React应用 +让我们利用所学一起解决一个特殊情况,并将这一技巧运用到你下一个(或者现在的)React 应用 🤔 假设我们希望我们的应用具备以下功能: 1. 当用户点击内部`div`或者按钮元素,仅被点击的元素被触发(或如下文例子,改变电视的频道)。 2. 当用户点击外部的父元素`div`,父元素被触发(这在弹出模型中常见,当用户点击模型外部,淡出关闭,或如下文例子,电视重新打开)。 Currently, you know that if you click either the parent/child element, React’s SyntheticEvent system would trigger bubbling. -目前你所知的是不论是点击子还是父元素,React的合成事件会触发冒泡。 +目前你所知的是不论是点击子还是父元素,React 的合成事件会触发冒泡。 你同时知道可以使用`event.stopPropagation()`来阻止冒泡。 于是我们进入一个两难的境地。 -如果你想要一个事件处理去可以在一个情景下被触发(上述功能1),另一个事件处理器在另一个情景下触发(上述功能2),该怎么办? +如果你想要一个事件处理去可以在一个情景下被触发(上述功能 1),另一个事件处理器在另一个情景下触发(上述功能 2),该怎么办? ⚠️ 若使用`event.stopPropagation()`可以阻止一个事件处理器触发,但是就再也不能在另一个场景内触发另一个事件处理器,如何处理这个问题? -可以使用React的状态模型! +可以使用 React 的状态模型! 注意此处我使用了箭头函数,所以没必要使用`bind`,如果你不熟悉这个方法,可以查看[我写的这篇文章](/news/learn-es6-the-dope-way-part-ii-arrow-functions-and-the-this-keyword-381ac7a32881/)。 -ℹ️ 下文包括了React的class组件版本和React Hooks版本任君选择。确保你仔细阅读了注解。 +ℹ️ 下文包括了 React 的 class 组件版本和 React Hooks 版本任君选择。确保你仔细阅读了注解。 ```javascript import React, { Fragment, Component } from "react"; @@ -563,7 +563,7 @@ class TV extends Component { export default TV; ``` -用class组件的例子 +用 class 组件的例子 ```javascript import React, { Fragment, useState } from "react"; @@ -648,14 +648,14 @@ const TV = () => { export default TV; ``` -用React Hooks写的例子 +用 React Hooks 写的例子 🤔 运行代码会出现以下情况: 1. 当点击 `Change Channel`, 频道增加。 注意其他两个事件处理器并没有运行。 -2. 当点击 `Turn Off TV`, UI发生变化,当我们点击父元素`div`外部,其他两个事件监听器并没有运行。 +2. 当点击 `Turn Off TV`, UI 发生变化,当我们点击父元素`div`外部,其他两个事件监听器并没有运行。 3. 当电视关闭时点击外部父元素`div`内部,只有这个事件处理器运行。 -请注意:我在例子中用了`state ={}`而不是`constructor(){...}`因为`Babel`(一种JavaScript的编译器)会转移React代码,在内部添加`constructor`,如果你知道这个信息,可以跳过下图: +请注意:我在例子中用了`state ={}`而不是`constructor(){...}`因为`Babel`(一种 JavaScript 的编译器)会转移 React 代码,在内部添加`constructor`,如果你知道这个信息,可以跳过下图: ![](https://www.freecodecamp.org/news/content/images/2021/09/image-27.png) 截图自 Mariya Diminsky 图片来源于[Babel](https://babeljs.io/) @@ -711,15 +711,15 @@ const Modal = ({ header, content, cancelButtonText, confirmButtonText, history, 至此你学习到了: - 事件委托的定义以及事件冒泡和事件捕获如何运作。 -- JavaScript和React中事件传播的不同。 -- 进一步了解了React中事件处理的优缺点。 +- JavaScript 和 React 中事件传播的不同。 +- 进一步了解了 React 中事件处理的优缺点。 - 特殊情况的一些处理问题的办法。 - `Event.target`和`Event.currentTarget` 的区别以及事件监听器绑定的地方不一定是事件被触发的地方。 -- 在现代JavaScript中事件传播是如何运行的,如果需要使用捕获阶段如何使用`useCapture`参数。 -- 并不是所有原生JavaScript中的事件以及与他们功能类似的事件会冒泡。 -- 几乎所有React合成事件会冒泡(一些React17中的事件除外)。 -- 使用React状态组件来处理特殊情况,使得在触发父元素事件的同时不影响其他的事件处理器。 -### 更多资源/ 拓展阅读: +- 在现代 JavaScript 中事件传播是如何运行的,如果需要使用捕获阶段如何使用`useCapture`参数。 +- 并不是所有原生 JavaScript 中的事件以及与他们功能类似的事件会冒泡。 +- 几乎所有 React 合成事件会冒泡(一些 React17 中的事件除外)。 +- 使用 React 状态组件来处理特殊情况,使得在触发父元素事件的同时不影响其他的事件处理器。 +### 更多资源/ 拓展阅读 - [https://www.youtube.com/watch?v=Q6HAJ6bz7bY](https://www.youtube.com/watch?v=Q6HAJ6bz7bY) - [https://javascript.info/bubbling-and-capturing](https://javascript.info/bubbling-and-capturing) @@ -727,7 +727,7 @@ const Modal = ({ header, content, cancelButtonText, confirmButtonText, history, - [https://chrisrng.svbtle.com/event-propagation-and-event-delegation](https://chrisrng.svbtle.com/event-propagation-and-event-delegation) - [https://jsbin.com/hilome/edit?js,output](https://jsbin.com/hilome/edit?js,output) -👋🏻你好! 👩🏻‍💻我是 Mariya Diminsky, 一位充满热情的自学[软件工程师](https://github.com/maariyadiminsky). 我是一个全栈工程师, 也专注于前端开发 (我 💖 React), 也是Unity/C# 开发。 我是 [TrinityMoon Studios](https://trinitymoonstudios.com/) 的创始人,我创造了 [The Girl Who Knew Time](https://play.google.com/store/apps/details?id=com.trinitymoonstudios.thegirlwhoknewtime). +👋🏻你好! 👩🏻‍💻我是 Mariya Diminsky, 一位充满热情的自学[软件工程师](https://github.com/maariyadiminsky). 我是一个全栈工程师, 也专注于前端开发 (我 💖 React), 也是 Unity/C# 开发。 我是 [TrinityMoon Studios](https://trinitymoonstudios.com/) 的创始人,我创造了 [The Girl Who Knew Time](https://play.google.com/store/apps/details?id=com.trinitymoonstudios.thegirlwhoknewtime). -✨🥰 如果你喜欢这篇文章,并且想要了解更多有关React和系统设计的话题,可以关注我最近的更新。 🎉 +✨🥰 如果你喜欢这篇文章,并且想要了解更多有关 React 和系统设计的话题,可以关注我最近的更新。 🎉 diff --git a/chinese/articles/execution-context-how-javascript-works-behind-the-scenes.md b/chinese/articles/execution-context-how-javascript-works-behind-the-scenes.md index 5a3b83cdf..3bf9e000c 100644 --- a/chinese/articles/execution-context-how-javascript-works-behind-the-scenes.md +++ b/chinese/articles/execution-context-how-javascript-works-behind-the-scenes.md @@ -5,14 +5,14 @@ ![JavaScript Execution Context – How JS Works Behind The Scenes](https://www.freecodecamp.org/news/content/images/size/w2000/2022/02/header.png) -所有JavaScript代码都需要在某种环境中托管运行。在大多数情况下,网络浏览器就是这个环境。 +所有 JavaScript 代码都需要在某种环境中托管运行。在大多数情况下,网络浏览器就是这个环境。 -当一段JavaScript代码在网络浏览器中执行时,幕后发生很多事情。 在这篇文章中,我们将对运行在浏览器的JavaScript代码的幕后一探究竟。 +当一段 JavaScript 代码在网络浏览器中执行时,幕后发生很多事情。 在这篇文章中,我们将对运行在浏览器的 JavaScript 代码的幕后一探究竟。 在深入研究前,需要先了解一些概念,因为这些概念会在后文反复提及。 - ****编译器****: 编译器或语法编译器是一个能够逐行读取代码的程序。它了解代码如何匹配编程语言所定义的语法,以及代码应该做什么。 -- ****JavaScript引擎****: JavaScript引擎是一个计算机程序,它接收JavaScript源代码并将其编译成CPU可以理解的二进制指令(机器码)。JavaScript引擎通常是由浏览器供应商开发的,每一个主流浏览器都有一个自己开发的引擎。如:谷歌Chrome浏览器的 [V8引擎](https://v8.dev/),Firefox的[SpiderMonkey](https://firefox-source-docs.mozilla.org/js/index.html)和IE的[Chakra](https://en.wikipedia.org/wiki/Chakra_(JScript_engine))。 +- ****JavaScript 引擎****: JavaScript 引擎是一个计算机程序,它接收 JavaScript 源代码并将其编译成 CPU 可以理解的二进制指令(机器码)。JavaScript 引擎通常是由浏览器供应商开发的,每一个主流浏览器都有一个自己开发的引擎。如:谷歌 Chrome 浏览器的 [V8 引擎](https://v8.dev/),Firefox 的[SpiderMonkey](https://firefox-source-docs.mozilla.org/js/index.html)和 IE 的[Chakra](https://en.wikipedia.org/wiki/Chakra_(JScript_engine))。 - ****函数声明****: 指的是被命名的函数。 ```javascript @@ -25,19 +25,19 @@ statements; 概念解释完毕,让我们开始深入研究吧! -## **JavaScript是如何被执行的** +## **JavaScript 是如何被执行的** -你或许不知道,浏览器并不理解我们在应用中编写的高级JavaScript代码。代码需要被转换成浏览器和计算机能够理解的格式——机器码。 +你或许不知道,浏览器并不理解我们在应用中编写的高级 JavaScript 代码。代码需要被转换成浏览器和计算机能够理解的格式——机器码。 -浏览器在读取HTML时,如果遇到了` ``` -#### 使用 axios 创建请求: +#### 使用 axios 创建请求 基于 Axios,你可以使用 `GET` 和 `POST` 来向服务端请求数据和发送数据。 -#### GET: +#### GET ![](https://cdn-media-1.freecodecamp.org/images/1*4wmqiPsSN5mdgjJiRaKVZg.png) `axios` 需要一个必填参数,当然你也可以提供第二个可选参数。这个示例调用一些数据作简单的查询。 -#### POST: +#### POST ![](https://cdn-media-1.freecodecamp.org/images/1*ey6-vwsrm9RAhyoU15u6xQ.png) diff --git a/chinese/articles/higher-order-functions-in-javascript.md b/chinese/articles/higher-order-functions-in-javascript.md index 11bc8b208..40a9010f6 100644 --- a/chinese/articles/higher-order-functions-in-javascript.md +++ b/chinese/articles/higher-order-functions-in-javascript.md @@ -5,9 +5,9 @@ ![Higher Order Functions in JavaScript – Beginner's Guide](https://www.freecodecamp.org/news/content/images/size/w2000/2022/06/Blog-8---Freecodecamp-Banner.png) -在JavaScript中函数是一等公民。函数可以作为值赋值给另一个变量,或者作为参数传入另一个函数,甚至从另一个函数返回。 +在 JavaScript 中函数是一等公民。函数可以作为值赋值给另一个变量,或者作为参数传入另一个函数,甚至从另一个函数返回。 -这就赋予了JavaScript高阶函数的能力。 +这就赋予了 JavaScript 高阶函数的能力。 基本上如果一个函数可以将另外一个函数作为参数,或者返回另一个函数,则被称为高阶函数。 @@ -126,7 +126,7 @@ filterFunction(arr, isEven) 我们相当于将逻辑函数从主函数抽离出来,现在我们可以传入任意的过滤逻辑,并且不需要对 `filterFunction`做任何修改。 -例如,如果我们想要过滤出所有大于5的数字,我们可以这样编写逻辑函数: +例如,如果我们想要过滤出所有大于 5 的数字,我们可以这样编写逻辑函数: ```javascript function isGreaterThanFive(x) { @@ -154,7 +154,7 @@ filterFunction(arr, (x) => x > 5) ### 如何创建[Polyfill](https://developer.mozilla.org/zh-CN/docs/Glossary/Polyfill) -我们知道JavaScript内置了一些高阶函数,如: `map()`、`filter()`、`reduce()`等, 我们可以自定义这些函数吗?让我们深入研究一下。 +我们知道 JavaScript 内置了一些高阶函数,如: `map()`、`filter()`、`reduce()`等, 我们可以自定义这些函数吗?让我们深入研究一下。 在上一个部分,我们创建了过滤函数。让我们为 `filterFunction`函数创建一个数组原型,这样我们就可以在任意数组使用这个函数,这个原型如下: @@ -177,7 +177,7 @@ arr.filterFunction(callbackFn) 那么 `this`就指代数组 `arr`。 -现在我们就可以像使用JS内置的`filter()`函数一样使用`filterFunction`。 我们可以编写这样的代码: +现在我们就可以像使用 JS 内置的`filter()`函数一样使用`filterFunction`。 我们可以编写这样的代码: ```javascript arr.filterFunction(isEven) @@ -206,11 +206,11 @@ arr.filter((x) => x > 5) ``` -这样,我们就给内置`filter()`函数写了一个polyfill。 +这样,我们就给内置`filter()`函数写了一个 polyfill。 ### 函数链 -我们也可以在`filter()`和我们自定义的原型方法中采取链式调用。让我们先筛选出所有大于5的数字,然后从结果中筛选出所有的偶数。代码如下: +我们也可以在`filter()`和我们自定义的原型方法中采取链式调用。让我们先筛选出所有大于 5 的数字,然后从结果中筛选出所有的偶数。代码如下: ```javascript //使用我们自定义的filterFunction() @@ -222,13 +222,13 @@ arr.filter((x) => x > 5).filter((x) => x % 2 === 0) // 两者都会打印出: [ 6, 8, 10 ] ``` -这就是我们如何在JS 中使用高阶函数来编写模式模块、以及编写更简洁、更易于维护的代码。 +这就是我们如何在 JS 中使用高阶函数来编写模式模块、以及编写更简洁、更易于维护的代码。 接下来,让我们看看如何从函数返回另一个函数 ![lets-move-on-proceed](https://www.freecodecamp.org/news/content/images/2022/06/lets-move-on-proceed.gif) -## 如何在JavaScript中从函数返回另一个函数 +## 如何在 JavaScript 中从函数返回另一个函数 我们可以从函数中返回函数,是因为函数被当作了值,请看以下例子 @@ -247,7 +247,7 @@ function calculate(operation) { } ``` -在上述代码中,当我们传入参数触发`calculate` 时, 函数通过switch条件评估参数,最终返回一个匿名函数。 所以如果我们调用 `calculate()`并将结果存储到一个变量,并在控制台打印变量,会得到以下结果: +在上述代码中,当我们传入参数触发`calculate` 时, 函数通过 switch 条件评估参数,最终返回一个匿名函数。 所以如果我们调用 `calculate()`并将结果存储到一个变量,并在控制台打印变量,会得到以下结果: ```javascript const calculateAdd = calculate("ADD"); @@ -312,10 +312,10 @@ calculate("SUBTRACT")(2, 3); 在这篇文章中,我们学习了: -- 在JS中为什么函数是一等公民 +- 在 JS 中为什么函数是一等公民 - 什么是高阶函数 - 如何将函数作为参数传入另一个函数 -- 如何创建一个数组原型、函数链、以及为内置方法`filter()`编写自定义polyfill +- 如何创建一个数组原型、函数链、以及为内置方法`filter()`编写自定义 polyfill - 如何在函数中返回函数,以及两种调用返回函数的方法 ## 收尾 diff --git a/chinese/articles/host-an-angular-application-on-github-pages-with-travis-ci.md b/chinese/articles/host-an-angular-application-on-github-pages-with-travis-ci.md index 27ec73ea8..ef44e11ae 100644 --- a/chinese/articles/host-an-angular-application-on-github-pages-with-travis-ci.md +++ b/chinese/articles/host-an-angular-application-on-github-pages-with-travis-ci.md @@ -90,7 +90,7 @@ ![travisci-step4](https://www.freecodecamp.org/news/content/images/2022/04/travisci-step4.png) -字段 NAME的值填写为 GITHUB\_TOKEN ,VALUE 的值填写为你在 GitHub 上生成的令牌的值,然后单击按钮 Add。 +字段 NAME 的值填写为 GITHUB_TOKEN ,VALUE 的值填写为你在 GitHub 上生成的令牌的值,然后单击按钮 Add。 ![travisci-step5](https://www.freecodecamp.org/news/content/images/2022/04/travisci-step5.png) diff --git a/chinese/articles/how-i-learned-to-code-without-a-cs-degree-or-bootcamp.md b/chinese/articles/how-i-learned-to-code-without-a-cs-degree-or-bootcamp.md index 39507458f..d89e64f19 100644 --- a/chinese/articles/how-i-learned-to-code-without-a-cs-degree-or-bootcamp.md +++ b/chinese/articles/how-i-learned-to-code-without-a-cs-degree-or-bootcamp.md @@ -14,7 +14,7 @@ ![](https://www.freecodecamp.org/news/content/images/2020/12/hiltc-1.JPG) -## 编程曾经只是一个兴趣而已... +## 编程曾经只是一个兴趣而已 应该可以从高中说起,我在那时候发现网络世界,自学了基础的 HTML 和 CSS。当时的我只是觉得好玩。 diff --git a/chinese/articles/how-reduce-reduceright-works-javascript.md b/chinese/articles/how-reduce-reduceright-works-javascript.md index 827594ec9..929e25597 100644 --- a/chinese/articles/how-reduce-reduceright-works-javascript.md +++ b/chinese/articles/how-reduce-reduceright-works-javascript.md @@ -5,7 +5,7 @@ ![How the JavaScript reduce and reduceRight Methods Work](https://www.freecodecamp.org/news/content/images/size/w2000/2022/05/reduce-cover-with-title-3.jpg) -`reduce`和`reduceRight`是JavaScript内置的两个数组方法,这两个方法的学习曲线略微陡峭。 +`reduce`和`reduceRight`是 JavaScript 内置的两个数组方法,这两个方法的学习曲线略微陡峭。 其实这两个方法的本质就像下面的算术题一样简单。 @@ -29,7 +29,7 @@ 不用把`reduce`和`reduceRight`看得太复杂,其实它们就相当于上面算术题的概括。在这篇文章我们会讲解这两个方法的重要信息。 -本文将列举易于理解的算法例子来演示JavaScript中的归约(reduction)。 +本文将列举易于理解的算法例子来演示 JavaScript 中的归约(reduction)。 就这个话题我还制作了视频,如果你喜欢通过视频学习新知识的话,可以点击观看。 @@ -90,7 +90,7 @@ callbackfn(previousValue, currentElement, index, array) **`initialValue`(初始值)**: 这是 `previousValue` (累加器)的一个可选值。 如果存在,且 `myArray`中包含元素, 首次调用 `callbackfn`时会将它视为 `previousValue`传入。 -**注意**:`callbackfn`通常被称为 **reducer函数**(或简写为**reducer**)。 +**注意**:`callbackfn`通常被称为 **reducer 函数**(或简写为**reducer**)。 ## 3 通过图表理解`reduce`/`reduceRight`[§](#understanding-reduce-reduceright-with-diagram) @@ -100,7 +100,7 @@ callbackfn(previousValue, currentElement, index, array) ![reduce-diagram1-1](https://www.freecodecamp.org/news/content/images/2022/05/reduce-diagram1-1.png) -展示reduce和reduceRight区别的图表 +展示 reduce 和 reduceRight 区别的图表 注意: @@ -112,7 +112,7 @@ callbackfn(previousValue, currentElement, index, array) ## 4 `reduce`/`reduceRight`的演算规则 [§](#the-algorithm-of-reduce-reduceright) -下面的29行算法代码乍一看可能让人生畏。但你会发现理解他们比理解上述解释性的句子要容易得多。 +下面的 29 行算法代码乍一看可能让人生畏。但你会发现理解他们比理解上述解释性的句子要容易得多。 所以放轻松,享受这些步骤,别忘了可以在控制台实践这些步骤: @@ -216,7 +216,7 @@ console.log(arr.length); // 这个特征可以帮助我们更快添加新元素 ``` -reduce和reduceRight仅对 `myArray`中真实存在的元素调用`callbackfn`。 例如,你的数组是 `[1,,3,,5]`回调函数不会考虑没有元素的索引`1`和`3`。猜一猜下面的代码会打印什么内容: +reduce 和 reduceRight 仅对 `myArray`中真实存在的元素调用`callbackfn`。 例如,你的数组是 `[1,,3,,5]`回调函数不会考虑没有元素的索引`1`和`3`。猜一猜下面的代码会打印什么内容: ```js [,,,3,,,4].reduce((_, cv, i) => { @@ -226,7 +226,7 @@ reduce和reduceRight仅对 `myArray`中真实存在的元素调用`callbackfn` 如果你的答案是`6`,你是对的! -⚠️ **注意**: 不建议使用`callbackfn`来修改 `myArray`,因为这样会复杂化代码,容易产生bug。 +⚠️ **注意**: 不建议使用`callbackfn`来修改 `myArray`,因为这样会复杂化代码,容易产生 bug。 如果你到目前为止都理解的话,那么恭喜你已经了解 `reduce`/`reduceRight`的运作了。 diff --git a/chinese/articles/how-to-add-commit-hooks-to-git-with-husky-to-automate-code-tasks.md b/chinese/articles/how-to-add-commit-hooks-to-git-with-husky-to-automate-code-tasks.md index 9d3845eac..1da2e6680 100644 --- a/chinese/articles/how-to-add-commit-hooks-to-git-with-husky-to-automate-code-tasks.md +++ b/chinese/articles/how-to-add-commit-hooks-to-git-with-husky-to-automate-code-tasks.md @@ -6,33 +6,33 @@ ![How to Add Commit Hooks to Git with Husky to Automate Code Tasks](https://www.freecodecamp.org/news/content/images/size/w2000/2020/10/husky.jpg) -有很多工具可以使我们的代码任务自动化。我们可以用ESLint检查语法问题,用Prettier格式化我们的代码。 +有很多工具可以使我们的代码任务自动化。我们可以用 ESLint 检查语法问题,用 Prettier 格式化我们的代码。 -但并不是团队中的每个人都会记得每次提交时都要运行这些命令。我们如何使用Husky来添加Git钩子来为我们运行这些命令? +但并不是团队中的每个人都会记得每次提交时都要运行这些命令。我们如何使用 Husky 来添加 Git 钩子来为我们运行这些命令? - [什么是 Git Hooks?](./#what-are-git-hooks) - [什么是 Husky?](./#what-is-husky) - [我们要构建什么?](./#what-are-we-going-to-build) -- [第0步:建立一个新的项目](./#step-0-setting-up-a-new-project) -- [第1步:将 Husky 安装到一个项目上](./#step-1-installing-husky-to-a-project) -- [第2步:配置Husky以运行Git钩子](./#step-2-configuring-husky-to-run-git-hooks) -- [第3步:使用Husky用Prettier格式化代码](./#step-3-using-husky-to-format-code-with-prettier) +- [第 0 步:建立一个新的项目](./#step-0-setting-up-a-new-project) +- [第 1 步:将 Husky 安装到一个项目上](./#step-1-installing-husky-to-a-project) +- [第 2 步:配置 Husky 以运行 Git 钩子](./#step-2-configuring-husky-to-run-git-hooks) +- [第 3 步:使用 Husky 用 Prettier 格式化代码](./#step-3-using-husky-to-format-code-with-prettier)

什么是 Husky?

-[Husky](https://github.com/typicode/husky)是一个工具,它允许我们轻松地处理Git Hooks 并在提交代码时运行我们想要的脚本。 +[Husky](https://github.com/typicode/husky)是一个工具,它允许我们轻松地处理 Git Hooks 并在提交代码时运行我们想要的脚本。 -> 它的工作原理是在我们的 `package.json` 文件中加入一个对象,配置 Husky 来运行我们指定的脚本。之后,Husky会管理我们的脚本将在Git生命周期的哪个阶段运行。 +> 它的工作原理是在我们的 `package.json` 文件中加入一个对象,配置 Husky 来运行我们指定的脚本。之后,Husky 会管理我们的脚本将在 Git 生命周期的哪个阶段运行。

我们要构建什么?

-我们将建立一个简单的项目,用来测试Git Hooks。 +我们将建立一个简单的项目,用来测试 Git Hooks。 虽然你应该能够跟上你正在使用的任何项目,但我将使用 [Next.js](https://nextjs.org/) 作为这个项目的起点,只是因为我们可以运行一个命令来启动项目。 @@ -40,17 +40,17 @@ Prettier 是一个会自动为我们格式化代码的工具,如果你不期望它发生,就会造成很大的压力。跟随我使用 Next.js 项目,可以让你在不做任何无意改动的情况下测试一下。 -至于测试Git Hooks,我们将从添加一个简单的命令行语句开始,看看 Husky 的工作。但我们还将测试添加Prettier,它将自动为我们格式化我们的代码。 +至于测试 Git Hooks,我们将从添加一个简单的命令行语句开始,看看 Husky 的工作。但我们还将测试添加 Prettier,它将自动为我们格式化我们的代码。 -最后,在写这篇文章的时候,Husky 发布了一个 [v5 Alpha](https://typicode.github.io/husky/#/) 版本的Git Hooks 解决方案。鉴于它还只是一个Alpha版本,我们将继续使用[v4](https://github.com/typicode/husky/tree/v4.3.0),它允许我们用npm轻松安装 Husky 。 +最后,在写这篇文章的时候,Husky 发布了一个 [v5 Alpha](https://typicode.github.io/husky/#/) 版本的 Git Hooks 解决方案。鉴于它还只是一个 Alpha 版本,我们将继续使用[v4](https://github.com/typicode/husky/tree/v4.3.0),它允许我们用 npm 轻松安装 Husky 。

第0步:建立一个新的项目

正如我所提到的,你可以按照同样的步骤来处理任何用`package.json`文件管理的项目。 -Next.js对于本攻略来说绝对是多余的,但我们的目标是尽量减少实际使用Husky时的设置步骤。 +Next.js 对于本攻略来说绝对是多余的,但我们的目标是尽量减少实际使用 Husky 时的设置步骤。 -要开始使用Next.js,请进入到你想启动项目的目录,并运行以下程序。: +要开始使用 Next.js,请进入到你想启动项目的目录,并运行以下程序。: ```shell yarn create next-app my-husky-project @@ -60,7 +60,7 @@ npx create-next-app my-husky-project _注意:请随意将`my-husky-project`替换为你想命名的目录。_ -这将创建一个新的文件夹,创建一个新的Next.js项目,并安装所有的依赖项。 +这将创建一个新的文件夹,创建一个新的 Next.js 项目,并安装所有的依赖项。 ![create-next-app](https://www.freecodecamp.org/news/content/images/2020/10/create-next-app.jpg) @@ -70,7 +70,7 @@ _注意:请随意将`my-husky-project`替换为你想命名的目录。_

第1步:将 Husky 安装到一个项目上

-要安装Husky,我们可以使用 yarn 或 npm。 +要安装 Husky,我们可以使用 yarn 或 npm。 ```shell yarn add husky @@ -78,7 +78,7 @@ yarn add husky npm install husky ``` -_注意:如果此时安装 Husky 会安装v5 版,这意味着v5已经正式发布。请参阅 [更新 Husky 文档](https://typicode.github.io/husky/#/),或者你可以在安装时指定husky@4.3.0(或任何最新的版本)来安装最新的v4版本。_ +_注意:如果此时安装 Husky 会安装 v5 版,这意味着 v5 已经正式发布。请参阅 [更新 Husky 文档](https://typicode.github.io/husky/#/),或者你可以在安装时指定 husky@4.3.0(或任何最新的版本)来安装最新的 v4 版本。_ 一旦软件包安装完毕,我们就应该准备好使用 Husky。 @@ -86,7 +86,7 @@ _注意:如果此时安装 Husky 会安装v5 版,这意味着v5已经正式

第2步:配置Husky以运行Git钩子

-接下来,我们要设置Husky,这样我们就可以使用它作为我们的Git hooks。 +接下来,我们要设置 Husky,这样我们就可以使用它作为我们的 Git hooks。 在我们的`package.json`文件中,创建一个名为`husky`的新属性,并设置一个空对象。 @@ -104,9 +104,9 @@ _注意:如果此时安装 Husky 会安装v5 版,这意味着v5已经正式 }, ``` -这就是我们要添加Git hooks 的地方。Husky 几乎支持[Git定义的所有Git钩子](https://git-scm.com/docs/githooks),所以我们可以在Git事件流程中尽可能灵活地使用。 +这就是我们要添加 Git hooks 的地方。Husky 几乎支持[Git 定义的所有 Git 钩子](https://git-scm.com/docs/githooks),所以我们可以在 Git 事件流程中尽可能灵活地使用。 -为了测试这一点,我创建了 [一个新的分支](https://github.com/colbyfayock/my-husky-project/tree/main+test),在那里我添加了该页面上的所有Git hooks,包括一个简单地写入终端 `[Husky] 事件名称` 的脚本。 +为了测试这一点,我创建了 [一个新的分支](https://github.com/colbyfayock/my-husky-project/tree/main+test),在那里我添加了该页面上的所有 Git hooks,包括一个简单地写入终端 `[Husky] 事件名称` 的脚本。 _注意:不要觉得你需要这样做,除非你很好奇。我们的目标是通过我的例子向你展示它是如何工作的。_ @@ -119,15 +119,15 @@ _注意:不要觉得你需要这样做,除非你很好奇。我们的目标 "pre-commit": "echo \"[Husky] pre-commit\"", ``` -这样做的目的是告诉Husky,在每一个Git hooks 的阶段,都要告诉我们! +这样做的目的是告诉 Husky,在每一个 Git hooks 的阶段,都要告诉我们! 当我提交这个改动时,我们可以立即看到 Husky 启动了我们的一些脚本。 ![husky-commit-hooks](https://www.freecodecamp.org/news/content/images/2020/10/husky-commit-hooks.jpg) -这些都是Git允许我们在提交过程中钩住的所有事件。 +这些都是 Git 允许我们在提交过程中钩住的所有事件。 -同样,如果我把这些改动推送到Github,我可以看到推送过程中运行了`pre-push` hook! +同样,如果我把这些改动推送到 Github,我可以看到推送过程中运行了`pre-push` hook! ![husky-push-hooks](https://www.freecodecamp.org/news/content/images/2020/10/husky-push-hooks.jpg) @@ -135,21 +135,21 @@ _注意:不要觉得你需要这样做,除非你很好奇。我们的目标 但能够看到这一点是非常棒的,无论是运行格式化我们的代码、防止秘密访问密钥被提交的代码,还是其他真正能够帮助你的工作流程自动化的重要任务。 -我们现在可以看到,我们可以通过在`package.json`中指定配置和挂钩来配置Husky。 +我们现在可以看到,我们可以通过在`package.json`中指定配置和挂钩来配置 Husky。 [Follow along with the commit](https://github.com/colbyfayock/my-husky-project/commit/108583a7e96564baf0fac994eafa6cf98d65d03e). -_注意:如果你想查看我的分支,其中包括每一个用于测试的Git挂钩,[你可以在Github上找到它](https://github.com/colbyfayock/my-husky-project/tree/main+test)。_ +_注意:如果你想查看我的分支,其中包括每一个用于测试的 Git 挂钩,[你可以在 Github 上找到它](https://github.com/colbyfayock/my-husky-project/tree/main+test)。_

第3步:使用Husky用Prettier格式化代码

-最后,对于一个真实世界的用例,我们要测试一下使用Prettier来自动格式化我们的代码。 +最后,对于一个真实世界的用例,我们要测试一下使用 Prettier 来自动格式化我们的代码。 -Prettier是一个有固定风格的代码格式化工具,它允许你轻松地清理你的代码,使它看起来像一个人写的。 +Prettier 是一个有固定风格的代码格式化工具,它允许你轻松地清理你的代码,使它看起来像一个人写的。 -为什么像Prettier这样的工具很重要?当通过代码工作时,特别是与一个团队一起工作时,保持一致性是很重要的,这样每个人都知道应该期待什么。这将有助于防止在代码审查中为一个分号争论不休,但也有助于发现语法错误和防止错误。 +为什么像 Prettier 这样的工具很重要?当通过代码工作时,特别是与一个团队一起工作时,保持一致性是很重要的,这样每个人都知道应该期待什么。这将有助于防止在代码审查中为一个分号争论不休,但也有助于发现语法错误和防止错误。 -_警告:运行 Prettier 会自动格式化你所有的代码。虽然我们要在提交修改前进行测试,但一旦你把它作为Git Hook应用,它就会自动完成这个过程。_ +_警告:运行 Prettier 会自动格式化你所有的代码。虽然我们要在提交修改前进行测试,但一旦你把它作为 Git Hook 应用,它就会自动完成这个过程。_ 为了开始使用 Prettier,让我们用我们的软件包管理器安装它。: @@ -159,9 +159,9 @@ yarn add prettier -D npm install prettier --save-dev ``` -注意:我们将Prettier作为一个 `devDependency` 来安装,因为我们的应用程序不需要它来运行。 +注意:我们将 Prettier 作为一个 `devDependency` 来安装,因为我们的应用程序不需要它来运行。 -接下来,我们可以在我们的 `package.json` 中添加一个新的脚本,这将使我们更容易运行Prettier来进行测试。 +接下来,我们可以在我们的 `package.json` 中添加一个新的脚本,这将使我们更容易运行 Prettier 来进行测试。 在`scripts`属性里面,添加: @@ -179,19 +179,19 @@ yarn lint npm run lint ``` -而一旦我们这样做,我们可以看到,Prettier告诉我们,会改变列出的文件。 +而一旦我们这样做,我们可以看到,Prettier 告诉我们,会改变列出的文件。 ![prettier-check](https://www.freecodecamp.org/news/content/images/2020/10/prettier-check.jpg) -在这一点上,我们的代码将保持不变。但如果我们想真正运行Prettier来进行这些修改,我们可以先添加一个额外的脚本: +在这一点上,我们的代码将保持不变。但如果我们想真正运行 Prettier 来进行这些修改,我们可以先添加一个额外的脚本: ```json "format": "prettier --write ." ``` -而如果我们运行该脚本,它将更新所有这些文件,使代码的格式符合Prettier的规范。 +而如果我们运行该脚本,它将更新所有这些文件,使代码的格式符合 Prettier 的规范。 -_警告:只是另一个注意,运行Prettier来写修改,将在你的文件中进行修改。这些都是代码风格的改变,不应该影响代码的运行,而是影响代码的外观。在运行格式之前,你应该用Git提交来保存任何修改,这样你就可以在不满意的情况下轻松地恢复这些修改。 +_警告:只是另一个注意,运行 Prettier 来写修改,将在你的文件中进行修改。这些都是代码风格的改变,不应该影响代码的运行,而是影响代码的外观。在运行格式之前,你应该用 Git 提交来保存任何修改,这样你就可以在不满意的情况下轻松地恢复这些修改。 现在你可以用以下方式运行脚本: @@ -203,9 +203,9 @@ yarn format ![prettier-write](https://www.freecodecamp.org/news/content/images/2020/10/prettier-write.jpg) -现在是与本攻略相关的部分:我们可以把它作为一个Git hook加入。这样,当有人试图提交代码时,Prettier 会在代码被保存之前运行。这意味着我们将始终保持代码与Prettier的格式化风格一致。 +现在是与本攻略相关的部分:我们可以把它作为一个 Git hook 加入。这样,当有人试图提交代码时,Prettier 会在代码被保存之前运行。这意味着我们将始终保持代码与 Prettier 的格式化风格一致。 -在我们的Husky hook 配置里面,让我们添加: +在我们的 Husky hook 配置里面,让我们添加: ```json "husky": { @@ -217,7 +217,7 @@ yarn format 如果你注意到在我们的预提交 hook,我们也加入了`git add -A .`。 -当Husky运行时,它只是运行提供的脚本。当运行我们的 Prettier 命令时,我们只是对代码进行格式化,但我们从未将这些修改作为过程的一部分来保存。所以我们使用`git add`来存储所有这些修改,并将其纳入提交。 +当 Husky 运行时,它只是运行提供的脚本。当运行我们的 Prettier 命令时,我们只是对代码进行格式化,但我们从未将这些修改作为过程的一部分来保存。所以我们使用`git add`来存储所有这些修改,并将其纳入提交。 To test this out, I reverted the changes to all of the files that were formatted before. If you’re following along with the same project, you can run: @@ -227,7 +227,7 @@ git checkout pages 这将重置 `pages` 中的所有修改,使之成为最后一次提交。 -现在,让我们试着用Git添加所有的文件并提交修改。 +现在,让我们试着用 Git 添加所有的文件并提交修改。 ![git-commit-husky-precommit-prettier](https://www.freecodecamp.org/news/content/images/2020/10/git-commit-husky-precommit-prettier.jpg) @@ -237,13 +237,13 @@ git checkout pages ## 我接下来能做什么? -### 使用linet-staged,只对更改过的文件运行格式化。 +### 使用 linet-staged,只对更改过的文件运行格式化 -我们在预提交 hook 中使用Prettier,并指定`.`,这意味着它每次都会在所有文件上运行。 +我们在预提交 hook 中使用 Prettier,并指定`.`,这意味着它每次都会在所有文件上运行。 -我们可以使用一个叫做[lint-staged](https://github.com/okonet/lint-staged)的工具,它允许我们仍然用Husky来运行我们的Git钩子,但它只会运行在已经提交的文件上。 +我们可以使用一个叫做[lint-staged](https://github.com/okonet/lint-staged)的工具,它允许我们仍然用 Husky 来运行我们的 Git 钩子,但它只会运行在已经提交的文件上。 -例如,如果我们想用Husky和Prettier来做这件事,我们的配置: +例如,如果我们想用 Husky 和 Prettier 来做这件事,我们的配置: ```json "husky": { @@ -258,23 +258,23 @@ git checkout pages 作为 `lint-staged` 运行方式的一部分,它将自动为我们把更改的文件附加到我们的 Prettier 语句的末尾。 -你也会注意到我们没有包括`git add`,lint-staged也会为我们自动添加任何变化到Git上。 +你也会注意到我们没有包括`git add`,lint-staged 也会为我们自动添加任何变化到 Git 上。 -### 设置一个Prettier配置来定制格式化规则 +### 设置一个 Prettier 配置来定制格式化规则 Prettier 是非常固定风格的 。有一些东西我个人并不喜欢,你可能也有同样的感觉。 -幸运的是,Prettier允许你设置一个配置文件,可以覆盖其中的一些文件,使你的代码只是你和你的团队想要的方式。 +幸运的是,Prettier 允许你设置一个配置文件,可以覆盖其中的一些文件,使你的代码只是你和你的团队想要的方式。 -### 告诉Prettier用.prettierignore来忽略文件 +### 告诉 Prettier 用.prettierignore 来忽略文件 -你也可能不希望Prettier运行在 **所有的东西** 上(也许你想)。 +你也可能不希望 Prettier 运行在 **所有的东西** 上(也许你想)。 -Prettier允许你在项目根目录下的`package.json`旁边设置一个`.prettierignore`文件,类似于`.gitignore`,它允许你告诉Prettier不应该处理哪些文件。 +Prettier 允许你在项目根目录下的`package.json`旁边设置一个`.prettierignore`文件,类似于`.gitignore`,它允许你告诉 Prettier 不应该处理哪些文件。 [![Follow me for more Javascript, UX, and other interesting things!](https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card)](https://twitter.com/colbyfayock) -- [在Twitter 关注我](https://twitter.com/colbyfayock) +- [在 Twitter 关注我](https://twitter.com/colbyfayock) - [订阅我的 Youtube 频道](https://youtube.com/colbyfayock) - [✉️ 注册订阅我的 Newsletter](https://www.colbyfayock.com/newsletter/) - [赞助我](https://github.com/sponsors/colbyfayock) diff --git a/chinese/articles/how-to-be-a-successful-software-engineer.md b/chinese/articles/how-to-be-a-successful-software-engineer.md index b056363bf..660c8e382 100644 --- a/chinese/articles/how-to-be-a-successful-software-engineer.md +++ b/chinese/articles/how-to-be-a-successful-software-engineer.md @@ -20,23 +20,23 @@ - 先自己探索再寻求帮助 - 简化 -如果你喜欢以视频形式观看,我在这里做了一个Youtube视频: +如果你喜欢以视频形式观看,我在这里做了一个 Youtube 视频: #### 少承诺多做事,做的成果大于预期 低估完成一项新的功能所需的工作量,是极其普遍的错误,甚至是有经验的工程师。 -如果你看看那些超出预算和/或延迟交付的项目的数量,你会感到非常惊讶。这是一个疯狂的数字,大约是50% 。 +如果你看看那些超出预算和/或延迟交付的项目的数量,你会感到非常惊讶。这是一个疯狂的数字,大约是 50% 。 让我们考虑一下这个数字。**50%的项目不是超预算就是延迟交付。** -这意味着在每1000个项目中,有500个,或其中的一半,是延迟交付或超预算。这让我很困惑。 +这意味着在每 1000 个项目中,有 500 个,或其中的一半,是延迟交付或超预算。这让我很困惑。 我清楚地记得在我的第一个技术项目中,我被赋予了领导开发的自主权。这意味着我是一个能写出技术设计文件的人,详细说明开发整个功能需要多少时间,我们需要多少工程师,等等。 -作为一个热心的、年轻的工程师,我 **严重低估**了完成工作所需的时间。实际完成需要了2-3倍的样子。 +作为一个热心的、年轻的工程师,我 **严重低估**了完成工作所需的时间。实际完成需要了 2-3 倍的样子。 -作为一个热心的、年轻的工程师,我***严重低估了完成工作所需的时间。大概是2-3倍的样子。 +作为一个热心的、年轻的工程师,我***严重低估了完成工作所需的时间。大概是 2-3 倍的样子。 后来我的经理让我坐下来,给我一些改变生活的建议。他对我说,**少承诺多做事,做的成果大于预期** 。 @@ -47,9 +47,9 @@ 1. 给你充足的时间来开发和重构。功能开发总是需要很多的时间,可以回去修复一些你(或你之前的团队)多年来积累的 [技术债务][2]。 2. 允许有时间找出 **最好的** 设计,而不仅仅是一个工作设计。 3. 在功能开发过程中,有很多事情会出错。同事休假,你生病,开会,你的孩子生病,你的车被撞,这样的事情不胜枚举。认识到事情可能出错是很重要的,你要确保你的日程安排中有一个缓冲区。 -4. 由于第2点,你能够持续地产生高质量的工作,由于第3点,你能够每次都按时交付,你现在被认为是公司内部的**高绩效者**,知道你在说什么,并且可以被信任。这就是双赢。 +4. 由于第 2 点,你能够持续地产生高质量的工作,由于第 3 点,你能够每次都按时交付,你现在被认为是公司内部的**高绩效者**,知道你在说什么,并且可以被信任。这就是双赢。 -你可能会说,承诺不足的坏处是,别人会认为你在偷懒,因为你估计了10周的工作,而这些工作本来可以在2周内完成。 +你可能会说,承诺不足的坏处是,别人会认为你在偷懒,因为你估计了 10 周的工作,而这些工作本来可以在 2 周内完成。 说实话,我也曾为此纠结过一段时间。然而,我后来意识到,只要 **你在整个过程中与利益相关者开诚布公并保持一致**,你就会没事。 @@ -71,7 +71,7 @@ 这里的 **关键是认识到我们并不了解一切**。你可能没有意识到,但你所做的大多数决定背后都有无数的选择。 -例如,作为一个计算机科学专业的学生,大学毕业后的第一份工作应该怎么做?除了为创业公司或大型科技公司工作外,你还可以在海外做自由职业者,开一个 Youtube 频道,在 Udemy上 教计算机科学,开一个博客,或者开自己的公司。选择的清单是无限的,你今天做出的决定将基于几个关键标准。 +例如,作为一个计算机科学专业的学生,大学毕业后的第一份工作应该怎么做?除了为创业公司或大型科技公司工作外,你还可以在海外做自由职业者,开一个 Youtube 频道,在 Udemy 上 教计算机科学,开一个博客,或者开自己的公司。选择的清单是无限的,你今天做出的决定将基于几个关键标准。 你最终会做出一个对你来说 **最好的** 决定,而不一定是客观上最好的,因为每个人都是独特的,没有一刀切的情况。 @@ -81,7 +81,7 @@ 失败的概率 -我思考的方式是,我为最坏的情况分配一个概率,并将其用于我的决策过程。这是我从一本叫做 [Ray Dalio的《原则》][3]的书中学到的,这是一本我强烈推荐的好书。 +我思考的方式是,我为最坏的情况分配一个概率,并将其用于我的决策过程。这是我从一本叫做 [Ray Dalio 的《原则》][3]的书中学到的,这是一本我强烈推荐的好书。 如果发生可怕错误的概率在统计学上是显著的: @@ -109,7 +109,7 @@ 其好处有两方面: 1. **这可以减少功能变更,并迫使你专注于**。关于这一点,很多书都有记载,我不会用具体的细节来烦扰你。无论你在什么位置,保持注意力聚焦是至关重要的。 -2. 我们人类 **喜欢即时的满足感。** 如果你能在两周内发布一些东西,那就去做吧! 提前计划你的项目在2周内应该能够实现的目标,然后把它交到你的用户手中!这是非常令人满意的。这是一种非常令人满意的感觉,即使它真的惨遭失败,至少你坚持了你的目标,你现在已经收集了有价值的反馈来进行迭代。 +2. 我们人类 **喜欢即时的满足感。** 如果你能在两周内发布一些东西,那就去做吧! 提前计划你的项目在 2 周内应该能够实现的目标,然后把它交到你的用户手中!这是非常令人满意的。这是一种非常令人满意的感觉,即使它真的惨遭失败,至少你坚持了你的目标,你现在已经收集了有价值的反馈来进行迭代。 这使我想到了我的下一个观点。 @@ -144,7 +144,7 @@ 1. 深入探索使你能够探索你以前可能从未探索过的领域。它让你有机会接触并熟悉不同的代码库。 2. 当你升到一个更高的位置时,人们会依赖你的指导。随着时间的推移,你将成为小组中的高级人员。人们向你寻求帮助,无论如何你以后都需要学习这种技能。 -许多开发人员开玩笑说,他们的大部分时间都花在 Google 和 StackOverflow上。我完全同意这一点,但不是因为你可能认为的原因。 +许多开发人员开玩笑说,他们的大部分时间都花在 Google 和 StackOverflow 上。我完全同意这一点,但不是因为你可能认为的原因。 为模棱两可的问题寻找答案的能力是一种非常令人羡慕的技能。随着时间的推移,互联网已经发展成为一个容易获取的信息宝库,你可以在任何时间、任何地点、任何设备上使用。 @@ -178,7 +178,7 @@ - [Purple Cow][9] —? 很喜欢这本书。我主要用它来测试我心中的不同创业想法。它谈到了如何推销你的创业公司,以及如何使它从其他公司中脱颖而出。 - [Evernote][10] — 这是我用过的最好的记事应用程序。我曾经使用过。强烈推荐它! - [Moleskin notebook][11] — 我真的很喜欢这个人。它的质量非常高。价格略高,但由于我每天都在使用它,我认为这是一项很好的投资。每天拿着一本漂亮的笔记本,让我更有动力去写更多的笔记。 -- [Pilot G2 (Black)][12] — 这是我使用过的最好的笔,也是我唯一会使用的笔。我从亚马逊大量购买这些笔,走到哪里都带着它们。我的背包里有一支,办公室里有一支,我的家庭办公室里也有一支,这样我身边总是有一支笔。它写得很好,墨水流动顺畅,我喜欢用它写字的感觉。再加上Moleskin,有时我只想拿起G2在上面随意写写画画,因为这两样东西在一起是如此完美。 +- [Pilot G2 (Black)][12] — 这是我使用过的最好的笔,也是我唯一会使用的笔。我从亚马逊大量购买这些笔,走到哪里都带着它们。我的背包里有一支,办公室里有一支,我的家庭办公室里也有一支,这样我身边总是有一支笔。它写得很好,墨水流动顺畅,我喜欢用它写字的感觉。再加上 Moleskin,有时我只想拿起 G2 在上面随意写写画画,因为这两样东西在一起是如此完美。 _关注我在 [Twitter][13], [Facebook][14], 和 [LinkedIn][15]。加入 [我的邮件列表][16], 我定期发送技巧、窍门和行业知识。_ diff --git a/chinese/articles/how-to-beat-coders-block-and-stay-productive.md b/chinese/articles/how-to-beat-coders-block-and-stay-productive.md index 5305a7bae..c9863bc66 100644 --- a/chinese/articles/how-to-beat-coders-block-and-stay-productive.md +++ b/chinese/articles/how-to-beat-coders-block-and-stay-productive.md @@ -71,7 +71,7 @@ 当我第一次去寻找克服程序员的障碍的技巧的时候,我就意识到了这一点。 刚开始,我对我的休息时间非常负责——当我想理清头绪时,只需要散步五分钟或走到咖啡机面前。 -但很快,我发现自己在找理由让自己在我所坚持的所有事情上分心。 就像我去寻找 [使用 Netflix 的最佳 VPN列表](https://cooltechzone.com/netflix-vpn) 时候一样,因为我想知道他们在欧洲隐藏了哪些其他外语系列节目,比如《暗黑》。 或者我花了半天时间在互联网上搜索价格合理不需要等待的显卡([感谢加密货币੯‧̀͡u\\](https://www.tomshardware.com/news/gpu-shortage-2017,34964.html))。 +但很快,我发现自己在找理由让自己在我所坚持的所有事情上分心。 就像我去寻找 [使用 Netflix 的最佳 VPN 列表](https://cooltechzone.com/netflix-vpn) 时候一样,因为我想知道他们在欧洲隐藏了哪些其他外语系列节目,比如《暗黑》。 或者我花了半天时间在互联网上搜索价格合理不需要等待的显卡([感谢加密货币੯‧̀͡u\\](https://www.tomshardware.com/news/gpu-shortage-2017,34964.html))。 重点是,休息是尝试突破程序员的障碍的合理方法,但前提是有策略地使用它们。 diff --git a/chinese/articles/how-to-become-a-certified-kubernetes-application-developer.md b/chinese/articles/how-to-become-a-certified-kubernetes-application-developer.md index 071992969..c319d577b 100644 --- a/chinese/articles/how-to-become-a-certified-kubernetes-application-developer.md +++ b/chinese/articles/how-to-become-a-certified-kubernetes-application-developer.md @@ -5,15 +5,15 @@ ![如何成为K8S认证开发者](https://www.freecodecamp.org/news/content/images/size/w2000/2021/04/kubernetes-ckad-color-1024x1003.png) -本指南是我对最近通过的K8S认证开发者考试的学习笔记总结。 +本指南是我对最近通过的 K8S 认证开发者考试的学习笔记总结。 -即使你对认证不感兴趣,你可以把K8S看作`一站式商店`:你了解的主要技术概念的解释,已经无数的例子在集合在一起。 +即使你对认证不感兴趣,你可以把 K8S 看作`一站式商店`:你了解的主要技术概念的解释,已经无数的例子在集合在一起。 此外,它还有一些来自我准备和参加考试经验和附加内容。 -在写本文时,CKAD的课程(研究领域和每个领域的比重)如下: +在写本文时,CKAD 的课程(研究领域和每个领域的比重)如下: - 13% – 核心概念 - 18% – 配置 - 10% – 多容器 Pods @@ -24,25 +24,25 @@ 本指南涵盖了这些课程,只是顺序不同。 -我假设你已经知道K8S的基础知识(基本的containers和pods),并希望将你的技能提高一个新的水平。通过这个考试将你的简历脱颖而出,因为它是一个非常抢手的认证。 +我假设你已经知道 K8S 的基础知识(基本的 containers 和 pods),并希望将你的技能提高一个新的水平。通过这个考试将你的简历脱颖而出,因为它是一个非常抢手的认证。 ## 内容 -- [Kubernetes简介](#Kubernetes简介) -- [如何管理Kubernetes的集群](#如何管理Kubernetes集群) +- [Kubernetes 简介](#Kubernetes简介) +- [如何管理 Kubernetes 的集群](#如何管理Kubernetes集群) - [超越 Pods 和 Deployments](#超越Pods和部署) -- [如何配置Pods和Containers](#如何配置Pods和Containers) -- [如何在Kubernetes调度Pods](#怎样在Kubernetes中调度Pods) -- [Kubernetes的存储](#Kubernetes的存储) -- [Kubernetes的网络和安全](#Kubernetes中的网络和安全) -- [Kubernetes中的观察性和调试](#Kubernetes中的可观察性和调试) +- [如何配置 Pods 和 Containers](#如何配置Pods和Containers) +- [如何在 Kubernetes 调度 Pods](#怎样在Kubernetes中调度Pods) +- [Kubernetes 的存储](#Kubernetes的存储) +- [Kubernetes 的网络和安全](#Kubernetes中的网络和安全) +- [Kubernetes 中的观察性和调试](#Kubernetes中的可观察性和调试) - [技巧和窍门](#技巧和窍门) - [练习时间](#练习时间) - [结论](#结论) -## Kubernetes简介 +## Kubernetes 简介 -Kubernetes是一项技术,允许在多个节点上轻松部署和管理容器化程序。它的一些最突出的特点是: +Kubernetes 是一项技术,允许在多个节点上轻松部署和管理容器化程序。它的一些最突出的特点是: - Container 配置和部署 - 系统监控 @@ -52,31 +52,31 @@ Kubernetes是一项技术,允许在多个节点上轻松部署和管理容器 还有更多。 -Kubernetes的工作方式是声明式的:你在集群中定义你想要的状态,Kubernetes是确保集群始终处于这种状态。 +Kubernetes 的工作方式是声明式的:你在集群中定义你想要的状态,Kubernetes 是确保集群始终处于这种状态。 - REST 调用 -- 命令行工具`kubectl`。你可以通过这个教程,在你的机器上安装它[ 点击这里](https://kubernetes.io/docs/tasks/tools/). +- 命令行工具`kubectl`。你可以通过这个教程,在你的机器上安装它[点击这里](https://kubernetes.io/docs/tasks/tools/). -如果你不能访问一个Kubernetes集群,我建议在你的本地机器上安装[minikube](https://minikube.sigs.k8s.io/docs/start/)。一旦完成安装并启动,允许以下命令来创建你的第一个Pod。 +如果你不能访问一个 Kubernetes 集群,我建议在你的本地机器上安装[minikube](https://minikube.sigs.k8s.io/docs/start/)。一旦完成安装并启动,允许以下命令来创建你的第一个 Pod。 ```yaml kubectl run --image=busybox --restart=Never --rm -it -- echo "Welcome to Kubernetes!!" ``` -这个pod一旦打印出欢迎消息,它将被自动删除。 +这个 pod 一旦打印出欢迎消息,它将被自动删除。 -## 如何管理Kubernetes集群 +## 如何管理 Kubernetes 集群 -管理集群不是成为CKAD的课程的一部分。就考试而言,你不需要知道如何如何创建一个集群,管理nodes等。 +管理集群不是成为 CKAD 的课程的一部分。就考试而言,你不需要知道如何如何创建一个集群,管理 nodes 等。 ### Namespaces Namespaces 允许你创建虚拟集群,也就是说将资源隔离在同一物理集群的不同部分。比如说: - 将以下不同的环境隔开 development, stage, QA, and production -- 将一个复杂的系统分解成更小的子系统,你可以为前端组件创建一个Namespace,也可以为后端组件创建另一个Namespace,以此类推。 -- 避免了名称冲突: 同一资源可以在不同的Namespace中以相同的名称创建。这使得创建不同的环境 (think stage and prod),看起来相同。 +- 将一个复杂的系统分解成更小的子系统,你可以为前端组件创建一个 Namespace,也可以为后端组件创建另一个 Namespace,以此类推。 +- 避免了名称冲突: 同一资源可以在不同的 Namespace 中以相同的名称创建。这使得创建不同的环境 (think stage and prod),看起来相同。 -你可以通过运行以下命令创建一个Namespace: +你可以通过运行以下命令创建一个 Namespace: ```bash kubectl create ns my-namespace @@ -84,9 +84,9 @@ kubectl create ns my-namespace ### 资源分配 -如果你想限制开发者在Namespace中可以创建的资源数量(包括物理资源和Kubernetes对象,如pods) [资源分配](https://kubernetes.io/docs/concepts/policy/resource-quotas/). +如果你想限制开发者在 Namespace 中可以创建的资源数量(包括物理资源和 Kubernetes 对象,如 pods) [资源分配](https://kubernetes.io/docs/concepts/policy/resource-quotas/). -例如,你可以通过`ResourceQuota`下的Kubernetes _secrets_ 的数量来限制用户在集群上创建: +例如,你可以通过`ResourceQuota`下的 Kubernetes _secrets_ 的数量来限制用户在集群上创建: ```yaml apiVersion: v1 @@ -108,14 +108,14 @@ kubectl create quota my-quota --hard="secrets=2" ### Labels -Labels(标签)允许你在Kubernetes集群组织资源,label是一个键值对,你可以在创建资源时,添加到资源上,也可以添加到现有资源。你能使用labels过滤资源。 +Labels(标签)允许你在 Kubernetes 集群组织资源,label 是一个键值对,你可以在创建资源时,添加到资源上,也可以添加到现有资源。你能使用 labels 过滤资源。 例如: -- 你想只显示 `backend` pods。你将label `tier=backed`添加到pods (键和值都是任意的,你可以使用任何你想的),然后运行 `kubectl get pods -l tier=backend`去检索需要的pods. -- 你想定义一个部署或服务的相关pods。告诉 deployment/service,它需要关注通过labels选择pods. +- 你想只显示 `backend` pods。你将 label `tier=backed`添加到 pods (键和值都是任意的,你可以使用任何你想的),然后运行 `kubectl get pods -l tier=backend`去检索需要的 pods. +- 你想定义一个部署或服务的相关 pods。告诉 deployment/service,它需要关注通过 labels 选择 pods. -以下时一些常见的label-related的命令: +以下时一些常见的 label-related 的命令: ```bash # 给Pods 添加一个label, 等等 @@ -134,9 +134,9 @@ kubect get pods -l 'tier in (frontend,backend)' kubect get pods -l tier=frontend,deployer=coolest-team ``` -Annotations和labels相似,都是键值对,但Annotations不能用于选择资源,它们的目的是不同的。 +Annotations 和 labels 相似,都是键值对,但 Annotations 不能用于选择资源,它们的目的是不同的。 -Annotations通常为其他工具添加。比如,如果你运行Prometheus来收集指标,你可以把这个配置添加到你的描述符中。 +Annotations 通常为其他工具添加。比如,如果你运行 Prometheus 来收集指标,你可以把这个配置添加到你的描述符中。 ```yaml metadata: labels: @@ -146,27 +146,27 @@ metadata: prometheus.io/port: '9102' ``` -它让Prometheus将 _scrape_ 和 _port_ 的默认值分别改为 `true`和 `9102`。 +它让 Prometheus 将 _scrape_ 和 _port_ 的默认值分别改为 `true`和 `9102`。 -## 超越 Pods和部署 +## 超越 Pods 和部署 - Multi-container pods是Kubernetes的基本单元。在大多数情况下,你可以认为它是一个容器,但是一个pod可以由多个容器组成。由于pod是短暂的,我们需要一种机制确保当我们现有的pod死亡时,新的pod被创建。 + Multi-container pods 是 Kubernetes 的基本单元。在大多数情况下,你可以认为它是一个容器,但是一个 pod 可以由多个容器组成。由于 pod 是短暂的,我们需要一种机制确保当我们现有的 pod 死亡时,新的 pod 被创建。 - 通过部署,你的可以定义一个理想的状态,例如,有三个应用程序的副本一直在运行,Kubernetes将努力实现并在集群中保持这种状态。 + 通过部署,你的可以定义一个理想的状态,例如,有三个应用程序的副本一直在运行,Kubernetes 将努力实现并在集群中保持这种状态。 也可以轻松管理任何时候运行的副本数量,执行滚动更新,回滚到以前的版本等等。 还有很多工作负荷。 ### 多容器 pod -一个pod可以运行一个以上的容器。容器间可以无缝通讯,因为它们在同一网络和而且它们可以使用 _volumes_ 共享数据。 +一个 pod 可以运行一个以上的容器。容器间可以无缝通讯,因为它们在同一网络和而且它们可以使用 _volumes_ 共享数据。 -现在,让我们深入了解一些多容器pod设计模式,在本指南后面,我们将详细介绍volumes(卷),以及如何部署这些模式中的一些。 +现在,让我们深入了解一些多容器 pod 设计模式,在本指南后面,我们将详细介绍 volumes(卷),以及如何部署这些模式中的一些。 #### Sidecar(边车) 在这个模式中,我们有一个运行主程序的容器,同时还有另一个容器,运行次要任务 ***以增强主容器的功能***。 -一个经典的例子是,在运行Web服务(主容器)的同时,还有一个处理日志、监控、刷新pod volume(卷)的数据,终端TLS等任务的`side` 容器 +一个经典的例子是,在运行 Web 服务(主容器)的同时,还有一个处理日志、监控、刷新 pod volume(卷)的数据,终端 TLS 等任务的`side` 容器 #### 适配器 同样,你可以有你的主容器和一个次要容器, **转换主容器的输出** @@ -180,30 +180,30 @@ metadata: ### 服务 -Pods 可以通过其他的Pods的IP地址,连接其他的Pods。然而,pods是短暂的,当它们死亡时,假设你有个复控制器,一个新的pod将被创建,有一个新的IP地址。这使人们很难直接使用IP连接到pods。 +Pods 可以通过其他的 Pods 的 IP 地址,连接其他的 Pods。然而,pods 是短暂的,当它们死亡时,假设你有个复控制器,一个新的 pod 将被创建,有一个新的 IP 地址。这使人们很难直接使用 IP 连接到 pods。 -Kubernetes提供了一个名为服务的抽象,**一个资源有固定的IP地址** ,并将请求发送到对应的pod。 +Kubernetes 提供了一个名为服务的抽象,**一个资源有固定的 IP 地址** ,并将请求发送到对应的 pod。 -而不是直接连接到pods,你可以通过它的IP地址到达它们的服务。或者通过DNS服务使用完整限定名称查找,这样会更好。此外,一些服务类型也将你的应用程序暴露在集群外面。 +而不是直接连接到 pods,你可以通过它的 IP 地址到达它们的服务。或者通过 DNS 服务使用完整限定名称查找,这样会更好。此外,一些服务类型也将你的应用程序暴露在集群外面。 #### 服务类型 你可以创建的主要服务类型: -- **ClusterIP –** 在集群内暴露你的应用。如果你不指定类型,这就是Kubernetes创建的默认服务。 -- **NodePort –** 它会在集群中的每一个node打开一个端口,将你的应用程序暴露给世界。 +- **ClusterIP –** 在集群内暴露你的应用。如果你不指定类型,这就是 Kubernetes 创建的默认服务。 +- **NodePort –** 它会在集群中的每一个 node 打开一个端口,将你的应用程序暴露给世界。 - **LoadBalancer –** 使用云服务提供商的负载均衡器向外部暴露服务。 #### 如何在集群内部暴露你的应用程序 -假设你想把你的应用程序`my-app`暴露给集群中的其他节点,端口为80。你可以创建一个命令部署你的应用程序。 +假设你想把你的应用程序`my-app`暴露给集群中的其他节点,端口为 80。你可以创建一个命令部署你的应用程序。 ```bash kubectl create deploy my-app --image=my-app --port=80 ``` -这将创建集群内部其他的Pod只能通过IP访问的pod,如果pod重新启动,其IP将改变。 +这将创建集群内部其他的 Pod 只能通过 IP 访问的 pod,如果 pod 重新启动,其 IP 将改变。 -下一步是为这些pod创建一个ClusterIP服务。下面的命令创建一个可以通过80端口的服务,并将请求转发到的你的应用程序(80端口)。 +下一步是为这些 pod 创建一个 ClusterIP 服务。下面的命令创建一个可以通过 80 端口的服务,并将请求转发到的你的应用程序(80 端口)。 ```bash kubectl expose deploy my-app --port=80 @@ -211,7 +211,7 @@ kubectl expose deploy my-app --port=80 #### 如何将你的应用程序暴露在集群之外 -如果你想把你的应用服务暴露给世界,你可以使用这个配置创建一个NodePort服务。 +如果你想把你的应用服务暴露给世界,你可以使用这个配置创建一个 NodePort 服务。 ```yaml kind: Service @@ -230,90 +230,90 @@ spec: ### [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) -服务并不是将你的应用程序给世界的唯一方法。你也可以用 **Ingress对象**作为你的集群的一个入口点。 +服务并不是将你的应用程序给世界的唯一方法。你也可以用 **Ingress 对象**作为你的集群的一个入口点。 -除此之外,你还需要一个 **Ingress控制器**,比如Nginx,来实现对Ingress对象中定义的规则。 +除此之外,你还需要一个 **Ingress 控制器**,比如 Nginx,来实现对 Ingress 对象中定义的规则。 -由于Ingresses不在CKAD的范围内,我们将转到其他的主题。 +由于 Ingresses 不在 CKAD 的范围内,我们将转到其他的主题。 ### [Jobs](https://kubernetes.io/docs/concepts/workloads/controllers/job/) -一个Job将创建一个或多个pods **如果它们成功,将不会被重启启动**。你可以使用它们来执行批处理, **一次性任务** ,如并行计算,备份一些文件,转换和导出一些数据等等。 +一个 Job 将创建一个或多个 pods **如果它们成功,将不会被重启启动**。你可以使用它们来执行批处理, **一次性任务** ,如并行计算,备份一些文件,转换和导出一些数据等等。 -除非另有说明, pod 将运行一次。你可以使用参数 `completions`来指定一项工作需要运行多少次才能被认为已经完成。默认情况下, pods将按照顺序运行,但你可以设置job,使它们并行运行。 +除非另有说明, pod 将运行一次。你可以使用参数 `completions`来指定一项工作需要运行多少次才能被认为已经完成。默认情况下, pods 将按照顺序运行,但你可以设置 job,使它们并行运行。 如果它们没有成功运行, 你将它们配置为 `Never` 重启 (重试)。或者重启失败最多的`backoffLimit` 次数, -然后Kubernetes认为job已经失败。 +然后 Kubernetes 认为 job 已经失败。 -下面是如何从命令行中创建一个简单的job: +下面是如何从命令行中创建一个简单的 job: ```bash kubectl create job my-job --image=busybox -- echo "Hello World" ``` -如果它们的job运行成功,其状态是`COMPLETED`,其pod(s)不会被删除,这样你就可以访问它们的日志。你可以通过以下方式看到它们。 +如果它们的 job 运行成功,其状态是`COMPLETED`,其 pod(s)不会被删除,这样你就可以访问它们的日志。你可以通过以下方式看到它们。 ```bash kubectll get pods --show-all ``` -在默认情况下,2分钟后它们在默认的pod列表中是不可见的。 +在默认情况下,2 分钟后它们在默认的 pod 列表中是不可见的。 ### [Cronjobs](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) -Kubernetes提供了`Conjobs`来创建需要定期或者在未来特定时间运行的`jobs`:定时清理和备份任务,更新TLS证书等等。 +Kubernetes 提供了`Conjobs`来创建需要定期或者在未来特定时间运行的`jobs`:定时清理和备份任务,更新 TLS 证书等等。 -Kubernetes会尽力只运行另一个`job`来执行你在`Cronjob`配置中指定的任务。然而,有3个常见的问题你应该注意。 +Kubernetes 会尽力只运行另一个`job`来执行你在`Cronjob`配置中指定的任务。然而,有 3 个常见的问题你应该注意。 -- 不能保证job会 **准确地在所需的时间运行**。想象一下,你需要你的工作在 上午9点运行,你可以将`startingDeadlineSeconds`属性设置为x秒。这样,如果job没在上午9点x秒之后开始, 它将标记为失败,不会再运行。 -- **2个job可能被安排在同一时间** 执行任务。在这种情况下,你需要确保任务是 _idempotent_ ,如果任务被多次执行,执行的任务的结果也不会改变。 -- **没有job可以安排**。 为了克服这个问题,请确保Cronjob能运行前一次未完成的job。 +- 不能保证 job 会 **准确地在所需的时间运行**。想象一下,你需要你的工作在 上午 9 点运行,你可以将`startingDeadlineSeconds`属性设置为 x 秒。这样,如果 job 没在上午 9 点 x 秒之后开始, 它将标记为失败,不会再运行。 +- **2 个 job 可能被安排在同一时间** 执行任务。在这种情况下,你需要确保任务是 _idempotent_ ,如果任务被多次执行,执行的任务的结果也不会改变。 +- **没有 job 可以安排**。 为了克服这个问题,请确保 Cronjob 能运行前一次未完成的 job。 -这是创建一个简单的Cronjob,每分钟打印出"Hello World" +这是创建一个简单的 Cronjob,每分钟打印出"Hello World" ```bash kubectl create cronjob my-job --image=busybox --schedule="*/1 * * * *" -- echo "Hello World" ``` -我推荐这个 [网站](https://crontab.guru/) 帮助你写出正确的cron定时任务。 +我推荐这个 [网站](https://crontab.guru/) 帮助你写出正确的 cron 定时任务。 -接下的3个资源不是CKAD考试的一部分,但我认为你至少应该对它们有一个基本的了解。 +接下的 3 个资源不是 CKAD 考试的一部分,但我认为你至少应该对它们有一个基本的了解。 ### [Daemon sets](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) -Daemon sets(守护设置)确保在你的集群的每一个 **node** 都有一个pod 调度。除此以外,pod总是在运行:如果pod死了或者有人删除了它,pod将被重新创建。 +Daemon sets(守护设置)确保在你的集群的每一个 **node** 都有一个 pod 调度。除此以外,pod 总是在运行:如果 pod 死了或者有人删除了它,pod 将被重新创建。 -一个常见的使用场景是,Daemon sets(守护设置)用来收集每个node的日志和指标。但是,即使你不创建任何东西,它们已经在你的集群中运行了一些Daemo sets(守护设置): Kubernetese创建一个 Daemon set(守护设置),在每个node上运行`kube-proxy` 组件。 +一个常见的使用场景是,Daemon sets(守护设置)用来收集每个 node 的日志和指标。但是,即使你不创建任何东西,它们已经在你的集群中运行了一些 Daemo sets(守护设置): Kubernetese 创建一个 Daemon set(守护设置),在每个 node 上运行`kube-proxy` 组件。 -如果你从集群中移除一个node,Kubernetes不会在别的node上创建它的守护进程,因为这个这个node已经在运行Daemon set(守护设置)。如果你在集群中添加一个node,它会自动运行 Daemon set(守护设置)。 +如果你从集群中移除一个 node,Kubernetes 不会在别的 node 上创建它的守护进程,因为这个这个 node 已经在运行 Daemon set(守护设置)。如果你在集群中添加一个 node,它会自动运行 Daemon set(守护设置)。 ### [Stateful set](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/) -到目前为止,我们应该看到部署无状态的应用或者pods的工具,它们有自己的持久性储存,不随工具停止运行而消失。为了部署有状态的应用,你可以是使用 Stateful set(状态设置) +到目前为止,我们应该看到部署无状态的应用或者 pods 的工具,它们有自己的持久性储存,不随工具停止运行而消失。为了部署有状态的应用,你可以是使用 Stateful set(状态设置) -由于这不是CKAD考试的一部分,我们就不多讲了。 +由于这不是 CKAD 考试的一部分,我们就不多讲了。 ### [静态 pods](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/) -静态pods是由`kubelet`管理的颇多,而不是由Kubernetes API管理的。 +静态 pods 是由`kubelet`管理的颇多,而不是由 Kubernetes API 管理的。 -要创建它们,你只需要创建一个普通的pod的配置文件和配置kubelet 扫描该目录。如何你重启`kubelet`后,它将创建pod,并在失败时重启pod。 +要创建它们,你只需要创建一个普通的 pod 的配置文件和配置 kubelet 扫描该目录。如何你重启`kubelet`后,它将创建 pod,并在失败时重启 pod。 -Kubernetes将创建pod的镜像,这是Kubernetes API服务器中的一个副本。当你运行`kubectl get pods`,这个pod会出现,但是如果你试图用`kubectl delete podName`删除它,通过`kubelet`创建的静态pod 运行在这个node上,它将直接出现在pod列表中。 +Kubernetes 将创建 pod 的镜像,这是 Kubernetes API 服务器中的一个副本。当你运行`kubectl get pods`,这个 pod 会出现,但是如果你试图用`kubectl delete podName`删除它,通过`kubelet`创建的静态 pod 运行在这个 node 上,它将直接出现在 pod 列表中。 -## 如何配置Pods和Containers +## 如何配置 Pods 和 Containers -我们刚刚看到在可以在Kubernetes集群上部署不同的工作负载。现在让我们花些时间学习如何配置工作负载中的pods和containers(容器)。 +我们刚刚看到在可以在 Kubernetes 集群上部署不同的工作负载。现在让我们花些时间学习如何配置工作负载中的 pods 和 containers(容器)。 ### [初始化容器](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-initialization/) -你可以使用 _init containers_ 来设置你的pod初始状态: 向pod的卷写入一些数据,下载些文件等等。 +你可以使用 _init containers_ 来设置你的 pod 初始状态: 向 pod 的卷写入一些数据,下载些文件等等。 -你可以定义一个或者多个init容器,它们将按顺序执行,只在所有的容器完成后,pod才会运行,因此,init容器也可以用来让pod在执行前等待某个条件。 +你可以定义一个或者多个 init 容器,它们将按顺序执行,只在所有的容器完成后,pod 才会运行,因此,init 容器也可以用来让 pod 在执行前等待某个条件。 -例如,你可以让一个pod在启动前等待另一个服务的容器完成启动和运行。 +例如,你可以让一个 pod 在启动前等待另一个服务的容器完成启动和运行。 -你可以通过在pod的定义的 `spec`部分添加类似的这样的内容定义init容器。 +你可以通过在 pod 的定义的 `spec`部分添加类似的这样的内容定义 init 容器。 ```yaml spec: @@ -326,7 +326,7 @@ spec: ### [ConfigMaps](https://kubernetes.io/docs/concepts/configuration/configmap/) -将你的应用程序与代码分开是你应该遵循的实践。ConfigMaps 允许你在Kubernetes这样做。 +将你的应用程序与代码分开是你应该遵循的实践。ConfigMaps 允许你在 Kubernetes 这样做。 ConfigMaps 是使用 **存储非机密数据的键值对**, 我们将在下一节看到 _Secrects_ 存储机密数据(如密码)。 @@ -338,13 +338,13 @@ kubectl create configmap my-map --from-literal=db_url=my-url --from-literal=user kubectl create configmap another-map --from-file=my-file ``` -一旦创建,你的应用程序可以在同一个namespace的pod以多种方式使用ConfigMaps。 +一旦创建,你的应用程序可以在同一个 namespace 的 pod 以多种方式使用 ConfigMaps。 - 作为命令行参数 - 作为环境变量 - 从只读卷中的一个文件 -让我们看看一个pod的定义使用这些办法从ConfigMap中读取。 +让我们看看一个 pod 的定义使用这些办法从 ConfigMap 中读取。 ```yaml apiVersion: v1 @@ -383,7 +383,7 @@ spec: ### [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/) -`Secrets`跟ConfigMaps很相似,但你用它们来存储 **机密数据**。创建和管理`Secrets`是一个敏感的话题。请务必阅读文档。在这里,我们将学习基础知识。 +`Secrets`跟 ConfigMaps 很相似,但你用它们来存储 **机密数据**。创建和管理`Secrets`是一个敏感的话题。请务必阅读文档。在这里,我们将学习基础知识。 创建`Secrets`的最简单方法是: @@ -394,7 +394,7 @@ kubectl create secret generic secret-name --from-literal=password=password kubectl create secret generic secret-name --from-file=path-to-file ``` -然后,你可以将你的secrets导入pod,以环境变量或者文件的形式: +然后,你可以将你的 secrets 导入 pod,以环境变量或者文件的形式: ```yaml apiVersion: v1 @@ -433,38 +433,38 @@ spec: ### [Probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) -Probes(探针)允许你设置判断pod是否健康的规则,是否为服务流量做好准备,是否准备好启动。在本节的最后,我将介绍一个带有这些Probes(探针)的样本。 +Probes(探针)允许你设置判断 pod 是否健康的规则,是否为服务流量做好准备,是否准备好启动。在本节的最后,我将介绍一个带有这些 Probes(探针)的样本。 #### Liveness probes(活性探针) -Kubernetes 会自动重启 _crashed containers_ (崩溃的容器)。然而,这不会考虑到下面的bug (像你的应用程序的无限循环), 死锁等等。 你可以定义 `Liveness probes`(活性探针) **检测你的应用程序是否健康**。 Kubernetes 使用这探针来决定是否要重启容器。 +Kubernetes 会自动重启 _crashed containers_ (崩溃的容器)。然而,这不会考虑到下面的 bug (像你的应用程序的无限循环), 死锁等等。 你可以定义 `Liveness probes`(活性探针) **检测你的应用程序是否健康**。 Kubernetes 使用这探针来决定是否要重启容器。 -你可以定义三种Liveness probes(活性探针): +你可以定义三种 Liveness probes(活性探针): -- HTTP Get, 在你的应用程序中定义一个HTTP路由(如/health),Kubernetes可以通过HTTP请求获取应用程序是否健康 -- TCP socket 探针,尝试建立一个TCP连接到一个特定的端口。如果连接不能建立,应用程序将重启。 -- Exec 探针, 在容器内运行一个命令,如果退出状态码不是0,将重启容器。 +- HTTP Get, 在你的应用程序中定义一个 HTTP 路由(如/health),Kubernetes 可以通过 HTTP 请求获取应用程序是否健康 +- TCP socket 探针,尝试建立一个 TCP 连接到一个特定的端口。如果连接不能建立,应用程序将重启。 +- Exec 探针, 在容器内运行一个命令,如果退出状态码不是 0,将重启容器。 #### Readiness probes(准备就绪探针) -想象一下,一个服务在启动时需要加载一个大的配置文件。容器本身是健康的(这可以用上面的Liveness probes检查),但是没有启动完,接收请求。 +想象一下,一个服务在启动时需要加载一个大的配置文件。容器本身是健康的(这可以用上面的 Liveness probes 检查),但是没有启动完,接收请求。 -Kubernetes使用Readiness probes(准备就绪探针)来确定你的应用程序是否准备好,处理请求。 +Kubernetes 使用 Readiness probes(准备就绪探针)来确定你的应用程序是否准备好,处理请求。 -关于Liveness probes(活性探针)的东西也适用于Readiness probes(准备就绪探针): +关于 Liveness probes(活性探针)的东西也适用于 Readiness probes(准备就绪探针): - 它们可以被配置为初始延迟,超时,阈值,周期等等。 -- 它们可以基于HTTP get调用,TCP socket连接,或者在容器内执行命令。 +- 它们可以基于 HTTP get 调用,TCP socket 连接,或者在容器内执行命令。 -然而,当Liveness probes(活性探针)告诉Kubernetes信息,Kubernetes让不健康的容器重启。Readiness probes(准备就绪探针)用来从可以接收请求的容器池移除没准备好的容器。一旦容器准备好了,它就可以为请求提供服务。 +然而,当 Liveness probes(活性探针)告诉 Kubernetes 信息,Kubernetes 让不健康的容器重启。Readiness probes(准备就绪探针)用来从可以接收请求的容器池移除没准备好的容器。一旦容器准备好了,它就可以为请求提供服务。 #### Startup probes(启动探针) -Startup probes(启动探针)只能在启动期间使用,例如应用程序启动缓慢。如果Startup probes(启动探针)返回成功,liveness and readiness 探针(如果已经配置),将定期运行。 +Startup probes(启动探针)只能在启动期间使用,例如应用程序启动缓慢。如果 Startup probes(启动探针)返回成功,liveness and readiness 探针(如果已经配置),将定期运行。 ### 例如 -这个pod 将执行以下命令: +这个 pod 将执行以下命令: - sleep 20 - touch /tmp/healthy @@ -477,10 +477,10 @@ Startup probes(启动探针)只能在启动期间使用,例如应用程序启 - **initialDelaySeconds** 开始探针延迟秒数 - **periodSeconds** 探针频率 - **timeoutSeconds** 探针的超时时间 -- **failureThreshold** Kubernetes尝试次数,超过这个,将放弃尝试 -根据我们的配置, 这些探针将在pod创建后10秒后开始。在前20秒内, 文件 `/tmp/healthy`是不存在。因此, readiness probe(准备就绪探针)返回失败。 在接下30秒内readiness probe(准备就绪探针)返回成功,直到我们删除`/tmp/healthy`文件。 +- **failureThreshold** Kubernetes 尝试次数,超过这个,将放弃尝试 +根据我们的配置, 这些探针将在 pod 创建后 10 秒后开始。在前 20 秒内, 文件 `/tmp/healthy`是不存在。因此, readiness probe(准备就绪探针)返回失败。 在接下 30 秒内 readiness probe(准备就绪探针)返回成功,直到我们删除`/tmp/healthy`文件。 -liveness probe(活性探针) 是简单的`echo "I'm healthy"`命令。如果它能运行,说明这个pod是健康的,否则,这个pod 要重启。 +liveness probe(活性探针) 是简单的`echo "I'm healthy"`命令。如果它能运行,说明这个 pod 是健康的,否则,这个 pod 要重启。 ```yaml apiVersion: v1 @@ -514,7 +514,7 @@ spec: status: {} ``` -执行这个命令可以看到pods的状态: +执行这个命令可以看到 pods 的状态: ```bash kubectl get pods --watch @@ -530,25 +530,25 @@ kubectl describe pods my-pod ### [容器资源](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) -你可以通过定义创建一个pod: +你可以通过定义创建一个 pod: - 它运行所需的最小资源量,被称为 _requests_. -- pod可以使用的最大资源量,被称为 _limits_. +- pod 可以使用的最大资源量,被称为 _limits_. -Kubernetes在调度pod时,会考虑到你请求的资源。它不会在一个没有足够容量的节点上调度pod,不管当前资源的消耗情况如何。 +Kubernetes 在调度 pod 时,会考虑到你请求的资源。它不会在一个没有足够容量的节点上调度 pod,不管当前资源的消耗情况如何。 -例如, pod A和podB 在node N上运行,Kubernetes将通过下面的方法计算新的pod C 是否可以在node N上运行: +例如, pod A 和 podB 在 node N 上运行,Kubernetes 将通过下面的方法计算新的 pod C 是否可以在 node N 上运行: -`Total capacity of N - (resources requested by A + resources requested by B) <= resources requested by C`( node N的总资源减去 A B需要的最小资源后,是否少于pod C需要的最小资源量) +`Total capacity of N - (resources requested by A + resources requested by B) <= resources requested by C`( node N 的总资源减去 A B 需要的最小资源后,是否少于 pod C 需要的最小资源量) -即使pod A和B没有使用它们请求的最小资源, Kubernetes **承诺** A和B会有它们所请求的最小资源可使用。这是为什么当前的资源使用情况不在前面的公式表达。 +即使 pod A 和 B 没有使用它们请求的最小资源, Kubernetes **承诺** A 和 B 会有它们所请求的最小资源可使用。这是为什么当前的资源使用情况不在前面的公式表达。 -如果一个pod超过它的资源限制(它可以使用的最大资源量时),会发生两种情况: +如果一个 pod 超过它的资源限制(它可以使用的最大资源量时),会发生两种情况: -- 如果它超过CPU限制,它将被杀掉 +- 如果它超过 CPU 限制,它将被杀掉 - 如果它超过内存限制,它将会被重新启动 -现在让我们创建一个pod,设置好它的最小内存和cpu和最大内存和cpu使用: +现在让我们创建一个 pod,设置好它的最小内存和 cpu 和最大内存和 cpu 使用: ```yaml apiVersion: v1 @@ -573,7 +573,7 @@ spec: cpu: 0.5 ``` -作为一个练习,设置分配的内存和cpu高得离谱一些的值,去创建pod。你会看到pod从未准备好,将有输出 +作为一个练习,设置分配的内存和 cpu 高得离谱一些的值,去创建 pod。你会看到 pod 从未准备好,将有输出 ```bash kubectl describe pods resource-limited-pod @@ -581,17 +581,17 @@ kubectl describe pods resource-limited-pod 会告诉你为什么. -## [怎样在Kubernetes中调度Pods](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node) +## [怎样在 Kubernetes 中调度 Pods](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node) -这一部分不是CKAD考试教程的一部分。但我相信,你应该对我在这样阐述的概念有一些基本的了解,因为你在使用Kubernetes的过程中,它们很可能会出现。 +这一部分不是 CKAD 考试教程的一部分。但我相信,你应该对我在这样阐述的概念有一些基本的了解,因为你在使用 Kubernetes 的过程中,它们很可能会出现。 -Kubernetes允许你通过多种机制来指定你希望的pods,被安排到哪个nodes。 +Kubernetes 允许你通过多种机制来指定你希望的 pods,被安排到哪个 nodes。 -最简单的是使用 **nodeSelector** 字段,根据一个label选择一个node。其他功能的使用,允许更复杂的设置。 +最简单的是使用 **nodeSelector** 字段,根据一个 label 选择一个 node。其他功能的使用,允许更复杂的设置。 ### [Taints and tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) -你可以 **taint(污损) 一个node 去防止在其中的pods被调度**, 不用去修改pods本身。 当你 taint(污损)一个node, Kubernetes 会调度 这个node 中的 pods,**容忍** taint(污损)。 +你可以 **taint(污损) 一个 node 去防止在其中的 pods 被调度**, 不用去修改 pods 本身。 当你 taint(污损)一个 node, Kubernetes 会调度 这个 node 中的 pods,**容忍** taint(污损)。 你可以看到这个动作,如果你运行: @@ -605,48 +605,48 @@ kubectl describe node master.Kubernetes Taints: node-role.kubernetes.io/master:NoSchedule ``` -这种污损会阻止pods在master node中被调度 (除非pods能容忍这种taint). +这种污损会阻止 pods 在 master node 中被调度 (除非 pods 能容忍这种 taint). - 有三种类型的taint: + 有三种类型的 taint: -- NoSchedule: 如果node不能容忍taint,Kubernetes不会在node中调度pod。 -- PreferNoSchedule: pods不能容忍taint,不会调度在一个node中,除非pods不能调度到其他地方。 -- NoExecute: 如果已经在node中运行的pods不容忍taint,就把pods从node中移除出去。 +- NoSchedule: 如果 node 不能容忍 taint,Kubernetes 不会在 node 中调度 pod。 +- PreferNoSchedule: pods 不能容忍 taint,不会调度在一个 node 中,除非 pods 不能调度到其他地方。 +- NoExecute: 如果已经在 node 中运行的 pods 不容忍 taint,就把 pods 从 node 中移除出去。 -**Taints 并不保证 pods 会被调度到特定的nodes**。 为了实现这一点,你需要探索 _Node affinity_ (node亲和性) 概念. +**Taints 并不保证 pods 会被调度到特定的 nodes**。 为了实现这一点,你需要探索 _Node affinity_ (node 亲和性) 概念. -### Node亲和性 +### Node 亲和性 -Node 亲和性 告诉 Kubernetes 去 **调度pods到特定的nodes 上**. +Node 亲和性 告诉 Kubernetes 去 **调度 pods 到特定的 nodes 上**. -想象一下,你有一个服务S,需要特定硬件去运行。你像把专用硬件用于这个服务,并希望S的pod在上面运行。你怎样才能实现? +想象一下,你有一个服务 S,需要特定硬件去运行。你像把专用硬件用于这个服务,并希望 S 的 pod 在上面运行。你怎样才能实现? -你可以taint这种设备上的nodes,以便只有来自服务S的pod可以在这些node上被调度。但是,Kubernetes可以在其他不不具备的所需硬件上的nodes部署这些pods。 +你可以 taint 这种设备上的 nodes,以便只有来自服务 S 的 pod 可以在这些 node 上被调度。但是,Kubernetes 可以在其他不不具备的所需硬件上的 nodes 部署这些 pods。 -我们可以看到: 组合taints和node 亲和性来确保只有来自服务S的pod 在我们的专门的nodes运行。 +我们可以看到: 组合 taints 和 node 亲和性来确保只有来自服务 S 的 pod 在我们的专门的 nodes 运行。 -- Node 亲和性调度将S的pods安排到专门的nodes(而不是其他节点)。 -- Taints 确保没有其他的pod会被安排在专门的nodes,只有来自服务S的pod。 +- Node 亲和性调度将 S 的 pods 安排到专门的 nodes(而不是其他节点)。 +- Taints 确保没有其他的 pod 会被安排在专门的 nodes,只有来自服务 S 的 pod。 -## Kubernetes的存储 +## Kubernetes 的存储 -默认情况下,当一个pod内的容器重启,容器内的数据会丢失,设计上是这样的(无状态)。 +默认情况下,当一个 pod 内的容器重启,容器内的数据会丢失,设计上是这样的(无状态)。 -如果你想让数据比容器,pod,甚至是node都持久,你需要用 **卷**。另外,如果一个pod是由多个容器组成的,那么这个pod 的卷可以被所有的容器使用。 +如果你想让数据比容器,pod,甚至是 node 都持久,你需要用 **卷**。另外,如果一个 pod 是由多个容器组成的,那么这个 pod 的卷可以被所有的容器使用。 ### [卷](https://kubernetes.io/docs/concepts/storage/volumes/) -在你定义了卷后,在pod层,你需要在每个访问的容器装载它。 +在你定义了卷后,在 pod 层,你需要在每个访问的容器装载它。 有很多类型的卷,以满足不同的使用情况(通常,取决于你想在 _pod_ 被销毁时发生什么)。一些常见的卷类型: -- emptyDir: 创建一个初始是空的目录。这是在pod不同容器之间共享文件的简单方法。 -- hostPath: 将一个文件或者目录从node的文件系统挂载到pod,在pod被删除后,这些文件将保留在主机种。 -- 在AWS, GCP, 或者 Azure云上的卷. +- emptyDir: 创建一个初始是空的目录。这是在 pod 不同容器之间共享文件的简单方法。 +- hostPath: 将一个文件或者目录从 node 的文件系统挂载到 pod,在 pod 被删除后,这些文件将保留在主机种。 +- 在 AWS, GCP, 或者 Azure 云上的卷. 关于这些和其他类型的卷的更多信息,请参考文档。 -在容器种挂载一个卷,你的pod的描述符(我们把这个文件称为 `with-mounted-volume.yaml`),应该是这样的。 +在容器种挂载一个卷,你的 pod 的描述符(我们把这个文件称为 `with-mounted-volume.yaml`),应该是这样的。 ```yaml apiVersion: v1 @@ -671,7 +671,7 @@ spec: restartPolicy: Never ``` -由于我们已经创建了一个`hostPath`的卷,它的内容寿命将超过pod。让我们试一下。 +由于我们已经创建了一个`hostPath`的卷,它的内容寿命将超过 pod。让我们试一下。 ```bash # 创建一个 pod @@ -688,15 +688,15 @@ kubectl apply -f with-mounted-volume.yaml kubectl exec -it with-mounted-volume -- cat /tmp/my-volume-path/newfile ``` -#### Revisiting 多容器的pods +#### Revisiting 多容器的 pods -现在我们熟悉了卷,让我们创建一个多容器的pod,它将用在一个挂载了卷的容器间共享日期。 +现在我们熟悉了卷,让我们创建一个多容器的 pod,它将用在一个挂载了卷的容器间共享日期。 - 我们的 pod 描述定义 在文件`communicating-containers.yaml` -- 我们将会一个带有2个 `busybox` 容器的pod。 +- 我们将会一个带有 2 个 `busybox` 容器的 pod。 - 其中一个容器将"Hello World" 追加到一个文件中。 - 另一个容器可以访问这个文件的内容(以及放在这个共享卷的任何东西)。我们将查看这个共享文件夹,看另一个容器是如何将`Hello World`追加到文件里的。 -这就是我们定义的pod: +这就是我们定义的 pod: ```yaml apiVersion: v1 @@ -729,13 +729,13 @@ spec: restartPolicy: Never ``` -让我们创建pod: +让我们创建 pod: ```bash kubectl apply -f communicating-containers.yaml ``` - 一旦pod运行起来,我们就可以tail`container-2`文件,查看文件尾部内容: + 一旦 pod 运行起来,我们就可以 tail`container-2`文件,查看文件尾部内容: ```bash kubectl exec -it communicating-containers -c container-2 -- tail -f /etc/a-different-location/log @@ -745,15 +745,15 @@ kubectl exec -it communicating-containers -c container-2 -- tail -f /etc/a-diffe ### [Persistent Volumes(持久化卷)](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) -Persistent Volumes (PVs 持久化卷) 和 Persistent Volume Claims (PVCs 持久化卷声明) 通过存储技术跟pod解耦。 PVs是通过集群管理员创建 或者动态根据 [存储类型](https://kubernetes.io/docs/concepts/storage/storage-classes/). +Persistent Volumes (PVs 持久化卷) 和 Persistent Volume Claims (PVCs 持久化卷声明) 通过存储技术跟 pod 解耦。 PVs 是通过集群管理员创建 或者动态根据 [存储类型](https://kubernetes.io/docs/concepts/storage/storage-classes/). -相对我们之前创建的卷,在pod层定义的,PV(持久化卷)有自己的生命周期,独立于任何pod。 +相对我们之前创建的卷,在 pod 层定义的,PV(持久化卷)有自己的生命周期,独立于任何 pod。 -在创建PVs 后, _用户_ 能创建 Persistent Volume Claims (持久化卷声明)来获得它们需要的存储, _而不需要关心其存储的实际基础设施_. +在创建 PVs 后, _用户_ 能创建 Persistent Volume Claims (持久化卷声明)来获得它们需要的存储, _而不需要关心其存储的实际基础设施_. #### 例如 -首先, 我们定义一个 Persistent Volume(持久化卷)在pv.yaml 文件: +首先, 我们定义一个 Persistent Volume(持久化卷)在 pv.yaml 文件: ```yaml apiVersion: v1 @@ -771,7 +771,7 @@ spec: path: /etc/foo ``` -对于 Persistent Volume Claim(持久化卷声明), 在文件pvc.yaml描述: +对于 Persistent Volume Claim(持久化卷声明), 在文件 pvc.yaml 描述: ```yaml apiVersion: v1 @@ -796,7 +796,7 @@ kubectl apply -f pv.yaml kubectl apply -f pvc.yaml ``` -我们可以从前面的例子获取pod的定义,稍作调整,开始使用这个持久化卷,代替我们定义的hostPath,这是我们唯一要改的地方。 +我们可以从前面的例子获取 pod 的定义,稍作调整,开始使用这个持久化卷,代替我们定义的 hostPath,这是我们唯一要改的地方。 ```yaml volumes: @@ -805,17 +805,17 @@ volumes: claimName: mypvc # 名称是我们刚才创建的pvc ``` -## Kubernetes中的网络和安全 +## Kubernetes 中的网络和安全 -关于安全问题, 这些概念是你应该熟悉的最低限度,以便通过CKAD考试。 +关于安全问题, 这些概念是你应该熟悉的最低限度,以便通过 CKAD 考试。 ### [Network Policies(网络策略)](https://kubernetes.io/docs/concepts/services-networking/network-policies/) -默认情况下,所有的ingress流量(即流入你的应用程序的流量)和egress流量(即你的应用程序流出的流量)都是允许的。如何pod都可以连接到其他的pod,即使它们在不同的namespaces。 +默认情况下,所有的 ingress 流量(即流入你的应用程序的流量)和 egress 流量(即你的应用程序流出的流量)都是允许的。如何 pod 都可以连接到其他的 pod,即使它们在不同的 namespaces。 -你可以定义网络策略,根据不同的标准控制ingress流量和egress流量 +你可以定义网络策略,根据不同的标准控制 ingress 流量和 egress 流量 -为了说明这一点,我们创建一个网络策略,只允许带有label `access: allowed`的pod访问的我们的数据库: +为了说明这一点,我们创建一个网络策略,只允许带有 label `access: allowed`的 pod 访问的我们的数据库: ```yaml kind: NetworkPolicy @@ -835,7 +835,7 @@ spec: ### [Security Context(安全背景)](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) -当配置一个Security Context(安全背景), 你可以启用一些安全功能,比如防止容器以root身份运行, 选择什么用户运行pod等等,下面是一个例子: +当配置一个 Security Context(安全背景), 你可以启用一些安全功能,比如防止容器以 root 身份运行, 选择什么用户运行 pod 等等,下面是一个例子: ```bash spec: @@ -852,17 +852,17 @@ spec: ### [Services Accounts(服务账号)](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) -你的应用程序可以连接到API 服务器进行数据交换。例如, 检索有关pod信息在一个namespace。Service accounts(服务账号) 允许应用服务对API服务器进行认证,以便它们进行数据交互。 +你的应用程序可以连接到 API 服务器进行数据交换。例如, 检索有关 pod 信息在一个 namespace。Service accounts(服务账号) 允许应用服务对 API 服务器进行认证,以便它们进行数据交互。 -这是一个 _default service account_ (默认的服务账号), 但是这不一个好的实践,因为不是每个应用程序都需要在API服务器上执行相同的操作。 +这是一个 _default service account_ (默认的服务账号), 但是这不一个好的实践,因为不是每个应用程序都需要在 API 服务器上执行相同的操作。 -创建service account(服务账号)的最简单的方法是通过命令行: +创建 service account(服务账号)的最简单的方法是通过命令行: ```bash kubectl create serviceaccount my-sa ``` -一旦创建, 你一个分配 `serviceAccountName` 字段 (pod 描述符)分配给一个pod: +一旦创建, 你一个分配 `serviceAccountName` 字段 (pod 描述符)分配给一个 pod: ```bash spec: @@ -876,32 +876,32 @@ spec: ### [Role Based Access Control(基于角色的访问控制)](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) -管理授权的一个常见办法是 **Role Based Access Control (RBAC)** (基于角色的访问控制)。 Service accounts(服务账号) 被链接到一个或者多个role(角色)。 每个role(角色)都有在某个资源上执行特定操作的权限。 +管理授权的一个常见办法是 **Role Based Access Control (RBAC)** (基于角色的访问控制)。 Service accounts(服务账号) 被链接到一个或者多个 role(角色)。 每个 role(角色)都有在某个资源上执行特定操作的权限。 RBAC 授权是通过两个独立的步骤来定义的: -- Role 创建, 使用 Role和ClusterRole 资源, 以指定在某些资源上可以执行的操作。 -- 绑定 roles到账号上, 通过使用 RoleBinding 和 ClusterRoleBinding 。 +- Role 创建, 使用 Role 和 ClusterRole 资源, 以指定在某些资源上可以执行的操作。 +- 绑定 roles 到账号上, 通过使用 RoleBinding 和 ClusterRoleBinding 。 -正如你所想,包含前缀`Cluster`的资源在整个集群范围内可用,而其他的资源只在特定的namespace中定义。 +正如你所想,包含前缀`Cluster`的资源在整个集群范围内可用,而其他的资源只在特定的 namespace 中定义。 -由于这不属于CKAD考试的范围,我们将不讨论如何在你的集群中创建和配置RBAC的细节。 +由于这不属于 CKAD 考试的范围,我们将不讨论如何在你的集群中创建和配置 RBAC 的细节。 -## Kubernetes中的可观察性和调试 +## Kubernetes 中的可观察性和调试 一旦你部署你的应用程序, 你如何知道你的集群发生了什么? -我们已经介绍了Probes作为一种机制决定是否重启pod,以及pod是否为网络流量做好了准备。在这里,我们将看到更多的机制来更好地了解我们的集群状态,并对任何问题故障排除。 +我们已经介绍了 Probes 作为一种机制决定是否重启 pod,以及 pod 是否为网络流量做好了准备。在这里,我们将看到更多的机制来更好地了解我们的集群状态,并对任何问题故障排除。 -#### 获取Pod的基本信息 +#### 获取 Pod 的基本信息 -访问pods的当前状态,有两个基本选项。这是第一选项: +访问 pods 的当前状态,有两个基本选项。这是第一选项: ```bash kubect get pods ``` -这将显示你的pods `STATUS`, `AGE`, `RESTARTS`的数量和多少pods内的容器处于 `READY`状态. 你可以用这个命令去显示pod的ip地址、labels等等。 +这将显示你的 pods `STATUS`, `AGE`, `RESTARTS`的数量和多少 pods 内的容器处于 `READY`状态. 你可以用这个命令去显示 pod 的 ip 地址、labels 等等。 第二个选项提供了更详细的信息: @@ -913,42 +913,42 @@ kubectl describe pods my-pod - Liveness/readiness probes 失败 - 拉取镜像出现错误 -- 没有足够的内存分配给pod +- 没有足够的内存分配给 pod 等等。 ### 如何获取容器的日志 -如果一个pod正在运行,你可以使用以下命令访问它的日志。 +如果一个 pod 正在运行,你可以使用以下命令访问它的日志。 ```bash kubectl logs pod-name [-c container-name] [-n namespace] ``` -如果你的pod有不止一个容器,你只需传递容器名称。同样,从不同的namespace中检索日志要用namespace 标志符(-n) +如果你的 pod 有不止一个容器,你只需传递容器名称。同样,从不同的 namespace 中检索日志要用 namespace 标志符(-n) -你甚至tail日志 `-f` 标志符,看到日志尾部,获取最新的变化: +你甚至 tail 日志 `-f` 标志符,看到日志尾部,获取最新的变化: ``` kubectl logs -f pod-name [-c container-name] [-n namespace] ``` -这对认证来讲是足够了。然而, 在真实的生产环境中,手动检查日志是很麻烦和低效的。 如果你在用GCP(谷歌云),你会希望用其他技术来管理你的日志和服务,像StackDriver技术。如果你想了解更多的有关StackDriver和GCP的信息,请务必查看我的[GCP指南](/news/google-cloud-platform-from-zero-to-hero/). +这对认证来讲是足够了。然而, 在真实的生产环境中,手动检查日志是很麻烦和低效的。 如果你在用 GCP(谷歌云),你会希望用其他技术来管理你的日志和服务,像 StackDriver 技术。如果你想了解更多的有关 StackDriver 和 GCP 的信息,请务必查看我的[GCP 指南](/news/google-cloud-platform-from-zero-to-hero/). ### 处理故障的小技巧 -虽然在排除故障时没有灵丹妙药, 但从pod层开始通常是一个好注意,可以从可以找到问题的根本原因。 +虽然在排除故障时没有灵丹妙药, 但从 pod 层开始通常是一个好注意,可以从可以找到问题的根本原因。 -你的基本工具是我们上面提供的命令,除此之外,你可以随时在pod里打开一个终端。 +你的基本工具是我们上面提供的命令,除此之外,你可以随时在 pod 里打开一个终端。 ```bash # 假设你的容器镜像使用 sh kubectl exec -it my-pod -c container -n namespace -- sh ``` -**不建议** 用这种方式去解决pod的内部问题, 因为根据设计,pods是短暂的,在pods重启后,你的修改会丢失, 问题会复现。 当然, 这是一个好的方法去很好地了解问题。 +**不建议** 用这种方式去解决 pod 的内部问题, 因为根据设计,pods 是短暂的,在 pods 重启后,你的修改会丢失, 问题会复现。 当然, 这是一个好的方法去很好地了解问题。 -获取你的pod或者node的指标,你可以运行以下命令。 +获取你的 pod 或者 node 的指标,你可以运行以下命令。 ```bash kubectl top pod pod-name @@ -958,7 +958,7 @@ kubectl top pod pod-name ## 技巧和窍门 -这是我发现的一些技巧,在我日常的Kubernetes工作中,特别是在通过CKAD考试时很有用。 **提高考试速度和效率部分** 。考虑到这点,让我们来看看可以做些什么来表现得更好。 +这是我发现的一些技巧,在我日常的 Kubernetes 工作中,特别是在通过 CKAD 考试时很有用。 **提高考试速度和效率部分** 。考虑到这点,让我们来看看可以做些什么来表现得更好。 ### 设置别名 @@ -987,14 +987,14 @@ alias kds='kd svc' ### 更快搜索你的命令历史 -我见过很多工程师,其中很多是高级工程师(seniors),笨拙按了20次以上的箭头键,在他们的命令历史中找东西。 +我见过很多工程师,其中很多是高级工程师(seniors),笨拙按了 20 次以上的箭头键,在他们的命令历史中找东西。 不同的是,确保你自如地使用`Ctrl + r`,只有按下它并开始输入你要输入的命令的某些部分。然后,继续按`Ctrl + r`,直到你找到它。现在你可以按`Enter`运行它,或者简单开始修改后再运行。 这是一个节省大量时间的方法,并不是每个人都知道的。 ### 熟悉文档 -你可以在考试期间查阅文档,然而,这不是学习新东西的时候。访问文档获得yaml的模板,检查特定的参数等等。 +你可以在考试期间查阅文档,然而,这不是学习新东西的时候。访问文档获得 yaml 的模板,检查特定的参数等等。 确保你对 **文档的结构** 很熟悉和知道去哪里找东西。 使用 **搜索功能** ,更快找到. @@ -1006,7 +1006,7 @@ alias kds='kd svc' 事实上,我建议你在查阅文档之前这样做,因为这要快得多。 -想象一下,你想创建一个资源配额对象。你不记得你需要写的yaml或者创建它的命令。你知道 `kubectl command` 和 `--help` 参数。那么在查阅文档之前,你应该先试一试这个: +想象一下,你想创建一个资源配额对象。你不记得你需要写的 yaml 或者创建它的命令。你知道 `kubectl command` 和 `--help` 参数。那么在查阅文档之前,你应该先试一试这个: ```bash # 我们可以创建得到创建资源配额对象所需要的东西 @@ -1018,9 +1018,9 @@ kubeclt create quota my-quota --hard="secrets=2" 你有大量的信息来源,因此你不需要记得住这么多的命令或者参数。 然而,考试期间时间非常有限,我认为值得熟记一些命令和任何使用它们。 -### 快速生成一个基本的pod +### 快速生成一个基本的 pod -**这一点尤为重要**。与其每一次需要一个pod时从文档中复制和粘贴,不如使用下面的命令获得一个描述符,你可以在以后修改它以满足你的需求。 +**这一点尤为重要**。与其每一次需要一个 pod 时从文档中复制和粘贴,不如使用下面的命令获得一个描述符,你可以在以后修改它以满足你的需求。 ```bash # 运行一个pod nginx,并将其参数写入一个pod.yaml文件中 @@ -1036,7 +1036,7 @@ kubectl run nginx --image=nginx --restart=Never --port=80 --labels=key=val --dr -it # 进入交换式终端,直接在pod中有运行命令 ``` -例如,你可以用它创建一个临时的pod,用它验证你的工作: +例如,你可以用它创建一个临时的 pod,用它验证你的工作: ```bash kubectl run tmp --image=busybox --restart=Never --rm -it -- /bin/sh @@ -1044,9 +1044,9 @@ kubectl run tmp --image=busybox --restart=Never --rm -it -- /bin/sh 现在你可以运行命令 `wget -O- svc:port` ,看看你的服务是否在运行,网络策略是否在工作等等。 -##### 注意: +##### 注意 - `--dry-run=client -o yaml`不仅适用于pod,也适用于很多资源。如果我们回到上一节,我们创建一个资源配额,我们可以得到这样描述符: + `--dry-run=client -o yaml`不仅适用于 pod,也适用于很多资源。如果我们回到上一节,我们创建一个资源配额,我们可以得到这样描述符: ```bash kubeclt create quota my-quota --hard="secrets=2" --dry-run=client -o yaml @@ -1064,25 +1064,25 @@ kubectl describe pods my-pod | grep -i -C 2 ip ... ``` -### 观察pods 的状态变化 +### 观察 pods 的状态变化 -与其每隔几秒手动运行`kubeclt get pods`来查看变化,不如通过`watch` 参数来查看你的pods是如何变化: +与其每隔几秒手动运行`kubeclt get pods`来查看变化,不如通过`watch` 参数来查看你的 pods 是如何变化: ```bash kubectl get pods --watch ``` -### 快速删除Pods +### 快速删除 Pods -犯错是学习过程的一部分。在考试中你也会犯错。由于时间紧迫,我们需要确保在删除资源时不要等待很长时间,通过这些参数可以快速删除pods: +犯错是学习过程的一部分。在考试中你也会犯错。由于时间紧迫,我们需要确保在删除资源时不要等待很长时间,通过这些参数可以快速删除 pods: ```bash k delete pods my-pod --force --grace-period=0 ``` -### 用一个特定命令运行Pod +### 用一个特定命令运行 Pod -我发现学习如何用命令行想pod、job、cronjobs等传递命令很有用,比如这个例子: +我发现学习如何用命令行想 pod、job、cronjobs 等传递命令很有用,比如这个例子: ```bash kubectl run loop --image=busybox -o yaml --dry-run=client --restart=Never \ @@ -1090,7 +1090,7 @@ kubectl run loop --image=busybox -o yaml --dry-run=client --restart=Never \ > pod.yaml ``` -这会产生一个文件pod.yaml: +这会产生一个文件 pod.yaml: ```bash apiVersion: v1 @@ -1114,7 +1114,7 @@ spec: status: {} ``` -你可以运行一个命令,也可以运行多个命令链,就像一个小型的bash脚本一样: +你可以运行一个命令,也可以运行多个命令链,就像一个小型的 bash 脚本一样: ```bash # 运行一个特定的可执行文件 @@ -1123,7 +1123,7 @@ kubectl run busybox --image=busybox -it --restart=Never -- echo 'hello world' kubectl run busybox --image=busybox -it --restart=Never -- /bin/sh -c 'echo hello world' ``` -这不是一个巨大的区别,但你不需要记住如何在pod的描述符中写,也不需要浪费时间去打开文档。 **你只需要使用你已经知道的东西** +这不是一个巨大的区别,但你不需要记住如何在 pod 的描述符中写,也不需要浪费时间去打开文档。 **你只需要使用你已经知道的东西** ### roll out(展开) @@ -1139,20 +1139,20 @@ kubectl rollout -h 最后这些提示不是针对认证的,而是针对日常工作的: -- 这个[kube-ps1 模块](https://github.com/jonmosco/kube-ps1) 让你很容易知道你是在那个集群和namespace中操作的, 以防止犯错误,比如你不应该乱用 prod(生产环境)资源。 -- 另外,我推荐你看一下[Helm](https://helm.sh/)。Helm 是一个软件包管理器,可以用来轻松部署应用程序 (像`npm`)。 Helm还允许你编写模板,你可以根据不同的值 (name, resource requests and limits, and so on)重复使用,创建对象。 +- 这个[kube-ps1 模块](https://github.com/jonmosco/kube-ps1) 让你很容易知道你是在那个集群和 namespace 中操作的, 以防止犯错误,比如你不应该乱用 prod(生产环境)资源。 +- 另外,我推荐你看一下[Helm](https://helm.sh/)。Helm 是一个软件包管理器,可以用来轻松部署应用程序 (像`npm`)。 Helm 还允许你编写模板,你可以根据不同的值 (name, resource requests and limits, and so on)重复使用,创建对象。 ## 练习时间 **做事永远比读书学得多**。所以我在这里留下一些我在准备考试已经解决的问题,供你练习。 查看[项目地址](https://github.com/dgkanatsios/CKAD-exercises) 和 [这篇文章](https://medium.com/bb-tutorials-and-thoughts/practice-enough-with-these-questions-for-the-ckad-exam-2f42d1228552). -我建议在你先解决自己的所有问题,再看我上面提到资料。同时验证你做的东西: 检查日志,创建一个pod来连接你的服务等等。这也将有助建立你在考试中肌肉记忆。 +我建议在你先解决自己的所有问题,再看我上面提到资料。同时验证你做的东西: 检查日志,创建一个 pod 来连接你的服务等等。这也将有助建立你在考试中肌肉记忆。 尝试根据我的提示来练习这些练习,就像你真的在考试中一样,不分心,在浏览器里只打开一个标签--Kubernetes 的官方文档。 ## 结论 -本指南包含了将提高你的技能到新的水平,通过CKAD考试以及成为一名真正的Kubernetes开发者所需要的一切。这一切在你手中,你只需要付出一些努力,祝你好运! +本指南包含了将提高你的技能到新的水平,通过 CKAD 考试以及成为一名真正的 Kubernetes 开发者所需要的一切。这一切在你手中,你只需要付出一些努力,祝你好运! 你可以访问到我的博客[www.yourdevopsguy.com](https://www.yourdevopsguy.com/) 和 [在推特上关注我](https://twitter.com/CodingLanguages) 获得更多高质量的技术内容。 diff --git a/chinese/articles/how-to-become-a-technical-writer.md b/chinese/articles/how-to-become-a-technical-writer.md index 7fdd161af..ad621e43d 100644 --- a/chinese/articles/how-to-become-a-technical-writer.md +++ b/chinese/articles/how-to-become-a-technical-writer.md @@ -76,15 +76,15 @@ 文章名称应具有描述性并对读者有帮助。 -例如,当内容是关于React中的元素渲染时,就不要将文章命名为《深入了解React》。这样会让那些期望在阅读完你的文章后能了解React所有知识的读者失望。 +例如,当内容是关于 React 中的元素渲染时,就不要将文章命名为《深入了解 React》。这样会让那些期望在阅读完你的文章后能了解 React 所有知识的读者失望。 -相反,想一个具体的标题,准确描述你文章中所写的内容,如《如何在React中渲染元素》。 +相反,想一个具体的标题,准确描述你文章中所写的内容,如《如何在 React 中渲染元素》。 ## 培养你的技术能力 作为技术文档工程师,你的目标是帮助读者以最直接的方式理解高度复杂的过程或概念。 -要做到这一点,你需要熟悉你所写的主题。这意味着如果你想写一篇关于React.js的技术文章或文档,你应该做到自己也会使用React。 +要做到这一点,你需要熟悉你所写的主题。这意味着如果你想写一篇关于 React.js 的技术文章或文档,你应该做到自己也会使用 React。 我将以阿尔伯特·爱因斯坦(Albert Einstein)的这句流行名言来结束本节: @@ -102,11 +102,11 @@ 研究的方式包括在你喜欢的搜索引擎上提问、请教对该主题了解的人(如果你认识的话)或者阅读书籍。 -如果你决定****使用搜索引擎的方式****,就针对你想要发现的内容提问。例如,如果你想了解如何在React中使用GSAP ScrollTrigger插件,你的问题应该遵循这样的格式_“我如何在React中使用GSAP ScrollTrigger插件”_。 +如果你决定****使用搜索引擎的方式****,就针对你想要发现的内容提问。例如,如果你想了解如何在 React 中使用 GSAP ScrollTrigger 插件,你的问题应该遵循这样的格式_“我如何在 React 中使用 GSAP ScrollTrigger 插件”_。 如果你决定****询问对该主题了解的人****,记得要有礼貌,直奔主题。你可以遵循以下询问方式,而不是说完 “_你好_” 就等着对方回复后才提问。 -“_嗨,Rita,我叫Edidiong。我知道你对使用GSAP ScrollTrigger插件非常了解。这些年来,我看过一些你的CodePen演示,它们看起来都非常棒。我很想知道如何操作GSAP补间滚动触发动画?如果你因为工作繁忙而不能回复,我完全理解。但如果你能回复,我将很感激。”_ +“_嗨,Rita,我叫 Edidiong。我知道你对使用 GSAP ScrollTrigger 插件非常了解。这些年来,我看过一些你的 CodePen 演示,它们看起来都非常棒。我很想知道如何操作 GSAP 补间滚动触发动画?如果你因为工作繁忙而不能回复,我完全理解。但如果你能回复,我将很感激。”_ 你可能觉得这是一条相当长的信息,但它包含了最重要的事情:你的名字、你对对方工作的钦佩、你的需求,以及你明白你无权占用对方的时间。 @@ -164,9 +164,9 @@ 首先,你需要想好一个你想写的主题。然后你应该做必要的研究,写出文章初稿,并校对文章(至少两遍)。准备就绪后,你就可以在你的博客上发布文章了。 -你不需要从头开始建立你的博客,因为这会占用大量时间且分散你的注意力,使你无法专注于写作这件正事。就我而言,我用[Hashnode](https://hashnode.com/@didicodes/joinme/)创建了我的[博客](http://edidiongasikpo.com/),因为Hashnode速度超快,它有一个强大的社区,并且允许你将博客映射到你自己的域。 +你不需要从头开始建立你的博客,因为这会占用大量时间且分散你的注意力,使你无法专注于写作这件正事。就我而言,我用[Hashnode](https://hashnode.com/@didicodes/joinme/)创建了我的[博客](http://edidiongasikpo.com/),因为 Hashnode 速度超快,它有一个强大的社区,并且允许你将博客映射到你自己的域。 -在你对写作得心应手后,可以申请成为freeCodeCamp的专栏作者。如果你通过审核,就可以在freeCodeCamp的平台上发布文章,以此接触到更多的读者。 +在你对写作得心应手后,可以申请成为 freeCodeCamp 的专栏作者。如果你通过审核,就可以在 freeCodeCamp 的平台上发布文章,以此接触到更多的读者。 ### 坚持写作 @@ -176,13 +176,13 @@ 就像其它技能一样,当你坚持写作时,你的写作能力就会变得越来越好。争取每个月至少写一篇文章,如果你坚持不懈,终会惊喜发现自己的写作技巧有了提高。 -作为一名技术文档工程师,如果你想要锻炼你坚持不懈的精神,可以试试这个[#1周2篇文章挑战](https://hashnode.com/2articles1week)。 +作为一名技术文档工程师,如果你想要锻炼你坚持不懈的精神,可以试试这个[#1 周 2 篇文章挑战](https://hashnode.com/2articles1week)。 -### 什么是1周2篇文章挑战? +### 什么是 1 周 2 篇文章挑战? 这项挑战的目标是鼓励技术文档工程师确定自己的写作目标,了解写作标准,最重要的是坚持写作。 -参与者需要在自己的博客上 **每周至少发表2篇文章,共持续4周** 。如果能做到这一点,你将能在短短一个月内在你的博客上创建并发表8篇文章。很有趣,对吧?😉 +参与者需要在自己的博客上 **每周至少发表 2 篇文章,共持续 4 周** 。如果能做到这一点,你将能在短短一个月内在你的博客上创建并发表 8 篇文章。很有趣,对吧?😉 我看到很多人都在谈论参加这个挑战的好处。我相信它将帮助你开始坚持写作。 @@ -200,11 +200,11 @@ 这项活动非常重要,因为通过开源项目的文档,用户不仅可以理解该项目,还可以为项目做贡献。 -在项目期间,入选的技术文档工程师将花费3-5个月的时间来构建一套新的文档、改进现有文档的结构、开发一个急需的教程或改进一个开源组织的贡献流程和指南。 +在项目期间,入选的技术文档工程师将花费 3-5 个月的时间来构建一套新的文档、改进现有文档的结构、开发一个急需的教程或改进一个开源组织的贡献流程和指南。 -这个项目的有趣之处在于,作为技术文档工程师参与开源项目贡献,**你可以获得3000至15000美元的报酬** 。你也会有更大的机会加入谷歌的技术写作团队,还有可能在项目结束后被开源组织留用,继续从事技术写作工作。 +这个项目的有趣之处在于,作为技术文档工程师参与开源项目贡献,**你可以获得 3000 至 15000 美元的报酬** 。你也会有更大的机会加入谷歌的技术写作团队,还有可能在项目结束后被开源组织留用,继续从事技术写作工作。 -## 帮助你开始写作的6个技术写作技巧 +## 帮助你开始写作的 6 个技术写作技巧 完成初稿后,注意以下事项: @@ -217,7 +217,7 @@ ## 总结 -技术写作仍然是专业工作场所中一项令人羡慕的技能。[预计](https://www.bls.gov/ooh/media-and-communication/technical-writers.htm)从2014年到2024年,技术写作需求将至少增长10%。 +技术写作仍然是专业工作场所中一项令人羡慕的技能。[预计](https://www.bls.gov/ooh/media-and-communication/technical-writers.htm)从 2014 年到 2024 年,技术写作需求将至少增长 10%。 写作像许多其它手艺一样,需要多年的实践来打磨。写作最好的地方在于你可以看到自己的进步。如果你努力练习,随着时间的推移,你就能看到你的写作能力比之前有很大的进步。 diff --git a/chinese/articles/how-to-become-an-astounding-junior-developer.md b/chinese/articles/how-to-become-an-astounding-junior-developer.md index 25c22be94..ed84b2ca0 100644 --- a/chinese/articles/how-to-become-an-astounding-junior-developer.md +++ b/chinese/articles/how-to-become-an-astounding-junior-developer.md @@ -385,7 +385,7 @@ https://jamesclear.com/marginal-gains 我见过无数的开发人员尝试调试和修复一个问题的时候(我自己也做过很多次),他们甚至没有在正确的文件中进行调试!快速打印或者在“控制台”中可以显示您实际上正在查看实际运行的代码。 -### #5 一次做一件事。 +### #5 一次做一件事 每个新开发人员都会犯的一个常见错误是: **他们一次做的事情太多了。** diff --git a/chinese/articles/how-to-build-a-modal-with-javascript.md b/chinese/articles/how-to-build-a-modal-with-javascript.md index a447ef815..8cac9b296 100644 --- a/chinese/articles/how-to-build-a-modal-with-javascript.md +++ b/chinese/articles/how-to-build-a-modal-with-javascript.md @@ -11,23 +11,23 @@ 你可以使用模态框来做一些事情,比如存储你不想立即在网页上看到的信息,创建导航菜单,添加召唤行动元素,等等。 -一个很好的例子是,当你试图关闭推文编辑菜单时,Twitter上出现的弹框。 +一个很好的例子是,当你试图关闭推文编辑菜单时,Twitter 上出现的弹框。 ![推特警告消息模态框](https://www.freecodecamp.org/news/content/images/2022/10/twitter_warning_message_modal.png) 你还可以将模态框用于其他事情,如创建召唤行动元素、导航菜单、通讯部件等。 -作为一名web开发人员,知道如何构建一个模态框是个很方便的技能。在本教程中,我将带你走过如何使用HTML、CSS和JavaScript创建一个简单模态的过程。 +作为一名 web 开发人员,知道如何构建一个模态框是个很方便的技能。在本教程中,我将带你走过如何使用 HTML、CSS 和 JavaScript 创建一个简单模态的过程。 以下是我们将制作的内容截图: ![一个用 html, css ,javascript创建的模态框](https://www.freecodecamp.org/news/content/images/2022/10/modal.png) -这些步骤很容易遵循,这样你就可以定制或创建你自己的scratch——这完全取决于你。在本文的最后,我将提供codepen文件供你玩一玩。 +这些步骤很容易遵循,这样你就可以定制或创建你自己的 scratch——这完全取决于你。在本文的最后,我将提供 codepen 文件供你玩一玩。 ## Step 1 – 添加标签 -让我们从html开始吧。 +让我们从 html 开始吧。 首先,你要添加一个`section`元素,并赋予它两个类,`modal`和`hidden`。在这个元素下,你还会有一个`div`元素,它的类是`overlay`和`hidden`。最后,添加一个` ``` -**注意** ⚠️ Take note of the hidden class added to the modal and the overlay element. This is very important because you'll target these classes to hide your modal and overlay using CSS.注意添加到模态框和覆盖元素的`hidden`类。这是非常重要的,因为你将使用CSS针对这些类来隐藏你的模态框和覆盖层。 +**注意** ⚠️ Take note of the hidden class added to the modal and the overlay element. This is very important because you'll target these classes to hide your modal and overlay using CSS.注意添加到模态框和覆盖元素的`hidden`类。这是非常重要的,因为你将使用 CSS 针对这些类来隐藏你的模态框和覆盖层。 这里是输出的结果: @@ -81,7 +81,7 @@ 让我们从取消页面所有元素的内外边距开始,之后把模态框和打开模态框的按钮都居中对齐。 -现在跳转到CSS页面添加这些样式: +现在跳转到 CSS 页面添加这些样式: ```css * { @@ -173,15 +173,15 @@ button { ![complete-modal-style](https://www.freecodecamp.org/news/content/images/2022/10/complete-modal-style.png) -刚才你设置了模态框元素的样式,和绝对定位。定位起作用的原因是之前给body元素设置了相对定位。 +刚才你设置了模态框元素的样式,和绝对定位。定位起作用的原因是之前给 body 元素设置了相对定位。 你也给模态框内部的元素设置了样式,但是我不会更深地解释细节,因为这部分不是我们的重要内容。 -## Step 3 – 添加覆盖层overlay +## Step 3 – 添加覆盖层 overlay 对于覆盖层,你想让它在整个页面上有一个微妙的黑色背景和模糊。 -由于你已经给body设置了相对定位,所以你可以用固定定位让overlay覆盖在body上。宽和高要设置为视窗的100%。 +由于你已经给 body 设置了相对定位,所以你可以用固定定位让 overlay 覆盖在 body 上。宽和高要设置为视窗的 100%。 ```css .overlay { @@ -202,7 +202,7 @@ button { ![overlay](https://www.freecodecamp.org/news/content/images/2022/10/overlay.png) -如果你只想让overlay只覆盖body而不覆盖模态框,你需要让模态框modal处在更高的`z-index`。 +如果你只想让 overlay 只覆盖 body 而不覆盖模态框,你需要让模态框 modal 处在更高的`z-index`。 ```css .modal { @@ -210,7 +210,7 @@ button { } ``` -现在模态框将在overlay的上层而不被覆盖。 +现在模态框将在 overlay 的上层而不被覆盖。 ![modal](https://www.freecodecamp.org/news/content/images/2022/10/modal-1.png) @@ -224,13 +224,13 @@ button { } ``` -现在,页面中只有按钮显示。现在你可以在JavaScript中为模态框的功能做些事情了。 +现在,页面中只有按钮显示。现在你可以在 JavaScript 中为模态框的功能做些事情了。 ## Step 4 – 模态框的功能 -在我们继续之前,我认为最好解释一下模态框是如何工作的。还记得你如何使用' hidden '类来隐藏模态框和覆盖层吗?要从元素中添加或删除这个类,需要使用DOM的classList元素。 +在我们继续之前,我认为最好解释一下模态框是如何工作的。还记得你如何使用' hidden '类来隐藏模态框和覆盖层吗?要从元素中添加或删除这个类,需要使用 DOM 的 classList 元素。 -但是,收敛,你需要使用DOM的`querySelector` 方法选中并把它们存在变量里,以便重复使用。 +但是,收敛,你需要使用 DOM 的`querySelector` 方法选中并把它们存在变量里,以便重复使用。 ```js const modal = document.querySelector(".modal"); @@ -241,7 +241,7 @@ const closeModalBtn = document.querySelector(".btn-close"); ## 如何打开模态框 -为了显示模态框,创建一个函数 `openModal`。在这个函数中,你将使用DOM `classList` 的属性,它采用了不同的方法比如'。remove() '和'。add() '从' modal '和' overlay '中移除' hidden '类。 +为了显示模态框,创建一个函数 `openModal`。在这个函数中,你将使用 DOM `classList` 的属性,它采用了不同的方法比如'。remove() '和'。add() '从' modal '和' overlay '中移除' hidden '类。 ```js const openModal = function () { @@ -299,15 +299,15 @@ overlay.addEventListener("click", closeModal); 除了在单击关闭按钮或覆盖层时关闭模态框外,您还可以附加一个事件侦听器来监视键盘事件。 -在本例中,你可以希望在按下“Escape”键时关闭模态框,这与Twitter compose模态框示例非常相似。 +在本例中,你可以希望在按下“Escape”键时关闭模态框,这与 Twitter compose 模态框示例非常相似。 ```js document.addEventListener("keydown"); ``` -但是这次你想要的事件类型不是` " click " `事件——你想使用` " keydown " `事件来执行你的函数。 +但是这次你想要的事件类型不是`" click "`事件——你想使用`" keydown "`事件来执行你的函数。 -接下来,你将编写一个条件,检查当前按下的键是否为`Escape`键,模态框是否包含`hidden`类。现在它打开了,你想执行` closemmodal `函数(本质上就是关闭模态窗口)。 +接下来,你将编写一个条件,检查当前按下的键是否为`Escape`键,模态框是否包含`hidden`类。现在它打开了,你想执行`closemmodal`函数(本质上就是关闭模态窗口)。 ```js document.addEventListener("keydown", function (e) { @@ -319,9 +319,9 @@ document.addEventListener("keydown", function (e) { 当模态框打开时,你点击Esc键,它会关闭模态框。 -有了这个,你已经成功地用HTML, CSS和JavaScript创建了一个模态组件,它就像预期的那样工作。🥳 +有了这个,你已经成功地用 HTML, CSS 和 JavaScript 创建了一个模态组件,它就像预期的那样工作。🥳 -下面是测试模态框效果的codepen文件: +下面是测试模态框效果的 codepen 文件: See the Pen Modal with overlay and blur by Eke (@evavic44) on CodePen. diff --git a/chinese/articles/how-to-build-an-html-calculator-app-from-scratch-using-javascript.md b/chinese/articles/how-to-build-an-html-calculator-app-from-scratch-using-javascript.md index ccf2df994..fe4782642 100644 --- a/chinese/articles/how-to-build-an-html-calculator-app-from-scratch-using-javascript.md +++ b/chinese/articles/how-to-build-an-html-calculator-app-from-scratch-using-javascript.md @@ -138,7 +138,7 @@ if (action === 'clear') { 让我们思考一下,一个普通人拿到一个计算器之后,会做什么呢?**这个普通人会做什么的问题被称作 happy path**。 这个普通人我们就称作 Mary 吧。 -当 Mary拿起计算器时,她可能会点击任何一个按键: +当 Mary 拿起计算器时,她可能会点击任何一个按键: 1. 一个数字键(0-9) 2. 一个操作键 (+,-,×,÷) @@ -167,7 +167,7 @@ const display = document.querySelector('.calculator__display') ``` -**如果计算器显示0,我们需要用点击按键的数字替换计算器显示屏的数字。** 我们可以通过显示屏的`textContent`属性进行替换。 +**如果计算器显示 0,我们需要用点击按键的数字替换计算器显示屏的数字。** 我们可以通过显示屏的`textContent`属性进行替换。 ```js if (!action) { if (displayedNum === '0') { @@ -403,7 +403,7 @@ if (!displayedNum.includes('.')) { 接下来,如果 Tim 在点击任何操作键之后点击了小数点键,那么应该显示为`0.`。 ![](https://cdn-media-1.freecodecamp.org/images/fLLhOqkyFZqsOZIxgMPAkpezrUisGpDKFEsw) -我们需要知道上一个按键是否是操作符键。 我们可以通过上节课设置的自定义属性 `data-previous-key-type `来判断。 +我们需要知道上一个按键是否是操作符键。 我们可以通过上节课设置的自定义属性 `data-previous-key-type`来判断。 当然`data-previous-key-type`还没有完成,为了判断`previousKeyType`是否是操作符,我们还需要在每次点击按键时更新`previousKeyType`。 ```js if (!action) { @@ -469,10 +469,10 @@ if ( ``` -尽管我们在第二次点击操作键的时候我们可以得到一个计算的值,但这里依然有一个bug存在————额外点击操作键会计算出一个不应该的值。 +尽管我们在第二次点击操作键的时候我们可以得到一个计算的值,但这里依然有一个 bug 存在————额外点击操作键会计算出一个不应该的值。 ![](https://cdn-media-1.freecodecamp.org/images/8ktjtHeYaRTEn-lPbOM3fhEg3qrvDl5WfOVY) -为了防止计算器在后续点击操作键时进行计算,我们需要检查 `previousKeyType `是否是一个操作键。如果是,我们不执行计算。 +为了防止计算器在后续点击操作键时进行计算,我们需要检查 `previousKeyType`是否是一个操作键。如果是,我们不执行计算。 ```js if ( firstValue && @@ -499,13 +499,13 @@ if ( 接着,我们让使用者点击一下减号键,在他点击减号键之后,我们设置`firstValue`为 99,同样的设置`operator`为`subtract`。 ![](https://cdn-media-1.freecodecamp.org/images/0K-KPTzdCBgfVvVaDNcVDYSjXfUO8p5LRs2v) -第三步,假设用户这次输入的数字是 1,此时,将显示的数字改成1,但是我们的 `firstValue`,`operator` 和 `secondValue`保持不变。 +第三步,假设用户这次输入的数字是 1,此时,将显示的数字改成 1,但是我们的 `firstValue`,`operator` 和 `secondValue`保持不变。 ![](https://cdn-media-1.freecodecamp.org/images/0MacG-A5Tl7rZeB6NLeNvghVyBpmSqaZQkn9) 第四步,用户再次点击减号键。就在他们点击减法后,在计算结果之前,我们设置`secondValue`作为显示的数字。 ![](https://cdn-media-1.freecodecamp.org/images/RgDMKK92og4djxxmaYO1HUYiVoetKDK9x0j7) -第五步,我们用`firstValue` 99,`operator`减号以及`secondValue` 1进行计算,得到结果 98。 +第五步,我们用`firstValue` 99,`operator`减号以及`secondValue` 1 进行计算,得到结果 98。 计算出结果后,我们将显示设置为结果。然后,我们设置`operator`为减法,`firstValue`为之前显示的数字。 ![](https://cdn-media-1.freecodecamp.org/images/X3VFJ5ar--k84pP3pM5VDVODvYlX4fCwHcnS) @@ -593,7 +593,7 @@ if (firstValue) { 第二步,让用户点击减号键,再点击减号键之后,我们设置`firstValue`为 5,同时设置`operator`为减号。 ![](https://cdn-media-1.freecodecamp.org/images/Fc-QupYbv3HInXqv1vHFCc1avhDe3iyEErhs) -第三步,让用户输入第二个值,假设是数字 1。此时,显示的数字应该被更新为1,但是我们的`firstValue,`operator`和`secondValue`是保持不变的。 +第三步,让用户输入第二个值,假设是数字 1。此时,显示的数字应该被更新为 1,但是我们的`firstValue,`operator`和`secondValue`是保持不变的。 ![](https://cdn-media-1.freecodecamp.org/images/lW3CtoXJ1gxpUS5SZM3zh3zmqSB-ksM6E0vr) 第四步,用户点击等号键。紧接着用户点击了等号,但是在计算之前,我们设置`secondValue`为`displayedNum`。 @@ -685,7 +685,7 @@ if (action === 'decimal') { 第五,如果 Tim 再点击等号之后又点击了操作键,计算器则不应该进行计算。 ![](https://cdn-media-1.freecodecamp.org/images/uuifuJ41Oo86NXMsPj44RSQf7ExULROc2GaI) -为此,我们在用操作键进行计算之前,先检查 `previousKeyType `是否为 `calculate`。 +为此,我们在用操作键进行计算之前,先检查 `previousKeyType`是否为 `calculate`。 ```js if ( action === 'add' || @@ -717,7 +717,7 @@ if ( 当计算器处于默认状态时,应该显示 "AC"。 ![](https://cdn-media-1.freecodecamp.org/images/22fj2VLJJ1SPexybqdWIqPRkj9JkrlI3AAYl) -首先,如果 Tim 点击了一个键(除了清ad除键之外的任何键),`AC`应该被改成`CE`。 +首先,如果 Tim 点击了一个键(除了清 ad 除键之外的任何键),`AC`应该被改成`CE`。 ![](https://cdn-media-1.freecodecamp.org/images/Hs9tjp3JQIYOaAgh8KDnxj5QShScU0nMkDa7) 我们通过检查`data-action`是不是`clear`来判断,如果不是`clear`,我们找到清除按钮,并改变`textContent`。 @@ -728,7 +728,7 @@ if (action !== 'clear') { } ``` -接下来,如果 Tim 点击`CE`,显示的数字应该为0。与此同时,`CE`应该改为`AC`。所以 Tim 可以将计算器重置到初始状态。 +接下来,如果 Tim 点击`CE`,显示的数字应该为 0。与此同时,`CE`应该改为`AC`。所以 Tim 可以将计算器重置到初始状态。 ![](https://cdn-media-1.freecodecamp.org/images/Dv6SFw5LY8wB0WqTFQBe46-QoraBiq8TvpdY) ```js @@ -888,7 +888,7 @@ _`keys.addEventListener('click', e => { 这里`createResultString`是一个纯函数,我们需要把它的返回值显示在计算器上, `updateCalculatorState` 是一个不纯函数,可以改变计算器的自定义属性和外观。 ### 实现 createResultString -像之前所说的,`createResultString`的返回值需要显示在计算器上,你可以通过display.textContent = 'some value`.来得到这部分值。 +像之前所说的,`createResultString`的返回值需要显示在计算器上,你可以通过 display.textContent = 'some value`.来得到这部分值。 ```js display.textContent = 'some value' ``` @@ -1088,7 +1088,7 @@ const createResultString = () => { } ``` -然后我们可以将 `if/else `语句重构为三元操作符。 +然后我们可以将 `if/else`语句重构为三元操作符。 ```js const createResultString = () => { @@ -1310,7 +1310,7 @@ const createResultString = (key, displayedNum, state) => { ``` -### if语句的一致性 +### if 语句的一致性 在`createResultString`中,我们使用以下条件来测试被点击的键的类型: ```js @@ -1364,7 +1364,7 @@ const createResultString = (key, displayedNum, state) => { 我们完成了`createResultString`。让我们继续进行`updateCalculatorState`。 -### 实现updateCalculatorState +### 实现 updateCalculatorState `updateCalculatorState`是一个改变计算器的外观和自定义属性的函数。 @@ -1594,9 +1594,9 @@ keys.addEventListener('click', e => { 你可以通过[这个链接][12]来获取重构部分的源代码(往下滚动,在下面输入你的邮箱地址,我会直接把源代码发到你的邮箱里)。 -我希望你喜欢这篇文章。你可能也会喜欢[Learn JavaScript][13]————在这个课程中,我向你展示如何一步步构建20个组件,就像我们今天如何构建这个计算器一样。 +我希望你喜欢这篇文章。你可能也会喜欢[Learn JavaScript][13]————在这个课程中,我向你展示如何一步步构建 20 个组件,就像我们今天如何构建这个计算器一样。 -注意:我们可以通过添加键盘支持和像Live regions这样的可访问性功能来进一步改进计算器。想知道如何改进吗?去看看《学习JavaScript》吧:) +注意:我们可以通过添加键盘支持和像 Live regions 这样的可访问性功能来进一步改进计算器。想知道如何改进吗?去看看《学习 JavaScript》吧:) [1]: https://zellwk.com/blog/js-if-else [2]: https://zellwk.com/blog/js-for-loops diff --git a/chinese/articles/how-to-build-explicit-apis-with-openapi.md b/chinese/articles/how-to-build-explicit-apis-with-openapi.md index 2692d1d68..6208eb593 100644 --- a/chinese/articles/how-to-build-explicit-apis-with-openapi.md +++ b/chinese/articles/how-to-build-explicit-apis-with-openapi.md @@ -86,7 +86,7 @@ 很好,现在我们已经介绍了 API 定义的开放标准和背景,让我们来实现一个 Express 待办事项应用,演示怎么解决前面的挑战。 -我们将使用 Express 库 [**express-openapi**](https://github.com/kogosoftwarellc/open-api/tree/master/packages/express-openapi) 的 OpenAPI。请注意,这个库提供的高级功能(响应验证、认证、中间件设置......)超出了本文的范围。 +我们将使用 Express 库 [**express-openapi**](https://github.com/kogosoftwarellc/open-api/tree/master/packages/express-openapi) 的 OpenAPI。请注意,这个库提供的高级功能(响应验证、认证、中间件设置……)超出了本文的范围。 你可以在**[这个仓库](https://github.com/aperkaz/express-open-api)**中找到演示的完整代码。 diff --git a/chinese/articles/how-to-build-your-on-heroku-with-dokku.md b/chinese/articles/how-to-build-your-on-heroku-with-dokku.md index 09511848f..5d87d3a95 100644 --- a/chinese/articles/how-to-build-your-on-heroku-with-dokku.md +++ b/chinese/articles/how-to-build-your-on-heroku-with-dokku.md @@ -6,63 +6,63 @@ ![How to Build Your Own Heroku with Dokku](https://www.freecodecamp.org/news/content/images/size/w2000/2022/02/dokku.png) -Heroku是一个被开发者广泛使用的知名PaaS。而作为一个有趣和有用的项目,你可以用Dokku轻松地制作你自己的类似Heroku的PaaS。 +Heroku 是一个被开发者广泛使用的知名 PaaS。而作为一个有趣和有用的项目,你可以用 Dokku 轻松地制作你自己的类似 Heroku 的 PaaS。 ![dokku-logo-with-name5-1](https://www.freecodecamp.org/news/content/images/2022/02/dokku-logo-with-name5-1.png) -## 什么是Heroku? +## 什么是 Heroku? -Heroku是一家成立于2007年的平台即服务(PaaS)公司。该平台在AWS上运行,其临时存储系统被称为 "Dyno"。 +Heroku 是一家成立于 2007 年的平台即服务(PaaS)公司。该平台在 AWS 上运行,其临时存储系统被称为 "Dyno"。 -Heroku是开发者使用最多的PaaS之一,这是有原因的--它很容易使用,有很好的文档,并支持多种编程语言。 +Heroku 是开发者使用最多的 PaaS 之一,这是有原因的--它很容易使用,有很好的文档,并支持多种编程语言。 -但是,如果你可以通过一个简单易用的应用程序来部署你自己的类似Heroku的平台,包括CI/CD管道、数据库连接、HTTPS连接等等,会怎么样呢? +但是,如果你可以通过一个简单易用的应用程序来部署你自己的类似 Heroku 的平台,包括 CI/CD 管道、数据库连接、HTTPS 连接等等,会怎么样呢? -嗯,这就是Dokku所提供的,甚至更多。让我们来看看。 +嗯,这就是 Dokku 所提供的,甚至更多。让我们来看看。 -## 什么是PaaS? +## 什么是 PaaS? 平台即服务(PaaS)是一种软件架构风格,为部署你的应用程序的代码和管理它提供一个易于使用的抽象层。 这使你可以专注于编写业务逻辑,而不是担心平台本身。 -PaaS供应商通常提供他们自己的数据库服务以及其他相关服务,这可以大大简化常见的开发任务。 +PaaS 供应商通常提供他们自己的数据库服务以及其他相关服务,这可以大大简化常见的开发任务。 -PaaS的巨大优势在于,应用程序开发人员不需要执行任何系统管理工作。相反,你只需将你的代码和配置设置上传到一个中央服务器平台。 +PaaS 的巨大优势在于,应用程序开发人员不需要执行任何系统管理工作。相反,你只需将你的代码和配置设置上传到一个中央服务器平台。 然后,该服务负责部署代码,根据需要进行扩展,备份数据,处理托管和正常运行时间问题,等等。 -## 什么是Dokku? +## 什么是 Dokku? -Dokku是一个托管的平台即服务,使开发者能够轻松地部署他们的应用程序。 +Dokku 是一个托管的平台即服务,使开发者能够轻松地部署他们的应用程序。 来自他们的网站: -> 你见过的最小的PaaS实现 +> 你见过的最小的 PaaS 实现 -Dokku基于Docker,使用Heroku的构建包来编译和打包你的应用程序。 +Dokku 基于 Docker,使用 Heroku 的构建包来编译和打包你的应用程序。 -Dokku最好的一点是,它非常轻量级,可以安装在一台服务器或虚拟机上。 +Dokku 最好的一点是,它非常轻量级,可以安装在一台服务器或虚拟机上。 -它包括使用Docker容器的可扩展主机,使用Git的持续部署,以及其他流行的DevOps工具。 +它包括使用 Docker 容器的可扩展主机,使用 Git 的持续部署,以及其他流行的 DevOps 工具。 -Dokku还提供各种功能,如支持多种语言、自定义域、自动部署等。 +Dokku 还提供各种功能,如支持多种语言、自定义域、自动部署等。 -你可以轻松地将Postgres数据库甚至是文件存储连接到你的应用程序。 +你可以轻松地将 Postgres 数据库甚至是文件存储连接到你的应用程序。 你可以在 [https://dokku.com/](https://dokku.com/) 查看更多信息,或在文件中查看。[https://dokku.com/docs/getting-started/installation/](https://dokku.com/docs/getting-started/installation/)。 -你也可以对 [这里的GitHub开源项目](https://github.com/dokku/dokku) 点个赞。 +你也可以对 [这里的 GitHub 开源项目](https://github.com/dokku/dokku) 点个赞。 -## 如何安装Dokku +## 如何安装 Dokku -为了安装Dokku,你将需要一个Linux VPS和一个域名。 +为了安装 Dokku,你将需要一个 Linux VPS 和一个域名。 -你可以在没有域名的情况下安装和使用Dokku,但使用域名就简单多了。我推荐使用云VPS,因为它使访问和配置更容易。 +你可以在没有域名的情况下安装和使用 Dokku,但使用域名就简单多了。我推荐使用云 VPS,因为它使访问和配置更容易。 -连接域名时,可以将单个域名或通配符与服务器的IP联系起来。 +连接域名时,可以将单个域名或通配符与服务器的 IP 联系起来。 -我将使用托管在 [Hetzner](https://hetzner.cloud/) 的VPS,安装了Ubuntu 20.04。 +我将使用托管在 [Hetzner](https://hetzner.cloud/) 的 VPS,安装了 Ubuntu 20.04。 我们首先要确保我们的系统是最新的,有这些命令: @@ -72,7 +72,7 @@ $ sudo apt update $ sudo apt upgrade -y ``` -然后我们可以下载并运行Dokku的安装脚本: +然后我们可以下载并运行 Dokku 的安装脚本: ```bash # Install Dokku with the install script @@ -96,11 +96,11 @@ $ sudo DOKKU_TAG=v0.26.8 bash bootstrap.sh echo 'CONTENTS_OF_ID_RSA_PUB_FILE' | dokku ssh-keys:add admin ``` -安装脚本将安装Docker和所有必要的依赖项,同时也安装Dokku本身,如上面的代码所示。 +安装脚本将安装 Docker 和所有必要的依赖项,同时也安装 Dokku 本身,如上面的代码所示。 -安装完成后,我们需要分配SSH密钥来访问,同时配置我们的域名。 +安装完成后,我们需要分配 SSH 密钥来访问,同时配置我们的域名。 -如果你已经设置了用SSH访问你的VPS(你应该这样做),那么你已经有了必要的密钥--你只需要把它们添加到Dokku中: +如果你已经设置了用 SSH 访问你的 VPS(你应该这样做),那么你已经有了必要的密钥--你只需要把它们添加到 Dokku 中: ```bash # Assign SSH key to Dokku @@ -109,7 +109,7 @@ $ cat ~/.ssh/authorized_keys | dokku ssh-keys:add admin SHA256:6O1TLVOUkWV+zmTWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ``` -如果你在服务器中还没有SSH密钥,那么你需要生成一个密钥对: +如果你在服务器中还没有 SSH 密钥,那么你需要生成一个密钥对: ```bash # Generate SSH key @@ -131,7 +131,7 @@ The key's randomart image is: +----[SHA256]-----+ ``` -然后你可以把它添加到Dokku: +然后你可以把它添加到 Dokku: ```bash # Add SSH key to Dokku @@ -140,7 +140,7 @@ $ dokku ssh-keys:add admin /root/.ssh/id_rsa.pub SHA256:7T6BbRCVWjGtcSUXXXXXXXXXXXXXXXXXXXXXXXX ``` -下一步,也是最后一步,是为你的Dokku安装分配域名。我们用以下命令来做这件事: +下一步,也是最后一步,是为你的 Dokku 安装分配域名。我们用以下命令来做这件事: ```bash # Set installation global domain @@ -149,31 +149,31 @@ $ dokku domains:set-global domain.com -----> Set domain.com ``` -确保你用你自己的域名替换'domain.com',并且你的域名DNS指向服务器的IP地址。 +确保你用你自己的域名替换'domain.com',并且你的域名 DNS 指向服务器的 IP 地址。 -这就是你安装和设置Dokku所需要做的一切。真的就这么简单。 +这就是你安装和设置 Dokku 所需要做的一切。真的就这么简单。 你现在可以开始添加你的应用程序了。 -让我们在下一节中通过添加一个标准的Django应用程序来看看这个例子。 +让我们在下一节中通过添加一个标准的 Django 应用程序来看看这个例子。 -## 如何在Dokku中创建你的应用程序 +## 如何在 Dokku 中创建你的应用程序 -为了创建和部署我们的第一个应用程序,我们需要在Dokku上做一些准备工作。 +为了创建和部署我们的第一个应用程序,我们需要在 Dokku 上做一些准备工作。 -要在Dokku上部署一个应用程序,请遵循以下步骤: +要在 Dokku 上部署一个应用程序,请遵循以下步骤: -- 在Dokku上创建应用程序,这意味着给它一个名字。 -- 创建关联数据库(或其他插件,如果需要)。这将创建并提供一个数据库,以便使用自动添加到应用程序的DATABASE\_URL,以方便部署。 -- 推送必要的代码到Dokku的应用程序内部GitHub端点。这也可以包括必要的发布步骤(比如说,运行Django迁移 +- 在 Dokku 上创建应用程序,这意味着给它一个名字。 +- 创建关联数据库(或其他插件,如果需要)。这将创建并提供一个数据库,以便使用自动添加到应用程序的 DATABASE_URL,以方便部署。 +- 推送必要的代码到 Dokku 的应用程序内部 GitHub 端点。这也可以包括必要的发布步骤(比如说,运行 Django 迁移 -代码推送后,Dokku将生成任何必要的Docker容器,并将运行我们的应用程序与任何相关的数据库(或其他插件)。 +代码推送后,Dokku 将生成任何必要的 Docker 容器,并将运行我们的应用程序与任何相关的数据库(或其他插件)。 现在我们已经涵盖了必要的步骤,让我们在实践中进行。 -让我们从创建我们的应用程序开始。在本教程中,我将创建一个非常简单的Django网站,其中包含我们测试Dokku的所有必要逻辑。 +让我们从创建我们的应用程序开始。在本教程中,我将创建一个非常简单的 Django 网站,其中包含我们测试 Dokku 的所有必要逻辑。 -我们用这个命令在Dokku上创建一个应用程序(在我们安装Dokku的服务器上): +我们用这个命令在 Dokku 上创建一个应用程序(在我们安装 Dokku 的服务器上): ```bash # Creating our application on Dokku @@ -186,7 +186,7 @@ $ dokku apps:create djangotutorial 数据存储由一系列的插件来处理。你可以 [在这里查看所有可用的插件](https://dokku.com/docs/community/plugins/#official-plugins-beta)。 -对于我们的应用程序,我们将创建一个Postgres数据存储。由于默认情况下没有安装插件,我们首先需要安装Postgres插件: +对于我们的应用程序,我们将创建一个 Postgres 数据存储。由于默认情况下没有安装插件,我们首先需要安装 Postgres 插件: ```bash # install the postgres plugin @@ -194,7 +194,7 @@ $ dokku apps:create djangotutorial sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git ``` -然后我们可以创建我们的Postgres数据存储: +然后我们可以创建我们的 Postgres 数据存储: ```bash # Create a Postgres datastore @@ -218,7 +218,7 @@ $ dokku postgres:create djangotutorial_datastore Version: postgres:14.1 ``` -我们可以检查我们的数据存储的Docker容器是否已经启动并运行,方法是: +我们可以检查我们的数据存储的 Docker 容器是否已经启动并运行,方法是: ```bash # Check running containers @@ -240,7 +240,7 @@ $ dokku postgres:link djangotutorial_datastore djangotutorial ! App image (dokku/djangotutorial:latest) not found ``` -你可以看到,一个DATABASE\_URL被自动创建并与应用程序相关联。 +你可以看到,一个 DATABASE_URL 被自动创建并与应用程序相关联。 上面的例子提到,我们的应用程序图像没有被发现,因为我们还没有向它推送任何代码。 @@ -254,29 +254,29 @@ $ dokku config:show djangotutorial DATABASE_URL: postgres://postgres:ea706cc108c805d5124d134d934024c5@dokku-postgres-djangotutorial-datastore:5432/djangotutorial_datastore ``` -我们现在已经在Dokku方面完成了所有必要的配置,以支持我们应用程序的部署。 +我们现在已经在 Dokku 方面完成了所有必要的配置,以支持我们应用程序的部署。 -接下来,我们将为我们的应用程序创建代码,并将其部署到Dokku,以实现自动化的CI/CD管道。 +接下来,我们将为我们的应用程序创建代码,并将其部署到 Dokku,以实现自动化的 CI/CD 管道。 -## 如何在PyCharm上创建我们的应用代码 +## 如何在 PyCharm 上创建我们的应用代码 -在我们部署一个应用程序之前,我们需要有它的源代码来推送到Dokku。 +在我们部署一个应用程序之前,我们需要有它的源代码来推送到 Dokku。 -在本教程中,我们将创建一个非常简单的 Django 应用程序,同时展示Postgres数据库的使用。 +在本教程中,我们将创建一个非常简单的 Django 应用程序,同时展示 Postgres 数据库的使用。 我们将使用 PyCharm 作为我们的 IDE 来创建和管理我们的项目。 -我们在PyCharm中创建一个新的项目--让我们称之为 `DjangoTutorial`: +我们在 PyCharm 中创建一个新的项目--让我们称之为 `DjangoTutorial`: ![PyCharm-NewProject](https://www.freecodecamp.org/news/content/images/2022/02/PyCharm-NewProject.png) -在PyCharm上创建一个新项目--作者截图 +在 PyCharm 上创建一个新项目--作者截图 我个人更喜欢在已经有了虚拟环境的情况下创建新项目,这让生活变得更加简单。 -如果你创建的项目有一个默认的main.py文件(就像我一样,因为我一直忘了去掉复选标记),你现在可以安全地删除它。我们不打算使用它了。 +如果你创建的项目有一个默认的 main.py 文件(就像我一样,因为我一直忘了去掉复选标记),你现在可以安全地删除它。我们不打算使用它了。 -第一步当然是安装Django,以便我们能够构建我们的应用程序。我们使用pip进行安装: +第一步当然是安装 Django,以便我们能够构建我们的应用程序。我们使用 pip 进行安装: ```bash $ pip install django @@ -294,7 +294,7 @@ Installing collected packages: tzdata, sqlparse, asgiref, django Successfully installed asgiref-3.5.0 django-4.0.2 sqlparse-0.4.2 tzdata-2021.5 ``` -然后,我们用以下命令创建我们的Django项目: +然后,我们用以下命令创建我们的 Django 项目: ```bash django-admin startproject DjangoTutorial . @@ -302,13 +302,13 @@ django-admin startproject DjangoTutorial . 注意命令末尾的'.'。我喜欢用它,这样它就会在当前目录下创建项目,而不是创建一个额外的子目录。 -现在你在PyCharm中应该有一个这样的项目结构: +现在你在 PyCharm 中应该有一个这样的项目结构: ![PyCharm-Project](https://www.freecodecamp.org/news/content/images/2022/02/PyCharm-Project.png) -我们的Django应用程序的PyCharm文件夹结构--作者的屏幕截图 +我们的 Django 应用程序的 PyCharm 文件夹结构--作者的屏幕截图 -我们可以用标准的Django运行方式来运行我们的项目: +我们可以用标准的 Django 运行方式来运行我们的项目: ```bash $ python manage.py runserver @@ -326,23 +326,23 @@ Starting development server at http://127.0.0.1:8000/ Quit the server with CTRL-BREAK. ``` -> 我们还没有应用我们的迁移,所以我们将在讨论了本地和Dokku访问的数据库配置之后再做这个。 +> 我们还没有应用我们的迁移,所以我们将在讨论了本地和 Dokku 访问的数据库配置之后再做这个。 导航到链接 [http://127.0.0.1:8000/](http://127.0.0.1:8000/),我们现在可以访问我们标准的 Django 欢迎页面。 ![Django](https://www.freecodecamp.org/news/content/images/2022/02/Django.png) -Django欢迎页--作者的屏幕截图 +Django 欢迎页--作者的屏幕截图 我们已经完成了 Django 的安装并开始运行,所以现在我们可以开始构建项目的其他部分。 -像大多数项目一样,我们将需要在数据库(或使用Dokku命名的数据库)中存储数据。 +像大多数项目一样,我们将需要在数据库(或使用 Dokku 命名的数据库)中存储数据。 -我们还希望能够在开发机器上本地调试和运行我们的应用程序(使用本地数据库,在这里是SQLite),并使用 Dokku 的 Postgres 数据库在云上运行它。 +我们还希望能够在开发机器上本地调试和运行我们的应用程序(使用本地数据库,在这里是 SQLite),并使用 Dokku 的 Postgres 数据库在云上运行它。 这意味着我们需要改变 settings.py 中的一些配置,以便能够支持两种使用情况,而不需要我们每次都改变任何标志或配置。 -我们首先安装dj-database-url软件包: +我们首先安装 dj-database-url 软件包: ```bash # Install packages for the database url @@ -354,9 +354,9 @@ $ pip install psycopg2 $ pip install python-decouple ``` -这个包使我们能够拥有一个Django数据库连接字典,通过简单地指定一个数据库URL来填充所有的数据。 +这个包使我们能够拥有一个 Django 数据库连接字典,通过简单地指定一个数据库 URL 来填充所有的数据。 -安装好软件包后,让我们更新settings.py中的配置: +安装好软件包后,让我们更新 settings.py 中的配置: ```Python # We need to add this import at the beginning to use environment variables @@ -418,7 +418,7 @@ class Counter(models.Model): counter/models.py -现在我们可以添加一个新的URL来加载我们的计数器页面。我们通过在'counter'文件夹中添加一个名为'urls.py'的新文件来做到这一点: +现在我们可以添加一个新的 URL 来加载我们的计数器页面。我们通过在'counter'文件夹中添加一个名为'urls.py'的新文件来做到这一点: ```Python from django.urls import path @@ -431,7 +431,7 @@ urlpatterns = [ counter/urls.py -我们现在有了模型和URL来加载我们的测试页面。现在我们需要的是视图和HTML模板来渲染页面。 +我们现在有了模型和 URL 来加载我们的测试页面。现在我们需要的是视图和 HTML 模板来渲染页面。 让我们通过编辑'views.py'文件来创建该视图(view): @@ -456,7 +456,7 @@ def counter(request): counter/views.py -现在我们可以创建我们的HTML模板,在页面上显示计数器的值。我们在一个新的 "templates "文件夹中创建一个名为 "counter.html "的新文件: +现在我们可以创建我们的 HTML 模板,在页面上显示计数器的值。我们在一个新的 "templates "文件夹中创建一个名为 "counter.html "的新文件: ```HTML @@ -477,7 +477,7 @@ counter/views.py counter/templates/counter.html -最后一步是将我们新创建的应用程序添加到'settings.py'文件中,以便Django能够识别它: +最后一步是将我们新创建的应用程序添加到'settings.py'文件中,以便 Django 能够识别它: ```python ..... @@ -497,7 +497,7 @@ INSTALLED_APPS = [ settings.py -而我们的主URLs文件的URL: +而我们的主 URLs 文件的 URL: ```Python from django.contrib import admin @@ -511,7 +511,7 @@ urlpatterns = [ urls.py -有了所有必要的代码和HTML,我们现在可以创建并运行我们的迁移,在数据库中创建我们的新模型。我们首先在本地服务器上通过运行: +有了所有必要的代码和 HTML,我们现在可以创建并运行我们的迁移,在数据库中创建我们的新模型。我们首先在本地服务器上通过运行: ```bash # Create and run migrations @@ -533,27 +533,27 @@ Running migrations: Applying sessions.0001_initial... OK ``` -正如你所看到的,我们不仅为我们的新应用程序应用了迁移(migrations),而且还为其他Django应用程序运行了初始迁移,因为这是我们第一次运行迁移(migrations)。 +正如你所看到的,我们不仅为我们的新应用程序应用了迁移(migrations),而且还为其他 Django 应用程序运行了初始迁移,因为这是我们第一次运行迁移(migrations)。 -我们可以再次在本地运行我们的服务器,我们应该能够访问URL [http://127.0.0.1:8000/counter/](http://127.0.0.1:8000/counter/) 并增加计数器: +我们可以再次在本地运行我们的服务器,我们应该能够访问 URL [http://127.0.0.1:8000/counter/](http://127.0.0.1:8000/counter/) 并增加计数器: ![CounterPage_Local](https://www.freecodecamp.org/news/content/images/2022/02/CounterPage_Local.gif) -运行我们的计数器应用程序 - 作者的GIF图 +运行我们的计数器应用程序 - 作者的 GIF 图 正如你所看到的,重新加载页面可以保持我们的计数器值,这意味着该值已经和我们的模型一起存储在数据库中。 -## 如何将我们的应用程序部署到Dokku上 +## 如何将我们的应用程序部署到 Dokku 上 现在我们有一个非常简单的应用程序正在运行,并与数据库集成以存储我们的计数器值。 我们准备把它部署到云端,这样我们就可以在那里进行测试,并确保我们的数据库也能在云端工作。 -在我们用Git推送将代码部署到Dokku之前,我们需要做一些准备。 +在我们用 Git 推送将代码部署到 Dokku 之前,我们需要做一些准备。 - 安装我们的网络服务器(gunicorn) - 创建我们的需求文件(为我们的软件包)。 -- 创建我们的Procfile(用于我们的部署命令) +- 创建我们的 Procfile(用于我们的部署命令) 让我们从安装我们的网络服务器开始,以便在云中使用: @@ -580,12 +580,12 @@ release: python manage.py migrate Procfile -我们创建了两个命令供Dokku运行: +我们创建了两个命令供 Dokku 运行: -- release - 这个命令是在我们的应用程序在Dokku的部署上执行的。我们用它来迁移我们的数据库。 -- web - 这个命令允许Dokku知道运行哪个webserver以允许访问该应用程序。 +- release - 这个命令是在我们的应用程序在 Dokku 的部署上执行的。我们用它来迁移我们的数据库。 +- web - 这个命令允许 Dokku 知道运行哪个 webserver 以允许访问该应用程序。 -最后,为了确保我们的代码被部署到Dokku时能收集到任何静态文件,我们需要在根目录上创建一个名为'static'的新目录。在里面我们创建一个名为'.gitkeep'的空文件(这将使我们以后能够将该目录添加到Git仓库中)。 +最后,为了确保我们的代码被部署到 Dokku 时能收集到任何静态文件,我们需要在根目录上创建一个名为'static'的新目录。在里面我们创建一个名为'.gitkeep'的空文件(这将使我们以后能够将该目录添加到 Git 仓库中)。 我们还需要在'settings.py'文件中为静态文件添加这个路径: @@ -599,15 +599,15 @@ STATIC_ROOT = BASE_DIR / "static" settings.py -现在所有的文件和逻辑都到位了,我们可以用标准的Git推送方式部署到Dokku。让我们检查一下我们当前的文件结构: +现在所有的文件和逻辑都到位了,我们可以用标准的 Git 推送方式部署到 Dokku。让我们检查一下我们当前的文件结构: ![PyCharm-FolderStrcuture-1](https://www.freecodecamp.org/news/content/images/2022/02/PyCharm-FolderStrcuture-1.png) -PyCharm文件夹结构--作者的截图 +PyCharm 文件夹结构--作者的截图 -为了能够将我们的代码推送到Dokku,我们需要将我们的项目添加到一个Git仓库。 +为了能够将我们的代码推送到 Dokku,我们需要将我们的项目添加到一个 Git 仓库。 -由于我们不想把文件夹结构中的所有文件都推送到Dokku的git仓库,所以我们创建一个".gitignore "来排除某些文件和目录。我使用这个优秀的Gist的内容来填充这个文件: +由于我们不想把文件夹结构中的所有文件都推送到 Dokku 的 git 仓库,所以我们创建一个".gitignore "来排除某些文件和目录。我使用这个优秀的 Gist 的内容来填充这个文件: [python pycharm gitignore @@ -638,7 +638,7 @@ $ git add . && git commit -m "initial commit" create mode 100644 requirements.txt ``` -在提交了我们的仓库后,我们现在可以把它推送到远程仓库,也就是我们应用程序的Dokku Git仓库: +在提交了我们的仓库后,我们现在可以把它推送到远程仓库,也就是我们应用程序的 Dokku Git 仓库: ```bash # Adding our remote repository (replace domain.com with your domain name) @@ -753,19 +753,19 @@ To domain.com:djangotutorial * [new branch] main -> main ``` -我们刚刚将我们的应用程序部署到Dokku。 +我们刚刚将我们的应用程序部署到 Dokku。 -刚刚发生了什么?嗯,Dokku为我们做了很多工作: +刚刚发生了什么?嗯,Dokku 为我们做了很多工作: - 安装 Python -- 根据 requirements 文件安装Python的包 +- 根据 requirements 文件安装 Python 的包 - 收集静态文件 -- 执行migrations -- 最后启动gunicorn服务器来部署我们的应用程序 +- 执行 migrations +- 最后启动 gunicorn 服务器来部署我们的应用程序 如果你有一个权限错误,那么你的私钥应该在你的本地开发环境中注册。如果你在推送时得到一个 "拒绝许可 "的错误,你可以按以下方式注册你的私钥。`ssh-add -k ~/`。 -你也可能在访问应用程序时看到关于 ALLOWED\_HOSTS 的错误。在这种情况下,你需要做的就是在Dokku服务器上运行以下命令,将环境变量设置为正确值: +你也可能在访问应用程序时看到关于 ALLOWED_HOSTS 的错误。在这种情况下,你需要做的就是在 Dokku 服务器上运行以下命令,将环境变量设置为正确值: ```shell # Set ALLOWED_HOSTS environment variable (make sure to use your domain name) @@ -776,15 +776,15 @@ $ dokku config:set djangotutorial ALLOWED_HOSTS=djangotutorial.domain.com ![PageCounter_Server](https://www.freecodecamp.org/news/content/images/2022/02/PageCounter_Server.gif) -在Dokku上运行我们的计数器应用程序 - 作者的GIF +在 Dokku 上运行我们的计数器应用程序 - 作者的 GIF -恭喜你,你刚刚在Dokku上部署了你的应用程序。 +恭喜你,你刚刚在 Dokku 上部署了你的应用程序。 -## 如何使用Let's Encrypt 加上 SSL +## 如何使用 Let's Encrypt 加上 SSL -我们可以做的最后一项配置是通过安装Let's Encrypt SSL证书为我们的应用程序添加SSL安全。 +我们可以做的最后一项配置是通过安装 Let's Encrypt SSL 证书为我们的应用程序添加 SSL 安全。 -我们可以通过Let's Encrypt插件在Dokku上非常容易地做到这一点: +我们可以通过 Let's Encrypt 插件在 Dokku 上非常容易地做到这一点: ```bash # Install the Let's Encrypt plugin @@ -807,32 +807,32 @@ dokku letsencrypt:cron-job --add ## 结语 -使用PaaS使开发者在构建网络应用时更容易。 +使用 PaaS 使开发者在构建网络应用时更容易。 -你可以使用像Heroku这样的托管PaaS,还有很多其他的,所以选择是有的。 +你可以使用像 Heroku 这样的托管 PaaS,还有很多其他的,所以选择是有的。 但也有一些主要的缺点: - 价格 - 托管解决方案可能在数据库存储或文件存储等方面有限制 -- 你不能控制部署PaaS的主机。最近AWS的例子表明,即使是最大的云服务商也不是没有问题的。 +- 你不能控制部署 PaaS 的主机。最近 AWS 的例子表明,即使是最大的云服务商也不是没有问题的。 -你可以通过自我托管你的PaaS来解决这些问题。 +你可以通过自我托管你的 PaaS 来解决这些问题。 -这允许在定价方面有更多的控制。你可以使用像 [Digital Ocean](https://www.digitalocean.com/)、[Hetzner](https://hetzner.cloud/) 和其他有相当便宜的VPS的主机供应商,它们可以完美地与 Dokku 一起工作。 +这允许在定价方面有更多的控制。你可以使用像 [Digital Ocean](https://www.digitalocean.com/)、[Hetzner](https://hetzner.cloud/) 和其他有相当便宜的 VPS 的主机供应商,它们可以完美地与 Dokku 一起工作。 -没有数据库限制。你可能有的唯一限制是内存和磁盘空间,但你可以随时升级你的VPS,价格比在Heroku获得一个新的数据库要低。 +没有数据库限制。你可能有的唯一限制是内存和磁盘空间,但你可以随时升级你的 VPS,价格比在 Heroku 获得一个新的数据库要低。 -Dokku很容易安装,就像我们看到的那样。创建和部署一个应用程序是一个3步过程: +Dokku 很容易安装,就像我们看到的那样。创建和部署一个应用程序是一个 3 步过程: -- 在Dokku上创建一个应用程序 -- 在Dokku上创建一个数据存储(如果需要,如Postgres)并链接到应用程序 -- 用Git将你的代码部署到Dokku上 +- 在 Dokku 上创建一个应用程序 +- 在 Dokku 上创建一个数据存储(如果需要,如 Postgres)并链接到应用程序 +- 用 Git 将你的代码部署到 Dokku 上 -此外,你可能需要配置一些环境变量和SSL证书,但这就是全部。 +此外,你可能需要配置一些环境变量和 SSL 证书,但这就是全部。 -Dokku确实是最小的PaaS实现。 +Dokku 确实是最小的 PaaS 实现。 -Django应用程序的完整源代码可在以下网站获得: +Django 应用程序的完整源代码可在以下网站获得: [ diff --git a/chinese/articles/how-to-center-a-div-with-css-10-different-ways.md b/chinese/articles/how-to-center-a-div-with-css-10-different-ways.md index b18e5fdff..2a22c1c82 100644 --- a/chinese/articles/how-to-center-a-div-with-css-10-different-ways.md +++ b/chinese/articles/how-to-center-a-div-with-css-10-different-ways.md @@ -5,19 +5,19 @@ ![How to Center a Div with CSS – 10 Different Ways](https://www.freecodecamp.org/news/content/images/size/w2000/2022/07/Group-49.png) -对一个开发者来说,将**一个div居中**可能是世界上最困难的工作。 +对一个开发者来说,将**一个 div 居中**可能是世界上最困难的工作。 -读完这篇文章,你就会觉得没有那么难了。这篇文章将讲解10种居中`div`的方式。我们将从CSS的 **position** 属性、**Flexbox**和**Grid**三个方面来探索如何实现居中。 +读完这篇文章,你就会觉得没有那么难了。这篇文章将讲解 10 种居中`div`的方式。我们将从 CSS 的 **position** 属性、**Flexbox**和**Grid**三个方面来探索如何实现居中。 我相信通读完整篇文章之后,你将成为居中`divs`的专家。 ## 如何居中一个`Div` -我将使用同样的HTML来讲解10种方法。这个HTML包含一个父`div`和一个子`div`元素。 +我将使用同样的 HTML 来讲解 10 种方法。这个 HTML 包含一个父`div`和一个子`div`元素。 -本文的目的是让内部`div`实现相对于父元素的居中。仅通过对CSS修改,来呈现10种不同的方法。 +本文的目的是让内部`div`实现相对于父元素的居中。仅通过对 CSS 修改,来呈现 10 种不同的方法。 -HTML文件如下: +HTML 文件如下: ```HTML @@ -65,7 +65,7 @@ HTML文件如下: ![Screenshot-2022-05-27-at-15.02.59](https://www.freecodecamp.org/news/content/images/2022/06/Screenshot-2022-05-27-at-15.02.59.png) -基本的HTML和CSS样式结果 +基本的 HTML 和 CSS 样式结果 我们创建了一个父元素`div`,并且将其`width`和`height`设置为`400px`,`color`设置为`#f55353`。 @@ -75,9 +75,9 @@ HTML文件如下: ![Group-23](https://www.freecodecamp.org/news/content/images/2022/06/Group-23.png) -## 如何使用CSS的`position`属性实现div的居中 +## 如何使用 CSS 的`position`属性实现 div 的居中 -### 1\. 如何运用position: relative、 absolute以及top、left偏移值 +### 1. 如何运用 position: relative、 absolute 以及 top、left 偏移值 ```CSS #parentContainer { @@ -91,19 +91,19 @@ HTML文件如下: } ``` -CSS中的**position**属性是设置元素在页面的定位方式。position属性的默认值为`static`,其他值包括:`relative`、 `absolute`、 `fixed`和`sticky`。 +CSS 中的**position**属性是设置元素在页面的定位方式。position 属性的默认值为`static`,其他值包括:`relative`、 `absolute`、 `fixed`和`sticky`。 -如果将一个DOM元素设置为`position: absolute`,该元素 **相对于整个页面的位置就是绝对的**。如果我们想要一个`div`相对于整个页面居中的话,可以采用这个方法。 +如果将一个 DOM 元素设置为`position: absolute`,该元素 **相对于整个页面的位置就是绝对的**。如果我们想要一个`div`相对于整个页面居中的话,可以采用这个方法。 此外,将父元素设置为 `position: relative`,同时将子元素的位置设置为(通过 `position: absolute`)**绝对,这时的绝对是相对于父元素的,而不是整个页面**。 上述代码例子就是采用这样的方法。我们给父元素添加 `position: relative`,子元素添加`position: absolute`。 -除了使用position属性,我们还可以使用`top`、`right`、`bottom`和`left`四个属性来定义元素的位置,这样决定元素最终的位置(定位)。 +除了使用 position 属性,我们还可以使用`top`、`right`、`bottom`和`left`四个属性来定义元素的位置,这样决定元素最终的位置(定位)。 `top`和`bottom`指定元素**垂直方向的定位**,`left`和`right`指定元素 **水平方向的定位**。 -### 2\. 如何使用position: relative和absolute, top、left、right和bottom偏移值以及margin: auto +### 2. 如何使用 position: relative 和 absolute, top、left、right 和 bottom 偏移值以及 margin: auto ```CSS #parentContainer { @@ -119,7 +119,7 @@ CSS中的**position**属性是设置元素在页面的定位方式。position属 } ``` -除了我们从第一点学习到的知识点外,在这里我们使用了CSS中的`margin`属性, `margin: auto`允许浏览器给子元素选择**合适的外边距** 。 +除了我们从第一点学习到的知识点外,在这里我们使用了 CSS 中的`margin`属性, `margin: auto`允许浏览器给子元素选择**合适的外边距** 。 通常子元素占据了指定的宽度后,浏览器会 **均匀地分配剩下的空间**,剩下的空间包括左右外边距、上下外边距和上下左右外边距三种情况。 @@ -127,28 +127,28 @@ CSS中的**position**属性是设置元素在页面的定位方式。position属 同样,如果我们只设置了`left: 0`、`right: 0`以及`margin: auto`,子元素就会**水平居中**。 -如果我们像代码示例这样,声明了所有属性,就会得到一个完美的**垂直且水平居中的div**。 +如果我们像代码示例这样,声明了所有属性,就会得到一个完美的**垂直且水平居中的 div**。 -## 如何使用CSS中Flexbox来居中Div +## 如何使用 CSS 中 Flexbox 来居中 Div -### 3\. 如何使用Flexbox、 justify-content和align-item +### 3. 如何使用 Flexbox、 justify-content 和 align-item 上述的两种方式是使用经典的办法实现页面元素居中。现代方法更多使用**Flexbox** (一维布局模型) 和**Grid**布局(更为复杂的二维布局模型) 属性。 -让我们来看看Flexbox方法: +让我们来看看 Flexbox 方法: -Flexbox不仅仅是个单一的属性,而是一个由一组属性组成的模块。其中一些属性用于**容器**(即父容器),一些用于其中的**子元素**。 +Flexbox 不仅仅是个单一的属性,而是一个由一组属性组成的模块。其中一些属性用于**容器**(即父容器),一些用于其中的**子元素**。 -下图显示Flexbox相关的父元素和子元素的属性列表: +下图显示 Flexbox 相关的父元素和子元素的属性列表: ![Group-42](https://www.freecodecamp.org/news/content/images/2022/07/Group-42.png) 通过本文来讲解所有的属性不太现实,所以我们仅讲解我们会用到的一些属性。 -如上所述,Flexbox模型中有两个不同的实体:父容器和子元素。 +如上所述,Flexbox 模型中有两个不同的实体:父容器和子元素。 -`display: flex`属性将容器定义为一个flex容器。`flex-direction`是另一个容器属性,包含四个值:`row` (默认值)、`row-reverse`、 `column`和`column-reverse`。 +`display: flex`属性将容器定义为一个 flex 容器。`flex-direction`是另一个容器属性,包含四个值:`row` (默认值)、`row-reverse`、 `column`和`column-reverse`。 -使用flexbox的时候,我们要思考两个轴, **主轴**和**交叉轴**。 +使用 flexbox 的时候,我们要思考两个轴, **主轴**和**交叉轴**。 当`flex-direction`的值为`row`或`row-reverse`时,**水平轴是主轴,垂直轴是交叉轴**。 @@ -174,11 +174,11 @@ Flexbox不仅仅是个单一的属性,而是一个由一组属性组成的模 } ``` -### 4\. 如何使用Flexbox、justify-content和align-self +### 4. 如何使用 Flexbox、justify-content 和 align-self 这个方法和上述方法类似,是上述方面的替换方案。 -取代`align-items`属性(父容器的属性),该属性是沿着交叉轴对齐**所以子元素**,我们使用`align-self` (子元素属性)设置沿着交叉轴的**单个flex元素**的对齐方式。 +取代`align-items`属性(父容器的属性),该属性是沿着交叉轴对齐**所以子元素**,我们使用`align-self` (子元素属性)设置沿着交叉轴的**单个 flex 元素**的对齐方式。 ```CSS #parentContainer { @@ -190,9 +190,9 @@ Flexbox不仅仅是个单一的属性,而是一个由一组属性组成的模 } ``` -### 5\. 如何使用Flexbox和margin: auto +### 5. 如何使用 Flexbox 和 margin: auto -Flexbox给予我们充分的能力来对齐元素和分配空间。如上文所述,`margin: auto`也可以使浏览器给子元素分配合适的外边距。 +Flexbox 给予我们充分的能力来对齐元素和分配空间。如上文所述,`margin: auto`也可以使浏览器给子元素分配合适的外边距。 在大多数情况下,它允许子元素采用其指定的宽度,并且浏览器在左右边距对或上下边距对或上下左右边距平均分配剩余空间。 @@ -207,29 +207,29 @@ Flexbox给予我们充分的能力来对齐元素和分配空间。如上文所 } ``` -## 如何使用CSS Grid居中Div +## 如何使用 CSS Grid 居中 Div -### 6\. 如何使用Grid、justify-content和align-items +### 6. 如何使用 Grid、justify-content 和 align-items -CSS Grid或者Grid使用的是**二维**布局模型,而Flexbox使用的是**一维**模型。 +CSS Grid 或者 Grid 使用的是**二维**布局模型,而 Flexbox 使用的是**一维**模型。 -与Flexbox类似,也有grid容器(父容器)和grid元素(子元素)这对概念。 +与 Flexbox 类似,也有 grid 容器(父容器)和 grid 元素(子元素)这对概念。 -下图列出了可用于父容器和子元素的所有属性。由于CSS Grid本身就是一个巨大的话题,因此本文不讨论每个属性。让我们讨论一下在本文中使用的属性。 +下图列出了可用于父容器和子元素的所有属性。由于 CSS Grid 本身就是一个巨大的话题,因此本文不讨论每个属性。让我们讨论一下在本文中使用的属性。 ![Group-45](https://www.freecodecamp.org/news/content/images/2022/07/Group-45.png) -`display: grid`将元素设置为一个grid容器。 +`display: grid`将元素设置为一个 grid 容器。 -`justify-items`和`align-items`在grid内对齐元素,分配沿着内联(横)轴和块(纵)轴。 +`justify-items`和`align-items`在 grid 内对齐元素,分配沿着内联(横)轴和块(纵)轴。 -另外,如果gird的总大小小于grid容器的话(当将所有grid元素都设置为固定元素单位如px时有可能发生),我们就可以在grid容器中使用 `justify-content`和`align-content`来控制内部元素的对齐方式。 +另外,如果 gird 的总大小小于 grid 容器的话(当将所有 grid 元素都设置为固定元素单位如 px 时有可能发生),我们就可以在 grid 容器中使用 `justify-content`和`align-content`来控制内部元素的对齐方式。 -`justify-content`和`align-content`对齐grid,分配沿着内联(横)轴和块(纵)轴。 +`justify-content`和`align-content`对齐 grid,分配沿着内联(横)轴和块(纵)轴。 -这里有一份完整的grid属性介绍: [Grid完整手册](https://css-tricks.com/snippets/css/complete-guide-grid/) +这里有一份完整的 grid 属性介绍: [Grid 完整手册](https://css-tricks.com/snippets/css/complete-guide-grid/) -由于在我们的示例中只有一个**grid单元格**,并且内部只有一个元素。所以使用`justify-content`和`justify-items`,或者 `align-content`和`align-items`得到相同的结果。 +由于在我们的示例中只有一个**grid 单元格**,并且内部只有一个元素。所以使用`justify-content`和`justify-items`,或者 `align-content`和`align-items`得到相同的结果。 ```CSS #parentContainer { @@ -239,7 +239,7 @@ CSS Grid或者Grid使用的是**二维**布局模型,而Flexbox使用的是** } ``` -### 7\. 如何使用Grid和place-items +### 7. 如何使用 Grid 和 place-items 可以使用`place-items`来在一次声明中设置`align-items`和`justify-items`。同样的,可以使用`place-content`在一次声明中设置`justify-content`和`align-content`。 @@ -252,11 +252,11 @@ CSS Grid或者Grid使用的是**二维**布局模型,而Flexbox使用的是** } ``` -### 8\. 如何使用Grid、align-self和justify-self +### 8. 如何使用 Grid、align-self 和 justify-self -和Flexbox一样,Grid也支持使用`align-self`和`justify-self`属性(子元素属性)来对齐单个grid元素。 +和 Flexbox 一样,Grid 也支持使用`align-self`和`justify-self`属性(子元素属性)来对齐单个 grid 元素。 -`justify-self`将元素在grid单元格内的沿着内联(横)轴对齐,`align-self`将元素在grid单元格内的沿着块(纵)轴对齐。 +`justify-self`将元素在 grid 单元格内的沿着内联(横)轴对齐,`align-self`将元素在 grid 单元格内的沿着块(纵)轴对齐。 ```CSS #parentContainer { @@ -268,7 +268,7 @@ CSS Grid或者Grid使用的是**二维**布局模型,而Flexbox使用的是** } ``` -### 9\. 如何使用Grid和place-self +### 9. 如何使用 Grid 和 place-self `place-self`属性通过一个声明设置`justify-self`和`align-self` 属性。所以,将子元素设置为`place-self: center`,就可以垂直水平居中该子元素。 @@ -281,11 +281,11 @@ CSS Grid或者Grid使用的是**二维**布局模型,而Flexbox使用的是** } ``` -### 10\. 如何使用Grid和margin: auto +### 10. 如何使用 Grid 和 margin: auto -和Flexbox类似 Grid也给予我们充分的能力来对齐元素和分配空间。 +和 Flexbox 类似 Grid 也给予我们充分的能力来对齐元素和分配空间。 -如第五种方法所示,我们可以像使用flexbox方法一样使用grid,å将子元素设置为`margin: auto`,可以得到相同的结果。 +如第五种方法所示,我们可以像使用 flexbox 方法一样使用 grid,å将子元素设置为`margin: auto`,可以得到相同的结果。 ```CSS #parentContainer { @@ -304,7 +304,7 @@ CSS Grid或者Grid使用的是**二维**布局模型,而Flexbox使用的是** ## 总结 -本文讨论了10种将div居中的方法,包括: +本文讨论了 10 种将 div 居中的方法,包括: 1. 使用**position: relative**、**absolute**和**top**、**left**偏移值 2. 使用**position**: **relative**和**absolute**、**top**、**left** **right**和**bottom**偏移值和**margin: auto** @@ -317,21 +317,21 @@ CSS Grid或者Grid使用的是**二维**布局模型,而Flexbox使用的是** 9. 使用**grid**和**place-self** 10. 使用**grid**和**margin: auto** -我们也讲解了`justify-content`、`align-items`、`position`等属性,它们是什么意思,如何搭配使用来使得div居中。 +我们也讲解了`justify-content`、`align-items`、`position`等属性,它们是什么意思,如何搭配使用来使得 div 居中。 ## 资源推荐 -1. [Flexbox完全手册](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) -2. [Grid完全手册](https://css-tricks.com/snippets/css/complete-guide-grid/) -3. [通过创建登陆页面来学习使用flexbox和grid](https://www.freecodecamp.org/news/css-flexbox-和-grid-tutorial/) +1. [Flexbox 完全手册](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) +2. [Grid 完全手册](https://css-tricks.com/snippets/css/complete-guide-grid/) +3. [通过创建登陆页面来学习使用 flexbox 和 grid](https://www.freecodecamp.org/news/css-flexbox-和-grid-tutorial/) -## GitHub链接 +## GitHub 链接 -你可以在github上找到所有示例的代码: [Github Link](https://github.com/sohamderoy/blog-setup-centring-divs) +你可以在 github 上找到所有示例的代码: [Github Link](https://github.com/sohamderoy/blog-setup-centring-divs) ## 结束语 -谢谢阅读!希望你喜欢这篇关于`div`居中的10种不同方法的文章,希望这篇文章将来对你有用。 +谢谢阅读!希望你喜欢这篇关于`div`居中的 10 种不同方法的文章,希望这篇文章将来对你有用。 可以把这篇文章分享给你的朋友——我将非常感谢。更多惊喜内容,请持续关注。再见!🖖 diff --git a/chinese/articles/how-to-center-objects-using-css.md b/chinese/articles/how-to-center-objects-using-css.md index e8a2762c9..667cbed05 100644 --- a/chinese/articles/how-to-center-objects-using-css.md +++ b/chinese/articles/how-to-center-objects-using-css.md @@ -26,9 +26,9 @@ Today I'm gonna show you how you can **center and align** your content with CSS. Methods -## You can watch this tutorial on YouTube as well if you like: +## You can watch this tutorial on YouTube as well if you like -## But.... Wait A Minute! +## But…… Wait A Minute! ![](https://www.freecodecamp.org/news/content/images/2021/06/Frame-35--3-.png) diff --git a/chinese/articles/how-to-code-on-your-phone-python-pydroid-android-app-tutorial.md b/chinese/articles/how-to-code-on-your-phone-python-pydroid-android-app-tutorial.md index 7d8907ba7..64a44dab5 100644 --- a/chinese/articles/how-to-code-on-your-phone-python-pydroid-android-app-tutorial.md +++ b/chinese/articles/how-to-code-on-your-phone-python-pydroid-android-app-tutorial.md @@ -3,7 +3,7 @@ > - 译者:ZhichengChen > - 校对者: -嘿,你好吗? 我今年18岁,是一名后端开发人员,希望成为机器学习工程师。 在本文中,我会介绍如何使用 Python😁在手机上构建 Web 应用程序。 开始吧。 +嘿,你好吗? 我今年 18 岁,是一名后端开发人员,希望成为机器学习工程师。 在本文中,我会介绍如何使用 Python😁在手机上构建 Web 应用程序。 开始吧。 ![](https://lh3.googleusercontent.com/TW_PdXBpgeWY4mLcHjFisp8e7Lk7Zsn1aFarXBkmvhEMP0XR5xzTDxhKcCizsrJ25rkPeMeWp7ctlG0Wy7_WFUS0bzT-JVJfpe6X_3OqnuE_df2q5B3KIrhl3EG47w3Dik3nIZE "Placeholder image") @@ -17,7 +17,7 @@ 如你所见,pydroid3 是一个可在手机上编写 Python 的移动应用程序,因此请继续安装它。 -接下来需要安装 Django。 如果你不熟悉Django,请查看[此处的 Django 文档](https://www.djangoproject.com/)。 +接下来需要安装 Django。 如果你不熟悉 Django,请查看[此处的 Django 文档](https://www.djangoproject.com/)。 要安装 Django,在 pydroid3 中打开侧边栏,然后选择 Terminal: @@ -125,7 +125,7 @@ def index(request): 如你所见,我们创建了在 `urls.py` 中调用的索引函数,并将一个请求参数传递给了它。 然后我们返回一个 `HttpResponse`。 -但是之前,需要用 `django.http import HttpResponse` 导入它 - 就像ABC一样简单。让我们尝试一下:打开终端并进入 myapp,然后输入 `python manage.py runserver` 进行测试。 +但是之前,需要用 `django.http import HttpResponse` 导入它 - 就像 ABC 一样简单。让我们尝试一下:打开终端并进入 myapp,然后输入 `python manage.py runserver` 进行测试。 ![](https://lh3.googleusercontent.com/Tqb7c-adOuVHbyi-7XBQsv0HHJvxjUhcAZ3N4d5nkOEVNVwfSXxkENlD0l0UI3Jd4qLhO3k8ELDW6yG8yRiP0MmjkO0Q4TvGTYunQIBNgSMNrXxfI7ygMHeN2FtjoJc37mVIVr0) @@ -158,7 +158,7 @@ def index(request): ## 如何设置静态文件 -现在要设置静态文件,在 todo 目录中创建一个新文件夹并将其命名为 static。 在该文件夹内,创建一个文件夹并将其命名为todo。 +现在要设置静态文件,在 todo 目录中创建一个新文件夹并将其命名为 static。 在该文件夹内,创建一个文件夹并将其命名为 todo。 所以应该是这样的:`/static/todo/`。 @@ -232,7 +232,7 @@ class Post(models.Model): 我们创建一个具有参数 `models.Model` 的类,并定义了一个变量 `content`,该变量包含一个 `CharField()`,更像是一个文本字段。 最后,我们创建了一个神奇的 `str`,它返回模型的名称而不是对象。 -接下来需要运行 migration。 打开终端,进入myapp,然后输入`python manage.py makemigrations`。 应该会看到以下内容: +接下来需要运行 migration。 打开终端,进入 myapp,然后输入`python manage.py makemigrations`。 应该会看到以下内容: ![](https://lh6.googleusercontent.com/UBbVNNg1d8jhPTusB-HRRoUsqFfxaZdJLzSIzNIt3P4kby8Tor4G8Bme1e-yq8mOLFgfrUh3nb6MC3BSaOUQDr68_tEmIRtQBS7N7Y66wTbXdMMg-0EJ0svM3tw3j9GLgquC_IU) diff --git a/chinese/articles/how-to-code-the-caesar-cipher-an-introduction-to-basic-encryption-3bf77b4e19f7.md b/chinese/articles/how-to-code-the-caesar-cipher-an-introduction-to-basic-encryption-3bf77b4e19f7.md index 667858323..980636afb 100644 --- a/chinese/articles/how-to-code-the-caesar-cipher-an-introduction-to-basic-encryption-3bf77b4e19f7.md +++ b/chinese/articles/how-to-code-the-caesar-cipher-an-introduction-to-basic-encryption-3bf77b4e19f7.md @@ -7,9 +7,9 @@ by Brendan Massey -恺撒密码是早期加密的一个著名实践。它会根据字母表上设定的密钥对句子进行重组加密。举个例子,密钥为3,取一个句子“I like to wear hats.”。 +恺撒密码是早期加密的一个著名实践。它会根据字母表上设定的密钥对句子进行重组加密。举个例子,密钥为 3,取一个句子“I like to wear hats.”。 -当这个句子使用密钥3加密后,它变成了: +当这个句子使用密钥 3 加密后,它变成了: L olnh wr zhdu kdwv. @@ -18,15 +18,15 @@ L olnh wr zhdu kdwv. 虽然这是一个非常简单的加密案例,但对于学习编码的人来说,它是一个完美的练习项目。 #### 理解加密 -为了实现这个代码,至少在JAVA里,你需要思考实际要做些什么。所以,让我们看看必要的编码步骤。 +为了实现这个代码,至少在 JAVA 里,你需要思考实际要做些什么。所以,让我们看看必要的编码步骤。 Step 1: 识别句子中的字符。 Step 2: 找到字符在字母表中的位置。 -Step 3: 识别字符位置+密钥key后的位置。 +Step 3: 识别字符位置+密钥 key 后的位置。 -注意\* 如果字符位置 + key > 26, 那么要从字母表的第1个字符继续循环。 +注意\* 如果字符位置 + key > 26, 那么要从字母表的第 1 个字符继续循环。 Step 4: 用新字符代替原来的字符,生成一个新句子。 @@ -52,7 +52,7 @@ Step 1: 识别句子中的字符。 为此,我们需要建立一张字母表用来查找字符。 -创建一个包含26个字母的变量“alphabet”。 +创建一个包含 26 个字母的变量“alphabet”。 ``` String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -61,7 +61,7 @@ String alphabet2 = alphabet.toLowerCase(); Step 2: 找到字符在字母表中的位置。 -创建一个for循环来遍历消息中的每个字符。创建一个StringBuilder可以更便于我们来做这件事。 +创建一个 for 循环来遍历消息中的每个字符。创建一个 StringBuilder 可以更便于我们来做这件事。 ``` StringBuilder encrypted = new StringBuilder(message); @@ -79,13 +79,13 @@ if (index != -1) { } ``` -Step 3: 识别字符位置+密钥key后的位置。 +Step 3: 识别字符位置+密钥 key 后的位置。 如果识别出字符是一个字母,那么我们要在修改后的字母表中找到它的位置。因此,我们需要建立一个修改后的字母表。 Step 4: 用新字符代替原来的字符,生成一个新句子。 -一旦我们在修改后的字母中找到了相应的值,我们应该将它设置到我们创建的StringBuilder中的相同位置。 +一旦我们在修改后的字母中找到了相应的值,我们应该将它设置到我们创建的 StringBuilder 中的相同位置。 ``` public String Encryption(String input, int key){ @@ -106,7 +106,7 @@ public String Encryption(String input, int key){ Step 5: 重复直到达到句子原来的长度。(循环) -现在,我们已经检查了字符是否为大写,但我们还需要检查字符是否为小写。为此,我们需要访问之前建立的alphabet2。 +现在,我们已经检查了字符是否为大写,但我们还需要检查字符是否为小写。为此,我们需要访问之前建立的 alphabet2。 ``` index = alphabet2.indexOf(currChar); @@ -120,7 +120,7 @@ if (index != -1) { Step 6: 返回结果 -现在,我们已经完成了For循环。剩下的就是退出循环并返回String。 +现在,我们已经完成了 For 循环。剩下的就是退出循环并返回 String。 ``` public String Encryption(String input, int key){ @@ -148,7 +148,7 @@ public String Encryption(String input, int key){ Step 7: 调试。 -但是等等!不工作!encrypted不是一个字符串,它是一个StringBuilder,这个函数特别要求返回一个字符串! +但是等等!不工作!encrypted 不是一个字符串,它是一个 StringBuilder,这个函数特别要求返回一个字符串! 幸运的是,有一个非常简单的函数可以纠正这种疏忽。 diff --git a/chinese/articles/how-to-contribute-to-open-source-for-technical-writers.md b/chinese/articles/how-to-contribute-to-open-source-for-technical-writers.md index ff2893531..d5f32d1be 100644 --- a/chinese/articles/how-to-contribute-to-open-source-for-technical-writers.md +++ b/chinese/articles/how-to-contribute-to-open-source-for-technical-writers.md @@ -17,7 +17,7 @@ ## 什么是开源? -开源是一种软件开发与发布的协作方式。世界各地的人们通过为软件添加功能、修复bug(错误)、回答问题、翻译文本或写作教程来做开源贡献。 +开源是一种软件开发与发布的协作方式。世界各地的人们通过为软件添加功能、修复 bug(错误)、回答问题、翻译文本或写作教程来做开源贡献。 ## 为什么要贡献? @@ -39,15 +39,15 @@ ## 如何参与开源项目 -首先,在[GitHub](https://github.com/)上找到你想要参与贡献的一个项目。你可以[阅读这份教程](https://www.freecodecamp.org/news/github-search-tips/) 查看如何在GitHub上进行搜索并找到项目。 +首先,在[GitHub](https://github.com/)上找到你想要参与贡献的一个项目。你可以[阅读这份教程](https://www.freecodecamp.org/news/github-search-tips/) 查看如何在 GitHub 上进行搜索并找到项目。 -然后,打开README文件,确保你理解里面提到的说明。 +然后,打开 README 文件,确保你理解里面提到的说明。 -接着,点击屏幕右上角的Fork按钮,将这个repo(仓库)fork(复制)下来。 +接着,点击屏幕右上角的 Fork 按钮,将这个 repo(仓库)fork(复制)下来。 ![x3IyU70meecZi1qYS4_CCZW0cOZqpcTdVfKjG3_TpM1TJj_tH15FhNaKmrAL2bl8fTU7fcUAditd6AzqJbJItmCavBxQObpD2bAJCRlYds-sX-Z3iyA4b_pajXsOnAJM1_8tbPdbyOGNrXyxCfu1Qk-x3AyDrtDrFxbbxlmIaSSwaj3kYX87ELMUSQ](https://lh5.googleusercontent.com/x3IyU70meecZi1qYS4_CCZW0cOZqpcTdVfKjG3_TpM1TJj_tH15FhNaKmrAL2bl8fTU7fcUAditd6AzqJbJItmCavBxQObpD2bAJCRlYds-sX-Z3iyA4b_pajXsOnAJM1_8tbPdbyOGNrXyxCfu1Qk-x3AyDrtDrFxbbxlmIaSSwaj3kYX87ELMUSQ) -使用下列命令将fork(复制)的仓库复制到你的本地电脑: +使用下列命令将 fork(复制)的仓库复制到你的本地电脑: ```bash git clone <链接到仓库> @@ -55,9 +55,9 @@ git clone <链接到仓库> 当你点击代码下拉菜单时,会看到仓库链接。 -复制好后,打开包含你的新fork的目录,开始添加你的贡献。 +复制好后,打开包含你的新 fork 的目录,开始添加你的贡献。 -完成后,使用下列说明将你的修改推送到GitHub: +完成后,使用下列说明将你的修改推送到 GitHub: ```bash git status //显示哪些修改已经分阶段完成 @@ -67,31 +67,31 @@ git branch -M changes //创建一个新的分支 git push -u origin changes //推送修改 ``` -将修改推送到复制的仓库后,GitHub页面会弹出提示创建一个Pull Request(PR,拉取请求)。创建PR,等待项目维护者将你复制的仓库合并到主仓库。 +将修改推送到复制的仓库后,GitHub 页面会弹出提示创建一个 Pull Request(PR,拉取请求)。创建 PR,等待项目维护者将你复制的仓库合并到主仓库。 如果你不久前叉入了该项目,**请确保将上游的修改纳入你的本地仓库** 。 -如果你遇到一个大文件,并且还没有安装`git-lfs`的话,就用这条命令`brew install git-lfs`去安装一个git-lsf。 +如果你遇到一个大文件,并且还没有安装`git-lfs`的话,就用这条命令`brew install git-lfs`去安装一个 git-lsf。 -Git LFS(Large File Storage大文件存储)是由Atlassian、GitHub以及其他开源贡献者开发的Git扩展。它通过缓慢下载大文件的相关版本来减少文件在仓库中的影响。 +Git LFS(Large File Storage 大文件存储)是由 Atlassian、GitHub 以及其他开源贡献者开发的 Git 扩展。它通过缓慢下载大文件的相关版本来减少文件在仓库中的影响。 你也可以根据这个[文档](https://docs.github.com/en/repositories/working-with-files/managing-large-files/installing-git-large-file-storage) 安装`git-lfs`。 ### 开源贡献的最佳实践 -1. 确定一个你可以提供帮助的领域,然后在GitHub上找到相关的项目。 +1. 确定一个你可以提供帮助的领域,然后在 GitHub 上找到相关的项目。 2. 阅读任何可能与你感兴趣的项目或程序相似的文档。这样你就会更了解贡献会涉及什么以及其他人做了什么贡献。 -3. 搜索标注了good first issue(第一个好问题)的问题,并通读这些问题——这些问题通常是很容易解决的。 +3. 搜索标注了 good first issue(第一个好问题)的问题,并通读这些问题——这些问题通常是很容易解决的。 4. 遵循项目的贡献指南,准备好你的代码。 -5. 写一个详细的PR,给出你的解决方案,并解释为什么你的方案可以解决当前问题。如果有必要,附上相关资源的链接。 -6. 提交你的PR以待审阅。项目团队会讨论是否将你的PR合入仓库,并将结果更新给你。 -7. 如果他们决定不接受你的PR,你就询问如何能解决他们的顾虑,这样他们之后就会重新考虑接受PR。 -8. 如果他们确实接受了你的PR请求,记得表示感谢! +5. 写一个详细的 PR,给出你的解决方案,并解释为什么你的方案可以解决当前问题。如果有必要,附上相关资源的链接。 +6. 提交你的 PR 以待审阅。项目团队会讨论是否将你的 PR 合入仓库,并将结果更新给你。 +7. 如果他们决定不接受你的 PR,你就询问如何能解决他们的顾虑,这样他们之后就会重新考虑接受 PR。 +8. 如果他们确实接受了你的 PR 请求,记得表示感谢! 9. 继续寻找要解决的新问题,并在此过程中分享你的进步! ## 我能做什么类型的贡献? -你可以通过多种方式为项目做贡献,包括为bug修复或功能新增提PR、编写软件使用文档、改进已有文档、翻译文档和修改拼写错误。 +你可以通过多种方式为项目做贡献,包括为 bug 修复或功能新增提 PR、编写软件使用文档、改进已有文档、翻译文档和修改拼写错误。 在深入参与贡献某个项目之前,你应该首先挑选一个你感兴趣的项目,并通读该项目的文档。 @@ -101,19 +101,19 @@ Git LFS(Large File Storage大文件存储)是由Atlassian、GitHub以及其 ## 去哪里找开源项目? -有很多地方都能找到开源项目。GitHub是最受欢迎的地方,但在BitBucket、Gitlab和其它网站也有开源项目的仓库。 +有很多地方都能找到开源项目。GitHub 是最受欢迎的地方,但在 BitBucket、Gitlab 和其它网站也有开源项目的仓库。 -如果你有一个想法,但还没有对应的开源项目,可以先把你的想法和计划放在一个`README`文件中。如果你不确定如何开始为一个已有的项目做贡献,那么可以查看该项目的文档或通读一些PR,然后提交自己的PR。 +如果你有一个想法,但还没有对应的开源项目,可以先把你的想法和计划放在一个`README`文件中。如果你不确定如何开始为一个已有的项目做贡献,那么可以查看该项目的文档或通读一些 PR,然后提交自己的 PR。 ## **示例项目** -- [HTML5 Boilerplate项目](https://github.com/h5bp/html5-boilerplate) 是一个受Web网页开发者欢迎的开源项目。项目支持通过HTML、CSS和Javascript代码来创建网站或网页应用。 -- [Bootstrap框架](https://github.com/twbs) 也是一个开源项目,是一个帮助开发者快速创建响应式网站的工具集合。 -- [Jekyll](https://jekyllrb.com/docs/contributing/) 是一个用Ruby编写的静态网站生成器,为个人博客而设计。 -- [React.js文档](https://github.com/reactjs/reactjs.org) 上有官方的React.js使用文档。 -- [GitHub pages](https://github.com/github/docs) 包含所有你需要知道的GitHub信息。 -- [Galaxy项目(培训资料)](https://github.com/galaxyproject/training-material) 是Galaxy项目的一个培训资料集。该项目是一个基于网页的开放平台,用于数据密集型的计算研究,其范围超出了生物科学。 -- [CNCF(云原生计算基金会)](https://github.com/cncf) CNCF是开源的云原生计算,托管Kubernetes和Prometheus等项目,使云原生无处不在且可持续。 +- [HTML5 Boilerplate 项目](https://github.com/h5bp/html5-boilerplate) 是一个受 Web 网页开发者欢迎的开源项目。项目支持通过 HTML、CSS 和 Javascript 代码来创建网站或网页应用。 +- [Bootstrap 框架](https://github.com/twbs) 也是一个开源项目,是一个帮助开发者快速创建响应式网站的工具集合。 +- [Jekyll](https://jekyllrb.com/docs/contributing/) 是一个用 Ruby 编写的静态网站生成器,为个人博客而设计。 +- [React.js 文档](https://github.com/reactjs/reactjs.org) 上有官方的 React.js 使用文档。 +- [GitHub pages](https://github.com/github/docs) 包含所有你需要知道的 GitHub 信息。 +- [Galaxy 项目(培训资料)](https://github.com/galaxyproject/training-material) 是 Galaxy 项目的一个培训资料集。该项目是一个基于网页的开放平台,用于数据密集型的计算研究,其范围超出了生物科学。 +- [CNCF(云原生计算基金会)](https://github.com/cncf) CNCF 是开源的云原生计算,托管 Kubernetes 和 Prometheus 等项目,使云原生无处不在且可持续。 你还可以看看[谷歌文档之季](https://developers.google.com/season-of-docs),它帮助开源项目优化文档,同时让技术熟练的技术文档作者获得开源方面的经验。 diff --git a/chinese/articles/how-to-contribute-to-open-source-projects-beginners-guide.md b/chinese/articles/how-to-contribute-to-open-source-projects-beginners-guide.md index 8e227b4d7..c2cb7dc43 100644 --- a/chinese/articles/how-to-contribute-to-open-source-projects-beginners-guide.md +++ b/chinese/articles/how-to-contribute-to-open-source-projects-beginners-guide.md @@ -46,7 +46,7 @@ _你为开源项目做贡献的原因是什么? 回复这篇文章或通过 [l 每个开源社区都是不同的。 每个社区都有不同的人,有着不同的角色和奖励。 他们每个人都有自己的职责和预期贡献。 -### 一个典型的开源项目会有这些人: +### 一个典型的开源项目会有这些人 - **作者** - 创建项目的人。 他们有权为其他成员分配角色以帮助维护项目。 - **所有者** - 所有者拥有项目的管理所有权(_可以与作者为同一人_) diff --git a/chinese/articles/how-to-create-a-chatbot-with-the-chatgpt-api.md b/chinese/articles/how-to-create-a-chatbot-with-the-chatgpt-api.md new file mode 100644 index 000000000..d387913a5 --- /dev/null +++ b/chinese/articles/how-to-create-a-chatbot-with-the-chatgpt-api.md @@ -0,0 +1,304 @@ +> - 原文地址:[How to Create a Chatbot With the ChatGPT API](https://www.freecodecamp.org/news/how-to-create-a-chatbot-with-the-chatgpt-api/) +> - 原文作者:[Kingsley Ubah](https://www.freecodecamp.org/news/author/ubahthebuilder/) +> - 译者: +> - 校对者: + +![How to Create a Chatbot With the ChatGPT API](https://www.freecodecamp.org/news/content/images/size/w2000/2023/07/levart_photographer-drwpcjkvxuU-unsplash--1-.jpg) + +OpenAI's ChatGPT is a great tool for getting information as quickly as possible for your coding projects. Even better, you can now integrate the artificial intelligence-powered chat capability of OpenAI's models directly into your application. + +Recently, the OpenAI team expanded their API by giving developers access to their pretrained AI models (DALL-E, Codex, and GPT-3). This means that you can send a question to the API, get the response, and use the data in your application, all within seconds. + +In this article, you'll learn how to create an OpenAI account, retrieve your API keys and query OpenAI's GPT-3 model from your Node.js application. Let's dive right in! + +## How to Sign Up for a ChatGPT Account + +The first thing you need to do is sign up for an [OpenAI account](https://platform.openai.com/overview) if you don't already have one. Once you're in, you'll be redirected back to the homepage. + +At the top right corner of the page, click on your profile image, then click on Manage Account. On the sidebar, click API Keys and then click the **create new secret key** button to generate a secret key: + +![Screenshot_of_OpenAI_API_key](https://www.freecodecamp.org/news/content/images/2023/07/Screenshot_of_OpenAI_API_key.jpg) + +Screenshot of secret key + +Copy the secret key and paste it somewhere safe and accessible because you'll need it later to connect your application with the OpenAI API. + +With the key safely stored, the next step is to create a Node.js project and spin up an Express server on top of it. Let's start with the installation and basic setup. + +## How to Set up the Project + +To follow along with this project, you need to have Node.js and npm installed on your local machine. The latest version of Node.js comes with npm, and it's available on the [official Node.js website](https://nodejs.org/en/download). + +Start by creating an empty directory in your computer. Next, launch the command prompt and `cd` into the folder you just created: + +```bash +cd path/to/project +``` + +Replace this with the path to your directory + +Once pointed to the directory, run the following command to create a Node project: + +```bash +npm init -y +``` + +This will generate a package.json file inside the folder + +Next, run the following command to install `express` and `openai` libraries from npm: + +```bash +npm i express openai +``` + +The next step is to create the server. + +## How to Create an Express Server + +For now, this server will only serve the static files. We'll implement the chat API towards the end of this article. + +Start by creating a file named **server.js** inside the root folder of your project. Next, open the file with a code editor and add the following code: + +```JavaScript +const express = require('express') +const app = express() + +app.use(express.static('public')) + +app.listen(5000, ()=> { + console.log("Server is active") +}) +``` + +With this code, you've created a web server that serves static files (i.e. HTML, CSS) from the **/public** folder. + +Next, we'll create the HTML file that renders the chat interface on the web page, as well as the stylesheet file and the JavaScript file. + +## How to Create the Chatbot + +Start by creating a folder named public inside the root of your project. Then inside the **/public** directory, create a file named **index.html**. + +Open the file with a text editor and add the following markup in the file: + +```html + + + + + + Document + + +
+ +
+ +
+
+ + +
+
+ + + +``` + +As you can see above, the page comprises of the chat area (where the messages are displayed) and the submit area (comprising the text area and the submit button). + +To style the page, add the following stylesheet between the opening and closing `` tags in your **/public/index.html** file: + +```HTML + +``` + +If you save the file and check the browser, you should find your page like this: + +![Document--1-](https://www.freecodecamp.org/news/content/images/2023/07/Document--1-.png) + +Screenshot of the page + +The chat area is empty for now because we haven't submitted any messages yet. To do that, we need to bring in JavaScript. + +## How to Add Front-end JavaScript + +When the user inputs a message in the text area and clicks on the submit button, we'll send the message to the backend, get the response from the API and display it on the page. + +Start by adding an empty script element within the `` tags in **index.html**: + +```HTML + +``` + +Inside the script tags, we're to call the `getResponse()` function whenever the user clicks on the submit button: + +```JavaScript +const btn = document.getElementById("btn") + +btn.addEventListener('click', getResponse) +``` + +The `getResponse` function essentially gets the user's question, sends it to our Node.js backend to fetch the answer, and displays the response on the page. + +Inside the function, we start by accessing the prompt submitted by the user: + +```JavaScript +async function getResponse() { + var inputText = document.getElementById("input").value + const parentDiv = document.getElementById("chat-area") + + // The remaining code goes inside this function +} +``` + +If the value of the text area is empty, we simply return nothing: + +```JavaScript +if(inputText === '') { return } +``` + +Otherwise, we first add the question to the chat area in the UI: + +```JavaScript +const question = document.createElement('div') +question.innerHTML = inputText +question.classList.add("box") +parentDiv.appendChild(question) +``` + +Next, we reset the text area so it's blank: + +```JavaScript +document.getElementById("input").value = '' +``` + +Then we send the question to our server so that the server can send it to the OpenAI API and send us back a response: + +```JavaScript +let res = await fetch('http://localhost:5000/chat', + { + method: 'POST', + headers: { + "Content-Type": 'application/json' + }, + body: JSON.stringify({ + question: inputText + }) + } +) + +const data = await res.json() +``` + +If the response has a `message` property, we add the message content to the chat area in the UI: + +```JavaScript +if(data.message) { + const answer = document.createElement('div') + answer.innerHTML = data.message + answer.classList.add("box", "answer") + parentDiv.appendChild(answer) +} +``` + +Now the frontend is all set. Let's move our focus back to the backend. + +## How to Send the API Response to the Client + +Our backend will serve as the middleman between the frontend and OpenAI's API. Basically, we'll get the prompt from the client, send it to the API, and send the response back to the client. + +In **server.js**, import these at the top of the file: + +```JavaScript +const { Configuration, OpenAIApi } = require("openai") +``` + +Next, create an instance of the `openai` connection using the API key you generated earlier: + +```JavaScript +const openai = new OpenAIApi(new Configuration({ + // replace your-api-key with your API key from ChatGPT + apiKey: 'your-api-key' +})) +``` + +Finally, create the route: + +```JavaScript +app.post('/chat', async (req, res)=> { + try { + const resp = await openai.createChatCompletion({ + model: "gpt-3.5-turbo", + messages: [ + { role: "user", content: req.body.question} + ] + }) + + res.status(200).json({message: resp.data.choices[0].message.content}) + } catch(e) { + res.status(400).json({message: e.message}) + } +}) +``` + +Save the file changes, then go to your browser and submit a question. You should get back a response after a few seconds. + +You can ask as many questions as you want, but you'll have to wait for a response to each question from the backend. + +![Document](https://www.freecodecamp.org/news/content/images/2023/07/Document.png) + +Screenshot of the questions and corresponding answers from ChatGPT + +If any error is encountered, check the console in your browser to inspect the error message. + +## Conclusion + +OpenAI's API offers you a way to include AI-powered chatbots in your application using JavaScript or even [HTMX](https://letsusetech.com/the-awesome-things-you-can-do-with-htmx) (if you're knowledgeable of HTML but not JavaScript). + +Get my [full-stack web development course](https://selar.co/r13v21) if you're looking to build an awesome web application project from scratch and learn full stack web development. \ No newline at end of file diff --git a/chinese/articles/how-to-create-a-react-app-with-a-node-backend-the-complete-guide.md b/chinese/articles/how-to-create-a-react-app-with-a-node-backend-the-complete-guide.md index 13daa4660..4f517899f 100644 --- a/chinese/articles/how-to-create-a-react-app-with-a-node-backend-the-complete-guide.md +++ b/chinese/articles/how-to-create-a-react-app-with-a-node-backend-the-complete-guide.md @@ -5,40 +5,40 @@ ![如何使用 Node 后端创建 React 应用程序:完整指南](https://www.freecodecamp.org/news/content/images/size/w2000/2021/02/how-to-build-a-react-app-with-a-node-backend-alt.png) -React前端与Node后端相配合,对于你想建立的任何应用程序来说都是一个坚如磐石的组合。 +React 前端与 Node 后端相配合,对于你想建立的任何应用程序来说都是一个坚如磐石的组合。 -本指南旨在帮助你尽可能容易地用React创建全栈项目。 +本指南旨在帮助你尽可能容易地用 React 创建全栈项目。 -让我们看看如何使用React和Node从头开始建立一个完整的项目,并将其部署到网络上。 +让我们看看如何使用 React 和 Node 从头开始建立一个完整的项目,并将其部署到网络上。 -> 想建立和部署你自己的React和Node应用程序吗? [查看我的课程系列](http://bit.ly/12-react-projects),它告诉你如何建立你自己的全栈React项目,比如这个项目。 +> 想建立和部署你自己的 React 和 Node 应用程序吗? [查看我的课程系列](http://bit.ly/12-react-projects),它告诉你如何建立你自己的全栈 React 项目,比如这个项目。 ## 你需要的工具 -1. 确保Node和NPM已经安装在你的电脑上。你可以在以下网站下载这两样东西[nodejs.org](https://nodejs.org) (NPM包含在你安装的Node中,不需要另外安装)。 -2. 使用你选择的代码编辑器。我正在使用并且个人推荐使用VSCode。你可以在以下网址下载VSCode [code.visualstudio.com](https://code.visualstudio.com). -3. 确保你的电脑上安装了Git。这对于用Heroku部署我们的应用程序是必要的。你可以在以下网站上得到它 [git-scm.com](https://git-scm.com) -4. 一个在[heroku.com](https://heroku.com)的账号。我们将使用Heroku将我们的应用程序完全免费地发布到网上。 +1. 确保 Node 和 NPM 已经安装在你的电脑上。你可以在以下网站下载这两样东西[nodejs.org](https://nodejs.org) (NPM 包含在你安装的 Node 中,不需要另外安装)。 +2. 使用你选择的代码编辑器。我正在使用并且个人推荐使用 VSCode。你可以在以下网址下载 VSCode [code.visualstudio.com](https://code.visualstudio.com). +3. 确保你的电脑上安装了 Git。这对于用 Heroku 部署我们的应用程序是必要的。你可以在以下网站上得到它 [git-scm.com](https://git-scm.com) +4. 一个在[heroku.com](https://heroku.com)的账号。我们将使用 Heroku 将我们的应用程序完全免费地发布到网上。 -## 第1步:创建你的Node(Express)后端 +## 第 1 步:创建你的 Node(Express)后端 首先为你的项目创建一个文件夹,命名为`react-node-app`(例如)。 然后,将该文件夹拖入你的代码编辑器。 -为了创建我们的Node项目,在你的终端运行以下命令。 +为了创建我们的 Node 项目,在你的终端运行以下命令。 ```bash npm init -y ``` -这将创建一个package.json文件,这将使我们能够跟踪我们所有的应用程序脚本,并管理我们的Node应用程序需要的任何依赖。 +这将创建一个 package.json 文件,这将使我们能够跟踪我们所有的应用程序脚本,并管理我们的 Node 应用程序需要的任何依赖。 我们的服务器代码将放在一个同名的文件夹中:`server`。让我们来创建这个文件夹。 在这个文件夹中,我们将放置一个文件,我们将从这个文件中运行我们的服务。`index.js`。 -我们将使用Express为我们创建一个简单的Web服务器,如果环境变量`PORT`没有给定值,则运行在3001端口(Heroku将在我们部署应用程序时设置这个值)。 +我们将使用 Express 为我们创建一个简单的 Web 服务器,如果环境变量`PORT`没有给定值,则运行在 3001 端口(Heroku 将在我们部署应用程序时设置这个值)。 ```js // server/index.js @@ -54,13 +54,13 @@ app.listen(PORT, () => { }); ``` -然后在我们的终端,我们将安装Express作为一个依赖项来使用它。 +然后在我们的终端,我们将安装 Express 作为一个依赖项来使用它。 ```bash npm i express ``` -之后,我们将在package.json中创建一个脚本,当我们用`npm start`运行它时,将启动我们的web服务。 +之后,我们将在 package.json 中创建一个脚本,当我们用`npm start`运行它时,将启动我们的 web 服务。 ```json // server/package.json @@ -72,7 +72,7 @@ npm i express ... ``` -最后,我们可以通过在终端运行npm start来运行我们的应用程序,我们应该看到它正在3001端口上运行。 +最后,我们可以通过在终端运行 npm start 来运行我们的应用程序,我们应该看到它正在 3001 端口上运行。 ```bash npm start @@ -84,15 +84,15 @@ Server listening on 3001 ![代码片段 1](https://reedbarger.nyc3.digitaloceanspaces.com/how-to-create-a-react-app-with-a-node-backend/clip-1.gif) -## 第2步:创建一个API +## 第 2 步:创建一个 API -我们想把我们的Node和Express服务器提供一个API,这样它就可以给我们的React应用提供数据,改变这些数据,或者做一些其他只有服务才能做的操作。 +我们想把我们的 Node 和 Express 服务器提供一个 API,这样它就可以给我们的 React 应用提供数据,改变这些数据,或者做一些其他只有服务才能做的操作。 -在我们的案例中,我们将简单地给我们的React应用发送一个JSON对象中的 "Hello from server!"消息。 +在我们的案例中,我们将简单地给我们的 React 应用发送一个 JSON 对象中的 "Hello from server!"消息。 -下面的代码为路由`/api`创建了一个endpoint。 +下面的代码为路由`/api`创建了一个 endpoint。 -如果我们的React应用向该路由发出一个GET请求,我们就会用我们的JSON数据进行响应(使用`res`,代表响应)。 +如果我们的 React 应用向该路由发出一个 GET 请求,我们就会用我们的 JSON 数据进行响应(使用`res`,代表响应)。 ```js // server/index.js @@ -109,29 +109,29 @@ app.listen(PORT, () => { _注意:请确保将其放在`app.listen`函数之前。_ -由于我们已经对Node代码进行了修改,我们需要重新启动我们的服务器。 +由于我们已经对 Node 代码进行了修改,我们需要重新启动我们的服务器。 -要做到这一点,在终端按Command/Ctrl+C结束你的启动脚本,然后再次运行`npm start`重新启动它。 +要做到这一点,在终端按 Command/Ctrl+C 结束你的启动脚本,然后再次运行`npm start`重新启动它。 为了测试这一点,我们可以简单地在浏览器中访问`http://localhost:3001/api`,看看我们获得的信息。 ![代码片段 2](https://reedbarger.nyc3.digitaloceanspaces.com/how-to-create-a-react-app-with-a-node-backend/clip-2.gif) -## 第3步:创建你的React前端 +## 第 3 步:创建你的 React 前端 在创建了我们的后端之后,让我们转到前端。 -打开另一个终端标签,使用create-react-app创建一个新的React项目,名称为`client`。 +打开另一个终端标签,使用 create-react-app 创建一个新的 React 项目,名称为`client`。 ```bash npx create-react-app client ``` -之后,我们将拥有一个安装了所有依赖项的React应用。 +之后,我们将拥有一个安装了所有依赖项的 React 应用。 -我们要做的唯一改变是在package.json文件中添加一个名为`proxy`的属性(`client`文件夹下的package.json文件)。 +我们要做的唯一改变是在 package.json 文件中添加一个名为`proxy`的属性(`client`文件夹下的 package.json 文件)。 -这将允许我们向我们的Node服务器发出请求,而不必在每次向它发出网络请求时提供它所运行的原点(http://localhost:3001)。 +这将允许我们向我们的 Node 服务器发出请求,而不必在每次向它发出网络请求时提供它所运行的原点(http://localhost:3001)。 ```bash // client/package.json @@ -141,9 +141,9 @@ npx create-react-app client ... ``` -然后我们可以通过运行它的启动脚本来启动我们的React应用,这和我们的Node服务器一样。首先确保`cd`进入新创建的`client`文件夹。 +然后我们可以通过运行它的启动脚本来启动我们的 React 应用,这和我们的 Node 服务器一样。首先确保`cd`进入新创建的`client`文件夹。 -之后,将在`localhost:3000`上启动(其实启动两个Node的进程,一个是React开发使用,一个是Express开发使用)。 +之后,将在`localhost:3000`上启动(其实启动两个 Node 的进程,一个是 React 开发使用,一个是 Express 开发使用)。 ```bash cd client @@ -158,19 +158,19 @@ Local: http://localhost:3000 ![代码片段 3](https://reedbarger.nyc3.digitaloceanspaces.com/how-to-create-a-react-app-with-a-node-backend/clip-3.gif) -## 第4步:从React向Node发出HTTP请求 +## 第 4 步:从 React 向 Node 发出 HTTP 请求 -现在我们有了一个工作的React应用,我们想用它来与我们的API进行交互。 +现在我们有了一个工作的 React 应用,我们想用它来与我们的 API 进行交互。 -让我们看看如何从我们之前创建的`/api`endpoint获取数据。 +让我们看看如何从我们之前创建的`/api`endpoint 获取数据。 -要做到这一点,我们可以前往`src`文件夹中的`App.js`组件,使用`useEffect`进行HTTP请求。 +要做到这一点,我们可以前往`src`文件夹中的`App.js`组件,使用`useEffect`进行 HTTP 请求。 -我们将使用Fetch API向我们的后端发出一个简单的GET请求,然后将我们的数据以JSON格式返回。 +我们将使用 Fetch API 向我们的后端发出一个简单的 GET 请求,然后将我们的数据以 JSON 格式返回。 一旦我们得到了返回的数据,我们将得到消息属性(抓取我们从服务器发送的问候语),然后把它放在一个叫做`data`的状态变量中。 -这将使我们能够在我们的页面中显示该消息,如果我们有的话。我们在JSX中使用一个条件,说如果我们的数据还没有,就显示文本 `Loading...`。 +这将使我们能够在我们的页面中显示该消息,如果我们有的话。我们在 JSX 中使用一个条件,说如果我们的数据还没有,就显示文本 `Loading...`。 ```js // client/src/App.js @@ -203,22 +203,22 @@ export default App; ![代码片段 5](https://reedbarger.nyc3.digitaloceanspaces.com/how-to-create-a-react-app-with-a-node-backend/clip-4.gif) -## 用Heroku将你的应用程序部署到网上 +## 用 Heroku 将你的应用程序部署到网上 最后,让我们把我们的应用程序部署到网络上。 -首先,在我们的`client`文件夹中,确保删除由create-react-app自动初始化的Git repo(rm -rf .git, `.git` 是隐藏文件夹,不能直接看到)。 +首先,在我们的`client`文件夹中,确保删除由 create-react-app 自动初始化的 Git repo(rm -rf .git, `.git` 是隐藏文件夹,不能直接看到)。 -这对部署我们的应用程序至关重要,因为我们要在我们项目的根文件夹(`react-node-app`)中建立Git repo,而不是在`client`中。 +这对部署我们的应用程序至关重要,因为我们要在我们项目的根文件夹(`react-node-app`)中建立 Git repo,而不是在`client`中。 ```bash cd client rm -rf .git ``` -当我们部署时,我们的Node后端和React前端都将在同一个域名(即mycoolapp.herokuapp.com)提供服务。 +当我们部署时,我们的 Node 后端和 React 前端都将在同一个域名(即 mycoolapp.herokuapp.com)提供服务。 -我们看到我们的请求是如何被我们的Node API处理的,所以我们需要写一些代码,当我们的React应用被用户请求时(例如,当我们进入我们的应用的主页时)显示我们的React应用。 +我们看到我们的请求是如何被我们的 Node API 处理的,所以我们需要写一些代码,当我们的 React 应用被用户请求时(例如,当我们进入我们的应用的主页时)显示我们的 React 应用。 我们可以在`server/index.js`中加入以下代码来完成这个工作。 @@ -243,13 +243,13 @@ app.get('*', (req, res) => { }); ``` -这段代码将首先允许Node使用`express.static`函数来访问我们建立的React项目的静态文件。 +这段代码将首先允许 Node 使用`express.static`函数来访问我们建立的 React 项目的静态文件。 -如果有一个GET请求进来,而这个请求没有被我们的`/api`路由处理后,我们的服务器将用我们的React应用来响应。 +如果有一个 GET 请求进来,而这个请求没有被我们的`/api`路由处理后,我们的服务器将用我们的 React 应用来响应。 -**这段代码允许我们的React和Node应用一起部署在同一个域名。** +**这段代码允许我们的 React 和 Node 应用一起部署在同一个域名。** -然后我们可以告诉我们的Node App如何做,在我们的服务器package.json文件中添加一个`build`脚本,为生产构建我们的React应用。 +然后我们可以告诉我们的 Node App 如何做,在我们的服务器 package.json 文件中添加一个`build`脚本,为生产构建我们的 React 应用。 ```json // server/package.json @@ -262,9 +262,9 @@ app.get('*', (req, res) => { ... ``` -我还建议提供一个名为`engines`的字段,在这里你要指定你用来构建项目的Node版本。这将被用于部署。 +我还建议提供一个名为`engines`的字段,在这里你要指定你用来构建项目的 Node 版本。这将被用于部署。 -你可以通过运行`node -v`来获得你的Node版本,你可以把结果放在`engines`中(例如14.15.4)。 +你可以通过运行`node -v`来获得你的 Node 版本,你可以把结果放在`engines`中(例如 14.15.4)。 ```json // server/package.json @@ -274,7 +274,7 @@ app.get('*', (req, res) => { } ``` -在这之后,我们准备使用Heroku进行部署,所以请确保你在[Heroku.com](https://heroku.com)有一个账户。 +在这之后,我们准备使用 Heroku 进行部署,所以请确保你在[Heroku.com](https://heroku.com)有一个账户。 当你登录并查看你的仪表板(dashboard),你将选择新建(New)>创建新的应用程序(Create New App),并提供一个唯一的应用程序名称。 @@ -282,7 +282,7 @@ app.get('*', (req, res) => { sudo npm i -g heroku ``` -当安装完毕,你将通过CLI使用`heroku login`命令登录到Heroku。 +当安装完毕,你将通过 CLI 使用`heroku login`命令登录到 Heroku。 ```bash heroku login @@ -292,7 +292,7 @@ Press any key to login to Heroku 登录后,只需在 "Deploy "选项卡中为我们创建的应用程序遵循部署说明。 -以下四个命令将为我们的项目初始化一个新的Git repo,将我们的文件添加到其中,提交它们,并为Heroku添加一个Git远程。 +以下四个命令将为我们的项目初始化一个新的 Git repo,将我们的文件添加到其中,提交它们,并为 Heroku 添加一个 Git 远程。 ``` git init @@ -301,17 +301,17 @@ git add . git commit -am "Deploy app to Heroku" ``` -然后,最后一步是通过推送我们刚刚添加的Heroku Git远程地址(heroku git:remote -a insert-your-app-name-here) ,来发布我们的应用程序。 +然后,最后一步是通过推送我们刚刚添加的 Heroku Git 远程地址(heroku git:remote -a insert-your-app-name-here) ,来发布我们的应用程序。 ```bash git push heroku master ``` - 恭喜!我们的全栈式React和Node应用已经上线。🎉 + 恭喜!我们的全栈式 React 和 Node 应用已经上线。🎉 ![代码片段 5](https://reedbarger.nyc3.digitaloceanspaces.com/how-to-create-a-react-app-with-a-node-backend/clip-5.gif) -当你想对你的应用程序进行修改时(并进行部署),你只需要用Git来添加你的文件(git add),提交它们(git commit),然后推送到我们的Heroku远程(git push)。 +当你想对你的应用程序进行修改时(并进行部署),你只需要用 Git 来添加你的文件(git add),提交它们(git commit),然后推送到我们的 Heroku 远程(git push)。 ```bash git add . @@ -319,8 +319,8 @@ git commit -m "my commit message" git push heroku master ``` -## 想用React建立像YouTube、Instagram和Twitter这样的真实世界的应用程序吗?这就是怎么做。 +## 想用 React 建立像 YouTube、Instagram 和 Twitter 这样的真实世界的应用程序吗?这就是怎么做 -在每个月的月底,我将发布一个独家课程,准确地告诉你如何复现从头到尾用React建立一个完整的应用程序。 +在每个月的月底,我将发布一个独家课程,准确地告诉你如何复现从头到尾用 React 建立一个完整的应用程序。 想在下一个课程出现时得到通知吗?**[在这里加入等候名单](http://bit.ly/12-react-projects).** \ No newline at end of file diff --git a/chinese/articles/how-to-create-a-telegram-bot-using-python.md b/chinese/articles/how-to-create-a-telegram-bot-using-python.md index 5d25728a5..2e18fef20 100644 --- a/chinese/articles/how-to-create-a-telegram-bot-using-python.md +++ b/chinese/articles/how-to-create-a-telegram-bot-using-python.md @@ -5,52 +5,52 @@ ![How to Create a Telegram Bot using Python](https://www.freecodecamp.org/news/content/images/size/w2000/2022/12/Telegram-Bot.png) -自动化的聊天机器人可以有效地促进互动,Slack、Discord和其他平台都支持创建机器人。 +自动化的聊天机器人可以有效地促进互动,Slack、Discord 和其他平台都支持创建机器人。 -在这篇文章里,笔者会教你做一个能告诉你星座运势的Telegram聊天机器人,话不多说,马上开始! +在这篇文章里,笔者会教你做一个能告诉你星座运势的 Telegram 聊天机器人,话不多说,马上开始! -## 如何获取你的机器人Token值 +## 如何获取你的机器人 Token 值 -在Telegram上创建一个机器人之前,你需要先跟 BotFather说一声,顾名思义,BotFather就是所有Bot的Father,但它并不是一个真实的人,也是一个机器人。 +在 Telegram 上创建一个机器人之前,你需要先跟 BotFather 说一声,顾名思义,BotFather 就是所有 Bot 的 Father,但它并不是一个真实的人,也是一个机器人。 -1\. 在Telegram上搜索 @botfather。 +1. 在 Telegram 上搜索 @botfather。 ![Screenshot-2022-12-16-092357](https://www.freecodecamp.org/news/content/images/2022/12/Screenshot-2022-12-16-092357.png) -BotFather机器人 +BotFather 机器人 -2\.  点击Start按钮与BotFather聊天. +2.  点击 Start 按钮与 BotFather 聊天. ![Screenshot-2022-12-16-092531](https://www.freecodecamp.org/news/content/images/2022/12/Screenshot-2022-12-16-092531.png) -点击Start按钮 +点击 Start 按钮 -3\.  输入 `/newbot`,然后输入你的机器人名字,再输入你的机器人ID,接着BotFather就会给你一个token值,后面我们会用这个token值来访问Telegram的API。 +3\.  输入 `/newbot`,然后输入你的机器人名字,再输入你的机器人 ID,接着 BotFather 就会给你一个 token 值,后面我们会用这个 token 值来访问 Telegram 的 API。 ![Screenshot-2022-12-16-093337](https://www.freecodecamp.org/news/content/images/2022/12/Screenshot-2022-12-16-093337.png) -获取访问token +获取访问 token -**注意:**一定要安全的存放上述申请到的token,因为这个token可以直接操控的你机器人。 +**注意:**一定要安全的存放上述申请到的 token,因为这个 token 可以直接操控的你机器人。 ## 如何初始化你的代码环境 -现在需要准备一下本地的代码环境。在Python中,很多库可以创建Telegram机器人,这里我们用的是[pyTelegramBotAPI](https://pypi.org/project/pyTelegramBotAPI/)。这是一个简单但是扩展性很强的库,并且有着很好的同步和异步处理能力。 +现在需要准备一下本地的代码环境。在 Python 中,很多库可以创建 Telegram 机器人,这里我们用的是[pyTelegramBotAPI](https://pypi.org/project/pyTelegramBotAPI/)。这是一个简单但是扩展性很强的库,并且有着很好的同步和异步处理能力。 -用pip命令安装pyTelegramBotAPI: +用 pip 命令安装 pyTelegramBotAPI: ```bash pip install pyTelegramBotAPI ``` -然后用你常用的代码编辑器创建一个 `.env` 文件,并在其中写下这一行来存储你的token: +然后用你常用的代码编辑器创建一个 `.env` 文件,并在其中写下这一行来存储你的 token: ```bash export BOT_TOKEN=your-bot-token-here ``` -然后,Linux/Mac系统下可以直接用终端在当前文件夹内使用 `source .env`命令,让BOT_TOKEN的值生效,而在Windows系统中,需要在终端中输入以下命令来设置环境变量: +然后,Linux/Mac 系统下可以直接用终端在当前文件夹内使用 `source .env`命令,让 BOT_TOKEN 的值生效,而在 Windows 系统中,需要在终端中输入以下命令来设置环境变量: ```bash set BOT_TOKEN=your-bot-token-here @@ -59,7 +59,7 @@ set BOT_TOKEN=your-bot-token-here ## 如何创建第一个机器人 -TeleBot类里面存储了所有的API接口,其中有很多方法可以用来监听收到的消息,同时也有像`send_message()`, `send_document()`这类方法来发送消息。 +TeleBot 类里面存储了所有的 API 接口,其中有很多方法可以用来监听收到的消息,同时也有像`send_message()`, `send_document()`这类方法来发送消息。 新建一个 `bot.py` 文件,然后把下面的代码粘贴到文件中: @@ -97,15 +97,15 @@ def echo_all(message): bot.reply_to(message, message.text) ``` -上面用到了一个` lambda` 函数,如果我们需要回应所有的消息,那我们就需要从`lambda`函数中返回一个 `True`。 +上面用到了一个`lambda` 函数,如果我们需要回应所有的消息,那我们就需要从`lambda`函数中返回一个 `True`。 -你现在有了一个简单的机器人,它可以对`/start` 和 `/hello `命令做出回应,并且可以返回其他发给它的消息。把下面这条代码放在`bot.py`的末尾,然后就可以启动机器人了。 +你现在有了一个简单的机器人,它可以对`/start` 和 `/hello`命令做出回应,并且可以返回其他发给它的消息。把下面这条代码放在`bot.py`的末尾,然后就可以启动机器人了。 ```python bot.infinity_polling() ``` -好啦!这样我们就完成了一个Telegram机器人,运行这个Python文件,然后就可以通过Telegram与机器人进行交互了。 +好啦!这样我们就完成了一个 Telegram 机器人,运行这个 Python 文件,然后就可以通过 Telegram 与机器人进行交互了。 如果你没保存机器人的联系方式,你可以通过之前设置的用户名来搜索。你可以尝试发送`/hello` 和 `/start` 以及其他消息感受一下。 @@ -115,15 +115,15 @@ bot.infinity_polling() 注意:所有的消息处理器都按照它们在源文件中声明的顺序进行测试。 -获取更多如何使用pyTelegramBotAPI库的学习资料,你可以去他们的[官方文档](https://github.com/eternnoir/pyTelegramBotAPI)了解。 +获取更多如何使用 pyTelegramBotAPI 库的学习资料,你可以去他们的[官方文档](https://github.com/eternnoir/pyTelegramBotAPI)了解。 ## 如何写一个星座运势机器人 现在让我们把注意力放在星座运势机器人上。我们会在机器人中使用消息链。机器人会先问你是什么星座,然后会问你需要哪天的运势,接着就会返回那一天对应的星座运势。 -往程序内部分析,这个机器人主要是通过API来获取星座运势数据的。 +往程序内部分析,这个机器人主要是通过 API 来获取星座运势数据的。 -我们将会使用一个笔者在另一个教程中写的[星座运势API](https://horoscope-app-api.vercel.app/)来进行后续的工作。如果你想学一下如何做一个相同的API,你可以看一下这份[教程](https://ashutoshkrris.hashnode.dev/how-to-create-a-horoscope-api-with-beautiful-soup-and-flask)。请确保在开始之前你已经在[网站](https://horoscope-app-api.vercel.app/)上探索了API的用法。 +我们将会使用一个笔者在另一个教程中写的[星座运势 API](https://horoscope-app-api.vercel.app/)来进行后续的工作。如果你想学一下如何做一个相同的 API,你可以看一下这份[教程](https://ashutoshkrris.hashnode.dev/how-to-create-a-horoscope-api-with-beautiful-soup-and-flask)。请确保在开始之前你已经在[网站](https://horoscope-app-api.vercel.app/)上探索了 API 的用法。 ## 如何抓取运势数据 @@ -147,7 +147,7 @@ def get_daily_horoscope(sign: str, day: str) -> dict: return response.json() ``` -在上面的Python代码中,我们创建了一个函数,它能接收`sign` 和`day`两个字符串参数,然后返回 JSON数据。我们需要向API链接发送一个GET请求,并且把`sign` 和`day` 作为参数加在请求里。 +在上面的 Python 代码中,我们创建了一个函数,它能接收`sign` 和`day`两个字符串参数,然后返回 JSON 数据。我们需要向 API 链接发送一个 GET 请求,并且把`sign` 和`day` 作为参数加在请求里。 如果你测试一下上面的函数,你会得到一个跟下面类似的返回值: @@ -162,7 +162,7 @@ def get_daily_horoscope(sign: str, day: str) -> dict: } ``` -注意:你可以通过这个[教程](https://ashutoshkrris.hashnode.dev/how-to-interact-with-web-services-using-python)来探索更多关于 `requests `库的内容 +注意:你可以通过这个[教程](https://ashutoshkrris.hashnode.dev/how-to-interact-with-web-services-using-python)来探索更多关于 `requests`库的内容 ## 如何添加一个消息处理器 @@ -214,14 +214,14 @@ def fetch_horoscope(message, sign): ## 机器人示例 -如果你运行Python文件的话,你可以测试这些功能,这里是一个示例: +如果你运行 Python 文件的话,你可以测试这些功能,这里是一个示例: ## 推荐下一步 -到目前为止,一旦我们停止Python应用程序,机器人就会停止工作。为了让它一直运行,你可以把机器人部署在Heroku、Render等平台上。 +到目前为止,一旦我们停止 Python 应用程序,机器人就会停止工作。为了让它一直运行,你可以把机器人部署在 Heroku、Render 等平台上。 -这是本文代码的[GitHub仓库链接](https://github.com/ashutoshkrris/Telegram-Horoscope-Bot) - 欢迎随时查看. +这是本文代码的[GitHub 仓库链接](https://github.com/ashutoshkrris/Telegram-Horoscope-Bot) - 欢迎随时查看. 在探索[Telegram APIs](https://core.telegram.org/)后,你可以向这个机器人添加更多的功能。 diff --git a/chinese/articles/how-to-create-your-own-command-in-linux.md b/chinese/articles/how-to-create-your-own-command-in-linux.md index 5449027fb..770bbd7e1 100644 --- a/chinese/articles/how-to-create-your-own-command-in-linux.md +++ b/chinese/articles/how-to-create-your-own-command-in-linux.md @@ -275,7 +275,7 @@ Let's learn this with an example. Say you have to create an alias command called `gohome`. Running this command should take you to the home directory and display the "Navigated to home directory" message. -### Method #1: +### Method #1 This way is the usual way of adding an `alias` command. You have to add the two commands separated by a semicolon (`;`). diff --git a/chinese/articles/how-to-deploy-nodejs-application-with-render.md b/chinese/articles/how-to-deploy-nodejs-application-with-render.md index 8d421b5d2..e6daa55b2 100644 --- a/chinese/articles/how-to-deploy-nodejs-application-with-render.md +++ b/chinese/articles/how-to-deploy-nodejs-application-with-render.md @@ -5,27 +5,27 @@ ![How to Deploy Your Node.js Application for Free with Render](https://www.freecodecamp.org/news/content/images/size/w2000/2022/09/pexels-pixabay-163235--1-.jpg) -长久以来,Heroku都是托管全栈应用的优秀平台。freeCodeCamp早期大量使用Heroku,其他各式各样的开源项目也是如此。 +长久以来,Heroku 都是托管全栈应用的优秀平台。freeCodeCamp 早期大量使用 Heroku,其他各式各样的开源项目也是如此。 -但由于Heroku决定停止免费提供服务,这一现象可能会发生改变。 +但由于 Heroku 决定停止免费提供服务,这一现象可能会发生改变。 -你可能已经收到了Heroku的邮件,通知你从2022年11月28日开始,在该平台托管应用不再免费,你可以购买付费计划。 +你可能已经收到了 Heroku 的邮件,通知你从 2022 年 11 月 28 日开始,在该平台托管应用不再免费,你可以购买付费计划。 ![no_free_heroku](https://www.freecodecamp.org/news/content/images/2022/08/no_free_heroku.png) -如果你想免费托管静态页面或者web应用可以使用[Netlify](https://www.netlify.com/),具体操作可以[查看这篇文章](https://www.freecodecamp.org/news/how-to-deploy-react-router-based-app-to-netlify/),但对于后端应用来说,并不存在太多如Heroku一样部署体验比较好的免费网站。 +如果你想免费托管静态页面或者 web 应用可以使用[Netlify](https://www.netlify.com/),具体操作可以[查看这篇文章](https://www.freecodecamp.org/news/how-to-deploy-react-router-based-app-to-netlify/),但对于后端应用来说,并不存在太多如 Heroku 一样部署体验比较好的免费网站。 -在这篇文章中,我们将学习如何使用[Render](https://render.com/)来部署搭配Express服务器的Node.js应用。 它的部署过程和Hero一样简单,并且免费。 +在这篇文章中,我们将学习如何使用[Render](https://render.com/)来部署搭配 Express 服务器的 Node.js 应用。 它的部署过程和 Hero 一样简单,并且免费。 让我们开始吧。 ## 在部署应用之前需要做什么? -根据使用Heroku的经验来看,每一个应用都部署在一个由Hero随机分配的端点,可以使用`process.env.PORT`变量访问。 +根据使用 Heroku 的经验来看,每一个应用都部署在一个由 Hero 随机分配的端点,可以使用`process.env.PORT`变量访问。 -使用Render平台也是一样的。 +使用 Render 平台也是一样的。 -所以你必须确保自己不是通过硬编码端点值来开启Express服务器,而是使用`process.env.PORT`变量: +所以你必须确保自己不是通过硬编码端点值来开启 Express 服务器,而是使用`process.env.PORT`变量: ```js const express = require("express"); @@ -39,11 +39,11 @@ app.listen(PORT, () => { }); ``` -## **如果使用Render部署一个Github仓库** +## **如果使用 Render 部署一个 Github 仓库** 改好端点之后,就可以开始部署你的应用了。 -我准备好了[这个GitHub仓库](https://github.com/myogeshchavan97/github-repos-nodejs-api)来使用Render部署。这个Github仓库展示了一个排名前几位的仓库清单,以及每一个仓库获得的星星数量(以JSON的格式)。 +我准备好了[这个 GitHub 仓库](https://github.com/myogeshchavan97/github-repos-nodejs-api)来使用 Render 部署。这个 Github 仓库展示了一个排名前几位的仓库清单,以及每一个仓库获得的星星数量(以 JSON 的格式)。 让我们开始吧! @@ -55,7 +55,7 @@ app.listen(PORT, () => { ![dashboard](https://www.freecodecamp.org/news/content/images/2022/08/dashboard.png) -点击`Web Services`选项卡下的 `New Web Service`按钮来部署Node.js应用。 +点击`Web Services`选项卡下的 `New Web Service`按钮来部署 Node.js 应用。 你也可以通过点击网页头部你头像旁边的 `New +` 按钮来选择 `Web Service`选择卡。 @@ -63,11 +63,11 @@ app.listen(PORT, () => { ![new_web_service](https://www.freecodecamp.org/news/content/images/2022/08/new_web_service.png) -点击Github菜单下方的`Connect account`按钮,会看到以下画面: +点击 Github 菜单下方的`Connect account`按钮,会看到以下画面: ![install_render](https://www.freecodecamp.org/news/content/images/2022/08/install_render.png) -点击`Configure`链接,就可以允许Render选取你所有或者选择的Github仓库。 +点击`Configure`链接,就可以允许 Render 选取你所有或者选择的 Github 仓库。 我希望只访问选择的仓库,也就是我需要部署的。所以我选择`Only select repositories`选项。 @@ -79,7 +79,7 @@ app.listen(PORT, () => { ![selected_repository](https://www.freecodecamp.org/news/content/images/2022/08/selected_repository.png) -点击绿色按钮`Install`,使得Render有权访问你选择的仓库。 +点击绿色按钮`Install`,使得 Render 有权访问你选择的仓库。 点击完毕后,你会被重定向到控制台,看到如下画面: @@ -91,7 +91,7 @@ app.listen(PORT, () => { 在`Name`框中,输入一个简短的名字来标识你的网站。 -**注意:** 请保持`Name`值的简单,因为部署完毕后,它将成为应用的URL。 所以如果我将`github-repos`设置为`Name`的值,我的应用URL会成为[`https://github-repos.onrender.com`](https://github-repos.onrender.com)。 +**注意:** 请保持`Name`值的简单,因为部署完毕后,它将成为应用的 URL。 所以如果我将`github-repos`设置为`Name`的值,我的应用 URL 会成为[`https://github-repos.onrender.com`](https://github-repos.onrender.com)。 所以请填写一个简单有意义的`Name`值。 @@ -99,7 +99,7 @@ app.listen(PORT, () => { ![details](https://www.freecodecamp.org/news/content/images/2022/08/details.png) -在`Build Command`中填写`yarn`就等同于`yarn install`命令。Yarn是一个类似于npm的包管理工具,但是比npm要快。 +在`Build Command`中填写`yarn`就等同于`yarn install`命令。Yarn 是一个类似于 npm 的包管理工具,但是比 npm 要快。 如果你的入口文件是 `index.js`,在`Start Command`中填写`node index.js`。 @@ -113,7 +113,7 @@ app.listen(PORT, () => { ![env_vars-1](https://www.freecodecamp.org/news/content/images/2022/08/env_vars-1.png) -注意`Auto-Deploy`的默认值是`Yes` – 所以一旦你向Github仓库推送了更新,它们就会被自动部署到Render。 +注意`Auto-Deploy`的默认值是`Yes` – 所以一旦你向 Github 仓库推送了更新,它们就会被自动部署到 Render。 如果你并不想每次更改仓库的时候都自动部署,你可以在 `Auto-Deploy`下拉菜单中选择`No`。 @@ -127,43 +127,43 @@ app.listen(PORT, () => { ![11](https://www.freecodecamp.org/news/content/images/2022/08/11.png) -你可以点击在顶部的应用URL,在我的例子中,这个URL是[https://github-repos.onrender.com/](https://github-repos.onrender.com/)。 +你可以点击在顶部的应用 URL,在我的例子中,这个 URL 是[https://github-repos.onrender.com/](https://github-repos.onrender.com/)。 如果是首次部署应用,可能在访问网站的时候会遇到`Page is not working`报错。 -等待一段时间,并且使用`Ctrl + R`或`Cmd + R(Mac)`来刷新页面。因为免费服务的硬件有限,所以Render平台需要一些时间来启动一个项目。 +等待一段时间,并且使用`Ctrl + R`或`Cmd + R(Mac)`来刷新页面。因为免费服务的硬件有限,所以 Render 平台需要一些时间来启动一个项目。 部署成功后,可以如下图一样看到你的应用: ![deployed_live](https://www.freecodecamp.org/news/content/images/2022/08/deployed_live.png) -**提示:** 想要JSON有格式地显示,你需要安装Chrome插件:[JSON Formatter](https://chrome.google.com/webstore/detail/json-formatter/bcjindcccaagfpapjjmafapmmgkkhgoa?hl=en)。 +**提示:** 想要 JSON 有格式地显示,你需要安装 Chrome 插件:[JSON Formatter](https://chrome.google.com/webstore/detail/json-formatter/bcjindcccaagfpapjjmafapmmgkkhgoa?hl=en)。 -你或许知道,在使用Heroku的免费账户的时候,没如果没有新的请求进来,你的应用会在30分钟后进入到休眠模式。也就是如果再次请求需要花上一点时间。 +你或许知道,在使用 Heroku 的免费账户的时候,没如果没有新的请求进来,你的应用会在 30 分钟后进入到休眠模式。也就是如果再次请求需要花上一点时间。 -同样的,使用Render的话,如果没有新的请求,应用会在15分钟后进入休眠模式。 +同样的,使用 Render 的话,如果没有新的请求,应用会在 15 分钟后进入休眠模式。 ### **感谢阅读!** 你可以在[这个仓库中](https://github.com/myogeshchavan97/github-repos-nodejs-api)找到完整的源代码。 -****你可以在[这里](https://github-repos.onrender.com/)查看实时demo****。 +****你可以在[这里](https://github-repos.onrender.com/)查看实时 demo****。 -如果你想从零开始学Redux,并且完成三个应用,以及[一个完整的食物点单应用](https://www.youtube.com/watch?v=2zaPDfCKAvM),可以查看我的[Mastering Redux](https://master-redux.yogeshchavan.dev/)课程。 +如果你想从零开始学 Redux,并且完成三个应用,以及[一个完整的食物点单应用](https://www.youtube.com/watch?v=2zaPDfCKAvM),可以查看我的[Mastering Redux](https://master-redux.yogeshchavan.dev/)课程。 在这篇课程中,你将学习: -- 基础和高阶的Redux +- 基础和高阶的 Redux - 如何管理复杂的数据和对象状态 -- 如何使用多个reducer来管理复杂的redux状态 -- 如果排除Redux应用的错误 -- 如果通过使用react-redux库来在React中使用Redux并使得你的应用是响应式的 -- 如何使用redux-thunk库来处理异步API调用 -- 使用Redux编写三个不同的app +- 如何使用多个 reducer 来管理复杂的 redux 状态 +- 如果排除 Redux 应用的错误 +- 如果通过使用 react-redux 库来在 React 中使用 Redux 并使得你的应用是响应式的 +- 如何使用 redux-thunk 库来处理异步 API 调用 +- 使用 Redux 编写三个不同的 app 以及更多。 最后我们将从零开始搭建一个[食物点单应用](https://www.youtube.com/watch?v=2zaPDfCKAvM) ,包括付费模块和应用的部署。 -****想要了解JavaScript、React或者Node.js最新的消息吗? 请在[LinkedIn上关注我](https://www.linkedin.com/in/yogesh-chavan97/)。**** +****想要了解 JavaScript、React 或者 Node.js 最新的消息吗? 请在[LinkedIn 上关注我](https://www.linkedin.com/in/yogesh-chavan97/)。**** diff --git a/chinese/articles/how-to-detect-outliers-in-machine-learning.md b/chinese/articles/how-to-detect-outliers-in-machine-learning.md index 02f2ea2ab..bc8b97cd7 100644 --- a/chinese/articles/how-to-detect-outliers-in-machine-learning.md +++ b/chinese/articles/how-to-detect-outliers-in-machine-learning.md @@ -1,4 +1,4 @@ -> - 原文地址:[如何在机器学习中检测异常值 – 4种检测异常值的方法](https://www.freecodecamp.org/news/how-to-detect-outliers-in-machine-learning/) +> - 原文地址:[如何在机器学习中检测异常值 – 4 种检测异常值的方法](https://www.freecodecamp.org/news/how-to-detect-outliers-in-machine-learning/) > - 原文作者:[Bala Priya C](https://www.freecodecamp.org/news/author/bala-priya/) > - 译者:TouDa > - 校对者: @@ -21,29 +21,29 @@ 处理异常值需要一定的专业性,在_不清楚_所面对的数据和消除异常值工具适用环境的情况下不应冒然使用。 -举例来说,如果你在房价数据集中发现_少量_房价处于150万美金并显著高于所有房价中位数时,这些150万美金的房价很可能是异常值。但是,当数据集包含有大量100万美金以上的房价时,房价呈现上涨趋势。此时将150万美金的房价将视为异常值是_不合适_的。在此情境下,数据分析者需要一定的房地产知识以便正确处理异常值。 +举例来说,如果你在房价数据集中发现_少量_房价处于 150 万美金并显著高于所有房价中位数时,这些 150 万美金的房价很可能是异常值。但是,当数据集包含有大量 100 万美金以上的房价时,房价呈现上涨趋势。此时将 150 万美金的房价将视为异常值是_不合适_的。在此情境下,数据分析者需要一定的房地产知识以便正确处理异常值。 发现异常值的目的是为了去除_真正的异常值_以便构建一个泛用的,即使面对未知数据依旧能表现良好的模型。我们将几个有助于发现异常值的统计工具。 ## 如何通过标准差发现异常值 -如果一组数据,或数据集中某些特征符合[正态分布](https://mathworld.wolfram.com/NormalDistribution.html)时,可以考虑使用正态分布及等效的Z-分数(z-score)来发现异常值。 +如果一组数据,或数据集中某些特征符合[正态分布](https://mathworld.wolfram.com/NormalDistribution.html)时,可以考虑使用正态分布及等效的 Z-分数(z-score)来发现异常值。 在统计学中,标准差(standard deviation)反映了_数据点和均值(mean)之间的关系+,一言以蔽之,标准差衡量的是数据点离数据的算数平均有多远。 -对于正态分布的数据来说,约68.2%的数据在均值的一倍标准差之内。约有95.4%和99.7%的数据点在均值的两倍和三倍标准差以内。 +对于正态分布的数据来说,约 68.2%的数据在均值的一倍标准差之内。约有 95.4%和 99.7%的数据点在均值的两倍和三倍标准差以内。 我们约定标准差为σ,算术平均为μ。 一个发现异常值的方法是将_阈值下限_设为均值减去三倍标准差 (μ - 3\*σ) ,_阈值上限_设为均值加上三倍标准差 (μ + 3\*σ) 。所有在阈值之外的数据点都被视为异常值。 -因为99.7%的数据点会在均值的±三倍标准差以内,此方法将会发现并标记0.3%的数据点为异常值。 +因为 99.7%的数据点会在均值的±三倍标准差以内,此方法将会发现并标记 0.3%的数据点为异常值。 ### 使用标准差检测异常点的代码 我们通过构造一个正态分布的学生分数数据集,用以解释发现异常点的过程。 -第一步,载入必须的python库。 +第一步,载入必须的 python 库。 ```Python import numpy as np @@ -51,7 +51,7 @@ import pandas as pd import seaborn as sns ``` -第二步,定义名为`generate_scores()`的方程,这个方程会生成一个有包含有200个数据的正态分布的分数数据集。我们会使用这个方程生成数据集并存储到变量`scores_data`中。 +第二步,定义名为`generate_scores()`的方程,这个方程会生成一个有包含有 200 个数据的正态分布的分数数据集。我们会使用这个方程生成数据集并存储到变量`scores_data`中。 ```Python @@ -63,7 +63,7 @@ def generate_scores(mean=60,std_dev=12,num_samples=200): scores_data = generate_scores() ``` -你可以使用Seaborn的`displot()`方程来生成数据集分布图像。通过下图可以看出,这个数据集服从正态分布。 +你可以使用 Seaborn 的`displot()`方程来生成数据集分布图像。通过下图可以看出,这个数据集服从正态分布。 ```Python sns.set_theme() @@ -72,7 +72,7 @@ sns.displot(data=scores_data).set(title="Distribution of Scores", xlabel="Scores ![EqfsNr10SYnFcCpdC_5Bdt9Z3jWIsaTI1yCcATGbf10BXTwwqKuJHUMuZT9n6M3bGuU8k4QOA8Vb87BStDzxQRRdQ-MzMwLT2EZZJL4ieB0_u0LnvsUXCkYBTllcll15mF1oGziS1QqZrfYR5A](https://lh6.googleusercontent.com/EqfsNr10SYnFcCpdC_5Bdt9Z3jWIsaTI1yCcATGbf10BXTwwqKuJHUMuZT9n6M3bGuU8k4QOA8Vb87BStDzxQRRdQ-MzMwLT2EZZJL4ieB0_u0LnvsUXCkYBTllcll15mF1oGziS1QqZrfYR5A) -图表1:正态分布 +图表 1:正态分布 接下来,我们可以把该数据集导入[Pandas dataframe](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html) 以便进一步分析。 @@ -126,11 +126,11 @@ score [198 rows x 1 columns] ``` -通过上面的输出,我们发现这个方法移除了两个数据点,数据集`df_scores_filtered`包含有198个数据点。 +通过上面的输出,我们发现这个方法移除了两个数据点,数据集`df_scores_filtered`包含有 198 个数据点。 -## 如何用过z-score检测异常值 +## 如何用过 z-score 检测异常值 -接下来我们尝试使用z-score检测异常值的方法。对于均值为μ标准差为σ的正态分数来说,数据点x的z-score可以这么计算: +接下来我们尝试使用 z-score 检测异常值的方法。对于均值为μ标准差为σ的正态分数来说,数据点 x 的 z-score 可以这么计算: **z = (x - μ)/σ** @@ -139,15 +139,15 @@ score - 当 x = μ 时, z-score 为 0. - 当 x = μ ± 1, μ ± 2, 或 μ ± 3 时, z-score 为 ± 1, ± 2, 或 ± 3. -我们可以发现,通过z-score检测异常值的方法其实等价于我们之前尝试过的通过标准差检测异常值的方法。通过对数据点进行标准化(z = (x - μ)/σ),所有低于标准差方法中低阈值(μ - 3\*σ)的数据点将等价于z-score小于-3的数据点。 +我们可以发现,通过 z-score 检测异常值的方法其实等价于我们之前尝试过的通过标准差检测异常值的方法。通过对数据点进行标准化(z = (x - μ)/σ),所有低于标准差方法中低阈值(μ - 3*σ)的数据点将等价于 z-score 小于-3 的数据点。 -类似的,通过标准化,大于上阈值(μ + 3\*σ)的数据点等价于z-score大于3的数据点。所以上下阈值`[lower_limit, upper_limit]`可以理解为\[-3, 3\]。 +类似的,通过标准化,大于上阈值(μ + 3*σ)的数据点等价于 z-score 大于 3 的数据点。所以上下阈值`[lower_limit, upper_limit]`可以理解为\[-3, 3\]。 -接下来我们将使用z-score方法来检测数据集`df_scores`中的异常点。 +接下来我们将使用 z-score 方法来检测数据集`df_scores`中的异常点。 -### 使用z-score检测异常点方法的代码 +### 使用 z-score 检测异常点方法的代码 -第一步,我们计算所有数据点的z-score,并把z\_score作为新的一列添加进数据集`df_scores`中。 +第一步,我们计算所有数据点的 z-score,并把 z_score 作为新的一列添加进数据集`df_scores`中。 ```Python df_scores['z_score']=(df_scores['score'] - df_scores['score'].mean())/df_scores['score'].std() @@ -161,9 +161,9 @@ score z_score 4 63.0 0.168291 ``` -第二步,我们用阈值\[-3, 3\]来过滤掉数据集`df_scores`中z-score不在范围内的数据点。由于此方法等价与之前解释过的标准差方法,过滤后的数据集包含有198个数据点。 +第二步,我们用阈值\[-3, 3\]来过滤掉数据集`df_scores`中 z-score 不在范围内的数据点。由于此方法等价与之前解释过的标准差方法,过滤后的数据集包含有 198 个数据点。 -以上两种方法(通过标准差检测异常值和通过z-score来检测异常值)只适用于服从正态分布的数据。 +以上两种方法(通过标准差检测异常值和通过 z-score 来检测异常值)只适用于服从正态分布的数据。 接下来,我们将研究两种对数据分布_没有要求_的检测异常值的方法。 @@ -171,17 +171,17 @@ score z_score 统计学上,四分位距(IQR)反映了给定数据集中第一和第三分位数之间的距离。 -- 第一份位数又称为四分之一分位数或25%分位数。 -- 我们约定`q25`为第一分位数,这意味这数据集中有25%的数据点小于`q25`。 -- 第三份位数又称为四分之三分位数或75%分位数。 -- 我们约定`q75`为第三分位数,这意味这数据集中有75%的数据点大于`q75`。 +- 第一份位数又称为四分之一分位数或 25%分位数。 +- 我们约定`q25`为第一分位数,这意味这数据集中有 25%的数据点小于`q25`。 +- 第三份位数又称为四分之三分位数或 75%分位数。 +- 我们约定`q75`为第三分位数,这意味这数据集中有 75%的数据点大于`q75`。 - 使用上述表述方法, `IQR = q75 - q25`. ### 通过四分位距(IQR)检测异常值方法的代码 我们可以使用箱型图或者箱线图来图形化数据集中的异常值。明显位于箱线图之外的数据点可以被视为异常值。 -我们可以使用Seaborn中的`boxplot`函数生成箱型图。 +我们可以使用 Seaborn 中的`boxplot`函数生成箱型图。 ```Python sns.boxplot(data=scores_data).set(title="Box Plot of Scores") @@ -207,7 +207,7 @@ min 20.000000 max 98.000000 ``` -我们使用25%和75%分位数来计算四分位距(IQR),并使用它们(25%分位数到75%分位数)作为阈值。 +我们使用 25%和 75%分位数来计算四分位距(IQR),并使用它们(25%分位数到 75%分位数)作为阈值。 ```Python IQR = 67-54 @@ -241,7 +241,7 @@ score [192 rows x 1 columns] ``` -从以上结果可以看出,四分位距方法把八个数据点标记为异常值,筛选后的数据集包含有192个数据点。 +从以上结果可以看出,四分位距方法把八个数据点标记为异常值,筛选后的数据集包含有 192 个数据点。 我们除了可以使用`.describe()`来计算分位数,还可以使用[NumPy](https://numpy.org/doc/stable/user/index.html#user)库中的`.percentile()`函数来计算分位数。 @@ -261,11 +261,11 @@ print(IQR) 上一节中的四分位距方法使用`[q25 - 1.5*IQR, q75 + 1.5*IQR]`作为阈值,不在此范围内的数据点被标记为异常值。四分位距方法适用于数据点分布较分散的数据集,这种方法倾向于过多的标记异常值。 -为了更好地标记异常值,我们可能需要使用除第一和第三分位数之外的其他分位数。接下来我们将通过百分位数方法再次检测scores数据集中的异常值。 +为了更好地标记异常值,我们可能需要使用除第一和第三分位数之外的其他分位数。接下来我们将通过百分位数方法再次检测 scores 数据集中的异常值。 ### 使用百分位数检测异常值方法的代码 -第一步,我们首先找到0.5百分位数和99.5百分位数的范围。我们可以利用`.percentile()`函数带入`q = [0.5, 99.5]`来计算这个范围,代码如下 +第一步,我们首先找到 0.5 百分位数和 99.5 百分位数的范围。我们可以利用`.percentile()`函数带入`q = [0.5, 99.5]`来计算这个范围,代码如下 ```Python lower_limit, upper_limit = np.percentile(a=df_scores,q=[0.5,99.5]) @@ -299,7 +299,7 @@ score [198 rows x 1 columns] ``` -通过以上代码的结果我们可以看出有两个数据点被标记为异常值,过滤后的数据集包含有198个数据点。 +通过以上代码的结果我们可以看出有两个数据点被标记为异常值,过滤后的数据集包含有 198 个数据点。 ## 结语 @@ -307,7 +307,7 @@ score 总结如下: -- 如果要研究的数据或特征符合正态分布,我们可以使用标准差或z-score来标记异常值。通常我们标记超出均值正负三倍标准差的数据点为异常值。 +- 如果要研究的数据或特征符合正态分布,我们可以使用标准差或 z-score 来标记异常值。通常我们标记超出均值正负三倍标准差的数据点为异常值。 - 如果要研究的数据不服从正态分布是,我们可以使用四分位距或者百分位数来检测异常值。 此外我们还介绍了检测异常值的一些特殊情况。当数据集中有大量数据被标记为异常值时要慎重,因为造成这种情况的原因可能是因为我们手头的数据集属于一个更大更完备的数据集,被标记的异常值可能描述了更大的数据集的分布情况。 diff --git a/chinese/articles/how-to-flush-dns-on-mac-macos-clear-dns-cache.md b/chinese/articles/how-to-flush-dns-on-mac-macos-clear-dns-cache.md index a66099ff4..525d70027 100644 --- a/chinese/articles/how-to-flush-dns-on-mac-macos-clear-dns-cache.md +++ b/chinese/articles/how-to-flush-dns-on-mac-macos-clear-dns-cache.md @@ -10,10 +10,10 @@ 以下是我们将在本指南中讨论的内容: 1. [什么是 DNS 缓存?](./#what-is-dns-cache?) - 1. [为什么刷新DNS缓存很重要](./#why-flushing-dns-cache-is-important) + 1. [为什么刷新 DNS 缓存很重要](./#why-flushing-dns-cache-is-important) 2. [如何在 MacOS 上刷新 DNS 缓存](./#how-to-flush-dns-on-macos) - 1. [如何在MacOS上访问终端应用程序](./#how-to-access-the-terminal-application-on-macos) - 2. [如何根据你的MacOS版本清除DNS缓存](./#how-to-clear-dns-cache-for-your-macos-version) + 1. [如何在 MacOS 上访问终端应用程序](./#how-to-access-the-terminal-application-on-macos) + 2. [如何根据你的 MacOS 版本清除 DNS 缓存](./#how-to-clear-dns-cache-for-your-macos-version)

什么是 DNS 缓存?

@@ -23,17 +23,17 @@ DNS(域名系统的简称)将域名映射到其相关的 IP 地址。 一个域名,如 `freecodecamp.org`,很容易被人类阅读、理解和回忆。 -IP地址(IP是互联网协议的简称)是一个机器可读的地址,由一系列独特的数字组成。这些数字可以识别连接到互联网的设备。 +IP 地址(IP 是互联网协议的简称)是一个机器可读的地址,由一系列独特的数字组成。这些数字可以识别连接到互联网的设备。 它们的格式对人不太友好,因为每次你想访问一个网站时,都很难记住一个准确的数字序列。 -然后 DNS 将 `freecodecamp.org` 映射到其相关的IP地址 - `104.26.3.33`。 +然后 DNS 将 `freecodecamp.org` 映射到其相关的 IP 地址 - `104.26.3.33`。 -把DNS缓存看作是你Mac上的一个本地存储区。 +把 DNS 缓存看作是你 Mac 上的一个本地存储区。 它暂时存储并跟踪你的计算机的活动记录,如最近的网站访问。 -每次你通过输入一个网站的URL(统一资源定位器的简称)访问该网站时,DNS缓存会保存与该网站相关的IP地址。 +每次你通过输入一个网站的 URL(统一资源定位器的简称)访问该网站时,DNS 缓存会保存与该网站相关的 IP 地址。 当你第二次访问同一网站时,查询过程更有效,查询时间也更短。 @@ -57,7 +57,7 @@ IP地址(IP是互联网协议的简称)是一个机器可读的地址,由 2.**刷新 DNS 缓存可以防止网络安全威胁、恶意攻击和 DNS 缓存中毒的发生**. -黑客可以访问并篡改你保存的DNS缓存记录。 +黑客可以访问并篡改你保存的 DNS 缓存记录。 例如,他们可以操纵和改变与你已经访问过的网站的域名相关的 IP 地址,并将其映射到一个恶意的地址。 @@ -69,7 +69,7 @@ IP地址(IP是互联网协议的简称)是一个机器可读的地址,由

如何在 MacOS 上刷新 DNS 缓存

-清除Mac上的DNS缓存是一个相对简单的过程,即使你没有很多技术知识。 +清除 Mac 上的 DNS 缓存是一个相对简单的过程,即使你没有很多技术知识。 以下是你将需要的东西。 @@ -116,7 +116,7 @@ MacOS 有一个内置的 CLI(命令行界面),名为 `Terminal.app`,它 在下面的表格中,你将看到按时间倒序排列的 MacOS 版本--从最近的版本到最老的版本。 -导航到你的Mac版本并复制相应的命令。 +导航到你的 Mac 版本并复制相应的命令。 | MacOS 版本 | 使用的命令 | | --- | --- | diff --git a/chinese/articles/how-to-get-a-docker-container-ip-address-explained-with-examples.md b/chinese/articles/how-to-get-a-docker-container-ip-address-explained-with-examples.md index 329eabb48..ead2e023b 100644 --- a/chinese/articles/how-to-get-a-docker-container-ip-address-explained-with-examples.md +++ b/chinese/articles/how-to-get-a-docker-container-ip-address-explained-with-examples.md @@ -21,7 +21,7 @@ Docker 提供了在隔离环境(容器)中打包和运行应用的能力。 ![](https://www.freecodecamp.org/news/content/images/2020/06/docker-network.png) -Docker 网络图摘自[了解Docker网络驱动程序及其用例][1] +Docker 网络图摘自[了解 Docker 网络驱动程序及其用例][1] `bridge` 网络是主机内部的专用网络,容器可以通过它进行通信。也可暴漏端口在外部访问。 @@ -47,7 +47,7 @@ ad4e4c24568e host host local ## Docker 容器的 IP 地址 -默认情况下,会为连接到容器的每个 Docker 网络分配一个IP地址,并为每个网络分配一个默认的子网掩码,用作稍后分配 IP的地址池。 +默认情况下,会为连接到容器的每个 Docker 网络分配一个 IP 地址,并为每个网络分配一个默认的子网掩码,用作稍后分配 IP 的地址池。 通常 Docker 默认使用 **172.17. 0.0/16** 作为容器网络的子网。 @@ -176,7 +176,7 @@ $ docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 7 172.18.0.5 ``` -之前不是说 Docker 使用容器网络默认的 **172.17. 0.0/16** 的子网吗?为什么和返回的 IP地址 **172.18.0.5** 并不在一个网段呢? +之前不是说 Docker 使用容器网络默认的 **172.17. 0.0/16** 的子网吗?为什么和返回的 IP 地址 **172.18.0.5** 并不在一个网段呢? ![](https://www.freecodecamp.org/news/content/images/2020/06/Screen-Shot-2020-06-22-at-3.25.07-PM.png) @@ -276,7 +276,7 @@ PING dockerhive_hive-server (172.18.0.4): 56 data bytes ## **拓展** -所有的例子都是在 Linux 的VM 计算引擎下执行,如果你用的是 macOS 或者 Windows 例子里的命令可能会有些区别。 +所有的例子都是在 Linux 的 VM 计算引擎下执行,如果你用的是 macOS 或者 Windows 例子里的命令可能会有些区别。 另外请记住,例子中所有的 IP 地址都是针对 `docker-hive_default` 网络内部的。如果需要在外部连接这些容器,则需要主机的外部 IP(假设容器正确暴漏了端口)。 diff --git a/chinese/articles/how-to-get-headings-and-ids-for-your-freecodecamp-blog-posts.md b/chinese/articles/how-to-get-headings-and-ids-for-your-freecodecamp-blog-posts.md index a72d1b123..0718b3fca 100644 --- a/chinese/articles/how-to-get-headings-and-ids-for-your-freecodecamp-blog-posts.md +++ b/chinese/articles/how-to-get-headings-and-ids-for-your-freecodecamp-blog-posts.md @@ -19,7 +19,7 @@ 问题是,我需要为其添加链接的文章中有 33 个标题。滚动浏览 10,000 字文档以获取标题然后滚动到顶部以将其添加到目录中的想法让我想知道是否有更好的方法来做到这一点! -### 目录: +### 目录 - [JavaScript 来拯救!](#javascript-to-the-rescue-) - [获取元素属性](#get-the-element-properties) diff --git a/chinese/articles/how-to-get-started-with-d3-and-react-c7da74a5bd9f.md b/chinese/articles/how-to-get-started-with-d3-and-react-c7da74a5bd9f.md index db5671f44..e6bd49565 100644 --- a/chinese/articles/how-to-get-started-with-d3-and-react-c7da74a5bd9f.md +++ b/chinese/articles/how-to-get-started-with-d3-and-react-c7da74a5bd9f.md @@ -276,7 +276,7 @@ Working with D3 can seem difficult in the beginning. Once you get the basics dow Finally, learning D3 is also a good way of getting fluent at traversing and manipulating the DOM. Understanding the DOM is often a quality interviewers look for in front end developers. -#### Resources: +#### Resources [D3 Tutorials](https://github.com/d3/d3/wiki/Tutorials) suggested by D3 diff --git a/chinese/articles/how-to-get-things-done-lessons-in-productivity.md b/chinese/articles/how-to-get-things-done-lessons-in-productivity.md index d9b4f03c6..72008754d 100644 --- a/chinese/articles/how-to-get-things-done-lessons-in-productivity.md +++ b/chinese/articles/how-to-get-things-done-lessons-in-productivity.md @@ -208,7 +208,7 @@ 让我们一个一个来看。 -### 1) 把你对第一步的要求从“A+”降低到“B”。 +### 1) 把你对第一步的要求从“A+”降低到“B” 如果你是一个完美主义者,你会产生拖延。如果你的目标过于宏大,以致不知如何迈出第一步,你也会产生拖延。 @@ -218,7 +218,7 @@ > 一个完美主义者要想解决拖延问题,就要从以“B”而非“A+”来要求自己的第一步开始做起。也就是说,你要退一步,让自己的第一步看着“还不错”就可以了,而非一定要“完完美美”。 -### 2) 明确接下来的一小步。 +### 2) 明确接下来的一小步 ![](https://images.unsplash.com/photo-1558800199-1f485762061b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MXwxMTc3M3wwfDF8c2VhcmNofDE5fHxjb25mdXNlZHxlbnwwfHx8&ixlib=rb-1.2.1&q=80&w=2000) [Image Credit][16] – 每当你感到不清楚下一步该干嘛时,你的意识就会阻碍你继续向前。每当你创建的任务含混不清时,你就为拖延症提供了可乘之机。 @@ -231,7 +231,7 @@ - _“绝佳”_ 具体指哪方面有多好呢? - 什么 [类型的编程项目][17] 应该放在我的作品集中呢? -- 我是重新做一个自己的App比较好还是用一个现有的模版呢? +- 我是重新做一个自己的 App 比较好还是用一个现有的模版呢? - 我需要学哪种语言呢?需要多长时间?什么时候学?怎么学? 当你头脑中有太多个悬而未决的问题有待解决时,你的意识就会拒绝采取任何行动,从而陷入拖延之中。 @@ -262,7 +262,7 @@ 你可以试试下面这些行为心理学的原理。 - **截止日期效应**:打开网址 [www.tomato-timer.com][21],设置一个 5 分钟的倒计时,然后尽最快速度去开始着手你的下一个最小一步。大多数情况下,你会发现自己工作了不止 5 分钟。这就对了,因为在 [蔡格尼克记忆效应][22] 作用下,你会倾向完成你已经开始的工作。 -- **负面增强,直到你开始工作为止**:如果你发现自己不知为什么在这5分钟啥也没干计时器就截止了,那就重新开始计时。一条附加的建议是,打开计时器设置,设置一个你极其讨厌的提醒铃声。重复这个过程5遍,每一遍都通过看着倒计时触发铃声的方式来惩罚自己! +- **负面增强,直到你开始工作为止**:如果你发现自己不知为什么在这 5 分钟啥也没干计时器就截止了,那就重新开始计时。一条附加的建议是,打开计时器设置,设置一个你极其讨厌的提醒铃声。重复这个过程 5 遍,每一遍都通过看着倒计时触发铃声的方式来惩罚自己! - **重新反思你的动力**:假如过了 5 遍之后,你还是什么也没干。那就说明你根本就没有动力去做这件事,你需要思考一个更加深层次的问题: > “我做完这件事对我有什么意义?” @@ -369,7 +369,7 @@ [**加入我的电子邮件社区**][32]:我会为编码小白和初级开发者分享一些技巧,以帮助他们更快成长,并使他们信心加倍。如果您有兴趣,欢迎[加入社区][33]。 -[**在Twitter上关注我**][34]:需要更多高效工作的技巧?或是有任何疑问?我的 Twitter 账号最为活跃,欢迎随时 [与我联系][35]。我的私信已打开。 +[**在 Twitter 上关注我**][34]:需要更多高效工作的技巧?或是有任何疑问?我的 Twitter 账号最为活跃,欢迎随时 [与我联系][35]。我的私信已打开。 [**阅读更多文章**][36]:如果您想阅读更多文章,也欢迎 [查看我博客上的其他文章][37]。 diff --git a/chinese/articles/how-to-google-like-a-pro-10-tips-for-effective-googling.md b/chinese/articles/how-to-google-like-a-pro-10-tips-for-effective-googling.md index 20ef9684d..680f679a5 100644 --- a/chinese/articles/how-to-google-like-a-pro-10-tips-for-effective-googling.md +++ b/chinese/articles/how-to-google-like-a-pro-10-tips-for-effective-googling.md @@ -5,23 +5,23 @@ ![How to Google like a Pro – 10 Tips for More Effective Googling](https://www.freecodecamp.org/news/content/images/size/w2000/2022/08/Group-61.png) -你几乎可以在互联网上搜索到任何内容,而Google是大多数人选择搜索信息的主要途径之一。 +你几乎可以在互联网上搜索到任何内容,而 Google 是大多数人选择搜索信息的主要途径之一。 -尽管频繁地使用Google,但是大部分互联网用户都不知道如何**快速**和**高效**地使用Google搜索。 +尽管频繁地使用 Google,但是大部分互联网用户都不知道如何**快速**和**高效**地使用 Google 搜索。 -可以说使用Google是一门艺术。 +可以说使用 Google 是一门艺术。 想要获得正确的答案,你需要提出正确的问题。想要快速地获得正确的答案,你必须知道 **如何** 提正确的问题。 每一个人都应该学学这个**实操(how)**部分,这也是一篇教程教学目的所在。 以下是一些提示和技巧,帮助你快速、高效地寻找到问题的正确答案。 -## 如何高效地使用Google搜索 +## 如何高效地使用 Google 搜索 ### 1\. 使用引号来获得“完全一致”的匹配搜获 -当我们按照惯常的习惯在Google上搜索的时候,实际上我们使用的是**浅**搜索。 这样得到的搜索结果可能会包括你的搜索查询里没有的词。 +当我们按照惯常的习惯在 Google 上搜索的时候,实际上我们使用的是**浅**搜索。 这样得到的搜索结果可能会包括你的搜索查询里没有的词。 -但如果你将自己的查询请求或者问题(词汇或者短语)用引号打包,像这样 —— `"你的问题"`,Google就会进入深度搜索模式。这样的搜索结果会**包含所有**在引号内你的搜索问题里的词汇。 +但如果你将自己的查询请求或者问题(词汇或者短语)用引号打包,像这样 —— `"你的问题"`,Google 就会进入深度搜索模式。这样的搜索结果会**包含所有**在引号内你的搜索问题里的词汇。 当你需要在搜索结果中呈现出某个特定的词汇或者短语的时候,这个方法就十分有用。 @@ -29,11 +29,11 @@ ### 2\. 使用 `site:` 在特定网站搜索 -如果你希望Google返回的搜索结果是来自某个特定的网站,仅需要在你的搜索问题前添加`site:` 。 +如果你希望 Google 返回的搜索结果是来自某个特定的网站,仅需要在你的搜索问题前添加`site:` 。 当你想要在某个没有内置搜索选项的网站,或者内置搜索不太好用的网站进行搜索的时候,这个方法可以派上用场。 -假设,你的搜索问题是 **site:freecodecamp.org react**, 返回的搜索结果仅会是来自[www.freecodecamp.org](https://www.freecodecamp.org/) 关于React的话题。 +假设,你的搜索问题是 **site:freecodecamp.org react**, 返回的搜索结果仅会是来自[www.freecodecamp.org](https://www.freecodecamp.org/) 关于 React 的话题。 ![img2](https://www.freecodecamp.org/news/content/images/2022/08/img2.PNG) @@ -41,7 +41,7 @@ 如果你不希望自己的搜索结果中出现某个词汇或者短语,可以在那个单词前添加 `-` 。 -假设你的搜索内容是 => **how to write components in React -class**(如何编写React组件 -class),返回的搜索结果会不包含"class"。所以, (如果你了解一点React),你会发现搜索结果只包含函数式组件。 +假设你的搜索内容是 => **how to write components in React -class**(如何编写 React 组件 -class),返回的搜索结果会不包含"class"。所以, (如果你了解一点 React),你会发现搜索结果只包含函数式组件。 ![img3](https://www.freecodecamp.org/news/content/images/2022/08/img3.PNG) @@ -49,67 +49,67 @@ 如果你想搜索特定大小的图片,可以在你的搜索查询中使用`imagesize:`标签,并且标注好以**pixels(像素)**为单位的宽和高。 -尺寸仅以 **pixels** 为单位 – 例如:`imagesize:500x500`的搜索结果是尺寸为 500px x 500px的图片。 所以如果搜索查询是 => "cute dog images imagesize:500x600"(可爱小狗图片 imagesize:500*600)的搜索结果是尺寸为`500px x 600px`的可爱小狗的图片。 +尺寸仅以 **pixels** 为单位 – 例如:`imagesize:500x500`的搜索结果是尺寸为 500px x 500px 的图片。 所以如果搜索查询是 => "cute dog images imagesize:500x600"(可爱小狗图片 imagesize:500*600)的搜索结果是尺寸为`500px x 600px`的可爱小狗的图片。 ![img4](https://www.freecodecamp.org/news/content/images/2022/08/img4.PNG) ### 5\. 使用`filetype:`搜索特定文件类型 -如果你希望搜索的结果包含了某个特定的文件类型,如:PDF或者PPT,就可以添加`filetype:<扩展名>` (不需要尖括号)。 如, **react tutorial filetype:pdf**(react教学 filetype:pdf)的搜索结果是PDF格式的,如图: +如果你希望搜索的结果包含了某个特定的文件类型,如:PDF 或者 PPT,就可以添加`filetype:<扩展名>` (不需要尖括号)。 如, **react tutorial filetype:pdf**(react 教学 filetype:pdf)的搜索结果是 PDF 格式的,如图: ![img5](https://www.freecodecamp.org/news/content/images/2022/08/img5.PNG) -### 6\. 在搜索中使用通配符`*` +### 6\. 在搜索中使用通配符 -如果你不确定搜索查询/问题或者忘记了其中的词汇,你可以在搜索中使用通配符`*`,Google会用相关的词汇来替换通配符。 +如果你不确定搜索查询/问题或者忘记了其中的词汇,你可以在搜索中使用通配符`*`,Google 会用相关的词汇来替换通配符。 -例如,如果你搜索 => the \* of money(钱的 *),会产生如下结果。 Google会返回一系列搜索页面,包括 "the _exchange_ of money"(金钱交易)、 "the _use_ of money"(金钱的用途)、 "the _role_ of money"(金钱的角色)、 "the _psychology_ of money"(金钱心理学)等。 +例如,如果你搜索 => the * of money(钱的 *),会产生如下结果。 Google 会返回一系列搜索页面,包括 "the _exchange_ of money"(金钱交易)、 "the _use_ of money"(金钱的用途)、 "the _role_ of money"(金钱的角色)、 "the _psychology_ of money"(金钱心理学)等。 ![img6](https://www.freecodecamp.org/news/content/images/2022/08/img6.PNG) ### 7\. 使用 `OR`(或)、 `AND`(且)逻辑合并搜索 -如果你希望搜索结果包含两个词汇,可以在其中使用`AND` 关键字。例如,你搜素=> **React AND Angular**, Google会返回即包含React,又包含Angular的结果。 +如果你希望搜索结果包含两个词汇,可以在其中使用`AND` 关键字。例如,你搜素=> **React AND Angular**, Google 会返回即包含 React,又包含 Angular 的结果。 -同样的,如果你希望搜索结果包含两个词汇中的任意一个, 可以在其中使用 `OR` 关键字。 例如,你搜索 => **React OR Angular** , Google会返回包含React或者Angular的结果,甚至包含两者的结果。 +同样的,如果你希望搜索结果包含两个词汇中的任意一个, 可以在其中使用 `OR` 关键字。 例如,你搜索 => **React OR Angular** , Google 会返回包含 React 或者 Angular 的结果,甚至包含两者的结果。 ![img7](https://www.freecodecamp.org/news/content/images/2022/08/img7.PNG) ### 8\. 在两个数字间使用 `AFTER:`(在……之后)、 `BEFORE:`(在……之前) 或 `..` 来筛选结果 -如果你希望Google的搜索结果是发布在特定年份之后的,你可以使用`AFTER:`标签。例如,你搜索 => **React tutorials AFTER:2020**(React教程 AFTER:2020)会返回迟于2020年发布的结果。 +如果你希望 Google 的搜索结果是发布在特定年份之后的,你可以使用`AFTER:`标签。例如,你搜索 => **React tutorials AFTER:2020**(React 教程 AFTER:2020)会返回迟于 2020 年发布的结果。 同样的,如果添加`BEFORE:` 标签,会返回早于某个特定年份的结果。 你也可以搜索发布在某个年份范围内的结果,或者介于两个数字之间的任意事物。仅在两个数字之间添加 `..`即可,如果有计算单位,也可以添加计算单位。 ![img8](https://www.freecodecamp.org/news/content/images/2022/08/img8.PNG) -### 9\. 使用 `related:`查询相关网站。 +### 9\. 使用 `related:`查询相关网站 如果你想知道和某个特定网站相关的其他网站,可以使用`related:`标签。 -例如,你搜索 **related:google.com** 就会得到所有和Google类似的网站,如:Bing、Yahoo、DuckDuckGo等。 +例如,你搜索 **related:google.com** 就会得到所有和 Google 类似的网站,如:Bing、Yahoo、DuckDuckGo 等。 ![img9](https://www.freecodecamp.org/news/content/images/2022/08/img9.PNG) -### 10\. 使用`cache:`查看Google对某个网站的缓存版本 +### 10\. 使用`cache:`查看 Google 对某个网站的缓存版本 -Google提供网站的缓存版本以提高搜索速度。想要知道Google是否缓存了某个网站,可以在网站URL前面添加`cache:`。 +Google 提供网站的缓存版本以提高搜索速度。想要知道 Google 是否缓存了某个网站,可以在网站 URL 前面添加`cache:`。 -这一招对于**web开发**来说非常管用,可以检查现在浏览的到底是Google缓存的网站,还是提交了一些修改之后的最近版本的网站。 例如,在写这篇教程的时候(2022年8月4日)搜索 => **cache:www.sohamderoy.dev** ,得到的结果如下: +这一招对于**web 开发**来说非常管用,可以检查现在浏览的到底是 Google 缓存的网站,还是提交了一些修改之后的最近版本的网站。 例如,在写这篇教程的时候(2022 年 8 月 4 日)搜索 => **cache:www.sohamderoy.dev** ,得到的结果如下: ![img10a](https://www.freecodecamp.org/news/content/images/2022/08/img10a.png) -你可以看到,即便我是在2022年8月4日搜索这个网站,我得到的搜索结果是Google缓存的2022年8月3日的版本。 +你可以看到,即便我是在 2022 年 8 月 4 日搜索这个网站,我得到的搜索结果是 Google 缓存的 2022 年 8 月 3 日的版本。 正如[Google](https://support.google.com/websearch/answer/2466433?visit_id=637790664879774647-1036329470&p=adv_pages_similar&hl=en&rd=1)自己描述的一样,我们必须知道“不是所有的搜索运算都会穷举所有结果。” -但我仍相信以上的方法可以帮助你提高使用Google搜索的效率。我希望我的解释足够清晰,并且在日后你能够将这些方法运用在日常的Google搜索中。 +但我仍相信以上的方法可以帮助你提高使用 Google 搜索的效率。我希望我的解释足够清晰,并且在日后你能够将这些方法运用在日常的 Google 搜索中。 ## 总结 -感谢阅读!希望你喜欢这篇关于提高Google搜索效率的简短教程。 +感谢阅读!希望你喜欢这篇关于提高 Google 搜索效率的简短教程。 -希望你可以转发给自己的朋友 —— 我将非常感谢。你可以在LinkedIn和Twitter上关注我的最新精彩内容!再见! 🖖 +希望你可以转发给自己的朋友 —— 我将非常感谢。你可以在 LinkedIn 和 Twitter 上关注我的最新精彩内容!再见! 🖖 ### 社交媒体链接 diff --git a/chinese/articles/how-to-install-arch-linux.md b/chinese/articles/how-to-install-arch-linux.md index 354743e79..3d27f3d87 100644 --- a/chinese/articles/how-to-install-arch-linux.md +++ b/chinese/articles/how-to-install-arch-linux.md @@ -11,7 +11,7 @@ 摘自 arch [wiki](https://wiki.archlinux.org/title/Arch_Linux), -> Arch Linux 是通用 x86-64 [GNU](https://wiki.archlinux.org/title/GNU)/Linux 发行版。Arch采用滚动升级模式,尽全力提供最新的稳定版软件。 +> Arch Linux 是通用 x86-64 [GNU](https://wiki.archlinux.org/title/GNU)/Linux 发行版。Arch 采用滚动升级模式,尽全力提供最新的稳定版软件。 > > 初始安装的 Arch 只是一个基本系统,随后用户可以根据自己的喜好安装需要的软件并配置成符合自己理想的系统. @@ -87,13 +87,13 @@ - 你的电脑使用的是 UEFI,而不是 BIOS - 你有一个足够大(4GB)的 U 盘,可以用它来启动 Linux - 有安装 Linux(Ubuntu/Fedora)的经验 -- 有足够的空间在硬盘或 SSD上安装 linux +- 有足够的空间在硬盘或 SSD 上安装 linux 差不多就是这样。如果你具备以上所有条件,可以开始了。 ## 如何创建可引导的 Arch Linux U 盘 -要下载 Arch Linux,请访问 [https://archlinux.org/download/](https://archlinux.org/download/) 并下载最新版本(本文撰写时为 2022.01.01)。ISO的大小应该在870兆字节左右。 +要下载 Arch Linux,请访问 [https://archlinux.org/download/](https://archlinux.org/download/) 并下载最新版本(本文撰写时为 2022.01.01)。ISO 的大小应该在 870 兆字节左右。 下载后,需要将其写入 U 盘。可以使用 [Fedora Media Writer](https://getfedora.org/en/workstation/download/)。在系统上下载并安装应用程序。连接 U 盘打开应用程序: @@ -113,7 +113,7 @@ 如何禁用此功能的详细说明因主板或笔记本电脑品牌而异。需要你自己在互联网中搜索来找到相应的方法。 -第二个操作仅在安装和 Windows 共存的 Arch Linux 双系统时才适用。Windows有一个名为“快速启动”的功能,它通过部分休眠来缩短计算机的启动时间。 +第二个操作仅在安装和 Windows 共存的 Arch Linux 双系统时才适用。Windows 有一个名为“快速启动”的功能,它通过部分休眠来缩短计算机的启动时间。 通常这是一个很好的特性,它可以防止双引导配置中的任何其他操作系统在此过程中访问硬盘。 @@ -747,7 +747,7 @@ mkdir /boot/efi mount /dev/sda1 /boot/efi ``` -希望你还记得我们在分区阶段将`/dev/sda1`设备格式化为EFI系统分区。确保为你的设备使用正确的设备。 +希望你还记得我们在分区阶段将`/dev/sda1`设备格式化为 EFI 系统分区。确保为你的设备使用正确的设备。 现在,我们将使用 `grub-install` 命令在新挂载的 EFI 系统分区中安装 GRUB: diff --git a/chinese/articles/how-to-lazy-load-images-in-react.md b/chinese/articles/how-to-lazy-load-images-in-react.md index da20cd27c..26c1784b5 100644 --- a/chinese/articles/how-to-lazy-load-images-in-react.md +++ b/chinese/articles/how-to-lazy-load-images-in-react.md @@ -5,11 +5,11 @@ ![How to Lazy Load Images in React](https://www.freecodecamp.org/news/content/images/size/w2000/2022/08/How-to-lazy-load-images-in-React-1.png) -优化网站上的资源(如图片懒加载)是提高web性能最有效的办法之一。 +优化网站上的资源(如图片懒加载)是提高 web 性能最有效的办法之一。 这样做可以确保网站高速运行、有良好的[SEO](https://en.wikipedia.org/wiki/Search_engine_optimization)以及用户体验。 -在这篇文章中,你将学习如何在React应用中实现懒加载。我将讲解这样做的好处,以及如何通过代码中实现。 +在这篇文章中,你将学习如何在 React 应用中实现懒加载。我将讲解这样做的好处,以及如何通过代码中实现。 在开始之前,让我解释一下什么是懒加载,以及它是如何运作的。 @@ -23,7 +23,7 @@ 这背后的理念是推迟视口以外图片的加载,以此来减少带宽的使用,提高用户体验,并加快页面加载的速度。 -现在懒加载已经是一种`image/iframe`,所以仅需在img/iframe元素中添加 `loading=" lazy"`属性,如下: +现在懒加载已经是一种`image/iframe`,所以仅需在 img/iframe 元素中添加 `loading=" lazy"`属性,如下: ```html Image Alt @@ -32,19 +32,19 @@ 在不需要更多配置的情况下就可以生效。 -遗憾的是,根据[caniuse.com](https://caniuse.com/?search=lazyloadin)显示,不是所有的浏览器都支持这样的方式。 同时,在Firefox中,`loading= "lazy"` 属性并不能在`iframe`元素中使用。 在这些情况下,你需要搭配其他的库或者工具一起使用。 +遗憾的是,根据[caniuse.com](https://caniuse.com/?search=lazyloadin)显示,不是所有的浏览器都支持这样的方式。 同时,在 Firefox 中,`loading= "lazy"` 属性并不能在`iframe`元素中使用。 在这些情况下,你需要搭配其他的库或者工具一起使用。 ## 开始实现懒加载 本文将使用一个名为[React Lazy Load Image Component](https://www.npmjs.com/package/react-lazy-load-image-component)的库。 -这个流行的库能够帮助你快速且轻松地在React应用中实现一些图片渲染的能力和效果。 +这个流行的库能够帮助你快速且轻松地在 React 应用中实现一些图片渲染的能力和效果。 -代码示例可以在[GitHub仓库](https://github.com/evavic44/react-lazyload)查看。 +代码示例可以在[GitHub 仓库](https://github.com/evavic44/react-lazyload)查看。 -### 步骤 1 – 安装React Lazy Load Image组件 +### 步骤 1 – 安装 React Lazy Load Image 组件 -我们首先通过NPM来安装React lazy load image组件: +我们首先通过 NPM 来安装 React lazy load image 组件: ```js // Yarn @@ -56,7 +56,7 @@ $ npm i --save react-lazy-load-image-component ### 步骤 2 – 引用组件 -这里我们将直接通过组件来引用,你也可以通过外部URL的方式来引用图片。在这个例子中,我将图片作为images目录下的组件引用: +这里我们将直接通过组件来引用,你也可以通过外部 URL 的方式来引用图片。在这个例子中,我将图片作为 images 目录下的组件引用: ```js import Image from "../images/bird.jpg"; @@ -89,7 +89,7 @@ export default function App() { 同时,需要明确定义图片的宽和高,以避免[累积布局偏移(CLS)](https://web.dev/cls/)问题。 -若要查看组件效果,可以打开浏览器的开发者工具,如果是Windows和Linux系统,使用组合键 Ctrl + Shift + E;如果是Mac使用 Cmd + Opt + E。然后将节流设置为较低的网络(3G),禁用缓存,并刷新页面。 +若要查看组件效果,可以打开浏览器的开发者工具,如果是 Windows 和 Linux 系统,使用组合键 Ctrl + Shift + E;如果是 Mac 使用 Cmd + Opt + E。然后将节流设置为较低的网络(3G),禁用缓存,并刷新页面。 ![network-tab](https://www.freecodecamp.org/news/content/images/2022/08/network-tab.png) @@ -102,7 +102,7 @@ export default function App() { Default(默认)时,因为图片并不在视口,图片没有加载。一旦你开始滚动,图片到达视图,懒加载就被禁用,我们就可以看到图片。 -[Live演示](https://react-lazyload.vercel.app/default) +[Live 演示](https://react-lazyload.vercel.app/default) ### 步骤 4 – 添加图片占位符 @@ -144,11 +144,11 @@ import PlaceholderImage from "../images/placeholder.jpg"; ### 步骤 5 – 添加模糊图 -LazyLoadImage提供一个插件,在加载前模糊图片,加载完成后去除掉模糊。 +LazyLoadImage 提供一个插件,在加载前模糊图片,加载完成后去除掉模糊。 将其与占位符图片结合,可以提升整体效果。 -为了使用模糊效果,我们必须引用控制它的CSS文件: +为了使用模糊效果,我们必须引用控制它的 CSS 文件: ```js import Image from "../images/bird.jpg"; @@ -167,13 +167,13 @@ import 'react-lazy-load-image-component/src/effects/blur.css'; ![React-lazyload---Blur](https://www.freecodecamp.org/news/content/images/2022/08/React-lazyload---Blur.gif) -[Live演示](https://react-lazyload.vercel.app/blur) +[Live 演示](https://react-lazyload.vercel.app/blur) ## 为什么要对图片实现懒加载? -你可能会好奇,为什么要在web应用中使用懒加载这么麻烦的技术。这样做值得吗,我为什么要关注文章中的观点? +你可能会好奇,为什么要在 web 应用中使用懒加载这么麻烦的技术。这样做值得吗,我为什么要关注文章中的观点? 以下是使用懒加载的理由: @@ -181,22 +181,22 @@ import 'react-lazy-load-image-component/src/effects/blur.css'; 由于在视口外的图片不被及时加载,懒加载可以节省带宽的使用。这有利于提升特别是手机用户的使用性能。 -### 2\. 懒加载减少CDN花销 +### 2. 懒加载减少 CDN 花销 -如[Cloudinary](https://cloudinary.com)或 [Imagekit](https://www.freecodecamp.org/news/how-to-lazy-load-images-in-react/imagekit.io)这样的媒体内容服务提供收费的媒体存储计划。懒加载可以确保仅从CDN请求的图片被加载,减少了服务器花销。 +如[Cloudinary](https://cloudinary.com)或 [Imagekit](https://www.freecodecamp.org/news/how-to-lazy-load-images-in-react/imagekit.io)这样的媒体内容服务提供收费的媒体存储计划。懒加载可以确保仅从 CDN 请求的图片被加载,减少了服务器花销。 -### 3\. 懒加载提升SEO +### 3. 懒加载提升 SEO -页面速度是影响SEO (搜索引擎也更有可能推荐你的页面)的关键要素。因为页面加载时间少,搜索引擎会乐于推荐你的页面。 +页面速度是影响 SEO (搜索引擎也更有可能推荐你的页面)的关键要素。因为页面加载时间少,搜索引擎会乐于推荐你的页面。 ## 总结 -我认为优化页面是每一个web开发者应该修炼的技术。它给用户提供了更好的体验,特别是使用手机的用户。 +我认为优化页面是每一个 web 开发者应该修炼的技术。它给用户提供了更好的体验,特别是使用手机的用户。 -这里是[GitHub上关于这篇文章的代码](https://github.com/Evavic44/react-lazyload)。 +这里是[GitHub 上关于这篇文章的代码](https://github.com/Evavic44/react-lazyload)。 -这篇文章就到这里,如果你已经读到了这里,我确信你对我其他的文章也会感兴趣。欢迎通过查看我的[博客](https://eke.hashnode.dev)或者关注我的twitter[@victorekea](https://twitter.com/victorekea)来获取更多内容。 +这篇文章就到这里,如果你已经读到了这里,我确信你对我其他的文章也会感兴趣。欢迎通过查看我的[博客](https://eke.hashnode.dev)或者关注我的 twitter[@victorekea](https://twitter.com/victorekea)来获取更多内容。 ## 其他资源 diff --git a/chinese/articles/how-to-learn-programming.md b/chinese/articles/how-to-learn-programming.md index 44b76c1b3..bbfd7ae22 100644 --- a/chinese/articles/how-to-learn-programming.md +++ b/chinese/articles/how-to-learn-programming.md @@ -9,11 +9,11 @@ 大多数人采用的学习编程的方式是挑一个流行的编程语言,然后一头扎进去。具体的表现形式可以是选一门线上的编程课程、教学项目,或者随机购买一个编程话题相关的书籍。 -极少的准开发者会从一个路线图开始,路线图相当于编码世界的一张鸟瞰图,梳理了一些相关的编程概念、语言以及几乎100%的开发者每天都会使用的工具。 +极少的准开发者会从一个路线图开始,路线图相当于编码世界的一张鸟瞰图,梳理了一些相关的编程概念、语言以及几乎 100%的开发者每天都会使用的工具。 -在这篇文章中,我将提出一种路线图。我梳理出来了专业开发者用来编写代码、合作和创建专业项目的14个步骤(每一个步骤探讨了一个关键的概念、语言或者工具)。 +在这篇文章中,我将提出一种路线图。我梳理出来了专业开发者用来编写代码、合作和创建专业项目的 14 个步骤(每一个步骤探讨了一个关键的概念、语言或者工具)。 -我精心挑选出来的这14个步骤是基于我长达20年的个人学习代码的经历。 +我精心挑选出来的这 14 个步骤是基于我长达 20 年的个人学习代码的经历。 我花了这么长时间才使自己在开发的时候感觉自在,一部分原因是我曾经只专注于具体的话题,而没有从更广阔的角度来看整个编码世界。 @@ -21,21 +21,21 @@ 我想在正式开始之前最后强调一点,你肯定不会因为这篇文章成为编程专家,这篇文章的本意也并非如此。这篇文章的目的是为了让你知道有这些内容,以及这些内容背后的运行逻辑,给你继续前行打下基础。 -## 给初级开发者的14步路线图 +## 给初级开发者的 14 步路线图 1. [熟悉计算机系统结构和数据基础](#1-familiarize-yourself-with-computer-architecture-and-data-basics) 2. [了解编程语言的运作](#2-learn-how-programming-languages-work) 3. [理解互联网的运作](#3-understand-how-the-internet-works) 4. [练习基本的命令行](#4-practice-some-command-line-basics) -5. [使用Vim来培养你的文本编辑器技能](#5-build-up-your-text-editor-skills-with-vim) -6. [学习一点HTML](#6-take-up-some-html) -7. [处理一些CSS](#7-tackle-some-css) -8. [由JavaScript开始编程](#8-start-programming-with-javascript) -9. [使用Python继续编程](#9-continue-programming-with-python) -10. [进一步了解Java](#10-further-your-knowledge-with-java) -11. [使用Git跟踪代码](#11-track-your-code-using-git) -12. [使用数据库和SQL存储数据](#12-store-data-using-databases-and-sql) -13. [阅读Web框架和MVC](#13-read-about-web-frameworks-and-mvc) +5. [使用 Vim 来培养你的文本编辑器技能](#5-build-up-your-text-editor-skills-with-vim) +6. [学习一点 HTML](#6-take-up-some-html) +7. [处理一些 CSS](#7-tackle-some-css) +8. [由 JavaScript 开始编程](#8-start-programming-with-javascript) +9. [使用 Python 继续编程](#9-continue-programming-with-python) +10. [进一步了解 Java](#10-further-your-knowledge-with-java) +11. [使用 Git 跟踪代码](#11-track-your-code-using-git) +12. [使用数据库和 SQL 存储数据](#12-store-data-using-databases-and-sql) +13. [阅读 Web 框架和 MVC](#13-read-about-web-frameworks-and-mvc) 14. [玩转包管理工具](#14-play-with-package-managers) 话不多说,让我们开始吧! @@ -46,7 +46,7 @@ 这种能力被称为**抽象**——使用高级工具(这里指编程语言)的能力,可以简化理解、缩小所需掌握技能范围。 -但这并不意味着了解机器本身一无是处。至少可以帮助你在工作场所和同事畅谈高CPU和内存使用率。 +但这并不意味着了解机器本身一无是处。至少可以帮助你在工作场所和同事畅谈高 CPU 和内存使用率。 以下是计算机基础的基本知识: @@ -56,13 +56,13 @@ 大部分现代计算机有一个叫做**中央处理器(CPU)**的微芯片。你可以把这个当作计算机的大脑。它处理大部分数字运算和逻辑任务。 -每一个CPU有一个**指令集**,是供CPU理解的二进制(0和1)指令的合集。幸运的是,作为软件开发人员,我们并不需要担心自己不理解这些指令,这就是抽象的力量。 +每一个 CPU 有一个**指令集**,是供 CPU 理解的二进制(0 和 1)指令的合集。幸运的是,作为软件开发人员,我们并不需要担心自己不理解这些指令,这就是抽象的力量。 -如果说CPU是大脑的逻辑中心的话,那么我们还需要一个存储器来短期或长期地存储记忆。 +如果说 CPU 是大脑的逻辑中心的话,那么我们还需要一个存储器来短期或长期地存储记忆。 计算机拥有**随机存取存储器(RAM)**作为“工作存储器“(或者叫做短期存储器)来存储程序运行时使用的信息。 -RAM是由一组**内存地址**组成,可用于存储数据位。在像C语言这样的历史久远的计算机语言中,程序员确实可以使用**指针**来直接处理内存地址,但是这在现代语言中很少见了。 +RAM 是由一组**内存地址**组成,可用于存储数据位。在像 C 语言这样的历史久远的计算机语言中,程序员确实可以使用**指针**来直接处理内存地址,但是这在现代语言中很少见了。 最后这个组件你肯定熟悉——硬盘驱动。硬盘相当于人类大脑的长期记忆。硬盘是存储数据的内部和外部设备,即便关机了,这些数据仍然存在。 @@ -70,29 +70,29 @@ RAM是由一组**内存地址**组成,可用于存储数据位。在像C语言 从表面来看,我们会想到文本文件、图像、视频、电子邮件、文件和文件夹。这些都是我们每天在计算机上创建和存储的高级数据结构。 -但位于底层的计算机芯片(CPU或者RAM芯片)并不知道什么是“图片”或者“视频”。 +但位于底层的计算机芯片(CPU 或者 RAM 芯片)并不知道什么是“图片”或者“视频”。 -从芯片的角度来看,这些数据结构被作为一长串0和1存储了起来,这些0和1被称为**位**。 +从芯片的角度来看,这些数据结构被作为一长串 0 和 1 存储了起来,这些 0 和 1 被称为**位**。 位通常以一组八位的形式存储,称为一个 **字节**。 一个字节就是一个简单的八位序列,例如 `00000001`、 `01100110` 或 `00001111`。这样表示信息的方法被称为 **二进制表示法**。

2)了解编程语言的运作

-在上一章节,我们提到了大部分计算机以依赖于CPU,CPU可以理解一串特定的0和1。 +在上一章节,我们提到了大部分计算机以依赖于 CPU,CPU 可以理解一串特定的 0 和 1。 -因此,理论上我们可以编写一串由0和1组成的代码告诉CPU要做什么。以二进制形式编写的指令被称为**机器码**。 +因此,理论上我们可以编写一串由 0 和 1 组成的代码告诉 CPU 要做什么。以二进制形式编写的指令被称为**机器码**。 -写这样的代码听上去相当可怕,反正我没试过,因为我使用高级编程语言如:JavaScript、Python和Java。 +写这样的代码听上去相当可怕,反正我没试过,因为我使用高级编程语言如:JavaScript、Python 和 Java。 -**高级编程语言**提供一组人类可以读取的关键字、声明和语法规则,使得人们可以更加容易地学习、改bug和使用。 +**高级编程语言**提供一组人类可以读取的关键字、声明和语法规则,使得人们可以更加容易地学习、改 bug 和使用。 编程语言提供一座桥梁连接人类大脑和计算机大脑(CPU)。 -我们编写的代码最终会转换为CPU理解的二进制指令(机器码)。 +我们编写的代码最终会转换为 CPU 理解的二进制指令(机器码)。 -根据你选择的语言,我们可以认为你的代码要么被**编译**,要么被**解释**为可以被CPU执行的机器码。大多数编程语言都包含一个**编译器**或者**解释器**来执行这个翻译步骤。 +根据你选择的语言,我们可以认为你的代码要么被**编译**,要么被**解释**为可以被 CPU 执行的机器码。大多数编程语言都包含一个**编译器**或者**解释器**来执行这个翻译步骤。 -举几个例子——JavaScript和Python是解释型语言,然而Java是编译型语言。一个语言是否为编译型或者解释型语言(或者两者结合)会对开发者的编写便利性、处理错误的方法和程序的性能造成影响。这里我们不赘述。 +举几个例子——JavaScript 和 Python 是解释型语言,然而 Java 是编译型语言。一个语言是否为编译型或者解释型语言(或者两者结合)会对开发者的编写便利性、处理错误的方法和程序的性能造成影响。这里我们不赘述。

3)理解互联网的运作

@@ -100,9 +100,9 @@ RAM是由一组**内存地址**组成,可用于存储数据位。在像C语言 互联网是一个连接了全球计算机的集合。也可以被认为是一个全球性的网络。互联网内部的每一台计算机都遵循特定的规则来达成彼此间交流,对于计算机来说,“交流”就是交换数据。 -如我们在之前章节介绍的那样,所有数据类型(网页、图像、视频、电子邮件等)都可以被0和1来表示。 +如我们在之前章节介绍的那样,所有数据类型(网页、图像、视频、电子邮件等)都可以被 0 和 1 来表示。 -因此,你可以把互联网当作一个巨大的计算机集合,这些计算机之间相互传输0和1,数据以0和1的形式存储,互联网就是一个数字对话媒介。 +因此,你可以把互联网当作一个巨大的计算机集合,这些计算机之间相互传输 0 和 1,数据以 0 和 1 的形式存储,互联网就是一个数字对话媒介。 如果互联网是一个巨大的对话场所,那让我们来定义一下对话的参与者。 @@ -134,9 +134,9 @@ RAM是由一组**内存地址**组成,可用于存储数据位。在像C语言 ### 如何使用命令行 -如果你是Mac用户,打开终端应用,它本质上是一个Unix命令行终端。 +如果你是 Mac 用户,打开终端应用,它本质上是一个 Unix 命令行终端。 -如果你使用非GUI(图形用户界面)的操作系统,例如Linux或Unix,则在默认情况下,启动计算机就是命令行。如果你的Linux或者Unix系统有GUI,你需要手动打开终端。 +如果你使用非 GUI(图形用户界面)的操作系统,例如 Linux 或 Unix,则在默认情况下,启动计算机就是命令行。如果你的 Linux 或者 Unix 系统有 GUI,你需要手动打开终端。 在提示符下,输入`pwd`并按下回车,命令行会打印出你当前所处的文件夹的路径。 @@ -201,45 +201,45 @@ RAM是由一组**内存地址**组成,可用于存储数据位。在像C语言 使用终端来编辑文件非常重要,因为计算机代码就是存储在有组织的文件中的文本。 -我们当然可以使用一些华丽的文本编辑器如微软的word(或者更适合于代码的编辑器如Sublime或Atom)来编写我们的代码,但这不是必须的。我们已经使用终端来运行命令行了,显然终端是最方便编辑代码的地方。 +我们当然可以使用一些华丽的文本编辑器如微软的 word(或者更适合于代码的编辑器如 Sublime 或 Atom)来编写我们的代码,但这不是必须的。我们已经使用终端来运行命令行了,显然终端是最方便编辑代码的地方。 -有优秀的文本编辑器,但是我推荐你[通过Vim来打基础](https://www.freecodecamp.org/news/vimrc-configuration-guide-customize-your-vim-editor/)。 +有优秀的文本编辑器,但是我推荐你[通过 Vim 来打基础](https://www.freecodecamp.org/news/vimrc-configuration-guide-customize-your-vim-editor/)。 -Vim是最早的一批编辑器,并且经受住了时间的考验。[Vim](https://zh.wikipedia.org/zh-tw/Vim)代表 "**_VI_** i**_M_**proved",因为它由**_Vi_**发展而来的。 +Vim 是最早的一批编辑器,并且经受住了时间的考验。[Vim](https://zh.wikipedia.org/zh-tw/Vim)代表 "**_VI_** i**_M_**proved",因为它由**_Vi_**发展而来的。 -如上文所述,Vim是可以直接在终端运行的编辑器,所以我们不需要额外打开一个窗口并使用鼠标来操作。Vim提供一组命令行和模式使得我们可以直接使用键盘来创建和编辑文本。 +如上文所述,Vim 是可以直接在终端运行的编辑器,所以我们不需要额外打开一个窗口并使用鼠标来操作。Vim 提供一组命令行和模式使得我们可以直接使用键盘来创建和编辑文本。 Vim[有一定的学习曲线](https://www.freecodecamp.org/news/how-not-to-be-afraid-of-vim-anymore-ec0b7264b0ae/),但是只要稍微练习,你整个程序员职业生涯都会受益。 -许多系统都预装了Vim,若要检查你的计算机是否安装了Vim,可以使用命令`vim -v`。 +许多系统都预装了 Vim,若要检查你的计算机是否安装了 Vim,可以使用命令`vim -v`。 -如果终端打开了Vim并且显示了版本,你就可以开始使用了。如果没有,就需要手动安装 (注意你可以通过输入`:!q`并点击回车来退出Vim)。更多安装Vim的信息,可以查看 https://vim.org。 +如果终端打开了 Vim 并且显示了版本,你就可以开始使用了。如果没有,就需要手动安装 (注意你可以通过输入`:!q`并点击回车来退出 Vim)。更多安装 Vim 的信息,可以查看 https://vim.org。 -我认为学习Vim最快速简单的方法是使用内置的**VimTutor**。运行前请确认系统已安装Vim,打开命令行输入`vimtutor`点击回车。 +我认为学习 Vim 最快速简单的方法是使用内置的**VimTutor**。运行前请确认系统已安装 Vim,打开命令行输入`vimtutor`点击回车。 -这个教程已经非常棒了,所以这里就不再赘述,现在打开你的VimTutor开始学习吧!我们下一章节见! +这个教程已经非常棒了,所以这里就不再赘述,现在打开你的 VimTutor 开始学习吧!我们下一章节见! -如果完成了VimTutor之后你还有精力学习更多内容的话,欢迎查看[7个大幅提升效率的Vim命令](https://initialcommit.com/blog/7-versatile-vim-commands)。 +如果完成了 VimTutor 之后你还有精力学习更多内容的话,欢迎查看[7 个大幅提升效率的 Vim 命令](https://initialcommit.com/blog/7-versatile-vim-commands)。

6)学习一点HTML

-你可以将HTML(**H**yper**T**ext **M**arkup **L**anguage(超文本标记语言)的缩写)视为网页的骨骼。它通过指定应该显示的元素及显示的顺序来确定页面的结构。 +你可以将 HTML(**H**yper**T**ext **M**arkup **L**anguage(超文本标记语言)的缩写)视为网页的骨骼。它通过指定应该显示的元素及显示的顺序来确定页面的结构。 -你浏览过的所有页面都和HTML多少有点关系。每当你浏览一个网页,托管网页的服务器就会传输一些HTML到达你的浏览器。浏览器读取这些HTML并展示出来。 +你浏览过的所有页面都和 HTML 多少有点关系。每当你浏览一个网页,托管网页的服务器就会传输一些 HTML 到达你的浏览器。浏览器读取这些 HTML 并展示出来。 -大多数网页包含一组相当标准的内容,包括标题、文本内容、图像链接、导航链接、页眉和页脚等等。所有这些信息都存储为HTML,定义页面的结构。 +大多数网页包含一组相当标准的内容,包括标题、文本内容、图像链接、导航链接、页眉和页脚等等。所有这些信息都存储为 HTML,定义页面的结构。 -需要注意的是HTML实际上并不是一种编程语言,虽然我们常常称它为"HTML代码"。 +需要注意的是 HTML 实际上并不是一种编程语言,虽然我们常常称它为"HTML 代码"。 -在后面的文章我们将看到其他编程语言具备**行为**,如执行一系列指令。HTML并不**做**任何事。我们不运行或执行HTML,HTML就在那儿等着被发送给浏览器,再显示在用户端。 +在后面的文章我们将看到其他编程语言具备**行为**,如执行一系列指令。HTML 并不**做**任何事。我们不运行或执行 HTML,HTML 就在那儿等着被发送给浏览器,再显示在用户端。 -事实上,HTML基本上只是数据。它是定义网页应该是什么样子的数据,仅此而已。 +事实上,HTML 基本上只是数据。它是定义网页应该是什么样子的数据,仅此而已。 -如何编写HTML呢?HTML使用一组**标签**(基本是一组标记)对应可以标记网页的元素。标签使用尖括号标记。 +如何编写 HTML 呢?HTML 使用一组**标签**(基本是一组标记)对应可以标记网页的元素。标签使用尖括号标记。 比方说,**title(标题)**标签写做`My Page Title`以及 **paragraph(段落)**标签写做`

A bunch of random text content.

`。 -每一个HTML元素都有一个起始标签和一个结束标签。起始标签是由一组尖括号和标签名组成: +每一个 HTML 元素都有一个起始标签和一个结束标签。起始标签是由一组尖括号和标签名组成: `` @@ -249,9 +249,9 @@ Vim[有一定的学习曲线](https://www.freecodecamp.org/news/how-not-to-be-af 两个尖括号之间的任意文本就是要在网页上显示的内容。 -让我们认识一下最常见的几个标签。 首先是``标签,定义了HTML页面的开始。对应的`` 标签 (注意正斜杠) 定义HTML页面的结束。 两个标签之间的任意内容都属于页面。 +让我们认识一下最常见的几个标签。 首先是``标签,定义了 HTML 页面的开始。对应的`` 标签 (注意正斜杠) 定义 HTML 页面的结束。 两个标签之间的任意内容都属于页面。 -第二个要介绍的是``标签。它定义了浏览器读取页面需要的额外信息。这里的大部分内容都不会展现给用户。对应的``标签定义了HEAD部分的结束。 +第二个要介绍的是``标签。它定义了浏览器读取页面需要的额外信息。这里的大部分内容都不会展现给用户。对应的``标签定义了 HEAD 部分的结束。 在上文中提到了``标签。它定义了页面的标题,会在浏览器的选择卡中显示。这个标签要在`<head>...</head>`内部使用。 @@ -271,16 +271,16 @@ Vim[有一定的学习曲线](https://www.freecodecamp.org/news/how-not-to-be-af </html> ``` -上面简单的HTML代码块展现了一个包含一个标题和一个段落的网页。 +上面简单的 HTML 代码块展现了一个包含一个标题和一个段落的网页。 -从这个例子我们可以看出HTML标签可以相互嵌套。也就是HTML标签可以放置在其他标签内部。 +从这个例子我们可以看出 HTML 标签可以相互嵌套。也就是 HTML 标签可以放置在其他标签内部。 -HTML提供了一系列标签供用户使用,我们不会逐一讲解,我只列举了一部分: +HTML 提供了一系列标签供用户使用,我们不会逐一讲解,我只列举了一部分: - `<p>`: 开启新一行的段落 - `<h1>`: 页面标题(heading)通常用作页面标题(title) - `<h2>`: 段落标题(heading)通常用作段落标题(title) -- `<hx>`: _x_ 表示3到6之间的数字,代表级别更小的标题(heading) +- `<hx>`: _x_ 表示 3 到 6 之间的数字,代表级别更小的标题(heading) - `<img>`: 一个图片 - `<a>`: 一个链接 - `<form>`: 表单,包含供用户填写和提交的字段或输入框 @@ -290,19 +290,19 @@ HTML提供了一系列标签供用户使用,我们不会逐一讲解,我只 <h2 id="7-tackle-some-css">7)处理一些CSS</h2> -缺少CSS(**C**ascading **S**tyle **S**heets层叠样式表)网页就像没有裱花的蛋糕。虽然也可以吃,但是索然无味。 +缺少 CSS(**C**ascading **S**tyle **S**heets 层叠样式表)网页就像没有裱花的蛋糕。虽然也可以吃,但是索然无味。 -CSS使得我们可以将样式的属性,如:背景色、字号、宽度、长度和HTML元素关联起来。 +CSS 使得我们可以将样式的属性,如:背景色、字号、宽度、长度和 HTML 元素关联起来。 -样式属性告知浏览器如何渲染对应的效果。和HTML一样,CSS并不是一门编程语言,它并不具备行为,但是可以给HTML骨骼添加样式。 +样式属性告知浏览器如何渲染对应的效果。和 HTML 一样,CSS 并不是一门编程语言,它并不具备行为,但是可以给 HTML 骨骼添加样式。 -让我们看看如何给HTML添加CSS样式,有三个重要的部分: +让我们看看如何给 HTML 添加 CSS 样式,有三个重要的部分: -**CSS选择器:** 标记我们需要添加样式的HTML元素 +**CSS 选择器:** 标记我们需要添加样式的 HTML 元素 -**CSS属性名:** 我们要添加到响应的HTML元素的特定样式属性的名称 +**CSS 属性名:** 我们要添加到响应的 HTML 元素的特定样式属性的名称 -**CSS属性值:** 我们要应用的样式属性的值 +**CSS 属性值:** 我们要应用的样式属性的值 以下是将这三个部分组合在一起来定义一个段落的颜色的字体大小: @@ -313,17 +313,17 @@ p { } ``` -让我们从头开始,在花括号之前是CSS选择器。在示例中**p**代表的是`<p>`(paragraph)这个HTML标记。也就是说网页中所有`<p>`标签都要应用这个样式。 +让我们从头开始,在花括号之前是 CSS 选择器。在示例中**p**代表的是`<p>`(paragraph)这个 HTML 标记。也就是说网页中所有`<p>`标签都要应用这个样式。 我们来看看花括号里面发生了什么——我们想要应用到目标元素的样式。 -CSS属性和值用冒号隔来。属性(示例中的"color"和"font-size")在左手边,属性的值(示例中的"red""12px")在右手边。每一个属性名/值对由一个分号结尾。 +CSS 属性和值用冒号隔来。属性(示例中的"color"和"font-size")在左手边,属性的值(示例中的"red""12px")在右手边。每一个属性名/值对由一个分号结尾。 -你应该已经看出来这是如何运作的,css代码块告诉浏览器使用红色、字号为12px样式来修饰`<p>`内的内容。 +你应该已经看出来这是如何运作的,css 代码块告诉浏览器使用红色、字号为 12px 样式来修饰`<p>`内的内容。 -那HTML如何包含这些CSS样式呢?使用`<link>` HTML标签。通常,会有一个独立的CSS文件 (**.css**文件)。也就意味着我们需要引入到HTML文件中,这样浏览器才会知道CSS文件的存在。 +那 HTML 如何包含这些 CSS 样式呢?使用`<link>` HTML 标签。通常,会有一个独立的 CSS 文件 (**.css**文件)。也就意味着我们需要引入到 HTML 文件中,这样浏览器才会知道 CSS 文件的存在。 -`<link>`标签就服务于这个目的。我们在`<head>`部分加入`<link>`元素,特指需要被引入的CCS文件: +`<link>`标签就服务于这个目的。我们在`<head>`部分加入`<link>`元素,特指需要被引入的 CCS 文件: ```css <head> @@ -335,35 +335,35 @@ CSS属性和值用冒号隔来。属性(示例中的"color"和"font-size")在左 </head> ``` -在这个示例中,我们使用**href**标签来引入CSS文件:_/home/style.css_。 +在这个示例中,我们使用**href**标签来引入 CSS 文件:_/home/style.css_。 在接下来的三个部分,我们(终于)要进入编程语言了! -我们将大概介绍一个JavaScript、Python和Java以及一些重要的编程概念。我们将通过代码示例来比较三种语言的相同和不同,给你进一步了解这三种语言打下基础。 +我们将大概介绍一个 JavaScript、Python 和 Java 以及一些重要的编程概念。我们将通过代码示例来比较三种语言的相同和不同,给你进一步了解这三种语言打下基础。 <h2 id="8-start-programming-with-javascript">由JavaScript开始编程</h2> -让我们先回答这个问题:如果HTML是网页骨架、CSS是样式,那我们还需要JavaScript做什么? +让我们先回答这个问题:如果 HTML 是网页骨架、CSS 是样式,那我们还需要 JavaScript 做什么? -答案是我们可以不需要JavaScript,一个样式好看的静态网站已经足够,所以只需要HTML和CSS就够了。 +答案是我们可以不需要 JavaScript,一个样式好看的静态网站已经足够,所以只需要 HTML 和 CSS 就够了。 -这个答案的关键词是“静态”,如果想要给网页添加一些动态效果的话,如改变文本或者添加更为复杂的用户交互,我们就需要使用JavaScript。 +这个答案的关键词是“静态”,如果想要给网页添加一些动态效果的话,如改变文本或者添加更为复杂的用户交互,我们就需要使用 JavaScript。 -### 什么是JavaScript? +### 什么是 JavaScript? -JavaScript到底是什么呢?JavaScript是专为互联网和网站而创建的编程语言。在第二部分我们提到过,大多数编程语言要么就是被编译要么就是被解释,程序基本上独立运行。 +JavaScript 到底是什么呢?JavaScript 是专为互联网和网站而创建的编程语言。在第二部分我们提到过,大多数编程语言要么就是被编译要么就是被解释,程序基本上独立运行。 -JavaScript在这点上有些与众不同,它被设计为直接在网络浏览器内部运行,我们可以编写代表一组行为的代码,直接在网页上执行,使得网站更加动态。 +JavaScript 在这点上有些与众不同,它被设计为直接在网络浏览器内部运行,我们可以编写代表一组行为的代码,直接在网页上执行,使得网站更加动态。 -你可以使用后缀为`.js`的文件来编写JavaScript代码或者直接在HTML的`<script>`标签内编写JavaScript。 +你可以使用后缀为`.js`的文件来编写 JavaScript 代码或者直接在 HTML 的`<script>`标签内编写 JavaScript。 -JavaScript在很长时间内都只能在浏览器内部运行,但是**Node.js**改变了这一运行范式,之后JavaScript就拥有了独立运行的环境。 +JavaScript 在很长时间内都只能在浏览器内部运行,但是**Node.js**改变了这一运行范式,之后 JavaScript 就拥有了独立运行的环境。 -除了在浏览器内部(即客户端)运行,Node.js可以被安装在任何计算机,创建运行JavaScript的环境。你也可以在网络服务器安装Node,这样JavaScript不仅可以编写前端代码也可以编写后端代码。 +除了在浏览器内部(即客户端)运行,Node.js 可以被安装在任何计算机,创建运行 JavaScript 的环境。你也可以在网络服务器安装 Node,这样 JavaScript 不仅可以编写前端代码也可以编写后端代码。 -在讲解了JavaScript基础之后,让我们来看看JavaScript的一些主要概念。 +在讲解了 JavaScript 基础之后,让我们来看看 JavaScript 的一些主要概念。 -### JavaScript中的变量和赋值 +### JavaScript 中的变量和赋值 变量或许是编程中最为基础的概念。变量是用来引用特定值的一个名称或者占位符。 @@ -375,13 +375,13 @@ JavaScript在很长时间内都只能在浏览器内部运行,但是**Node.js* 我们可以在整个代码中引用变量的值,所以变量十分有用。我们可以检查我们需要的变量的值,根据变量的改变来执行不同的行为。 -在JavaScript中我们使用`let`关键字来声明变量,如:`let x;`。 +在 JavaScript 中我们使用`let`关键字来声明变量,如:`let x;`。 -这样`x`这个变量就可以在代码中使用。注意我们在末尾添加了分号结束。在JavaScript(以及其他一些语言中)分号代表了声明的结束。 +这样`x`这个变量就可以在代码中使用。注意我们在末尾添加了分号结束。在 JavaScript(以及其他一些语言中)分号代表了声明的结束。 我们已经创建了变量 _x_,就可以使用等号给这个变量赋值,等号又可以称为 **赋值操作符**: `x = 10;` -我们将数字10赋值给名为 _x_ 的变量。无论何时我们在代码中使用 _x_ ,就会得到10。 +我们将数字 10 赋值给名为 _x_ 的变量。无论何时我们在代码中使用 _x_ ,就会得到 10。 变量声明和赋值可以在一行内进行: @@ -389,13 +389,13 @@ JavaScript在很长时间内都只能在浏览器内部运行,但是**Node.js* let x = 10; ``` -### JavaScript的数据类型 +### JavaScript 的数据类型 在上一部分,我们将整数存储到了命名为 _x_ 的变量。我们也可以存储十进制数,或者**浮点数**。我们可以这样写: `let x = 6.6;`。 能够被存储的不同的值被称为**数据类型**。现在我们只看到了数字类型(整数和浮点数),我们只触及了表面,我们也可以在变量中存储文本。 -在代码术语中,一段文本被称为 **字符串**。我们可以将字符串存储到变量x中,使用单引号或者双引号来框住字符串。 +在代码术语中,一段文本被称为 **字符串**。我们可以将字符串存储到变量 x 中,使用单引号或者双引号来框住字符串。 ```javascript let x = 'Hello there!'; @@ -403,7 +403,7 @@ let x = 'Hello there!'; let y = "Hey bud!"; ``` -接下来我们要讨论**布尔值**。布尔值只有`true`或`false`两个值 – 必须小写。在JavaScript中,true和false是专门用于表示布尔值的关键字。 +接下来我们要讨论**布尔值**。布尔值只有`true`或`false`两个值 – 必须小写。在 JavaScript 中,true 和 false 是专门用于表示布尔值的关键字。 ```javascript let x = true; @@ -415,15 +415,15 @@ let y = false; 我们经常使用布尔值来控制条件(if/else)语句中的程序流,接下来我们讲学习程序流。 -### JavaScript中的程序流控制语句 +### JavaScript 中的程序流控制语句 -现在我们已经了解了变量和基本的JavaScript数据类型,让我们来看看我们可以用它们做些什么。 +现在我们已经了解了变量和基本的 JavaScript 数据类型,让我们来看看我们可以用它们做些什么。 如果不告诉程序我们要用变量做什么,变量就没太大用处。我们可以使用**语句**来让变量做事情。 语句是特殊关键字,允许我们在代码中执行某些操作,这些操作通常基于我们定义的变量的值。语句让我们定义程序的逻辑流程,并执行许多有用的操作,这些操作将决定我们的程序如何工作。 -#### If / Else语句 +#### If / Else 语句 我们首先讨论 `if`语句。如果条件为真,`if`语句允许我们做某事。如下: @@ -437,15 +437,15 @@ if ( x > 5 ) { } ``` -我们创建一个命名为 _x_ 的变量并赋值为10, 然后使用 `if`语句。 在 `if`关键字之后,是一组括号,括号内部是有待评估的条件,示例中是`x > 5`。 _x_ 被赋值为10,所以示例中的条件为真。 +我们创建一个命名为 _x_ 的变量并赋值为 10, 然后使用 `if`语句。 在 `if`关键字之后,是一组括号,括号内部是有待评估的条件,示例中是`x > 5`。 _x_ 被赋值为 10,所以示例中的条件为真。 因为条件为真,所以花括号中的代码将执行,我们会看到 "X is GREATER than 5!" 被打印出来。(我们尚未讨论 `console.log()`的意思, 现在我们只知道它会打印括号里的内容)。 在该示例中,我们还使用了`else`语句。若条件为`false`,则执行内部的代码。 -#### While循环 +#### While 循环 -我们接下来要讨论**while循环**。 循环使得我们在不需要赋值和粘贴代码的情况下,反复执行同一块代码。 +我们接下来要讨论**while 循环**。 循环使得我们在不需要赋值和粘贴代码的情况下,反复执行同一块代码。 假设我们想要打印一句话五次,我们可以这样操作: @@ -457,7 +457,7 @@ console.log('This is a very important message!'); console.log('This is a very important message!'); ``` -这样可以打印5次,那如果是100次,1000次呢?我们需要更好的方式来重复,循环就派上用场。在代码术语中,重复一段代码被称为**遍历**。 +这样可以打印 5 次,那如果是 100 次,1000 次呢?我们需要更好的方式来重复,循环就派上用场。在代码术语中,重复一段代码被称为**遍历**。 `while`循环将在条件为真的时候不断循环: @@ -473,72 +473,72 @@ while ( x <= 100 ) { } ``` -在该示例中,我们将 _x_ 的初始值设定为1, 然后编写一个`while`循环。和`if`语句一样,我们将条件写在括号内。示例中的条件为`x <= 100`。 只要 _x_ 小于等于100,条件就一直为`true`。 +在该示例中,我们将 _x_ 的初始值设定为 1, 然后编写一个`while`循环。和`if`语句一样,我们将条件写在括号内。示例中的条件为`x <= 100`。 只要 _x_ 小于等于 100,条件就一直为`true`。 -然后我们在花括号内部写需要执行的代码。首先在控制台打印信息,然后使 _x_ 增加1。 +然后我们在花括号内部写需要执行的代码。首先在控制台打印信息,然后使 _x_ 增加 1。 -循环会再次评估条件是否为`true`。此时 _x_ 值为2,因为第一次循环结束后增加了1。2小于100所以条件仍为`true`。 +循环会再次评估条件是否为`true`。此时 _x_ 值为 2,因为第一次循环结束后增加了 1。2 小于 100 所以条件仍为`true`。 -循环中的代码不断执行直到 _x_ 值为101。这时,_x_ 比100大,条件为 `false`。循环停止执行。 +循环中的代码不断执行直到 _x_ 值为 101。这时,_x_ 比 100 大,条件为 `false`。循环停止执行。 -### HTML的<script>标签 +### HTML 的<script>标签 -我们已经介绍了JavaScript,如何将JavaScript添加到HTML页面?我们可以使用之前未讨论到的`<script>`标签。 +我们已经介绍了 JavaScript,如何将 JavaScript 添加到 HTML 页面?我们可以使用之前未讨论到的`<script>`标签。 -和`<link>`类似,`<link>`用于将CSS添加到HTML,而`<script>`专门用于添加JavaScript。 +和`<link>`类似,`<link>`用于将 CSS 添加到 HTML,而`<script>`专门用于添加 JavaScript。 -假设我们把上述代码保存为`customscript.js`文件,并放置在和HTML同一个文件夹中。我们可以在HTML的 `<head>...</head>` 部分添加JavaScript文件。 +假设我们把上述代码保存为`customscript.js`文件,并放置在和 HTML 同一个文件夹中。我们可以在 HTML 的 `<head>...</head>` 部分添加 JavaScript 文件。 ```html <script type="text/javascript" src="customscript.js"></script> ``` -当网页在浏览器中显示时执行时,会从这个文件中加载JavaScript代码,。 +当网页在浏览器中显示时执行时,会从这个文件中加载 JavaScript 代码,。 -一旦熟悉了JavaScript的技巧,你可以[尝试搭建一些简单的初学者项目](https://www.freecodecamp.org/news/javascript-projects-for-beginners/)来进一步练习。 +一旦熟悉了 JavaScript 的技巧,你可以[尝试搭建一些简单的初学者项目](https://www.freecodecamp.org/news/javascript-projects-for-beginners/)来进一步练习。 <h2 id="9-continue-programming-with-python"> 9)使用Python继续编程</h2> -我们已经学习了基础的JavaScript知识,这时开启一门新的语言十分有用,这门语言就是——Python。 +我们已经学习了基础的 JavaScript 知识,这时开启一门新的语言十分有用,这门语言就是——Python。 -大多数语言都具备一套同样的功能,包含:变量、算术运算符、if/else语句、循环和函数。 +大多数语言都具备一套同样的功能,包含:变量、算术运算符、if/else 语句、循环和函数。 比较不同的语言如何实现同样的功能对于我们的学习十分有帮助。概念通常是相似的,但是语法(如何编写代码)有些微不同。 -### 什么是Python? +### 什么是 Python? -我们先来介绍一下Python的背景知识。和JavaScript一样,Python也是一种高级编程语言,并且在设计上将“易开发”优先于“执行快”。 +我们先来介绍一下 Python 的背景知识。和 JavaScript 一样,Python 也是一种高级编程语言,并且在设计上将“易开发”优先于“执行快”。 -我认为Python是最适合初学者学习的编程语言之一。Python的语法十分简洁并且符合直觉,python在开源和商业领域都非常受欢迎。 +我认为 Python 是最适合初学者学习的编程语言之一。Python 的语法十分简洁并且符合直觉,python 在开源和商业领域都非常受欢迎。 -我们对比过编译型语言和解释型语言,Python是一门解释型语言。运行Python程序的时候,**Python解释器**会主动处理并逐行在机器上执行代码。 +我们对比过编译型语言和解释型语言,Python 是一门解释型语言。运行 Python 程序的时候,**Python 解释器**会主动处理并逐行在机器上执行代码。 和编译型语言不通的地方在于,编译型语言会先使用编译器将代码优化成更容易执行的形式,然后再执行。 -和JavaScript不同的是,Python并不是直接在浏览器中运行的代码。Python被创建为一种便捷的 _脚本语言_——一种可用于执行任意任务的代码,通常在用户的本地计算机上执行。 +和 JavaScript 不同的是,Python 并不是直接在浏览器中运行的代码。Python 被创建为一种便捷的 _脚本语言_——一种可用于执行任意任务的代码,通常在用户的本地计算机上执行。 -Python代码可以在任何安装了Python解释器的计算机上执行。它是一种常用的脚本语言,但也广泛用于数据科学和服务器端的应用程序。 +Python 代码可以在任何安装了 Python 解释器的计算机上执行。它是一种常用的脚本语言,但也广泛用于数据科学和服务器端的应用程序。 -### Python中的变量与赋值 +### Python 中的变量与赋值 -和JavaScript一样,Python也可以声明变量。可以直接使用等号来进行声明和赋值: +和 JavaScript 一样,Python 也可以声明变量。可以直接使用等号来进行声明和赋值: ```python x = 10 y = "cheese" ``` -Python和JavaScript在定义变量的时候有两点不同:在Python中不需要使用`let`关键字,也不需要在每行结尾添加分号。 +Python 和 JavaScript 在定义变量的时候有两点不同:在 Python 中不需要使用`let`关键字,也不需要在每行结尾添加分号。 -Python使用一组基于空格和缩进的语法规则。就不需要终止字符:分号;也不需要使用花括号来代表代码块。 +Python 使用一组基于空格和缩进的语法规则。就不需要终止字符:分号;也不需要使用花括号来代表代码块。 -### Python的数据类型 +### Python 的数据类型 -Python也有一组数据类型,我们可以将它们分配给变量。这些包括整数、浮点数(小数)、字符串、列表和字典。 +Python 也有一组数据类型,我们可以将它们分配给变量。这些包括整数、浮点数(小数)、字符串、列表和字典。 -整数、浮点数和字符串与JavaScript的相同,所以就不赘述。 +整数、浮点数和字符串与 JavaScript 的相同,所以就不赘述。 -Python中的布尔值与JavaScript非常相似,只是关键字True和False必须大写: +Python 中的布尔值与 JavaScript 非常相似,只是关键字 True 和 False 必须大写: ```python x = True @@ -548,11 +548,11 @@ y = False ### 程序流控制声明 -与JavaScript一样,Python也流控制语句集,但语法略有不同。 +与 JavaScript 一样,Python 也流控制语句集,但语法略有不同。 -#### If / Else语句 +#### If / Else 语句 -JavaScript`if/else`示例的等价Python示例如下: +JavaScript`if/else`示例的等价 Python 示例如下: ```python x = 10 @@ -564,23 +564,23 @@ else: print('X is NOT GREATER than 5!') ``` -我们先定义一个名为 _x_ 的变量并并赋值为10,紧接着是`if`语句。因为括号内部的条件被评估为`True`,`if`语句后缩进的代码被执行。我们便看到屏幕上打印出'X is GREATER than 5!'。 +我们先定义一个名为 _x_ 的变量并并赋值为 10,紧接着是`if`语句。因为括号内部的条件被评估为`True`,`if`语句后缩进的代码被执行。我们便看到屏幕上打印出'X is GREATER than 5!'。 -在Python中`print()`函数打印内容。 +在 Python 中`print()`函数打印内容。 同时也要注意`else`语句,如果 _x_ 条件为`False`,就会打印另外的语句。 -Python代码和我们之前看到的JavaScript代码有两大不同:Python使用冒号而不是花括号来指示`if`语句的开始。 +Python 代码和我们之前看到的 JavaScript 代码有两大不同:Python 使用冒号而不是花括号来指示`if`语句的开始。 -另外,在Python中`print()`函数的缩进对代码效果有影响。在JavaScript中,语句之间的缩进或空格无关紧要,因为JavaScript使用花括号标识代码块并使用分号标识语句的结尾。但是在这个Python 示例中,没有分号,也没有大括号! +另外,在 Python 中`print()`函数的缩进对代码效果有影响。在 JavaScript 中,语句之间的缩进或空格无关紧要,因为 JavaScript 使用花括号标识代码块并使用分号标识语句的结尾。但是在这个 Python 示例中,没有分号,也没有大括号! -这是因为Python实际上使用空格和换行符来标识语句和代码块的结尾。 +这是因为 Python 实际上使用空格和换行符来标识语句和代码块的结尾。 -冒号告诉Python解释器,`if`引导的代码块开始了。`if`之后的代码块必须缩进。(通常 1 tab = 4 空格)这样Python解释器才知道 `if` 的代码块在哪里。下一个未缩进的行意味着`if`代码块的结束。 +冒号告诉 Python 解释器,`if`引导的代码块开始了。`if`之后的代码块必须缩进。(通常 1 tab = 4 空格)这样 Python 解释器才知道 `if` 的代码块在哪里。下一个未缩进的行意味着`if`代码块的结束。 -#### While循环 +#### While 循环 -接下来我将讨论Python中的while循环。Python中的 `while`循环大体和JavaScript中的一致,但是语法有所不同: +接下来我将讨论 Python 中的 while 循环。Python 中的 `while`循环大体和 JavaScript 中的一致,但是语法有所不同: ```python x = 1 @@ -592,7 +592,7 @@ while ( x <= 100 ): print('This is not in the loop!') ``` -和JavaScript`while`循环不同的地方在于: +和 JavaScript`while`循环不同的地方在于: - 在定义变量的时候没有使用`let` - 行末没有使用分号结束 @@ -601,56 +601,56 @@ print('This is not in the loop!') 我们在循环之外打印了一条附加消息,以表明未缩进的代码行不是循环的一部分并且不会被重复。 -我推荐Python初学者看一下[Python的禅意](https://initialcommit.com/blog/zen-of-python),这篇文章介绍了编写Python代码的20条重要规则。 +我推荐 Python 初学者看一下[Python 的禅意](https://initialcommit.com/blog/zen-of-python),这篇文章介绍了编写 Python 代码的 20 条重要规则。 -如果你已经掌握了Python的基础,[可以尝试搭建一些初学者的Python项目](https://www.freecodecamp.org/news/python-projects-for-beginners/)。 +如果你已经掌握了 Python 的基础,[可以尝试搭建一些初学者的 Python 项目](https://www.freecodecamp.org/news/python-projects-for-beginners/)。 <h2 id="10-further-your-knowledge-with-java">10)进一步了解Java</h2> -我们已经了解了一组高级编程语言,让我们往后退一步,瞧一瞧Java。 +我们已经了解了一组高级编程语言,让我们往后退一步,瞧一瞧 Java。 -JavaScript和Python实时通过解释器来执行源码。但Java是一门编译型语言,需要编译器(而不是解释器)将Java源码转换为计算机可以理解的代码。 +JavaScript 和 Python 实时通过解释器来执行源码。但 Java 是一门编译型语言,需要编译器(而不是解释器)将 Java 源码转换为计算机可以理解的代码。 大多数编译器会生成一个或多个由机器码组成的可执行文件,这些代码可以在编译它们的特定操作系统和硬件平台上运行。 -但Java特殊的地方在于,它将源码转换为[**字节码**](https://zh.m.wikipedia.org/zh/Java%E5%AD%97%E8%8A%82%E7%A0%81),这和其他编译型语言生成的的机器码不太一样,Java字节码需要在**Java虚拟器(JVM)**执行。 +但 Java 特殊的地方在于,它将源码转换为[**字节码**](https://zh.m.wikipedia.org/zh/Java%E5%AD%97%E8%8A%82%E7%A0%81),这和其他编译型语言生成的的机器码不太一样,Java 字节码需要在**Java 虚拟器(JVM)**执行。 -可以将JVM想象成你安装在电脑的一个程序,这个程序允许你执行Java字节码,当人们谈论 "_是否在电脑安装了Java_," 他们指的是是否在电脑上安装了**JVM**。 +可以将 JVM 想象成你安装在电脑的一个程序,这个程序允许你执行 Java 字节码,当人们谈论 "_是否在电脑安装了 Java_," 他们指的是是否在电脑上安装了**JVM**。 -JVM和我们在前面章节讨论的解释器的功能类似,但是它没有把源码 (存储在 .java 文件)当作输入,而是把编译过的字节码作为输入。 +JVM 和我们在前面章节讨论的解释器的功能类似,但是它没有把源码 (存储在 .java 文件)当作输入,而是把编译过的字节码作为输入。 -这种设置的好处是它允许在特定操作系统和平台上编译的字节码由任何其他平台上的JVM执行。 +这种设置的好处是它允许在特定操作系统和平台上编译的字节码由任何其他平台上的 JVM 执行。 -假设我们有一个Java代码文件,该文件在运行Windows操作系统的计算机上编写并编译为字节码。这个字节码可以被JVM在任何平台上执行(即程序运行),包括Windows、Mac OS、Linux等。 +假设我们有一个 Java 代码文件,该文件在运行 Windows 操作系统的计算机上编写并编译为字节码。这个字节码可以被 JVM 在任何平台上执行(即程序运行),包括 Windows、Mac OS、Linux 等。 大多数编程语言并非如此,它们只能在编译它们的环境中执行。 -### Java中的变量和赋值 +### Java 中的变量和赋值 -Java和我们之前讨论的语言(Python和JavaScript)的一个主要的区别在于Java是**静态**语言。 +Java 和我们之前讨论的语言(Python 和 JavaScript)的一个主要的区别在于 Java 是**静态**语言。 这意味着我们的变量的数据类型必须在程序编译之前就建立并被理解。 -每次我们在Java代码中创建变量时,都需要显式指定该变量的数据类型,例如整数、字符串等。这称为变量**声明**。 +每次我们在 Java 代码中创建变量时,都需要显式指定该变量的数据类型,例如整数、字符串等。这称为变量**声明**。 一旦我们声明了一个变量的数据类型,它就只能在整个程序执行过程中保存该类型的数据。 -这与JavaScript和Python非常不同,它们的数据类型是在程序执行期间建立的,也称为**运行时**。因此,像JavaScript和Python这样的语言被称为**动态类型语言**——我们不会在源码中明确声明变量数据类型,也可以轻松地将变量动态地重新分配给任何类型。 +这与 JavaScript 和 Python 非常不同,它们的数据类型是在程序执行期间建立的,也称为**运行时**。因此,像 JavaScript 和 Python 这样的语言被称为**动态类型语言**——我们不会在源码中明确声明变量数据类型,也可以轻松地将变量动态地重新分配给任何类型。 -在Java中声明变量: +在 Java 中声明变量: ```java Datatype name = value; ``` -`Datatype`指的是我们需要存储的变量类型,如:整数、字符串等。`name`代表变量的名称,我们会在之后的代码中使用。`value`是我们要赋值给变量的值。注意和JavaScript一样,所有Java声明结尾都要使用分号。 +`Datatype`指的是我们需要存储的变量类型,如:整数、字符串等。`name`代表变量的名称,我们会在之后的代码中使用。`value`是我们要赋值给变量的值。注意和 JavaScript 一样,所有 Java 声明结尾都要使用分号。 -### Java中的数据类型 +### Java 中的数据类型 -在Java中,内置的数据类型被称为**原始**数据类型,因为有Python和JavaScript的基础,这些数据类型对于我们来说,看起来非常熟悉。主要的原始类型有: +在 Java 中,内置的数据类型被称为**原始**数据类型,因为有 Python 和 JavaScript 的基础,这些数据类型对于我们来说,看起来非常熟悉。主要的原始类型有: -- 整数 `int`: 存储 −2,147,483,648 到 2,147,483,647之间整数。 -- 浮点数 `float`: 存储 3.4x10^−038 到 3.4x10^038之间的浮点数。 +- 整数 `int`: 存储 −2,147,483,648 到 2,147,483,647 之间整数。 +- 浮点数 `float`: 存储 3.4x10^−038 到 3.4x10^038 之间的浮点数。 - 布尔值 `bool`: 存储`true`或`false`。 请注意,还有一些其他的原始类型(short、long、byte 和 double)我们不会在这里介绍,因为不经常使用。以下展示我们如何初始化这些数据类型: @@ -669,19 +669,19 @@ Datatype name = value; 我们将讨论的下一个数据类型是字符串——表示为文本数据的一系列字符、数字或符号。 -Java中的字符串是一种非原始数据类型,这意味着它们是由较小的部分组成的。要声明一个字符串变量,我们使用String数据类型并将分配的值放在双引号中: +Java 中的字符串是一种非原始数据类型,这意味着它们是由较小的部分组成的。要声明一个字符串变量,我们使用 String 数据类型并将分配的值放在双引号中: ```java String name = "Harry Potter"; ``` -### Java中的程序流控制语句 +### Java 中的程序流控制语句 -与JavaScript一样,Java使用花括号来定义`if`语句、循环和函数的代码块。我们将使用前几章程序控制语句,但改写成Java语法。 +与 JavaScript 一样,Java 使用花括号来定义`if`语句、循环和函数的代码块。我们将使用前几章程序控制语句,但改写成 Java 语法。 -#### If/Else语句 +#### If/Else 语句 -用Java语句编写上问的if/else: +用 Java 语句编写上问的 if/else: ```java int x = 10; @@ -693,9 +693,9 @@ if ( x > 5 ) { } ``` -这里的`if`例子几乎和 JavaScript一样,唯二不同是我们声明了 _x_ 的数据类型为`int`以及我们使用了 `System.out.println()` 而不是 `console.log()`来打印消息。 +这里的`if`例子几乎和 JavaScript 一样,唯二不同是我们声明了 _x_ 的数据类型为`int`以及我们使用了 `System.out.println()` 而不是 `console.log()`来打印消息。 -接下来,我们将讨论 Java 中的循环。Java和JavaScript语法非常相似,所以Java中的`while`循环与我们在JavaScript中看到的基本相同: +接下来,我们将讨论 Java 中的循环。Java 和 JavaScript 语法非常相似,所以 Java 中的`while`循环与我们在 JavaScript 中看到的基本相同: ```java int x = 1; @@ -709,19 +709,19 @@ while ( x <= 100 ) { } ``` -这个`while`循环将打印出指定的消息100次。 +这个`while`循环将打印出指定的消息 100 次。 -我们介绍编程语言的部分到此结束。由于我们使用相同的概念集介绍3种语言,因此可能稍显重复,但希望这有助于为你打下坚实的基础。 +我们介绍编程语言的部分到此结束。由于我们使用相同的概念集介绍 3 种语言,因此可能稍显重复,但希望这有助于为你打下坚实的基础。 接下来我将介绍一些其他相关话题,你不需要立刻就开始学习这些主题。 -我们将讨论一个名为 Git 的重要协作工具,然后我们将学习在数据库中存储和访问数据。接下来我们将简要介绍Web开发框架,最后我们稍微了解包管理器。 +我们将讨论一个名为 Git 的重要协作工具,然后我们将学习在数据库中存储和访问数据。接下来我们将简要介绍 Web 开发框架,最后我们稍微了解包管理器。 <h2 id="11-track-your-code-using-git">11)使用Git跟踪代码</h2> -Git是在最流行的版本控制系统(VCS)。它允许多个开发人员一起协作开发软件。在本节中,我们将了解 Git 是什么、它是如何工作的以及如何操作它的基本命令。 +Git 是在最流行的版本控制系统(VCS)。它允许多个开发人员一起协作开发软件。在本节中,我们将了解 Git 是什么、它是如何工作的以及如何操作它的基本命令。 -在直接进入Git之前,让我们补充一些编程项目共有的概念。 +在直接进入 Git 之前,让我们补充一些编程项目共有的概念。 一个软件项目的全部目录和文件被称为**代码库**。**项目根**是项目目录树中最高级别的文件夹。代码文件可以直接在项目根中,也可以被组织到多个级别的文件夹中。 @@ -749,17 +749,17 @@ Git是在最流行的版本控制系统(VCS)。它允许多个开发人员一起 - Subversion (SVN) - Mercurial (Hg) -Git赢得了VCS的桂冠。迄今为止,它是被全球政府、商业和开源社区使用。 +Git 赢得了 VCS 的桂冠。迄今为止,它是被全球政府、商业和开源社区使用。 -Git促成了基于Web的VCS平台(如 GitHub 和 Bitbucket)的流行。对于任何全面发展的开发人员来说,Git是一个必须要添加到技能包里必不可少的工具。 +Git 促成了基于 Web 的 VCS 平台(如 GitHub 和 Bitbucket)的流行。对于任何全面发展的开发人员来说,Git 是一个必须要添加到技能包里必不可少的工具。 -### 基本Git命令 +### 基本 Git 命令 -Git在**Git仓库**中创建和存储有关我们软件项目的信息。Git仓库只是计算机上的一个隐藏文件夹,Git使用它来存储有关软件项目中代码文件的数据。 +Git 在**Git 仓库**中创建和存储有关我们软件项目的信息。Git 仓库只是计算机上的一个隐藏文件夹,Git 使用它来存储有关软件项目中代码文件的数据。 -我们使用的每个软件项目通常都有自己的Git仓库,用于存储与该项目相关的信息。这样,可以单独跟踪与单个计算机上的不同项目相关的代码。 +我们使用的每个软件项目通常都有自己的 Git 仓库,用于存储与该项目相关的信息。这样,可以单独跟踪与单个计算机上的不同项目相关的代码。 -在计算机上创建Git仓库有两种主要方法。第一个是在文件系统的现有文件夹中创建一个全新的Git仓库。 +在计算机上创建 Git 仓库有两种主要方法。第一个是在文件系统的现有文件夹中创建一个全新的 Git 仓库。 只需打开命令行,在桌面创建一个新文件夹,然后进入: @@ -771,7 +771,7 @@ mkdir testgit cd testgit/ ``` -现在我们创建了一个新文件夹并进入它,我们可以使用以下命令初始化一个新的Git仓库: +现在我们创建了一个新文件夹并进入它,我们可以使用以下命令初始化一个新的 Git 仓库: ```sh git init @@ -783,21 +783,21 @@ git init Initialized empty Git repository in /Users/me/Desktop/testgit/.git/ ``` -我们要运行的所有Git命令都以`git`开头,接着一个空格,然后是我们要运行的具体Git命令。有时我们也会在Git命令后面添加标志和参数。 +我们要运行的所有 Git 命令都以`git`开头,接着一个空格,然后是我们要运行的具体 Git 命令。有时我们也会在 Git 命令后面添加标志和参数。 -`git init`命令在当前目录下创建一个名为`.git`的隐藏文件夹。这个文件夹就是我们上面提到的Git仓库。你可以通过运行`ls -al`命令来查看。 +`git init`命令在当前目录下创建一个名为`.git`的隐藏文件夹。这个文件夹就是我们上面提到的 Git 仓库。你可以通过运行`ls -al`命令来查看。 -获取Git仓库的第二种方法是从其他地方下载一个,例如Bitbucket或GitHub。 +获取 Git 仓库的第二种方法是从其他地方下载一个,例如 Bitbucket 或 GitHub。 -Bitbucket和Github是允许人们托管开源项目的网站,可以将项目下载到你的电脑。 +Bitbucket 和 Github 是允许人们托管开源项目的网站,可以将项目下载到你的电脑。 -如果你在Bitbucket或GitHub上浏览到一个你感兴趣的项目,你会看到一个标有Clone的按钮。这个按钮会给你提供一个命令和URL,你可以把它复制并粘贴到命令行终端: +如果你在 Bitbucket 或 GitHub 上浏览到一个你感兴趣的项目,你会看到一个标有 Clone 的按钮。这个按钮会给你提供一个命令和 URL,你可以把它复制并粘贴到命令行终端: ```sh git clone https://jacobstopak@bitbucket.org/jacobstopak/baby-git.git ``` -`git clone`命令从指定的URL下载版本库到你电脑上的一个新文件夹。这个URL可以是上面例子中的网页URL,也可以是SSH URL,如下所示: +`git clone`命令从指定的 URL 下载版本库到你电脑上的一个新文件夹。这个 URL 可以是上面例子中的网页 URL,也可以是 SSH URL,如下所示: ```sh git clone git@bitbucket.org:jacobstopak/baby-git.git @@ -805,49 +805,49 @@ git clone git@bitbucket.org:jacobstopak/baby-git.git 运行`git clone`命令后,你应该会看到创建了一个新文件夹。如果浏览它,将看到你下载的项目的所有文件和子文件夹。 -接下来我们要提到的命令是`git add <filename.ext>`。`git add`命令用来告诉Git我们想让它跟踪哪些文件,并把已经跟踪的文件的改动添加到 Git的**暂存区域**。 +接下来我们要提到的命令是`git add <filename.ext>`。`git add`命令用来告诉 Git 我们想让它跟踪哪些文件,并把已经跟踪的文件的改动添加到 Git 的**暂存区域**。 -一旦新文件或更改的文件被暂存,就可以用`git commit -m "Commit message" 命令将它们提交到仓库。将在Git仓库中存储所有暂存文件的更改。 +一旦新文件或更改的文件被暂存,就可以用`git commit -m "Commit message" 命令将它们提交到仓库。将在 Git 仓库中存储所有暂存文件的更改。 使用`git status`和`git log`命令可以查看工作目录的当前状态和项目的提交历史。 -我们在这里只触及了表面,[Git还有很多必要的命令](https://initialcommit.com/blog/Git-Cheat-Sheet-Beginner) 值得我们去熟悉。 +我们在这里只触及了表面,[Git 还有很多必要的命令](https://initialcommit.com/blog/Git-Cheat-Sheet-Beginner) 值得我们去熟悉。 <h2 id="12-store-data-using-databases-and-sql">12)使用数据库和SQL来存储数据</h2> 数据库是专门用于有效存储、更新、检索和删除大量数据的程序。简而言之,我们可以把数据库看成是一组表格的容器。 -你可能使用过Microsoft Excel中的表格。表只是一组包含数据的列和行。我们可以在数据库中设置表格来存储我们的程序正常工作所需的信息。 +你可能使用过 Microsoft Excel 中的表格。表只是一组包含数据的列和行。我们可以在数据库中设置表格来存储我们的程序正常工作所需的信息。 -无论我们是用JavaScript、Python、Java还是其他语言编写程序,我们都可以根据需要,告诉我们的程序与数据库交互。 +无论我们是用 JavaScript、Python、Java 还是其他语言编写程序,我们都可以根据需要,告诉我们的程序与数据库交互。 我们可以从数据库中检索数据,在网页上显示给我们的用户。我们可以接受一个用户的网络注册表,并将该用户的信息存储在数据库中供以后使用。 程序可以在运行时与数据库进行实时互动。要做到这一点,大多数数据库使用一种叫做**SQL**的语言,即**结构化查询语言**的简称。 -SQL是一种专门为数据库创建的编程语言。可以通过SQL告诉数据库要做什么。 +SQL 是一种专门为数据库创建的编程语言。可以通过 SQL 告诉数据库要做什么。 -一段SQL代码称为**查询(query)**。我们可以编写SQL查询来获取我们在特定时间需要的数据,或者将新数据插入到特定的表中。粗略地说,有两种主要的SQL查询类型:**read-SQL** 和 **write-SQL**。 +一段 SQL 代码称为**查询(query)**。我们可以编写 SQL 查询来获取我们在特定时间需要的数据,或者将新数据插入到特定的表中。粗略地说,有两种主要的 SQL 查询类型:**read-SQL** 和 **write-SQL**。 -read-SQL是一种简单地从数据库中获取数据供我们查看或使用的查询。它完全不改变数据库中的数据。 +read-SQL 是一种简单地从数据库中获取数据供我们查看或使用的查询。它完全不改变数据库中的数据。 -write-SQL要么在表中插入新数据,要么更新现有数据,要么删除现有数据。我们将在本节中学习如何编写一些基本的read-SQL查询。 +write-SQL 要么在表中插入新数据,要么更新现有数据,要么删除现有数据。我们将在本节中学习如何编写一些基本的 read-SQL 查询。 -在编写查询之前,我们要知道我们编写的查询的是什么! 传统的数据库是由列和行组成的**表**。当我们写一个read-SQL查询时,我们的目标通常是检索这些行和列的一个子集。 +在编写查询之前,我们要知道我们编写的查询的是什么! 传统的数据库是由列和行组成的**表**。当我们写一个 read-SQL 查询时,我们的目标通常是检索这些行和列的一个子集。 -例如,假设我们有一个名为`PERSON`的表,有4列,`FIRST_NAME`和`LAST_NAME`。我们可以使用下面的查询,只从`FIRST_NAME`列中选择所有数据。 +例如,假设我们有一个名为`PERSON`的表,有 4 列,`FIRST_NAME`和`LAST_NAME`。我们可以使用下面的查询,只从`FIRST_NAME`列中选择所有数据。 ```sql SELECT FIRST_NAME FROM PERSON; ``` -SELECT关键字告诉数据库我们要检索数据。后面接着我们想要查询的列名FIRST\_NAME。 +SELECT 关键字告诉数据库我们要检索数据。后面接着我们想要查询的列名 FIRST_NAME。 -然后我们使用FROM关键字告诉数据库要从哪个表中获取数据,在本例中为PERSON表。另外,请注意所有SQL命令都以分号结尾。 +然后我们使用 FROM 关键字告诉数据库要从哪个表中获取数据,在本例中为 PERSON 表。另外,请注意所有 SQL 命令都以分号结尾。 我们对数据最常见的要求之一是对其进行过滤。过滤意味着根据指定的条件来限制结果集。 -例如,我们可能只想从`PERSON`表中选择名为"PHIL"的人的记录。我们可以使用`WHERE`关键字在SQL查询中应用过滤器。 +例如,我们可能只想从`PERSON`表中选择名为"PHIL"的人的记录。我们可以使用`WHERE`关键字在 SQL 查询中应用过滤器。 ```sql SELECT * FROM PERSON WHERE FIRST_NAME = 'PHIL'; @@ -865,7 +865,7 @@ ORDER BY LAST_NAME; 这将返回`PERSON`表中的所有列,按姓氏的字母顺序排序。 -默认情况下,结果将按升序排序,从A到Z。我们可以添加可选的`ASC`或`DESC`关键字,以指定是按升序还是降序排序。 +默认情况下,结果将按升序排序,从 A 到 Z。我们可以添加可选的`ASC`或`DESC`关键字,以指定是按升序还是降序排序。 ```sql SELECT * @@ -875,30 +875,30 @@ ORDER BY LAST_NAME DESC; <h2 id="13-read-about-web-frameworks-and-mvc"> 13)阅读Web框架和MVC</h2> -很多时候,我们是在为非常常见的应用程序类型编写代码。web应用程序(或称**web apps**)是依靠互联网来运作的应用程序。web应用程序便是最常见的软件应用程序类型之一。 +很多时候,我们是在为非常常见的应用程序类型编写代码。web 应用程序(或称**web apps**)是依靠互联网来运作的应用程序。web 应用程序便是最常见的软件应用程序类型之一。 -web应用程序本质上是一个功能更强大的网站。大多数web应用程序实现了一些网络服务器上的后端代码,并在幕后执行逻辑,以支持应用程序的功能。 +web 应用程序本质上是一个功能更强大的网站。大多数 web 应用程序实现了一些网络服务器上的后端代码,并在幕后执行逻辑,以支持应用程序的功能。 -web应用程序后端代码使用的常见编程语言包括Python、Java和JavaScript等。 +web 应用程序后端代码使用的常见编程语言包括 Python、Java 和 JavaScript 等。 -Web应用程序共有的一些功能包括: +Web 应用程序共有的一些功能包括: - 提供一种方便的方式来动态更改网页上的内容 - 通过登录页面执行安全用户身份验证 - 提供强大的应用程序安全功能 - 读取和写入数据到数据库 -Web框架是一组代码库,其中包含所有Web应用程序可以开箱即用的常用功能。 Web框架为开发人员提供了一个系统来构建他们的应用程序,使用框架就不必担心Web应用程序常见的许多幕后任务的编写代码。 +Web 框架是一组代码库,其中包含所有 Web 应用程序可以开箱即用的常用功能。 Web 框架为开发人员提供了一个系统来构建他们的应用程序,使用框架就不必担心 Web 应用程序常见的许多幕后任务的编写代码。 -我们只需要利用框架中满足Web应用程序需求的部分即可。 +我们只需要利用框架中满足 Web 应用程序需求的部分即可。 -例如,如果我们不需要连接到特定Web应用程序中的数据库,我们可以忽略数据库功能并使用我们确实需要的其他功能。 +例如,如果我们不需要连接到特定 Web 应用程序中的数据库,我们可以忽略数据库功能并使用我们确实需要的其他功能。 -我们可以定制应用程序的网页、用户流和业务逻辑。你可以把web框架看成是一个编程工具包,我们可以用它来构建web应用。 +我们可以定制应用程序的网页、用户流和业务逻辑。你可以把 web 框架看成是一个编程工具包,我们可以用它来构建 web 应用。 -本文中所涉及的编程语言都有一个或多个流行web框架。它让开发团队可以灵活地使用他们最精通的语言的框架。 +本文中所涉及的编程语言都有一个或多个流行 web 框架。它让开发团队可以灵活地使用他们最精通的语言的框架。 -Java有Spring框架,通过**Spring Boot**使得开发变得方便。Python有**Django**框架。JavaScript有**Node.js**运行环境,包括**Express.js**和**Meteor.js**等多个框架选项。这些框架都是免费和开源的。 +Java 有 Spring 框架,通过**Spring Boot**使得开发变得方便。Python 有**Django**框架。JavaScript 有**Node.js**运行环境,包括**Express.js**和**Meteor.js**等多个框架选项。这些框架都是免费和开源的。 <h2 id="14-play-with-package-manager"> 14)玩转包管理工具</h2> @@ -912,23 +912,23 @@ Java有Spring框架,通过**Spring Boot**使得开发变得方便。Python有* ### Mac OS X: Homebrew -**Homebrew**是Mac OS X操作系统上最流行的包管理器。它提供在Mac上安装、更新、跟踪、列出和卸载软件包和应用程序的便捷方式。 +**Homebrew**是 Mac OS X 操作系统上最流行的包管理器。它提供在 Mac 上安装、更新、跟踪、列出和卸载软件包和应用程序的便捷方式。 -许多可以通过.dmg文件下载安装的应用程序也可以使用Homebrew下载和安装。 +许多可以通过.dmg 文件下载安装的应用程序也可以使用 Homebrew 下载和安装。 -以下是通过Homebrew安装`wget`包的示例: +以下是通过 Homebrew 安装`wget`包的示例: ```sh brew install wget ``` -### Linux: Apt和Yum +### Linux: Apt 和 Yum -Linux是围绕命令行构建的,包管理器是安装程序的默认方式也就不足为奇了。 +Linux 是围绕命令行构建的,包管理器是安装程序的默认方式也就不足为奇了。 -大多数主流版本的Linux都带有内置的包管理器。**Advanced Package Tool**(APT)是Debian和基于Ubuntu的Linux发行版的本地包管理器。 **Yellowdog Updater, Modified** (YUM)是RedHat Linux发行版的本地包管理器。 +大多数主流版本的 Linux 都带有内置的包管理器。**Advanced Package Tool**(APT)是 Debian 和基于 Ubuntu 的 Linux 发行版的本地包管理器。 **Yellowdog Updater, Modified** (YUM)是 RedHat Linux 发行版的本地包管理器。 -下面是一个使用APT安装Vim的例子: +下面是一个使用 APT 安装 Vim 的例子: ```sh sudo apt-get install vim @@ -942,9 +942,9 @@ sudo yum install vim ### JavaScript: Node Package Manager (NPM) -现在我们已经了解了一些操作系统级别的包管理器是如何工作的,让我们来看看编程语言的包管理器。这些可以帮助我们管理项目所依赖的软件库。 **Node Package Manager (NPM)**默认随Node.js一起安装。 +现在我们已经了解了一些操作系统级别的包管理器是如何工作的,让我们来看看编程语言的包管理器。这些可以帮助我们管理项目所依赖的软件库。 **Node Package Manager (NPM)**默认随 Node.js 一起安装。 -NPM与我们之前看到的包管理器之间的一个区别是NPM可以在**本地**或**全局**模式下运行。本地模式用于仅在我们正在处理的特定项目/目录中,而全局模式用于在系统上安装包。 +NPM 与我们之前看到的包管理器之间的一个区别是 NPM 可以在**本地**或**全局**模式下运行。本地模式用于仅在我们正在处理的特定项目/目录中,而全局模式用于在系统上安装包。 默认情况下,软件包安装在本地,但您可以使用`-g`标志全局安装软件包: @@ -954,7 +954,7 @@ npm install request -g ### Python: Pip -Python有一个名为**Pip**的包管理器。你的系统可能已经安装了Pip,因为它与最新版本的Python打包在一起。我们可以轻松地从**Python索引安装包**使用`pip install <package-name>`命令来安装Pip: +Python 有一个名为**Pip**的包管理器。你的系统可能已经安装了 Pip,因为它与最新版本的 Python 打包在一起。我们可以轻松地从**Python 索引安装包**使用`pip install <package-name>`命令来安装 Pip: ```sh pip install requests @@ -964,7 +964,7 @@ pip install requests **Apache Maven**(通常简称为** Maven**)是一个免费的开源工具套件,包括依赖管理。 -尽管也支持其他语言,Maven主要用于Java项目。 Maven可以做很多事情。它的使用稍微复杂一些,我们在这里不再赘述。 +尽管也支持其他语言,Maven 主要用于 Java 项目。 Maven 可以做很多事情。它的使用稍微复杂一些,我们在这里不再赘述。 ## 总结 @@ -974,9 +974,9 @@ pip install requests ## 未来规划 -如果你喜欢这篇文章,我写了一本名为[《面向开发人员的 Coding Essentials Guidebook》](https://initialcommit.com/store/coding-essentials-guidebook-for-developers)的书,一共14章,每章都涵盖了本文中讨论的一个主题。 +如果你喜欢这篇文章,我写了一本名为[《面向开发人员的 Coding Essentials Guidebook》](https://initialcommit.com/store/coding-essentials-guidebook-for-developers)的书,一共 14 章,每章都涵盖了本文中讨论的一个主题。 -在这本书中,我对这14个主题进行了更深入的研究,因此这本书是检验你是否从这篇文章有所收获的一个途径。 +在这本书中,我对这 14 个主题进行了更深入的研究,因此这本书是检验你是否从这篇文章有所收获的一个途径。 可能因为这篇文章被某种特定的语言、工具或概念所吸引,我鼓励您深入研究该领域以进一步学习。 diff --git a/chinese/articles/how-to-make-line-charts-in-javascript.md b/chinese/articles/how-to-make-line-charts-in-javascript.md index 65f6d99b4..8a799ba69 100644 --- a/chinese/articles/how-to-make-line-charts-in-javascript.md +++ b/chinese/articles/how-to-make-line-charts-in-javascript.md @@ -7,37 +7,37 @@ 数据可视化这个话题包含了众多不同的[图表](https://datavizcatalogue.com/)供你学习和构建。 -但是有几个基本的、常青的图表,每个处理分析的数据设计师和Web开发人员都应该知道。 +但是有几个基本的、常青的图表,每个处理分析的数据设计师和 Web 开发人员都应该知道。 其中之一是**折线图**,它主要用于表示随时间变化的数据。 -本教程将教会你如何使用JavaScript快速地创建漂亮的交互式折线(和阶梯线)图表。我们将看一些很酷的示例并逐步构建它们,过程既清晰又有趣。 +本教程将教会你如何使用 JavaScript 快速地创建漂亮的交互式折线(和阶梯线)图表。我们将看一些很酷的示例并逐步构建它们,过程既清晰又有趣。 方便起见,你可以在我的[CodePen](https://codepen.io/collection/pgPwyr)上找到所有示例,你可以进一步调整这些折线图,熟悉技能。 ### 我们的数据集 -在过去的20年里,网球世界的发展可谓精彩纷呈。 [三巨头](https://en.wikipedia.org/wiki/Big_Three_(tennis)) — 罗杰·费德勒、 拉斐尔·纳达尔以及诺瓦克·德约科维奇 — 赢得了惊人的63次 (过去一共78场)大满贯赛事。这些赛事是网球界最负盛名的锦标赛。 +在过去的 20 年里,网球世界的发展可谓精彩纷呈。 [三巨头](https://en.wikipedia.org/wiki/Big_Three_(tennis)) — 罗杰·费德勒、 拉斐尔·纳达尔以及诺瓦克·德约科维奇 — 赢得了惊人的 63 次 (过去一共 78 场)大满贯赛事。这些赛事是网球界最负盛名的锦标赛。 -我决定绘制他们出色的竞争关系。本教程中的JS折线图将直观地展现**三巨头的大满贯冠军争夺战**。而第一个发球已经来了! +我决定绘制他们出色的竞争关系。本教程中的 JS 折线图将直观地展现**三巨头的大满贯冠军争夺战**。而第一个发球已经来了! # **四步构建折线图** -在JavaScript中创建任何图表的过程都分为四个步骤,折线图也不例外: +在 JavaScript 中创建任何图表的过程都分为四个步骤,折线图也不例外: -1. 制作一个带容器(container)的HTML页面。 -2. 引用JavaScript文件。 +1. 制作一个带容器(container)的 HTML 页面。 +2. 引用 JavaScript 文件。 3. 添加数据。 4. 编写可视化代码。 让我们逐步拆解这些步骤。 -### 1\. 制作一个带容器(container)的HTML页面 +### 1. 制作一个带容器(container)的 HTML 页面 首先,设定一个你希望你的图表出现的地方。 -如果还没有确定,请创建一个基本网页。然后为折线图创建一个容器——添加一个HTML块级元素并为其提供唯一ID以供进一步引用。 +如果还没有确定,请创建一个基本网页。然后为折线图创建一个容器——添加一个 HTML 块级元素并为其提供唯一 ID 以供进一步引用。 代码如下: @@ -57,16 +57,16 @@ </html> ``` -`width`和`height`参数设置为100%,因此折线图会渲染整个web页面。当然你可以根据自己的喜好来设定这些参数。 +`width`和`height`参数设置为 100%,因此折线图会渲染整个 web 页面。当然你可以根据自己的喜好来设定这些参数。 -### 2\. 引用JavaScript文件 +### 2. 引用 JavaScript 文件 -然后在`<head>`部分引用所有我们需要的JavaScript文件。 +然后在`<head>`部分引用所有我们需要的 JavaScript 文件。 -有非常多[JavaScript图表库](https://en.wikipedia.org/wiki/Comparison_of_JavaScript_charting_libraries)辅助你通过简单的方式视觉化数据,其中大部分都支持折线图,你可以根据自己的项目需求做选择。 +有非常多[JavaScript 图表库](https://en.wikipedia.org/wiki/Comparison_of_JavaScript_charting_libraries)辅助你通过简单的方式视觉化数据,其中大部分都支持折线图,你可以根据自己的项目需求做选择。 -出于教学说明的目的,在本教程中我将使用[AnyChart JS Charts](https://www.anychart.com/)。它很方便灵活,附有[图表文档](https://docs.anychart.com)和[API参考](https://api.anychart.com),除非商用,你可以免费使用。 +出于教学说明的目的,在本教程中我将使用[AnyChart JS Charts](https://www.anychart.com/)。它很方便灵活,附有[图表文档](https://docs.anychart.com)和[API 参考](https://api.anychart.com),除非商用,你可以免费使用。 我通过[CDN](https://www.anychart.com/download/cdn/)选择了“base”模块。 (当然,你可以下载并放入自己的网站文件夹中,或者使用你自己的链接)。 @@ -90,7 +90,7 @@ </html> ``` -折线图的JavaScript代码将放在 `<script>`和`</script>`标签之间,该组标签位于 `<body>` 部分 (如果需要的话,你也可以放在`<head>`部分)。 +折线图的 JavaScript 代码将放在 `<script>`和`</script>`标签之间,该组标签位于 `<body>` 部分 (如果需要的话,你也可以放在`<head>`部分)。 ### 3\. 添加数据 @@ -98,7 +98,7 @@ 我清点了[费德勒、纳达尔和德约科维奇获得大满贯头衔](https://en.wikipedia.org/wiki/Big_Three_(tennis)#Grand_Slam_tournaments)的次数,以年为统计单位。我将以数组的形式添加到折线图数据中。 -如果你更倾向使用其他的形式, 如JSON、XML、CSV或者其他,你可以参考[处理数据的不同形式](https://docs.anychart.com/Working_with_Data/Overview)。 +如果你更倾向使用其他的形式, 如 JSON、XML、CSV 或者其他,你可以参考[处理数据的不同形式](https://docs.anychart.com/Working_with_Data/Overview)。 ```javascript var data = [ @@ -130,7 +130,7 @@ var data = [ ### 4\. 编写可视化代码 -热身环节结束,球场已经就绪。让我们开始比赛并且快速编写一些JavaScript代码吧! +热身环节结束,球场已经就绪。让我们开始比赛并且快速编写一些 JavaScript 代码吧! 首先,添加 `anychart.onDocumentReady()`: @@ -183,7 +183,7 @@ chart.legend().enabled(true); chart.title("Big Three's Grand Slam Title Race"); ``` -最后,引用容器元素ID并且绘制折线图 +最后,引用容器元素 ID 并且绘制折线图 ```javascript // 设置在哪里展现折线图 @@ -193,13 +193,13 @@ chart.container("container"); chart.draw(); ``` -就这么多!一个用JS绘制的图表就完成了,像是[直落两盘](https://sports.answers.com/Q/What_does_straight_sets_mean_in_a_game_of_tennis) 的胜利,不是吗? +就这么多!一个用 JS 绘制的图表就完成了,像是[直落两盘](https://sports.answers.com/Q/What_does_straight_sets_mean_in_a_game_of_tennis) 的胜利,不是吗? ![A basic multi-line chart, JS.](https://www.freecodecamp.org/news/content/images/2022/09/line-chart-1.png) -三巨头大满贯头衔比赛折线图 - 通过AnyChart创建 +三巨头大满贯头衔比赛折线图 - 通过 AnyChart 创建 -可以在[CodePen](https://codepen.io/shacheeswadia/pen/gOvjVaK)查看包含HTML/CSS/JS的基础版本折线图。为了避免意外,这里也是所有代码: +可以在[CodePen](https://codepen.io/shacheeswadia/pen/gOvjVaK)查看包含 HTML/CSS/JS 的基础版本折线图。为了避免意外,这里也是所有代码: ```html <html> @@ -287,7 +287,7 @@ chart.draw(); ### 1\. 命名轴 -即使已经很明显,标明折线图的每个轴代表什么总不会错。请使用以下命令将标题添加到X轴和Y轴: +即使已经很明显,标明折线图的每个轴代表什么总不会错。请使用以下命令将标题添加到 X 轴和 Y 轴: ```javascript chart.yAxis().title("Titles won"); @@ -308,7 +308,7 @@ thirdSeries.hovered().markers().type("circle").size(4); ### 3\. 启用十字准线 -十字准线是鼠标指针后的一对垂直线,可帮助您更好地了解当前悬停点的X和Y值。 +十字准线是鼠标指针后的一对垂直线,可帮助您更好地了解当前悬停点的 X 和 Y 值。 在我们的例子中,仅使用一条这样的垂直线来突出显示年份就足够了: @@ -327,7 +327,7 @@ chart.tooltip().positionMode("point"); chart.tooltip().position("right").anchor("left-center").offsetX(5).offsetY(5); ``` -查看自定义之后的折线图 (完整的live版本请移步[CodePen](https://codepen.io/shacheeswadia/pen/vYdaoyR)。) +查看自定义之后的折线图 (完整的 live 版本请移步[CodePen](https://codepen.io/shacheeswadia/pen/vYdaoyR)。) ![A custom line chart, JS.](https://www.freecodecamp.org/news/content/images/2022/09/line-chart-2.png) @@ -348,7 +348,7 @@ thirdSeries.normal().stroke("#43a7dc", 2.5); 本教程的最后一个更改(并使交互式折线图完整)是自定义标题和图例。 -可以添加副标题以提供更多相关信息,并且可以借助HTML进行一些文本样式的快速调整,使图表具吸引力: +可以添加副标题以提供更多相关信息,并且可以借助 HTML 进行一些文本样式的快速调整,使图表具吸引力: ```javascript chart @@ -361,13 +361,13 @@ chart ); ``` -对于图表图例来说,修改字体大小和padding很容易: +对于图表图例来说,修改字体大小和 padding 很容易: ```javascript chart.legend().enabled(true).fontSize(14).padding([10, 0, 10, 0]); ``` -看看结果如何!(可以在[CodePen](https://codepen.io/shacheeswadia/pen/wvyxVqZ)查看JS代码。) +看看结果如何!(可以在[CodePen](https://codepen.io/shacheeswadia/pen/wvyxVqZ)查看 JS 代码。) ![Advanced line chart built with JavaScript](https://www.freecodecamp.org/news/content/images/2022/09/line-chart-3.png) @@ -506,7 +506,7 @@ thirdSeries.name("Novak Djokovic"); # **总结** -从本教程可以看到,使用JavaScript创建交互折线图(阶梯线图)非常简单直白。如果你有任何的建议,欢迎告诉我。 +从本教程可以看到,使用 JavaScript 创建交互折线图(阶梯线图)非常简单直白。如果你有任何的建议,欢迎告诉我。 敬仰这些职业运动员在自己领域的成就非常鼓舞人心。 diff --git a/chinese/articles/how-to-make-your-tic-tac-toe-game-unbeatable-by-using-the-minimax-algorithm-9d690bad4b37.md b/chinese/articles/how-to-make-your-tic-tac-toe-game-unbeatable-by-using-the-minimax-algorithm-9d690bad4b37.md index 88b1c96fa..1c16fbd24 100644 --- a/chinese/articles/how-to-make-your-tic-tac-toe-game-unbeatable-by-using-the-minimax-algorithm-9d690bad4b37.md +++ b/chinese/articles/how-to-make-your-tic-tac-toe-game-unbeatable-by-using-the-minimax-algorithm-9d690bad4b37.md @@ -7,11 +7,11 @@ 我挣扎了数小时,看了各种各样的教学资料、视频,敲自己脑袋,试图使用人工智能(Artificial Intelligence)创建一个无法被击败的井字棋游戏。如果你也又同样的经历,我想给你介绍极小化极大算法([Minimax algorithm](https://zh.wikipedia.org/zh-tw/%E6%9E%81%E5%B0%8F%E5%8C%96%E6%9E%81%E5%A4%A7%E7%AE%97%E6%B3%95))。 -这种算法就像一个专业的象棋选手,总能多想几步,并且站在对手的立场来思考棋局。算法不断向前预测,直到触达棋局的最终棋子排列局面(**最终状态**),这一状态要么就是平局、胜利或者失败。一旦进入到最终状态,AI就会分配正分(+10)给胜利,负分(-10)给失败或者中性分(0)给平局。 +这种算法就像一个专业的象棋选手,总能多想几步,并且站在对手的立场来思考棋局。算法不断向前预测,直到触达棋局的最终棋子排列局面(**最终状态**),这一状态要么就是平局、胜利或者失败。一旦进入到最终状态,AI 就会分配正分(+10)给胜利,负分(-10)给失败或者中性分(0)给平局。 -同时,这个算法当轮到玩家下棋的时候,算法会评估玩家触达最终状态的步骤。当轮到AI的时候,算法会选择最大分数值的步骤,而当轮到玩家的时候,算法会选择最小分数值的步骤。极小化极大算法通过这个策略来避免AI输给玩家。 +同时,这个算法当轮到玩家下棋的时候,算法会评估玩家触达最终状态的步骤。当轮到 AI 的时候,算法会选择最大分数值的步骤,而当轮到玩家的时候,算法会选择最小分数值的步骤。极小化极大算法通过这个策略来避免 AI 输给玩家。 -你可以使用chrome浏览器自行测试这个游戏。 +你可以使用 chrome 浏览器自行测试这个游戏。 See the Pen <a href='https://codepen.io/abdolsa/pen/vgjoMb'>minimax 4 medium</a> by Ahmad Abdolsaheb (<a href='https://codepen.io/abdolsa'>@abdolsa</a>) on <a href='https://codepen.io'>CodePen</a>. @@ -23,21 +23,21 @@ See the Pen <a href='https://codepen.io/abdolsa/pen/vgjoMb'>minimax 4 medium</a> 4. 评估调用函数的返回值 5. 返回最佳值 -如果你不太熟悉递归的概念, 推荐你观看来自哈佛的CS50这个课程的[视频](https://www.youtube.com/watch?v=VrrnjYgDBEk)。 +如果你不太熟悉递归的概念, 推荐你观看来自哈佛的 CS50 这个课程的[视频](https://www.youtube.com/watch?v=VrrnjYgDBEk)。 让我们通过代码示例来理解极小化极大算法,在接下来的两节你会看到具体的代码以及代码的实现步骤。 ### 极小化极大算法的代码 -在这篇教程中,我们将从如图2所示的接近棋局尾声开始讲解。因为极小化极大算法评估了棋局的每一个状态(非常多),所以从尾声开始能够帮助你更容易地理解极小化极大的递归调用是如何运作的。 +在这篇教程中,我们将从如图 2 所示的接近棋局尾声开始讲解。因为极小化极大算法评估了棋局的每一个状态(非常多),所以从尾声开始能够帮助你更容易地理解极小化极大的递归调用是如何运作的。 -在下图中,X代表AI,O代表的是玩家。 +在下图中,X 代表 AI,O 代表的是玩家。 ![iYDAcMcMvbr0lBISCQqM-mBbfhqDx2sPqcYl](https://cdn-media-1.freecodecamp.org/images/iYDAcMcMvbr0lBISCQqM-mBbfhqDx2sPqcYl) -图2 +图 2 -为了更好地处理井字棋棋盘,我们可以把它定义为一个包含9个元素的数组。每一个元素的索引(index)为它的值,这个之后会派上用场。因为图上的棋盘已经下好了X和O,所以棋盘对应的数组(_origBoard_)如下: +为了更好地处理井字棋棋盘,我们可以把它定义为一个包含 9 个元素的数组。每一个元素的索引(index)为它的值,这个之后会派上用场。因为图上的棋盘已经下好了 X 和 O,所以棋盘对应的数组(_origBoard_)如下: ```javascript var origBoard = ["O",1,"X","X",4,"X",6,"O","O"]; @@ -45,7 +45,7 @@ var origBoard = ["O",1,"X","X",4,"X",6,"O","O"]; 然后声明 _aiPlayer_ 和 _huPlayer_ 两个变量并分别赋值为“X”和“O”。 -接下来,你需要创建寻找获胜的元素组合的函数,并在找到后返回true;还需要一个函数列举所有棋盘空位对应的索引。 +接下来,你需要创建寻找获胜的元素组合的函数,并在找到后返回 true;还需要一个函数列举所有棋盘空位对应的索引。 ```javascript /* 原始棋盘 @@ -97,7 +97,7 @@ function minimax(newBoard, player){ var availSpots = emptyIndexies(newBoard); ``` -同时,你需要检查最终状态,并且返回对应的值。如果O获胜,则返回-10;如果X获胜,则返回+10。另外,如果 _availSpots_ 数组的长度为0,则没有可以落子的空位,就是平局,返回0. +同时,你需要检查最终状态,并且返回对应的值。如果 O 获胜,则返回-10;如果 X 获胜,则返回+10。另外,如果 _availSpots_ 数组的长度为 0,则没有可以落子的空位,就是平局,返回 0. ```javascript @@ -153,11 +153,11 @@ function minimax(newBoard, player){ } ``` -然后。极小化极大算法需要在 _moves_ 数组中搜寻最佳 _move_ 。 当是AI下棋时,应该选择分数最高的 _move_,而当是玩家的时候,应该选择分数最低的 _move_。因此,如果 _player_ 是 _aiPlayer_,将变量 _bestScore_ 设置为一个非常低的数值,然后遍历整个 _moves_ 数组, 如果 _move_ 的 _score_ 比 _bestScore_ 高,算法就存储这个 _move_。如果发生了出现分数相同的情况,只存储第一个值。 +然后。极小化极大算法需要在 _moves_ 数组中搜寻最佳 _move_ 。 当是 AI 下棋时,应该选择分数最高的 _move_,而当是玩家的时候,应该选择分数最低的 _move_。因此,如果 _player_ 是 _aiPlayer_,将变量 _bestScore_ 设置为一个非常低的数值,然后遍历整个 _moves_ 数组, 如果 _move_ 的 _score_ 比 _bestScore_ 高,算法就存储这个 _move_。如果发生了出现分数相同的情况,只存储第一个值。 当 _player_ 是 _huPlayer_ 时,也进行同样的步骤,只是 _bestScore_ 会先被定义为一个非常大的数值,极小化极大算法再寻找最低的值存储。 -最终,Minimax会返回一个存储 _bestMove_ 的对象。 +最终,Minimax 会返回一个存储 _bestMove_ 的对象。 ```javascript // 如果轮到AI,就遍历所有步骤,并找到分数最高的步 @@ -187,11 +187,11 @@ function minimax(newBoard, player){ } ``` -> _这就是minimax函数的全部内容 :) 你可以在[github](https://github.com/ahmadabdolsaheb/minimaxarticle)和[codepen](https://codepen.io/abdolsa/pen/mABGoz?editors=1011)找到算法的代码。尝试不同的棋盘,并且在控制台查看结果。_ +> _这就是 minimax 函数的全部内容 :) 你可以在[github](https://github.com/ahmadabdolsaheb/minimaxarticle)和[codepen](https://codepen.io/abdolsa/pen/mABGoz?editors=1011)找到算法的代码。尝试不同的棋盘,并且在控制台查看结果。_ -在接下来的章节,我们将通过图2所示的棋盘,来逐条查看代码,了解minimax函数到底是如何运作的。 +在接下来的章节,我们将通过图 2 所示的棋盘,来逐条查看代码,了解 minimax 函数到底是如何运作的。 -### Minimax的运作 +### Minimax 的运作 让我们使用下面的数据,来一步一步查看的算法函数(以下简称**FC**)是怎么运作的。 @@ -199,43 +199,43 @@ function minimax(newBoard, player){ ![1_VG79nxl-mJQrsp6p3q79qA--1-](https://www.freecodecamp.org/news/content/images/2020/09/1_VG79nxl-mJQrsp6p3q79qA--1-.png) -图3 minimax函数被函数调用 +图 3 minimax 函数被函数调用 -**_1._**_origBoard_ 和 _aiPlayer_ 带入算法。算法创建由找到的三个空位组成列表,从第一个空位开始遍历所有空位,检查最终状态。然后,将 _aiPlayer_ 放入第一个空位改变 _newBoard_ 。然后用 _newBoard_ 和 _huPlayer_ 调用自身,等待FC返回值。 +**_1._**_origBoard_ 和 _aiPlayer_ 带入算法。算法创建由找到的三个空位组成列表,从第一个空位开始遍历所有空位,检查最终状态。然后,将 _aiPlayer_ 放入第一个空位改变 _newBoard_ 。然后用 _newBoard_ 和 _huPlayer_ 调用自身,等待 FC 返回值。 -**2\.** 当第一个FC还在运行的时候,第二个FC创建由找到的两个空位组成的列表,从第一个空格开始遍历所有空格,检查最终状态。然后,将 _huPlayer_ 放入第一个空位改变 _newBoard_ 。然后用 _newBoard_ 和 _aiPlayer_ 调用自身,等待FC返回值。 +**2\.** 当第一个 FC 还在运行的时候,第二个 FC 创建由找到的两个空位组成的列表,从第一个空格开始遍历所有空格,检查最终状态。然后,将 _huPlayer_ 放入第一个空位改变 _newBoard_ 。然后用 _newBoard_ 和 _aiPlayer_ 调用自身,等待 FC 返回值。 -**3\.** 最后,算法会创建一个空位的列表,并且在检查最终状态后,判定玩家获胜。这样,返回一个分数(score)属性值为-10的对象。 +**3\.** 最后,算法会创建一个空位的列表,并且在检查最终状态后,判定玩家获胜。这样,返回一个分数(score)属性值为-10 的对象。 -> _在第二个FC中列表有两个空位, 算法将_ huPlayer _带入以改变_ newBoard _。然后用_ newBoard _和_ aiPlayer _重新调用自己。_ +> _在第二个 FC 中列表有两个空位, 算法将_ huPlayer _带入以改变_ newBoard _。然后用_ newBoard _和_ aiPlayer _重新调用自己。_ -**4.** 算法创建一个空位列表,然后在检查最终状态时,发现玩家获胜,因此,返回一个分数属性值为-10的对象。 +**4.** 算法创建一个空位列表,然后在检查最终状态时,发现玩家获胜,因此,返回一个分数属性值为-10 的对象。 -> _在第二个FC中,算法收集了下一层的值 (第三个和第四个FC的返回值)。因为轮到_ huPlayer _所以要选择两个值中较小的一个。两个结果相同,所以算法选择第一个返回到上一层FC。_ +> _在第二个 FC 中,算法收集了下一层的值 (第三个和第四个 FC 的返回值)。因为轮到_ huPlayer _所以要选择两个值中较小的一个。两个结果相同,所以算法选择第一个返回到上一层 FC。_ -> _此时,第一个FC已经评估了将_ aiPlayer _放在第一个空位的分数情况。 然后调整_ newBoard _,把_ aiPlayer _放在第二个空位。 然后用_ newBoard _和_ huPlayer _调用自己。_ +> _此时,第一个 FC 已经评估了将_ aiPlayer _放在第一个空位的分数情况。 然后调整_ newBoard _,把_ aiPlayer _放在第二个空位。 然后用_ newBoard _和_ huPlayer _调用自己。_ -**5.** 到第五个FC,算法生成一个空位列表,并且在检查最终状态后发现AI获胜,因此返回一个对象,分数属性值为+10。 +**5.** 到第五个 FC,算法生成一个空位列表,并且在检查最终状态后发现 AI 获胜,因此返回一个对象,分数属性值为+10。 -> _然后,第一个FC继续改变_ newBoard _,将_ aiPlayer _放入第三个空格。 然后用_ newBoard _和_ huPlayer _调用自己。_ +> _然后,第一个 FC 继续改变_ newBoard _,将_ aiPlayer _放入第三个空格。 然后用_ newBoard _和_ huPlayer _调用自己。_ -**6.** 第六个FC首先创建一个有两个空位的列表, 然后修改 _newBoard_ ,把 _huPlayer_ 放在第一个空位。 然后使用 _newBoard_ 和 _aiPlayer_ 调用自己,并等到返回一个分数。 +**6.** 第六个 FC 首先创建一个有两个空位的列表, 然后修改 _newBoard_ ,把 _huPlayer_ 放在第一个空位。 然后使用 _newBoard_ 和 _aiPlayer_ 调用自己,并等到返回一个分数。 -**7.** 现在算法已经有两层递归。创建一个只有一个空位的列表,然后检查最终状态。修改 _newBoard_ ,将 _aiPlayer_ 放入空位。 之后使用 _newBoard_ 和 _huPlayer_ 调用自己,然后等待FC返回一个可以被评估的分数。 +**7.** 现在算法已经有两层递归。创建一个只有一个空位的列表,然后检查最终状态。修改 _newBoard_ ,将 _aiPlayer_ 放入空位。 之后使用 _newBoard_ 和 _huPlayer_ 调用自己,然后等待 FC 返回一个可以被评估的分数。 -**8\.** 到第八个FC,算法创建空位列表,检查最终状态后发现 _aiPlayer_ 获胜,因此,返回包含分数属性的对象,值为+10,并向上传递(第7个FC)。 +**8\.** 到第八个 FC,算法创建空位列表,检查最终状态后发现 _aiPlayer_ 获胜,因此,返回包含分数属性的对象,值为+10,并向上传递(第 7 个 FC)。 -> _第7个FC仅接受下面一层(第8个FC)的一个正值。因为当轮到_ aiPlayer _时,仅得到的这个值,算法需要返回在最底层里得到的最大值。所以,仅返回一个正值 (+10)到上一层(第六个FC)._ +> _第 7 个 FC 仅接受下面一层(第 8 个 FC)的一个正值。因为当轮到_ aiPlayer _时,仅得到的这个值,算法需要返回在最底层里得到的最大值。所以,仅返回一个正值 (+10)到上一层(第六个 FC)._ -> _因为第6个FC有两个空位, 算法修改_ newBoard _把_ huPlayer _放入第二个空位。 然后用_ newBoard _和_ aiPlayer _调用自己。_ +> _因为第 6 个 FC 有两个空位, 算法修改_ newBoard _把_ huPlayer _放入第二个空位。 然后用_ newBoard _和_ aiPlayer _调用自己。_ **9\.** 然后算法创建一个空格列表,检查最终状态后,发现 _aiPlayer_ 获胜。 因此返回一个对象,分数属性为+10。 -> _此时, 第六个FC需要从第7个FC的分数(+10)(一开始从第8个FC传上来的) 和第9个FC的分数(-10)间选择。 因为是轮到_ huPlayer _,算法使用(-10)并向上继续传递。_ +> _此时, 第六个 FC 需要从第 7 个 FC 的分数(+10)(一开始从第 8 个 FC 传上来的) 和第 9 个 FC 的分数(-10)间选择。 因为是轮到_ huPlayer _,算法使用(-10)并向上继续传递。_ -> _最终,三个分支的分数都被评估了( -10, +10, -10)。 因为是aiPlayer的轮数,算法返回最大的值(+10)和这个值的索引号(4)。_ +> _最终,三个分支的分数都被评估了( -10, +10, -10)。 因为是 aiPlayer 的轮数,算法返回最大的值(+10)和这个值的索引号(4)。_ -**在上述场景中,Minimax总结出来将X放置在棋盘中间会是最优解。:)** +**在上述场景中,Minimax 总结出来将 X 放置在棋盘中间会是最优解。:)** ### 总结! @@ -243,4 +243,4 @@ function minimax(newBoard, player){ _感谢阅读!如果你喜欢这篇文章,希望你转发到社交媒体上。_ -_特别鸣谢Tuba Yilmaz、Rick McGavin和Javid Askerov_ _对本文的审稿。_ +_特别鸣谢 Tuba Yilmaz、Rick McGavin 和 Javid Askerov_ _对本文的审稿。_ diff --git a/chinese/articles/how-to-manage-state-in-a-react-app.md b/chinese/articles/how-to-manage-state-in-a-react-app.md index b41a12a40..3de3e6a50 100644 --- a/chinese/articles/how-to-manage-state-in-a-react-app.md +++ b/chinese/articles/how-to-manage-state-in-a-react-app.md @@ -239,7 +239,7 @@ const [state, dispatch] = useReducer(reducer, { count: 0 }) <button onClick={() => dispatch({type: 'ADD'})}>Add 1</button> ``` -这比使用 useState 多了不少模板,但useReducer毕竟没有那么复杂。 +这比使用 useState 多了不少模板,但 useReducer 毕竟没有那么复杂。 总结一下,我们只需要: @@ -355,7 +355,7 @@ export const RESET = 'RESET' export const reset = () => ({ type: RESET }) ``` -这比我们之前看到的还要更多的模板(这也是Redux被批评的主要原因),所以让我们把它分解成几块: +这比我们之前看到的还要更多的模板(这也是 Redux 被批评的主要原因),所以让我们把它分解成几块: - 正如我提到的,Redux 是一个外部库,所以在进行任何操作之前,我们需要通过运行 `npm i redux react-redux` 来安装它。 `redux` 将带来管理状态所需的核心函数,而`react-redux` 将安装一些很酷的 hook,可以轻松地从我们的组件中读取和修改状态。 @@ -462,7 +462,7 @@ function App() { export default App ``` -Redux 是一个很好的工具,它同时解决了两个问题(prop drilling 和复杂的状态变化)。不过,它确实产生了很多模板,使状态管理成为一个更难理解的话题,特别是在处理不同的文件和实体,如 actions、reducers、store ...... +Redux 是一个很好的工具,它同时解决了两个问题(prop drilling 和复杂的状态变化)。不过,它确实产生了很多模板,使状态管理成为一个更难理解的话题,特别是在处理不同的文件和实体,如 actions、reducers、store …… 这里要提到的重要一点是,这些管理状态的工具或方法并不是相互排斥的,它们可以而且可能应该同时使用,并各自解决它们所擅长的具体问题。 @@ -655,7 +655,7 @@ Redux toolkit 旨在成为处理 Redux 的一种更简单的方法,但在我 ![2CYCmD92D](https://www.freecodecamp.org/news/content/images/2022/03/2CYCmD92D.png) -[Recoil](https://recoiljs.org/) 是一个开源状态管理库,专门用于由 Facebook(或 Meta,等等)构建的 React。根据他们的网站,Recoil是为 "最小化和响应式 "而建立的,在这个意义上,它看起来和感觉都像普通的React代码。 +[Recoil](https://recoiljs.org/) 是一个开源状态管理库,专门用于由 Facebook(或 Meta,等等)构建的 React。根据他们的网站,Recoil 是为 "最小化和响应式 "而建立的,在这个意义上,它看起来和感觉都像普通的 React 代码。 Recoil 基于**原子 (atom)** 的概念。来自他们的文档, diff --git a/chinese/articles/how-to-optimize-nodejs-apis.md b/chinese/articles/how-to-optimize-nodejs-apis.md index 22669d686..624a0d2b5 100644 --- a/chinese/articles/how-to-optimize-nodejs-apis.md +++ b/chinese/articles/how-to-optimize-nodejs-apis.md @@ -5,33 +5,33 @@ ![How to Optimize Your Node.js API](https://www.freecodecamp.org/news/content/images/size/w2000/2022/08/pexels-ann-marie-kennon-1296000.jpg) -在这篇文章中,我将讲解如何优化使用Node.js编写的API。 +在这篇文章中,我将讲解如何优化使用 Node.js 编写的 API。 ### 前提条件 想要充分了解本文内容,你必须了解以下概念: -- Node.js的设置与安装 -- 如何使用Node创建API -- 如何使用Postman -- JavaScript的async/await工作原理 -- Redis的基础操作 +- Node.js 的设置与安装 +- 如何使用 Node 创建 API +- 如何使用 Postman +- JavaScript 的 async/await 工作原理 +- Redis 的基础操作 -## API优化到底指的是什么 +## API 优化到底指的是什么 -优化包含了改善API的响应时间。响应时间越短,API的速度越快。 +优化包含了改善 API 的响应时间。响应时间越短,API 的速度越快。 -我将在本文分享一些技巧,帮助你缩短响应时间、降低延迟、管理错误和吞吐量,并且最大限度地减少CPU和内存的使用。 +我将在本文分享一些技巧,帮助你缩短响应时间、降低延迟、管理错误和吞吐量,并且最大限度地减少 CPU 和内存的使用。 -# 如何优化Node.js的API +# 如何优化 Node.js 的 API ## 1\. 始终使用异步函数 -异步函数就像JavaScript的心脏。因此,优化CPU使用率的最佳方法就是编写异步函数来执行非阻塞I/O操作。 +异步函数就像 JavaScript 的心脏。因此,优化 CPU 使用率的最佳方法就是编写异步函数来执行非阻塞 I/O 操作。 -I/O操作包括对数据的读和写。它可以在数据库、云存储或者任何本地磁盘上进行。 +I/O 操作包括对数据的读和写。它可以在数据库、云存储或者任何本地磁盘上进行。 -在大量使用I/O操作的应用使用异步函数可以提高效率。因为由于没有阻塞I/O,当一个请求在做输入/输出操作的时候,CPU可以同时处理多个请求。 +在大量使用 I/O 操作的应用使用异步函数可以提高效率。因为由于没有阻塞 I/O,当一个请求在做输入/输出操作的时候,CPU 可以同时处理多个请求。 举例如下: @@ -47,29 +47,29 @@ fs.readFile('/etc/passwd', function(err, file) { }); ``` -- 使用Node包**fs**来处理文件 +- 使用 Node 包**fs**来处理文件 - **readFileSync()**是同步函数,会在执行完成前阻塞线程 - **readFile()**是异步函数,会立刻返回并在后台运行 -## 2\. 避免在API中使用session和cookie,仅在API响应中发送数据 +## 2. 避免在 API 中使用 session 和 cookie,仅在 API 响应中发送数据 -当我们使用cookie或者session来存储临时状态的时候,会占用非常多的服务器内存。 +当我们使用 cookie 或者 session 来存储临时状态的时候,会占用非常多的服务器内存。 -现在通用无状态API,并且也有JWT、OAuth等验证机制。验证令牌保存在客户端以便服务器管理状态。 +现在通用无状态 API,并且也有 JWT、OAuth 等验证机制。验证令牌保存在客户端以便服务器管理状态。 -JWT是基于JSON的用于API验证的安全令牌。JWT可以被看到,但一旦发送就无法修改。JWT只是一个序列并没有加密。 OAuth不是API或服务 - 相反,它是授权的开放标准。OAuth是一组用于获取令牌的标准步骤。 +JWT 是基于 JSON 的用于 API 验证的安全令牌。JWT 可以被看到,但一旦发送就无法修改。JWT 只是一个序列并没有加密。 OAuth 不是 API 或服务 - 相反,它是授权的开放标准。OAuth 是一组用于获取令牌的标准步骤。 -同时,也不要把时间浪费在使用Node.js来服务静态文件。这方面NGINX和Apache做得更好。 +同时,也不要把时间浪费在使用 Node.js 来服务静态文件。这方面 NGINX 和 Apache 做得更好。 -使用Node搭建API的时候,不要在响应中发送完整的HTML页面。当仅有数据通过API发送的时候,Node服务得会更好。大部分Node应用都使用JSON数据。 +使用 Node 搭建 API 的时候,不要在响应中发送完整的 HTML 页面。当仅有数据通过 API 发送的时候,Node 服务得会更好。大部分 Node 应用都使用 JSON 数据。 ## 3\. 优化数据库查询 -优化Node API的重要一环是优化查询。特别是对于大型应用来说,我们需要多次查询数据库,所以一个糟糕的查询会降低应用的整体性能。 +优化 Node API 的重要一环是优化查询。特别是对于大型应用来说,我们需要多次查询数据库,所以一个糟糕的查询会降低应用的整体性能。 索引是一种优化数据库性能的方法,通过最小化处理查询时所需的磁盘访问次数来实现。它是一种数据结构技术,用于快速定位和访问数据库中的数据。索引是使用几个数据库列创建的。 -假设我们有一个没有索引的数据库模式,并且数据库包含100万条记录。与带有索引的模式相比,使用没有索引的模式做一个简单的find(查找)查询将扫描更多的记录来找到匹配的记录。 +假设我们有一个没有索引的数据库模式,并且数据库包含 100 万条记录。与带有索引的模式相比,使用没有索引的模式做一个简单的 find(查找)查询将扫描更多的记录来找到匹配的记录。 - 没有索引的查询 @@ -96,49 +96,49 @@ JWT是基于JSON的用于API验证的安全令牌。JWT可以被看到,但一 | 没有索引 | 有索引 | | 1039 | 1 | -## **4\. 使用PM2集群模式优化API** +## **4. 使用 PM2 集群模式优化 API** -PM2是为Node.js应用程序设计的生产流程管理器。它内置了负载平衡器,允许应用程序在不修改代码的情况下,作为多个进程运行。 +PM2 是为 Node.js 应用程序设计的生产流程管理器。它内置了负载平衡器,允许应用程序在不修改代码的情况下,作为多个进程运行。 -使用PM2时的应用停机时间几乎为零。总体来说,PM2确实可以提升API性能和并发性。 +使用 PM2 时的应用停机时间几乎为零。总体来说,PM2 确实可以提升 API 性能和并发性。 -在生产环境中部署代码并运行以下命令以查看PM2集群如何在所有可用CPU上进行扩展: +在生产环境中部署代码并运行以下命令以查看 PM2 集群如何在所有可用 CPU 上进行扩展: ```js pm2 start app.js -i 0 ``` -## **5\. 减少TTFB(第一字节时间)** +## **5. 减少 TTFB(第一字节时间)** -第一字节时间是一种测量方式,用作表示web服务器或者其他网络资源的响应时间。TTFB测量从用户或客户发出HTTP请求到客户的浏览器收到页面的第一个字节的时间。 +第一字节时间是一种测量方式,用作表示 web 服务器或者其他网络资源的响应时间。TTFB 测量从用户或客户发出 HTTP 请求到客户的浏览器收到页面的第一个字节的时间。 -所有用户访问浏览器的同一页面加载速度不可能在100毫秒之内,这仅仅是因为服务器和用户之间的物理距离。 +所有用户访问浏览器的同一页面加载速度不可能在 100 毫秒之内,这仅仅是因为服务器和用户之间的物理距离。 -我们可以通过使用CDN和全球本地数据中心缓存内容来减少第一个字节的时间。这有助于用户以最小的延迟访问内容。你可以从Cloudflare提供的CDN解决方案开始着手。 +我们可以通过使用 CDN 和全球本地数据中心缓存内容来减少第一个字节的时间。这有助于用户以最小的延迟访问内容。你可以从 Cloudflare 提供的 CDN 解决方案开始着手。 ## **6\. 使用带日志的错误脚本** -监视API是否正常工作最好的办法是记录行为,于是记录日志就派上用场。 +监视 API 是否正常工作最好的办法是记录行为,于是记录日志就派上用场。 一个常见的办法是将记录打印在控制台上 (使用`console.log()`)。 -比`console.log()`更高效的方法是使用Morgan、Buyan和Winston。我将在这里以Winston为例。 +比`console.log()`更高效的方法是使用 Morgan、Buyan 和 Winston。我将在这里以 Winston 为例。 -### 如何使用Winston记录 – 功能 +### 如何使用 Winston 记录 – 功能 -- 支持4个可以自由选择的日志等级,如:info、error、verbose、debug、silly 和 warn。 +- 支持 4 个可以自由选择的日志等级,如:info、error、verbose、debug、silly 和 warn。 - 支持查询日志 - 简单的分析 -- 可以使用相同的类型进行多个transports输出 -- 捕获并记录uncaughtException +- 可以使用相同的类型进行多个 transports 输出 +- 捕获并记录 uncaughtException -可以使用以下命令行设置Winston: +可以使用以下命令行设置 Winston: ```js npm install winston --save ``` -这里是使用Winston记录的基本配置: +这里是使用 Winston 记录的基本配置: ```js const winston = require('winston'); @@ -167,20 +167,20 @@ logger.stream = { }; ``` -## **7\. 使用HTTP/2而不是HTTP** +## **7. 使用 HTTP/2 而不是 HTTP** -除了上述使用的这些技巧,我们还可以使用HTTP/2而不是HTTP,因为它具备以下优势: +除了上述使用的这些技巧,我们还可以使用 HTTP/2 而不是 HTTP,因为它具备以下优势: - 多路复用 - 头部压缩 - 服务器推送 - 二进制格式 -它专注提高性能,并解决HTTP的问题。它使网页浏览更快、更容易,并且消耗更少的带宽。 +它专注提高性能,并解决 HTTP 的问题。它使网页浏览更快、更容易,并且消耗更少的带宽。 ## **8\. 并行任务** -使用[async.js](https://caolan.github.io/async/v3/)来运行任务。并行任务对API的性能有很大改善,它减少了延迟并最大限度地减少了阻塞操作。 +使用[async.js](https://caolan.github.io/async/v3/)来运行任务。并行任务对 API 的性能有很大改善,它减少了延迟并最大限度地减少了阻塞操作。 并行意味着同时运行多个任务。当你并行任务的时候,不需要控制程序的执行顺序。 @@ -208,24 +208,24 @@ async.parallel({ }); ``` -在以上例子中,我们使用了[async.js](https://caolan.github.io/async/v3/)以异步的形式执行了两个任务。task 1需要200毫秒完成,但是 task 2不需要等待task 1完成后再执行 – 它在设定的100毫米后执行。 +在以上例子中,我们使用了[async.js](https://caolan.github.io/async/v3/)以异步的形式执行了两个任务。task 1 需要 200 毫秒完成,但是 task 2 不需要等待 task 1 完成后再执行 – 它在设定的 100 毫米后执行。 -并行任务对API的性能有很大的影响。它减少了延迟并最大限度地减少了阻塞操作。 +并行任务对 API 的性能有很大的影响。它减少了延迟并最大限度地减少了阻塞操作。 -## **9\. 使用Redis缓存应用** +## **9. 使用 Redis 缓存应用** -Redis是Memcached的高级版本。它通过在服务器的主内存中存储和检索数据来优化API响应时间。它提高了数据库查询的性能,也减少了访问延迟。 +Redis 是 Memcached 的高级版本。它通过在服务器的主内存中存储和检索数据来优化 API 响应时间。它提高了数据库查询的性能,也减少了访问延迟。 -在下面的代码片段中,我们分别调用了不使用Redis和使用Redis的API,并比较了响应时间。 +在下面的代码片段中,我们分别调用了不使用 Redis 和使用 Redis 的 API,并比较了响应时间。 -响应时间差异巨大~ 899.37毫秒: +响应时间差异巨大~ 899.37 毫秒: | 方法 | 响应时间 | | --- | --- | -| 不使用Redis | 使用 Redis | +| 不使用 Redis | 使用 Redis | | 900ms | 0.621 | -以下是不使用Redis的Node: +以下是不使用 Redis 的 Node: ```js 'use strict'; @@ -261,7 +261,7 @@ app.listen(3000, function() { }); ``` -以下是使用Redis的Node: +以下是使用 Redis 的 Node: ```js 'use strict'; @@ -316,13 +316,13 @@ app.listen(3000, function() { ## 总结 -在本指南中,我们了解了如何优化Node.js API的响应时间。 +在本指南中,我们了解了如何优化 Node.js API 的响应时间。 -JavaScript重度依赖函数,因此,使用异步函数可以使脚本运行得更快并且不阻塞。 +JavaScript 重度依赖函数,因此,使用异步函数可以使脚本运行得更快并且不阻塞。 -除此之外,我们还可以使用缓存记忆(Redis)、数据库索引、TTFB和PM2集群来提高响应速度。 +除此之外,我们还可以使用缓存记忆(Redis)、数据库索引、TTFB 和 PM2 集群来提高响应速度。 -最后请记住,注意路由的安全性并尽可能优化路由也很重要。我们不能为了提高API响应速度而妥协掉安全性。因此,在Node.js中构建优化的API时,应该保留所有标准安全检查。 +最后请记住,注意路由的安全性并尽可能优化路由也很重要。我们不能为了提高 API 响应速度而妥协掉安全性。因此,在 Node.js 中构建优化的 API 时,应该保留所有标准安全检查。 你可以在[LinkedIn](https://www.linkedin.com/in/kadeniyi/)上联系我。 diff --git a/chinese/articles/how-to-pass-the-certified-kubernetes-security-specialist-exam.md b/chinese/articles/how-to-pass-the-certified-kubernetes-security-specialist-exam.md index 9f56c29b4..76124eaa3 100644 --- a/chinese/articles/how-to-pass-the-certified-kubernetes-security-specialist-exam.md +++ b/chinese/articles/how-to-pass-the-certified-kubernetes-security-specialist-exam.md @@ -27,7 +27,7 @@ It has an enormous community to support, and it's always building new features a * * * -## Table of Contents: +## Table of Contents - [Resources for the CKS Exam](#resourcesfortheexam) - [Aliases](#aliases) @@ -103,7 +103,7 @@ Since this exam requires you to write a lot of commands, I figured early on that I used the **vi** editor during the exam, so here I will share some useful tips for this editor. -### vi defaults for ~/.vimrc: +### vi defaults for ~/.vimrc ``` vi ~/.vimrc @@ -119,7 +119,7 @@ w: End of word GG: End of file ``` -### kubectl defaults for ~/.bashrc: +### kubectl defaults for ~/.bashrc ``` vi ~/.bashrc @@ -836,7 +836,7 @@ Here's the reference doc for a [list of open ports](https://kubernetes.io/docs/s ### How to restrict network access -#### How to identity a service running on port: +#### How to identity a service running on port ``` systemctl status ssh diff --git a/chinese/articles/how-to-perform-crud-operations-using-react.md b/chinese/articles/how-to-perform-crud-operations-using-react.md index 7a1838e84..9ac17ea2c 100644 --- a/chinese/articles/how-to-perform-crud-operations-using-react.md +++ b/chinese/articles/how-to-perform-crud-operations-using-react.md @@ -5,31 +5,31 @@ ![如何使用 React、React Hooks 和 Axios 执行 CRUD 操作](https://www.freecodecamp.org/news/content/images/size/w2000/2021/07/React-CRUD-Operations-using-React-and-React-Hooks.png) -如果你正在使用React,理解和实现API请求可能是相当困难的 +如果你正在使用 React,理解和实现 API 请求可能是相当困难的 -所以在这篇文章中,我们将通过使用React、React Hooks、React Router和Axios实现CRUD操作来学习这一切。 +所以在这篇文章中,我们将通过使用 React、React Hooks、React Router 和 Axios 实现 CRUD 操作来学习这一切。 让我们深入了解一下。 -## **如何安装Node和npm** +## **如何安装 Node 和 npm** -首先,让我们在系统中安装Node。我们将主要用它来执行我们的JavaScript代码。 +首先,让我们在系统中安装 Node。我们将主要用它来执行我们的 JavaScript 代码。 -去官方网站下载Node, [https://nodejs.org/en/](https://nodejs.org/en/). +去官方网站下载 Node, [https://nodejs.org/en/](https://nodejs.org/en/). -你还需要**node包管理器**,如npm,它是内建在Node上的。你可以用它来为你的JavaScript应用程序安装包。幸运的是,Node自带npm,所以你不需要单独下载它。 +你还需要**node 包管理器**,如 npm,它是内建在 Node 上的。你可以用它来为你的 JavaScript 应用程序安装包。幸运的是,Node 自带 npm,所以你不需要单独下载它。 -一旦它们都完成了,打开你的终端或命令提示符,输入`node -v`。这将检查你有哪个版本的Node。 +一旦它们都完成了,打开你的终端或命令提示符,输入`node -v`。这将检查你有哪个版本的 Node。 -## **如何创建你的React应用** +## **如何创建你的 React 应用** -为了创建你的React应用,在终端输入 ****`npx create-react-app <你的应用程序名称>`****, 或者**`npx create-react-app**react-crud`**在本例中。 +为了创建你的 React 应用,在终端输入 ****`npx create-react-app <你的应用程序名称>`****, 或者**`npx create-react-app**react-crud`**在本例中。 你会看到软件包正在被安装。 一旦软件包安装完毕,进入项目文件夹,输入`npm start`。 -你会看到默认的React模板,像这样。 +你会看到默认的 React 模板,像这样。 ![](https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-124754.png) @@ -37,11 +37,11 @@ ![](https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-124858.png) -我们的 App.js文件 +我们的 App.js 文件 -## **如何为React 安装 Semantic UI 包(库)** +## **如何为 React 安装 Semantic UI 包(库)** -让我们在我们的项目中安装Semantic UI React软件包。Semantic UI是一个为React制作的UI库,它有预建的UI组件,比如表格、按钮和许多功能。 +让我们在我们的项目中安装 Semantic UI React 软件包。Semantic UI 是一个为 React 制作的 UI 库,它有预建的 UI 组件,比如表格、按钮和许多功能。 你可以使用下面的一个命令来安装它,这取决于你的包管理器。 @@ -58,20 +58,20 @@ npm install semantic-ui-react semantic-ui-css 对于使用 NPM 包管理器 -同时,在你的应用程序的主入口文件中导入该库,也就是index.js。 +同时,在你的应用程序的主入口文件中导入该库,也就是 index.js。 ```js import 'semantic-ui-css/semantic.min.css' ``` -在你的index.js文件中粘贴上面一行内容。 +在你的 index.js 文件中粘贴上面一行内容。 -## **如何构建你的CRUD应用** +## **如何构建你的 CRUD 应用** -现在,让我们开始使用React构建我们的CRUD应用。 +现在,让我们开始使用 React 构建我们的 CRUD 应用。 首先,我们要给我们的应用程序添加一个标题。 -在我们的app.js文件中,添加一个标题,像这样。 +在我们的 app.js 文件中,添加一个标题,像这样。 ``` import './App.css'; @@ -91,7 +91,7 @@ export default App; 现在,让我们确保它居中。 -给父级div一个classname,即main。在App.css文件中,我们将使用Flexbox来使标题居中。 +给父级 div 一个 classname,即 main。在 App.css 文件中,我们将使用 Flexbox 来使标题居中。 ``` import './App.css'; @@ -107,7 +107,7 @@ function App() { export default App; ``` -app.js文件,在父 div 中的 className 为main的css定义。 +app.js 文件,在父 div 中的 className 为 main 的 css 定义。 ``` .main{ @@ -140,11 +140,11 @@ function App() { export default App; ``` -让我们从 Google Font导入一种字体. 从 [https://fonts.google.com/](https://fonts.google.com/)选择一种。 +让我们从 Google Font 导入一种字体. 从 [https://fonts.google.com/](https://fonts.google.com/)选择一种。 -选择任何你喜欢的字体,但我将使用Montserrat字体家族。 +选择任何你喜欢的字体,但我将使用 Montserrat 字体家族。 -在App.css文件中导入你选择的字体,像这样。 +在 App.css 文件中导入你选择的字体,像这样。 ``` @import url('https://fonts.googleapis.com/css2?family=Montserrat&display=swap'); @@ -159,7 +159,7 @@ export default App; ``` 给 `h2`一个 `lassName` 为 `main-header`,就像上面。 -然后, 在你的 App.css文件, 添加font family: +然后, 在你的 App.css 文件, 添加 font family: ``` .main-header{ @@ -171,17 +171,17 @@ export default App; 现在你会看到改变后的标题。 -## 如何创建你的CRUD组件 +## 如何创建你的 CRUD 组件 -让我们创建四个CRUD组件,它们将是创建、读取、更新和删除。 +让我们创建四个 CRUD 组件,它们将是创建、读取、更新和删除。 -在我们的src文件夹中,创建一个名为组件的文件夹。在这个文件夹中,创建三个文件--创建、读取和更新。对于删除,我们不需要任何额外的组件。 +在我们的 src 文件夹中,创建一个名为组件的文件夹。在这个文件夹中,创建三个文件--创建、读取和更新。对于删除,我们不需要任何额外的组件。 ![](https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-133242.png) 现在,让我们来实现这些。 -但为此,我们需要使用Mock API。这些API将向我们将要创建的假服务器发送数据,这只是为了学习的目的。 +但为此,我们需要使用 Mock API。这些 API 将向我们将要创建的假服务器发送数据,这只是为了学习的目的。 所以, 请前往 [https://mockapi.io/](https://mockapi.io/),创建账号。 @@ -213,15 +213,15 @@ MockAPI ![](https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-134140.png) -现在,我们已经创建了我们的 `fake API(假API)`,我把它命名为fakeData。 +现在,我们已经创建了我们的 `fake API(假API)`,我把它命名为 fakeData。 -点击fakeData,你会看到API在一个新的标签中打开。现在的数据库是空的。 +点击 fakeData,你会看到 API 在一个新的标签中打开。现在的数据库是空的。 ## 如何为(create Component)创建组件创建一个表格 -让我们使用Semantic UI库中的一个表单。 +让我们使用 Semantic UI 库中的一个表单。 -前往Semantic React,在左边的搜索栏中搜索Form。 +前往 Semantic React,在左边的搜索栏中搜索 Form。 ![](https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-134532.png) @@ -229,7 +229,7 @@ MockAPI ![](https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-134654.png) -复制这段代码并将其粘贴到你的Create.js文件中,像这样。 +复制这段代码并将其粘贴到你的 Create.js 文件中,像这样。 ``` import React from 'react' @@ -255,7 +255,7 @@ const Create = () => ( export default Create; ``` -在你的app.js文件中导入创建组件(Create Component)。 +在你的 app.js 文件中导入创建组件(Create Component)。 ``` import './App.css'; @@ -281,7 +281,7 @@ export default App; 但这里有一个问题--项目没有正确对齐,文本输入标签颜色是黑色的。所以,让我们来改变它。 -在create.js文件中,给**Form**一个`create-form`的className。 +在 create.js 文件中,给**Form**一个`create-form`的 className。 ``` import React from 'react' @@ -309,7 +309,7 @@ export default Create; app.js -并在你的App.css文件中添加以下类。 +并在你的 App.css 文件中添加以下类。 ``` .create-form label{ @@ -323,7 +323,7 @@ App.css 这个类将针对所有的表格字段标签并应用白烟的颜色。它还将改变字体并增加字体大小。 -现在,在我们的主`className`中,添加一个flex-direction属性。这个属性将设置方向为列,所以主`className`中的每个元素都将水平对齐。 +现在,在我们的主`className`中,添加一个 flex-direction 属性。这个属性将设置方向为列,所以主`className`中的每个元素都将水平对齐。 ``` .main{ @@ -343,9 +343,9 @@ App.css 你可以看到,我们的表单现在看起来好多了。 -接下来,让我们从表单字段中获取数据到我们的控制台(console)。为此,我们将使用React的`useState`钩子。 +接下来,让我们从表单字段中获取数据到我们的控制台(console)。为此,我们将使用 React 的`useState`钩子。 -在我们的create.js文件中,从React中导入`useState`。 +在我们的 create.js 文件中,从 React 中导入`useState`。 ``` import React, { useState } from 'react'; @@ -396,7 +396,7 @@ export default function Create() { 我们正在获得名字(first name)、姓氏(last name)和复选框(checkout)的状态。 -创建一个名为`postData`的函数,我们将用它来向API发送数据。在该函数中,写下这段代码。 +创建一个名为`postData`的函数,我们将用它来向 API 发送数据。在该函数中,写下这段代码。 ``` const postData = () => { @@ -408,7 +408,7 @@ const postData = () => { 我们在控制台中打印出名字(firstName)、姓氏(lastName)和复选框(checkbox)的值。 -在(Submit button)提交按钮上,使用onClick事件调用这个函数,这样,每当我们按下提交按钮,这个函数就会被调用。 +在(Submit button)提交按钮上,使用 onClick 事件调用这个函数,这样,每当我们按下提交按钮,这个函数就会被调用。 ``` <Button onClick={postData} type='submit'>Submit</Button> @@ -454,9 +454,9 @@ export default function Create() { ![](https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-142717.png) -## 如何使用Axios向Mock APIs发送请求 +## 如何使用 Axios 向 Mock APIs 发送请求 -让我们使用Axios来发送我们的表单数据到模拟服务器。 +让我们使用 Axios 来发送我们的表单数据到模拟服务器。 但首先,我们需要安装它。 @@ -466,15 +466,15 @@ export default function Create() { 软件包安装完毕后,让我们开始(create)创建操作。 -在文件的顶部导入Axios。 +在文件的顶部导入 Axios。 ``` import axios from 'axios'; ``` -导入Axios +导入 Axios -在`postData`函数中,我们将使用Axios来发送POST请求。 +在`postData`函数中,我们将使用 Axios 来发送 POST 请求。 ``` const postData = () => { @@ -488,9 +488,9 @@ const postData = () => { 发送 Post 请求 -如你所见,我们正在使用axios.post。在axios.post中, 我们有 API endpoint(接入点 请求地址), 这是我们之前创建的。然后,我们有被大括号包裹的表单字段。 +如你所见,我们正在使用 axios.post。在 axios.post 中, 我们有 API endpoint(接入点 请求地址), 这是我们之前创建的。然后,我们有被大括号包裹的表单字段。 -当我们点击提交时,这个函数将被调用,它将向API服务器发布数据。 +当我们点击提交时,这个函数将被调用,它将向 API 服务器发布数据。 ![](https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-174834.png) @@ -498,15 +498,15 @@ const postData = () => { ![](https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-174930.png) -然后,你检查这个API的返回值,你会得到你的名字、姓氏,复选框为真的值,被包裹在一个对象中。 +然后,你检查这个 API 的返回值,你会得到你的名字、姓氏,复选框为真的值,被包裹在一个对象中。 ## 如何实现读取和更新操作 -为了开始(read)读取操作,我们需要创建一个读取页面。我们还需要React Router包来导航到不同的页面。 +为了开始(read)读取操作,我们需要创建一个读取页面。我们还需要 React Router 包来导航到不同的页面。 前往[https://reactrouter.com/web/guides/quick-start](https://reactrouter.com/web/guides/quick-start)查看文档,同时运行 `npm i react-router-dom`进行安装。 -安装完毕后,从React Router导入一些东西: +安装完毕后,从 React Router 导入一些东西: ``` import { BrowserRouter as Router, Route } from 'react-router-dom' @@ -514,7 +514,7 @@ import { BrowserRouter as Router, Route } from 'react-router-dom' 从`React Router`中导入`Router`和`Route`。 -在我们的App.js中,把整个返回包成一个Router。这基本上意味着,无论这个Router里面有什么,都能在React中使用。 +在我们的 App.js 中,把整个返回包成一个 Router。这基本上意味着,无论这个 Router 里面有什么,都能在 React 中使用。 ``` import './App.css'; @@ -537,9 +537,9 @@ function App() { export default App; ``` -我们的App.js现在看起来会像上面的样子。 +我们的 App.js 现在看起来会像上面的样子。 -替换掉返回里面的Create,并添加以下代码。 +替换掉返回里面的 Create,并添加以下代码。 ``` import './App.css'; @@ -562,7 +562,7 @@ function App() { export default App; ``` -在这里,我们使用Route组件作为Create。我们已经将Create的路径设置为'/create'。因此,如果我们进入[http://localhost:3000/create](http://localhost:3000/create),我们将看到创建页面。 +在这里,我们使用 Route 组件作为 Create。我们已经将 Create 的路径设置为'/create'。因此,如果我们进入[http://localhost:3000/create](http://localhost:3000/create),我们将看到创建页面。 同样地,我们需要(read)读取和(update)更新的路由。 @@ -608,7 +608,7 @@ Read Route ### 读取操作 -对于读取操作,我们将需要一个表组件。因此,前往React Semantic UI,并使用库中的一个表。 +对于读取操作,我们将需要一个表组件。因此,前往 React Semantic UI,并使用库中的一个表。 ``` import React from 'react'; @@ -728,7 +728,7 @@ export default function Read() { Read.js -现在,让我们发送GET请求,从API获得数据。 +现在,让我们发送 GET 请求,从 API 获得数据。 当我们的应用程序加载时,我们需要这些数据。所以,我们要使用`useEffect`钩子(hook)。 @@ -740,7 +740,7 @@ import React, { useEffect } from 'react'; }, []) ``` -useEffect钩子(hook) +useEffect 钩子(hook) 创建一个包含传入数据的状态。这将是一个数组。 @@ -753,9 +753,9 @@ useEffect(() => { }, []) ``` -APIData state 来存储API传入的数据 +APIData state 来存储 API 传入的数据 -在`useEffect`钩子(hook)中,让我们发送GET请求。 +在`useEffect`钩子(hook)中,让我们发送 GET 请求。 ``` useEffect(() => { @@ -766,11 +766,11 @@ APIData state 来存储API传入的数据 }, []) ``` -因此,我们使用axios.get来向API发送GET请求。然后,如果请求被满足,我们就在我们的_APIData_状态中设置响应数据。 +因此,我们使用 axios.get 来向 API 发送 GET 请求。然后,如果请求被满足,我们就在我们的_APIData_状态中设置响应数据。 -现在,让我们根据API数据来映射我们的表行。 +现在,让我们根据 API 数据来映射我们的表行。 -我们将使用Map函数来做这件事。它将对数组进行迭代,并在输出中显示数据。 +我们将使用 Map 函数来做这件事。它将对数组进行迭代,并在输出中显示数据。 ``` <Table.Body> @@ -785,7 +785,7 @@ APIData state 来存储API传入的数据 </Table.Body> ``` -我们根据API中的数据来映射firstName、lastName和checkbox。但我们的复选框有一点不同。我在这里使用了一个三元操作符('?')。如果data.checkbox为真,输出将是Checked,否则将是Unchecked。 +我们根据 API 中的数据来映射 firstName、lastName 和 checkbox。但我们的复选框有一点不同。我在这里使用了一个三元操作符('?')。如果 data.checkbox 为真,输出将是 Checked,否则将是 Unchecked。 ![](https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-184955.png) @@ -793,7 +793,7 @@ Read.js Output ### 更新(Update)操作 -再为更新创建一个标题,并在表行中为更新按钮创建一列。使用Semantic UI React的按钮。 +再为更新创建一个标题,并在表行中为更新按钮创建一列。使用 Semantic UI React 的按钮。 ``` <Table.HeaderCell>Update</Table.HeaderCell> @@ -805,9 +805,9 @@ Read.js Output 创建(Update)更新按钮 -现在,当我们点击这个按钮,我们应该被重定向到更新页面。为此,我们需要React Router的链接。 +现在,当我们点击这个按钮,我们应该被重定向到更新页面。为此,我们需要 React Router 的链接。 -从React Router导入Link。并将更新按钮的表格单元格包装成Link标签。 +从 React Router 导入 Link。并将更新按钮的表格单元格包装成 Link 标签。 ``` import { Link } from 'react-router-dom'; @@ -823,7 +823,7 @@ import { Link } from 'react-router-dom'; 因此,如果我们点击更新按钮,我们将被重定向到更新页面。 -为了更新列的数据,我们需要它们各自的ID,这从APIs获得。 +为了更新列的数据,我们需要它们各自的 ID,这从 APIs 获得。 创建一个名为 "setData "的函数。将其绑定到更新按钮上。 @@ -851,7 +851,7 @@ Data in the console 点击表中的更新按钮,并查看控制台。你会得到相应表字段的数据。 -让我们把这些数据设置到localStorage中。 +让我们把这些数据设置到 localStorage 中。 ``` const setData = (data) => { @@ -865,9 +865,9 @@ const setData = (data) => { 在本地存储中(Local Storage)设置数据 -我们正在将我们的数据解构为id、firstName、lastName和checkbox,然后我们将这些数据设置到本地存储(Local Storage)。你可以使用本地存储(Local Storage)来在浏览器中的存储数据。 +我们正在将我们的数据解构为 id、firstName、lastName 和 checkbox,然后我们将这些数据设置到本地存储(Local Storage)。你可以使用本地存储(Local Storage)来在浏览器中的存储数据。 -现在,在更新组件中,我们需要一个表单来进行更新操作。让我们复制(reate component)创建组件中的表单。只要把函数的名称从Create改为Update。 +现在,在更新组件中,我们需要一个表单来进行更新操作。让我们复制(reate component)创建组件中的表单。只要把函数的名称从 Create 改为 Update。 ``` import React, { useState } from 'react'; @@ -902,7 +902,7 @@ export default function Update() { Our update Page -在Update组件中创建一个`useEffect'钩子(hook)。我们将用它来获取我们之前存储在本地存储的数据。同时,为ID字段再创建一个状态(state)。 +在 Update 组件中创建一个`useEffect'钩子(hook)。我们将用它来获取我们之前存储在本地存储的数据。同时,为 ID 字段再创建一个状态(state)。 ``` const [id, setID] = useState(null); @@ -915,7 +915,7 @@ useEffect(() => { }, []); ``` -根据你的keys(字典,map 数据结构)从本地存储设置相应的数据。我们需要在表格字段中设置这些值。当更新页面加载时,它将自动填入这些字段。 +根据你的 keys(字典,map 数据结构)从本地存储设置相应的数据。我们需要在表格字段中设置这些值。当更新页面加载时,它将自动填入这些字段。 ``` <Form className="create-form"> @@ -944,7 +944,7 @@ Update Page 现在,让我们创建更新请求来更新数据。 -创建一个名为`updateAPIData`的函数。在这个函数中,我们将使用axios.put来发送一个PUT请求,以更新我们的数据。 +创建一个名为`updateAPIData`的函数。在这个函数中,我们将使用 axios.put 来发送一个 PUT 请求,以更新我们的数据。 ``` const updateAPIData = () => { @@ -956,11 +956,11 @@ const updateAPIData = () => { } ``` -在这里,你可以看到我们在API端点上附加了一个id字段。 +在这里,你可以看到我们在 API 端点上附加了一个 id 字段。 -当我们点击表中的字段时,它的ID会被存储到本地存储器(Local Storage)中。而在更新页面,我们正在检索它。然后,我们将该ID存储在_`id`_状态中。 +当我们点击表中的字段时,它的 ID 会被存储到本地存储器(Local Storage)中。而在更新页面,我们正在检索它。然后,我们将该 ID 存储在_`id`_状态中。 -之后,我们将ID传递给端点。这使我们能够更新我们传递ID的字段。 +之后,我们将 ID 传递给端点。这使我们能够更新我们传递 ID 的字段。 将`updateAPIData`函数绑定到更新按钮上。 @@ -968,7 +968,7 @@ const updateAPIData = () => { <Button type='submit' onClick={updateAPIData}>Update</Button> ``` -将updateAPIData绑定到更新按钮上 +将 updateAPIData 绑定到更新按钮上 点击读取页面中表格的更新按钮,改变你的姓氏(last name),然后点击更新页面中的更新按钮。 @@ -976,7 +976,7 @@ const updateAPIData = () => { Updating the fields -回到 "阅读 "页面,或查看API。你会看到你的姓氏(last name)已被改变。 +回到 "阅读 "页面,或查看 API。你会看到你的姓氏(last name)已被改变。 ![](https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-194756.png) @@ -988,7 +988,7 @@ Our Read Table ### 删除操作 -在读取表(read table)中再添加一个Button,我们将用它来进行删除操作。 +在读取表(read table)中再添加一个 Button,我们将用它来进行删除操作。 ``` <Table.Cell> @@ -998,7 +998,7 @@ Our Read Table 读取表中的删除按钮 -创建一个名为 "onDelete "的函数,并将此函数绑定到删除按钮上。这个函数将在点击删除按钮时接收一个ID参数。 +创建一个名为 "onDelete "的函数,并将此函数绑定到删除按钮上。这个函数将在点击删除按钮时接收一个 ID 参数。 ``` const onDelete = (id) => { @@ -1016,13 +1016,13 @@ const onDelete = (id) => { } ``` -从API中删除字段 +从 API 中删除字段 -点击删除按钮并检查API。你会看到数据已经被删除。 +点击删除按钮并检查 API。你会看到数据已经被删除。 我们需要在表被删除后获得该表的数据。 -因此,创建一个函数来获得API数据。 +因此,创建一个函数来获得 API 数据。 ``` const getData = () => { @@ -1033,7 +1033,7 @@ const getData = () => { } ``` -获取API数据 +获取 API 数据 现在,在`onDelete`函数中,我们需要在删除一个字段后获得更新的数据。 @@ -1052,23 +1052,23 @@ const onDelete = (id) => { 读取表格 -因此,现在如果我们在任何字段上点击Delete,它将删除该字段并自动刷新表格。 +因此,现在如果我们在任何字段上点击 Delete,它将删除该字段并自动刷新表格。 ![](https://www.freecodecamp.org/news/content/images/2021/07/Screenshot-2021-07-24-201423.png) 删除一个字段后读表 -## 让我们对我们的CRUD应用程序做一些改进吧 +## 让我们对我们的 CRUD 应用程序做一些改进吧 -因此,当我们在Create页面发布我们的数据时,我们只是在模拟(mock)数据库中获得数据。当我们的数据在创建页面中被创建时,我们需要重定向到读取页面。 +因此,当我们在 Create 页面发布我们的数据时,我们只是在模拟(mock)数据库中获得数据。当我们的数据在创建页面中被创建时,我们需要重定向到读取页面。 -从React Router导入`useHistory'。 +从 React Router 导入`useHistory'。 ``` import { useHistory } from 'react-router'; ``` -从React Router导入useHistory +从 React Router 导入 useHistory 创建变量`history`使用 `let`。 @@ -1076,7 +1076,7 @@ import { useHistory } from 'react-router'; let history = useHistory(); ``` -然后,使用history.push函数,在post API被调用后推送到阅读页面。 +然后,使用 history.push 函数,在 post API 被调用后推送到阅读页面。 ``` const postData = () => { @@ -1090,7 +1090,7 @@ const postData = () => { } ``` -在发布API成功后推送到阅读页面 +在发布 API 成功后推送到阅读页面 它将使用`useHistory`钩子(hook)推送到阅读页面。 @@ -1148,8 +1148,8 @@ export default function Update() { Update.js -现在你知道如何使用React和React Hooks进行CRUD操作了吧! +现在你知道如何使用 React 和 React Hooks 进行 CRUD 操作了吧! -另外,如果你想补充学习,你可以观看我在Youtube上的[React CRUD操作]视频(https://youtu.be/-ZMP8ZladIQ)。 +另外,如果你想补充学习,你可以观看我在 Youtube 上的[React CRUD 操作]视频(https://youtu.be/-ZMP8ZladIQ)。 > __学习愉快__ \ No newline at end of file diff --git a/chinese/articles/how-to-pretty-print-json-in-python.md b/chinese/articles/how-to-pretty-print-json-in-python.md index 1ec6f1514..e5bcd8241 100644 --- a/chinese/articles/how-to-pretty-print-json-in-python.md +++ b/chinese/articles/how-to-pretty-print-json-in-python.md @@ -5,21 +5,21 @@ ![如何在Python中代码美化JSON](https://www.freecodecamp.org/news/content/images/size/w2000/2023/04/Shittu-Olumide-How-to-Pretty-Print-JSON-in-Python.png) -JSON(JavaScript Object Notation)是一种流行的数据交换格式。它在互联网应用、API、和数据库中被广泛应用。JSON是一个轻量的可读的格式,这种格式容易被解析和生成。 +JSON(JavaScript Object Notation)是一种流行的数据交换格式。它在互联网应用、API、和数据库中被广泛应用。JSON 是一个轻量的可读的格式,这种格式容易被解析和生成。 -但是当处理巨大和复杂的JSON数据时,读取和理解数据的结构变得复杂,因此我们需要代码美化。代码美化是通过格式修改,将JSON数据变得容易阅读容易理解的过程。 +但是当处理巨大和复杂的 JSON 数据时,读取和理解数据的结构变得复杂,因此我们需要代码美化。代码美化是通过格式修改,将 JSON 数据变得容易阅读容易理解的过程。 -在这篇文章里,我们会探索如何通过自带和第三方代码库,在Python中代码美化JSON。我们也会讨论代码美化JSON的最佳方法,以及它的用例。 +在这篇文章里,我们会探索如何通过自带和第三方代码库,在 Python 中代码美化 JSON。我们也会讨论代码美化 JSON 的最佳方法,以及它的用例。 ## 代码美化是什么意思? -在Python中,“代码美化”是指格式化和展示数据结构,来使得它们更加可读更加有条理。这些数据结构例如链表、字典和元组。 +在 Python 中,“代码美化”是指格式化和展示数据结构,来使得它们更加可读更加有条理。这些数据结构例如链表、字典和元组。 -为了在Python中代码美化JSON,我们可以使用内置的`json`模块。这个模块提供了`dump()`方程。它可以通过数列化使Python对象成为一个JSON格式的字符串。 +为了在 Python 中代码美化 JSON,我们可以使用内置的`json`模块。这个模块提供了`dump()`方程。它可以通过数列化使 Python 对象成为一个 JSON 格式的字符串。 -默认情况下,此函数生成一个没有任何格式的JSON字符串,但我们可以使用`indent`参数来指定用于缩进的空格数。 +默认情况下,此函数生成一个没有任何格式的 JSON 字符串,但我们可以使用`indent`参数来指定用于缩进的空格数。 -以下是一个如何在Python中代码美化JSON的例子: +以下是一个如何在 Python 中代码美化 JSON 的例子: ```python import json @@ -48,15 +48,15 @@ print(json_str) } ``` -如你所见,“indent”参数设置为“4”,它就会生成一个JSON字符串,其中每个级别的嵌套都缩进了四个空格。我们可以调整这个参数来控制输出中的缩进量。 +如你所见,“indent”参数设置为“4”,它就会生成一个 JSON 字符串,其中每个级别的嵌套都缩进了四个空格。我们可以调整这个参数来控制输出中的缩进量。 -> 注意 `json.dumps()` 函数也可以接受其它可选的参数, 比如说`sort_keys`,它可以被用来排列JSON输出中的键。你可以阅读json模块的文献来获取该函数的更多信息。 +> 注意 `json.dumps()` 函数也可以接受其它可选的参数, 比如说`sort_keys`,它可以被用来排列 JSON 输出中的键。你可以阅读 json 模块的文献来获取该函数的更多信息。 -## 代码美化JSON中的最佳习惯 +## 代码美化 JSON 中的最佳习惯 ### 使用`json`模块 -`json`模块是一个Python内置的模块, 它提供了处理JSON数据的很多方法。`json.dumps()`方法被用于将Python对象数列化成一个JSON格式的字符串。`json.dumps()`方法也可以接受`indent`参数。这个参数可以被用于指明缩进的空格数。 +`json`模块是一个 Python 内置的模块, 它提供了处理 JSON 数据的很多方法。`json.dumps()`方法被用于将 Python 对象数列化成一个 JSON 格式的字符串。`json.dumps()`方法也可以接受`indent`参数。这个参数可以被用于指明缩进的空格数。 以下是一个例子: @@ -85,7 +85,7 @@ print(json_str) ### 使用`pprint`模块 -`pprint`模块是一个Python内置的模块,它提供了一种代码美化Python数据结构的方式。它也可以处理JSON数据。`pprint.pprint()`方法被用来代码美化JSON数据。 +`pprint`模块是一个 Python 内置的模块,它提供了一种代码美化 Python 数据结构的方式。它也可以处理 JSON 数据。`pprint.pprint()`方法被用来代码美化 JSON 数据。 以下是一个例子: @@ -110,7 +110,7 @@ pprint.pprint(data) ### 使用第三方代码库 -在Python里,有很多用于代码美化JSON数据的第三方代码库,比如说`simplejson`、`ujson`和 `json5`。这些库提供了其他功能,比如更快的序列化和反序列化、对其他数据类型的支持以及更灵活的格式化选项。 +在 Python 里,有很多用于代码美化 JSON 数据的第三方代码库,比如说`simplejson`、`ujson`和 `json5`。这些库提供了其他功能,比如更快的序列化和反序列化、对其他数据类型的支持以及更灵活的格式化选项。 以下是一个使用`simplejson`的例子: @@ -137,21 +137,21 @@ print(json_str) } ``` -## 在Python中代码美化的用例 +## 在 Python 中代码美化的用例 -1.**调试JSON数据**:在处理JSON数据时,如果数据格式不好,读取和理解数据的结构可能会很困难。用Python代码美化JSON数据可以帮助我们快速识别数据中的任何问题,并更有效地调试代码。 +1.**调试 JSON 数据**:在处理 JSON 数据时,如果数据格式不好,读取和理解数据的结构可能会很困难。用 Python 代码美化 JSON 数据可以帮助我们快速识别数据中的任何问题,并更有效地调试代码。 -2.**在用户界面中显示JSON数据**:如果我们正在构建一个向用户显示JSON数据的web应用程序或移动端应用程序,那么代码美化可以通过使数据更具可读性和可呈现性来增强用户体验。 +2.**在用户界面中显示 JSON 数据**:如果我们正在构建一个向用户显示 JSON 数据的 web 应用程序或移动端应用程序,那么代码美化可以通过使数据更具可读性和可呈现性来增强用户体验。 -3.**与团队成员共享JSON数据**:如果我们正在与其他团队成员一起处理一个项目,并且需要与他们共享JSON数据,那么代码美化数据可以让他们更容易地理解并使用数据。 +3.**与团队成员共享 JSON 数据**:如果我们正在与其他团队成员一起处理一个项目,并且需要与他们共享 JSON 数据,那么代码美化数据可以让他们更容易地理解并使用数据。 -4.**记录JSON数据**:如果我们在Python应用中记录JSON数据,那么代码美化数据可以使读取和分析记录变得更容易。 +4.**记录 JSON 数据**:如果我们在 Python 应用中记录 JSON 数据,那么代码美化数据可以使读取和分析记录变得更容易。 ## 结论 -对任何处理JSON数据的人来说,在Python中代码美化JSON都是一个十分重要的技能。 +对任何处理 JSON 数据的人来说,在 Python 中代码美化 JSON 都是一个十分重要的技能。 -在这个教学中,我们学习了如何在Python中使用`json`模块来代码美化JSON,也学习了`pprint`模块。通过短短的几行代码,我们可以生成格式清晰的JSON输出,以方便我们阅读和导航。 +在这个教学中,我们学习了如何在 Python 中使用`json`模块来代码美化 JSON,也学习了`pprint`模块。通过短短的几行代码,我们可以生成格式清晰的 JSON 输出,以方便我们阅读和导航。 在[Twitter](https://www.twitter.com/Shittu_Olumide_)和[LinkedIn](https://www.linkedin.com/in/olumide-shittu)和我建立联系吧。你也可以订阅我的[YouTube](https://www.youtube.com/channel/UCNhFxpk6hGt5uMCKXq0Jl8A)频道。 diff --git a/chinese/articles/how-to-protect-against-dom-xss-attacks.md b/chinese/articles/how-to-protect-against-dom-xss-attacks.md index 6b9fa395e..c50925e4c 100644 --- a/chinese/articles/how-to-protect-against-dom-xss-attacks.md +++ b/chinese/articles/how-to-protect-against-dom-xss-attacks.md @@ -23,7 +23,7 @@ ### XSS 攻击的类型 -#### 反射型 XSS: +#### 反射型 XSS 这种恶意脚本来自 HTTP 请求。这是 XSS 攻击的最基本类型,在这种情况下,应用程序接收到恶意数据后立即向用户反映。 @@ -31,13 +31,13 @@ 其中一个例子是攻击者想方设法让某人点击钓鱼链接,然后攻击才生效。 -#### 存储型 XSS: +#### 存储型 XSS 这种恶意脚本来自网站的数据库。攻击者在服务器上输入恶意请求,除非手动处理,否则它可能永久存在。 例如,攻击者可以在评论字段中输入恶意脚本,该脚本将对访问该页面的所有人显示。即使没有直接与脚本互动,页面访问者也可能成为此攻击的受害者 -#### 基于 DOM 的 XSS: +#### 基于 DOM 的 XSS 这种是更高级的漏洞,它存在于客户端代码中的,而不是在服务器代码中。基于 DOM 的 XSS 既不是反射型的,也不存在于服务器上,而是存在于页面的文档对象模型(DOM)中。Web 应用程序读取恶意代码并将其作为 DOM 的一部分在浏览器中执行,这更难检测,因为它不通过服务器传递。 diff --git a/chinese/articles/how-to-remove-all-docker-images-a-docker-cleanup-guide.md b/chinese/articles/how-to-remove-all-docker-images-a-docker-cleanup-guide.md index 610f755f2..e7ec7112a 100644 --- a/chinese/articles/how-to-remove-all-docker-images-a-docker-cleanup-guide.md +++ b/chinese/articles/how-to-remove-all-docker-images-a-docker-cleanup-guide.md @@ -10,11 +10,11 @@ 不幸的是,这可能会占用大量的磁盘空间,最终你将会有一个完整的磁盘。 -如果你在设备或服务器上使用Docker,这并不重要。本指南告诉你如何分析已使用的磁盘空间和清理不同的Docker资源。 +如果你在设备或服务器上使用 Docker,这并不重要。本指南告诉你如何分析已使用的磁盘空间和清理不同的 Docker 资源。 -你所需要的只是一个正在运行的Docker守护进程和一个终端。 +你所需要的只是一个正在运行的 Docker 守护进程和一个终端。 -## 如何分析Docker使用了多少空间 +## 如何分析 Docker 使用了多少空间 你可以通过运行下面的命令来查看有多少空间被使用: @@ -28,7 +28,7 @@ Local Volumes 3 2 539.1MB 50.04MB (9%) Build Cache 76 0 1.242GB 1.242GB ``` -你可以通过使用verbose选项 `-v` 获得更多信息: +你可以通过使用 verbose 选项 `-v` 获得更多信息: ```sh $ docker system df -v @@ -63,9 +63,9 @@ nysus21ej7pf regular 0B 2 months ago 2 months ago - 本地卷的空间使用,以及 - 构建缓存的使用情况。 -## 如何在Docker中清理一切 +## 如何在 Docker 中清理一切 -你可以清理一切,也可以清理Docker中的特定资源,如镜像、容器卷或构建缓存。 +你可以清理一切,也可以清理 Docker 中的特定资源,如镜像、容器卷或构建缓存。 要尽可能地清理,不包括正在使用的组件,请运行这个命令: @@ -75,7 +75,7 @@ docker system prune -a `-a` 包括未使用的和悬空的容器。不提供`-a'将只删除悬空的镜像,这些镜像是没有标记的镜像,与任何其他镜像没有关系。 -如果你想清理大部分Docker资源,但仍然保留有标签的镜像,你可以执行这个命令: +如果你想清理大部分 Docker 资源,但仍然保留有标签的镜像,你可以执行这个命令: ```sh docker system prune @@ -109,20 +109,20 @@ docker container prune docker volume prune ``` -## 如何持续有效地管理你已使用的Docker空间 +## 如何持续有效地管理你已使用的 Docker 空间 你可以在日常或启动时运行一些东西。要跳过通常的提示,你需要在你想自动运行的命令中添加`-f`。 -请记住,这将导致你更频繁地下载镜像,因为你定期删除Docker资源。 +请记住,这将导致你更频繁地下载镜像,因为你定期删除 Docker 资源。 -如果你没有磁盘空间问题,那么不用担心。一旦Docker磁盘使用量过大引起你的注意,就立即清理。 +如果你没有磁盘空间问题,那么不用担心。一旦 Docker 磁盘使用量过大引起你的注意,就立即清理。 ## 结语 -如今,有很多方法可以使用`docker`命令来清理Docker磁盘空间。如果你想定期清理Docker资源,你甚至可以自动执行这些命令。 +如今,有很多方法可以使用`docker`命令来清理 Docker 磁盘空间。如果你想定期清理 Docker 资源,你甚至可以自动执行这些命令。 我希望你喜欢这篇文章。 -如果你喜欢它,觉得有必要给我点赞,或者只是想联系我,[在Twitter上关注我](https://twitter.com/sesigl)。 +如果你喜欢它,觉得有必要给我点赞,或者只是想联系我,[在 Twitter 上关注我](https://twitter.com/sesigl)。 -我在eBay Kleinanzeigen工作,这是全球最大的分类公司之一。顺便说一下,[我们正在招聘](https://jobs.ebayclassifiedsgroup.com/ebay-kleinanzeigen)! +我在 eBay Kleinanzeigen 工作,这是全球最大的分类公司之一。顺便说一下,[我们正在招聘](https://jobs.ebayclassifiedsgroup.com/ebay-kleinanzeigen)! diff --git a/chinese/articles/how-to-reverse-a-string-in-javascript-in-3-different-ways.md b/chinese/articles/how-to-reverse-a-string-in-javascript-in-3-different-ways.md index d9994fc99..52eadaeec 100644 --- a/chinese/articles/how-to-reverse-a-string-in-javascript-in-3-different-ways.md +++ b/chinese/articles/how-to-reverse-a-string-in-javascript-in-3-different-ways.md @@ -59,7 +59,7 @@ function reverseString(str) { reverseString('hello'); ``` -#### 三个方法组合形成链式调用: +#### 三个方法组合形成链式调用 ```js function reverseString(str) { @@ -99,7 +99,7 @@ function reverseString(str) { reverseString('hello'); ``` -#### 删除注释: +#### 删除注释 ```js function reverseString(str) { @@ -155,7 +155,7 @@ function reverseString(str) { ``` -#### 删除注释: +#### 删除注释 ```js function reverseString(str) { @@ -165,7 +165,7 @@ function reverseString(str) { reverseString('hello'); ``` -#### 使用三元表达式: +#### 使用三元表达式 ```js function reverseString(str) { diff --git a/chinese/articles/how-to-safely-use-reacts-life-cycles-with-fiber-s-async-rendering-fd4469ebbd8f.md b/chinese/articles/how-to-safely-use-reacts-life-cycles-with-fiber-s-async-rendering-fd4469ebbd8f.md index 9fa278bc1..0797f2a1b 100644 --- a/chinese/articles/how-to-safely-use-reacts-life-cycles-with-fiber-s-async-rendering-fd4469ebbd8f.md +++ b/chinese/articles/how-to-safely-use-reacts-life-cycles-with-fiber-s-async-rendering-fd4469ebbd8f.md @@ -29,7 +29,7 @@ For the sake of simplicity, here’s an example of subscribing to a [media query Before async rendering is enabled, we don’t have any issues because we can make the following guarantees about the component: -1. The `constructor` will be synchronously followed by `componentWillMount`, if we opt to use it, and then `render`. **Importantly, we won’t be interrupted before render.** Because of this, we can further guarantee… +1. The `constructor` will be synchronously followed by `componentWillMount`, if we opt to use it, and then `render`. **Importantly, we won’t be interrupted before render.** Because of this, we can further guarantee…… 2. If the component unmounts in the future, `componentWillUnmount`will clean up the event listener (subscription) beforehand. This means that the `window` won’t retain a reference to the component’s `handleMediaEvent` method via the media query list, therefore allowing the unmounted component to be garbage collected and hence avoid a memory leak. Failing to clean this up once wouldn’t be a big deal, but a component re-mounting and adding more listeners could cause issues over the lifetime of the app. > There is one caveat: error boundaries. I’ll touch on that in a bit. diff --git a/chinese/articles/how-to-set-up-a-front-end-development-project.md b/chinese/articles/how-to-set-up-a-front-end-development-project.md index bc9cfabcc..680f8b031 100644 --- a/chinese/articles/how-to-set-up-a-front-end-development-project.md +++ b/chinese/articles/how-to-set-up-a-front-end-development-project.md @@ -349,7 +349,7 @@ parcel build index.html —public-url '.' 就这样吧! 谢谢您的阅读 :) -### **订阅更多关于网站开发的教程:** +### **订阅更多关于网站开发的教程** Hunor Márton Borbély diff --git a/chinese/articles/how-to-sign-and-validate-json-web-tokens.md b/chinese/articles/how-to-sign-and-validate-json-web-tokens.md index 77c79eb95..e0fc13c3e 100644 --- a/chinese/articles/how-to-sign-and-validate-json-web-tokens.md +++ b/chinese/articles/how-to-sign-and-validate-json-web-tokens.md @@ -5,52 +5,52 @@ ![How to Sign and Validate JSON Web Tokens – JWT Tutorial](https://www.freecodecamp.org/news/content/images/size/w2000/2022/08/rohit-code-2400x1260.jpg) -从开始学习JSON Web令牌(JWT),我就一直很好奇,它是如何被验证的。 +从开始学习 JSON Web 令牌(JWT),我就一直很好奇,它是如何被验证的。 我了解我们签名了令牌,并且使用签名后的令牌来验证真实性。但是我还是特别好奇以及为什么我之前没有去了解内部的细节。 -希望这篇文章可以帮助你理解签名JWT是如何运作的,你是如何签名和验证令牌的。 +希望这篇文章可以帮助你理解签名 JWT 是如何运作的,你是如何签名和验证令牌的。 -## 什么是JWT? +## 什么是 JWT? -在我们正式开始之前,让我们快速回顾一下JSON Web令牌到底是什么。 +在我们正式开始之前,让我们快速回顾一下 JSON Web 令牌到底是什么。 -JSON Web令牌(JWT)是一种在两方之间紧凑的、URL安全的传输数据的方式。 +JSON Web 令牌(JWT)是一种在两方之间紧凑的、URL 安全的传输数据的方式。 它由开放标准(RFC 7519)定义,并由三个部分组成:header(头部)、payload(负载)以及一个加密部分。 -JWT在生成时会被签名,相同的签名JWT在收到时会被验证,以确保它在传输过程中没有被修改。 +JWT 在生成时会被签名,相同的签名 JWT 在收到时会被验证,以确保它在传输过程中没有被修改。 -如果你想要了解JWT的细节,我推荐你阅读我的博文——[JSON Web令牌(JWT)以及我们为什么使用它](https://blog.rohitjmathew.space/json-web-token-jwt-and-why-we-use-them). +如果你想要了解 JWT 的细节,我推荐你阅读我的博文——[JSON Web 令牌(JWT)以及我们为什么使用它](https://blog.rohitjmathew.space/json-web-token-jwt-and-why-we-use-them). ## 为什么你不需要知道签名和验证是如何工作的? 🤔 -现在的问题是,为什么大部分的JWT资源都会说“然后你签名并验证”就没有别的信息了?答案是因为这些信息很抽象。 +现在的问题是,为什么大部分的 JWT 资源都会说“然后你签名并验证”就没有别的信息了?答案是因为这些信息很抽象。 就好比当你在开车的时候,你并不需要知道引擎是如何工作的,或者也不需要靠自己来调整引擎,使汽车运行得更好。 相反,你信任制造商利用他们的专业、并且尽责为你制造了对你有用的产品。 -同样的,你也不需要了解签名和验证JWT的流程是什么,就可以高效地使用它来验证和授权你的应用和API。 +同样的,你也不需要了解签名和验证 JWT 的流程是什么,就可以高效地使用它来验证和授权你的应用和 API。 -请注意**你大概率是不需要自己签名和验证令牌的**,但了解背后的原理能够帮助你更有信心地使用JWT。但总的来说,身份供应商和身份(Identity-as-a-Service )即服务平台(如 Auth0、Okta 和 Microsoft Active Directory)可确保此过程很简单。 +请注意**你大概率是不需要自己签名和验证令牌的**,但了解背后的原理能够帮助你更有信心地使用 JWT。但总的来说,身份供应商和身份(Identity-as-a-Service )即服务平台(如 Auth0、Okta 和 Microsoft Active Directory)可确保此过程很简单。 如果你仍对它是如何运作的感兴趣(和我一样),欢迎你继续阅读。 -## JSON Web令牌由什么组成? 🤔 +## JSON Web 令牌由什么组成? 🤔 我在[这篇教程](https://blog.rohitjmathew.space/json-web-token-jwt-and-why-we-use-them)中做了深入的介绍,但是让我们快速回顾一下。 -JSON Web令牌由三段URL安全的字符串并用句号 `.`连接组成。 +JSON Web 令牌由三段 URL 安全的字符串并用句号 `.`连接组成。 -### JWT的Header(头部)部分 +### JWT 的 Header(头部)部分 第一个部分是头部,如下: `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9` -头部是一个JSON对象,包含了一个签名算法和一个令牌类型。它是由base64Url编码而成。 +头部是一个 JSON 对象,包含了一个签名算法和一个令牌类型。它是由 base64Url 编码而成。 解码后如下: @@ -61,15 +61,15 @@ JSON Web令牌由三段URL安全的字符串并用句号 `.`连接组成。 } ``` -### JWT的Payload(负载)部分 +### JWT 的 Payload(负载)部分 第二部分是负载: `eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0` -这是一个包含数据声明的JSON对象,其中包含有关用户的信息和其他与身份验证相关的信息。 +这是一个包含数据声明的 JSON 对象,其中包含有关用户的信息和其他与身份验证相关的信息。 -是JWT从一个实体传递到另一个实体的信息。它也是base64Url编码的。数据声明如下所示: +是 JWT 从一个实体传递到另一个实体的信息。它也是 base64Url 编码的。数据声明如下所示: ``` { @@ -81,31 +81,31 @@ JSON Web令牌由三段URL安全的字符串并用句号 `.`连接组成。 ``` -### JWT的加密/签名部分 +### JWT 的加密/签名部分 -最后一部分是加密/签名部分。JWT被签名之后不能在传输的过程中被修改。一旦一个授权的服务器发行了一个令牌,就使用密匙来签名。 +最后一部分是加密/签名部分。JWT 被签名之后不能在传输的过程中被修改。一旦一个授权的服务器发行了一个令牌,就使用密匙来签名。 -当客户端接收到ID的令牌,也通过密匙来验证签名。 +当客户端接收到 ID 的令牌,也通过密匙来验证签名。 签名算法不同,使用的密钥也会有所不同。如果使用的是非对称签名算法,则使用不同的密钥进行签名和验证。在这种情况下,只有授权服务器能够签名令牌。 -## JWT中的签名和验证是如何运作的? 🤔 +## JWT 中的签名和验证是如何运作的? 🤔 -### 如何签名一个JWT +### 如何签名一个 JWT -在这篇文章中,我将使用RS256签名算法。RS256是使用SHA-256的RSA电子签名。 +在这篇文章中,我将使用 RS256 签名算法。RS256 是使用 SHA-256 的 RSA 电子签名。 -SHA-256是一种非对称密钥加密算法,它使用一对密钥:一个公钥和一个私钥来加密和解密。 +SHA-256 是一种非对称密钥加密算法,它使用一对密钥:一个公钥和一个私钥来加密和解密。 在这里,授权服务器将使用私钥,而接收令牌以验证它的应用程序将使用公钥。 #### 签名输入 -首先让我来看看JWT的前两个部分(头部和负载),如下: +首先让我来看看 JWT 的前两个部分(头部和负载),如下: `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0` -基本上就是一个由base64url编码的头部和负载,并且由 `.`连接。 +基本上就是一个由 base64url 编码的头部和负载,并且由 `.`连接。 ``` base64UrlEncode(header) + "." @@ -116,7 +116,7 @@ base64UrlEncode(header) + "." #### 对签名输入做哈希加密 -然后我们使用[SHA-256哈希算法](https://dev.to/wagslane/how-sha-2-works-step-by-step-sha-256-11ci)对签名输入进行加密。哈希将一个值转换为另一个不同的值。哈希函数使用数学算法从现有值生成新值。 +然后我们使用[SHA-256 哈希算法](https://dev.to/wagslane/how-sha-2-works-step-by-step-sha-256-11ci)对签名输入进行加密。哈希将一个值转换为另一个不同的值。哈希函数使用数学算法从现有值生成新值。 注意: @@ -137,7 +137,7 @@ SHA-256 ( 接下来,我们给哈希后的签名输入加密。和哈希不同的是,加密是可逆的。授权服务器使用加密私钥给哈希后的签名加密,产生一个结果。 -这个最终结果(哈希过、加密过、头部和负载编码后)就是JWT的加密/签名部分。 +这个最终结果(哈希过、加密过、头部和负载编码后)就是 JWT 的加密/签名部分。 ``` RSA ( @@ -149,25 +149,25 @@ RSA ( ) ``` -这就是JSON Web令牌产生的过程 +这就是 JSON Web 令牌产生的过程 -### 如何验证JWT +### 如何验证 JWT -现在你知道令牌是如何签名的,我们可以进一步了解当收到令牌后,如何验证这个JWT是没有被篡改的。 +现在你知道令牌是如何签名的,我们可以进一步了解当收到令牌后,如何验证这个 JWT 是没有被篡改的。 -假设有一个接受JWT的应用,并且需要验证JWT。这个应用也可以访问授权服务器的公钥。 +假设有一个接受 JWT 的应用,并且需要验证 JWT。这个应用也可以访问授权服务器的公钥。 -JWT的验证是为了达到一个目的:即我们可以有效地将我们收到的与我们期望的进行比较。 +JWT 的验证是为了达到一个目的:即我们可以有效地将我们收到的与我们期望的进行比较。 #### 解码声明 应用可以对头部和负载解码来获取信息。 -请记住,这两个段是用base64Url编码,以使它们是URL安全的。这并不是密码学维度的安全。 +请记住,这两个段是用 base64Url 编码,以使它们是 URL 安全的。这并不是密码学维度的安全。 -你可以使用简单的在线base64解码工具来解码。一旦被解码,我们就可以轻松地读取其中的信息。 +你可以使用简单的在线 base64 解码工具来解码。一旦被解码,我们就可以轻松地读取其中的信息。 -例如,我们可以解码头部,看看JWT说它是用什么算法签名的。 +例如,我们可以解码头部,看看 JWT 说它是用什么算法签名的。 解码后的头部如下: @@ -178,28 +178,28 @@ JWT的验证是为了达到一个目的:即我们可以有效地将我们收 } ``` -当我们读取JWT头部的算法后,我们应该验证它是否和我们期待的配置匹配,如果不匹配,就马上拒绝这个令牌。 +当我们读取 JWT 头部的算法后,我们应该验证它是否和我们期待的配置匹配,如果不匹配,就马上拒绝这个令牌。 #### 哈希加密(再次) -如果令牌中的算法符合我们的期望(即使用RS256算法),我们需要生成头部和负载的SHA-256哈希。 +如果令牌中的算法符合我们的期望(即使用 RS256 算法),我们需要生成头部和负载的 SHA-256 哈希。 -请记住,哈希是不可逆的,但相同的输入总是会产生相同的输出。所以我们将哈希连接在一起的、由base64Url编码的头部和负载。现在我们在应用程序端重新哈希计算签名输入。 +请记住,哈希是不可逆的,但相同的输入总是会产生相同的输出。所以我们将哈希连接在一起的、由 base64Url 编码的头部和负载。现在我们在应用程序端重新哈希计算签名输入。 #### 解密 -哈希签名输入也在JWT的签名中,但它已由授权服务器使用私钥加密。应用程序可以访问公钥,因此我们可以解密签名。 +哈希签名输入也在 JWT 的签名中,但它已由授权服务器使用私钥加密。应用程序可以访问公钥,因此我们可以解密签名。 完成此操作后,我们就可以访问原始哈希:第一次生成令牌时由授权服务器生成的哈希。 #### 对比哈希值 -现在我们可以将解密的哈希与计算的哈希进行比较。如果它们相同,那么我们验证JWT头部和负载段中的数据在授权服务器创建令牌到应用程序收到它的之间没有被修改。 +现在我们可以将解密的哈希与计算的哈希进行比较。如果它们相同,那么我们验证 JWT 头部和负载段中的数据在授权服务器创建令牌到应用程序收到它的之间没有被修改。 #### 验证令牌声明 -此外,一旦我们验证了签名,我们就可以验证JSON Web令牌的数据。也可以验证负载段中的声明,因为它包含有关令牌颁发者、令牌到期时间、令牌的目标受众、令牌绑定到授权请求的信息等。 +此外,一旦我们验证了签名,我们就可以验证 JSON Web 令牌的数据。也可以验证负载段中的声明,因为它包含有关令牌颁发者、令牌到期时间、令牌的目标受众、令牌绑定到授权请求的信息等。 这些声明为应用程序提供了签名验证以外的详细信息。 @@ -207,11 +207,11 @@ JWT的验证是为了达到一个目的:即我们可以有效地将我们收 ## 总结 -我们已经介绍完毕JWT是如何签名和验证的,希望可以帮助你更好地理解JWT并且使用它。我想再重复一次**你不需要自己来签名和验证JWT**。 +我们已经介绍完毕 JWT 是如何签名和验证的,希望可以帮助你更好地理解 JWT 并且使用它。我想再重复一次**你不需要自己来签名和验证 JWT**。 -有[Auth0](https://auth0.com/)、 [Okta](https://www.okta.com/),、[Ping Identity](https://www.pingidentity.com/en.html)等身份平台帮助你完成。他们也提供应用端和API端的SDK、验证库和令牌管理系统。 +有[Auth0](https://auth0.com/)、 [Okta](https://www.okta.com/),、[Ping Identity](https://www.pingidentity.com/en.html)等身份平台帮助你完成。他们也提供应用端和 API 端的 SDK、验证库和令牌管理系统。 -如果你对使用Auth0感兴趣,你需要注册一个账号。你可以[免费在这里注册](https://a0.to/signup-for-auth0)。 +如果你对使用 Auth0 感兴趣,你需要注册一个账号。你可以[免费在这里注册](https://a0.to/signup-for-auth0)。 感谢阅读!希望你觉得这篇文章对你有帮助,我也很感兴趣收听你的想法,回答你的问题。如果你觉得本文对你有帮助,请你转发给其他人。 @@ -225,4 +225,4 @@ P.S 欢迎在[LinkedIn](https://www.linkedin.com/in/rohitjmathew/) 或[Twitter]( 以下是撰写这篇文章我参考的资料: - [Signing and Validating JSON Web Tokens (JWT) For Everyone](https://dev.to/kimmaida/signing-and-validating-json-web-tokens-jwt-for-everyone-25fb) 作者: [Kim Maida](https://twitter.com/KimMaida) -- [JSON Web Token (JWT) Signing Algorithms Overview](https://auth0.com/blog/json-web-token-signing-algorithms-overview/) 来自Auth0 +- [JSON Web Token (JWT) Signing Algorithms Overview](https://auth0.com/blog/json-web-token-signing-algorithms-overview/) 来自 Auth0 diff --git a/chinese/articles/how-to-start-unit-testing-javascript.md b/chinese/articles/how-to-start-unit-testing-javascript.md index ee825a141..a1cde253f 100644 --- a/chinese/articles/how-to-start-unit-testing-javascript.md +++ b/chinese/articles/how-to-start-unit-testing-javascript.md @@ -8,7 +8,7 @@ 我们都知道我们应该写单元测试。但是,很难知道从哪里开始,也很难知道与实际的实现相比,应该在测试上投入多少时间。那么,该从哪里开始呢?而且,仅仅是测试代码,还是单元测试还有其他好处? -在这篇文章中,我将解释不同类型的测试,以及单元测试给开发团队带来哪些好处。我将展示Jest - 一个JavaScript测试框架。 +在这篇文章中,我将解释不同类型的测试,以及单元测试给开发团队带来哪些好处。我将展示 Jest - 一个 JavaScript 测试框架。 ## 不同类型的测试 @@ -62,15 +62,15 @@ export function getAboutUsLink(language){ 当你在一开始就开始考虑单元测试时,它将帮助你更好地架构你的代码,实现适当的关注点分离。你将不会被诱惑为单一的代码块分配多个功能,因为这些将是单元测试的恶梦。 -**在编码之前**,你写下函数方法的签名,并立即开始实现它。哦,但是如果一个参数是空的,应该怎么办?如果它的值超出了预期范围或者包含了太多的字符怎么办?你是抛出一个异常还是返回null? +**在编码之前**,你写下函数方法的签名,并立即开始实现它。哦,但是如果一个参数是空的,应该怎么办?如果它的值超出了预期范围或者包含了太多的字符怎么办?你是抛出一个异常还是返回 null? 单元测试将帮助你发现所有这些情况。再看一下这些问题,你会发现这正是定义你的单元测试案例的内容。 我相信写单元测试还有很多好处。这些只是我从我的经验中回忆起来的。那些是我通过艰苦的方式学到的。 -## 如何编写你的第一个JavaScript单元测试 +## 如何编写你的第一个 JavaScript 单元测试 -但是,让我们回到JavaScript上来。我们将从[Jest](https://jestjs.io/) 开始,它是一个JavaScript测试框架。它是一个能够实现自动单元测试的工具,提供代码覆盖率,并让我们轻松地模拟对象。Jest也有一个Visual Studio Code的扩展 [可在此获得](https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest)。 +但是,让我们回到 JavaScript 上来。我们将从[Jest](https://jestjs.io/) 开始,它是一个 JavaScript 测试框架。它是一个能够实现自动单元测试的工具,提供代码覆盖率,并让我们轻松地模拟对象。Jest 也有一个 Visual Studio Code 的扩展 [可在此获得](https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest)。 还有其他的框架,如果你感兴趣,你可以在 [本文](https://www.browserstack.com/guide/top-javascript-testing-frameworks) 中查看它们。 @@ -110,7 +110,7 @@ test("Returns about-us for english language", () => { 在这个例子中,我们调用 `getAboutUsLink` 函数,语言参数为 `en-US`。我们期望的结果是 "/about-us"。 -现在我们可以全局安装Jest CLI并运行测试: +现在我们可以全局安装 Jest CLI 并运行测试: ```js npm i jest-cli -g @@ -132,17 +132,17 @@ Snapshots: 0 total Time: 2.389s ``` -伟大的工作! 这是第一个从头到尾的简单JavaScript单元测试。如果你安装了Visual Studio Code扩展,一旦你保存一个文件,它就会自动运行测试。让我们用这一行扩展测试来试试吧: +伟大的工作! 这是第一个从头到尾的简单 JavaScript 单元测试。如果你安装了 Visual Studio Code 扩展,一旦你保存一个文件,它就会自动运行测试。让我们用这一行扩展测试来试试吧: ```js expect(getAboutUsLink("cs-CZ")).toBe("/o-nas"); ``` -一旦你保存文件,Jest就会通知你测试失败。这有助于你在提交修改之前就发现潜在的问题。 +一旦你保存文件,Jest 就会通知你测试失败。这有助于你在提交修改之前就发现潜在的问题。 ## 测试高级功能和 Mocking(模拟) 服务 -在现实生活中,getAboutUsLink方法的语言代码不会在同一个文件中成为常量。它们的值通常会在整个项目中使用,所以它们会被定义在自己的模块中,并被导入到所有使用它们的函数中。 +在现实生活中,getAboutUsLink 方法的语言代码不会在同一个文件中成为常量。它们的值通常会在整个项目中使用,所以它们会被定义在自己的模块中,并被导入到所有使用它们的函数中。 ```js import { englishCode, spanishCode } from './LanguageCodes' @@ -175,11 +175,11 @@ export { UserStore } 为了正确的单元测试这个方法,我们需要 mock(模拟) `UserStore`。 mock 是原始对象的一个替代品。它允许我们将依赖关系和真实数据与测试方法的实现分开,就像假人帮助汽车的碰撞测试而不是真人一样。 -如果我们不使用mock,我们就会同时测试这个函数和商店。这将是一个集成测试,我们很可能需要对使用的数据库进行 mock(模拟)。 +如果我们不使用 mock,我们就会同时测试这个函数和商店。这将是一个集成测试,我们很可能需要对使用的数据库进行 mock(模拟)。 ### Mocking(模拟) 一个服务 -为了 mock(模拟) 对象,你可以提供一个 mock 函数或一个手动 mock。我将专注于后者,因为我有一个简单的用例。但你可以自由地[查看Jest其他的提供者(provides)](https://jestjs.io/docs/en/mock-functions.html)。 +为了 mock(模拟) 对象,你可以提供一个 mock 函数或一个手动 mock。我将专注于后者,因为我有一个简单的用例。但你可以自由地[查看 Jest 其他的提供者(provides)](https://jestjs.io/docs/en/mock-functions.html)。 ```js jest.mock('./UserStore', () => ({ @@ -205,11 +205,11 @@ test("Returns display name", () => { }) ``` -当我保存文件时,Jest告诉我有两个通过的测试。如果你正在手动执行测试,现在就这样做,确保你看到同样的结果。 +当我保存文件时,Jest 告诉我有两个通过的测试。如果你正在手动执行测试,现在就这样做,确保你看到同样的结果。 ### Code Coverage Report -现在我们知道了如何测试JavaScript代码,用测试覆盖尽可能多的代码是很好的。而这是很难做到的。说到底,我们只是人。我们想完成我们的任务,而单元测试通常会产生一些无意义的工作量,我们往往会本能的忽略。代码覆盖率统计工具是一个帮助我们对抗这种情况。 +现在我们知道了如何测试 JavaScript 代码,用测试覆盖尽可能多的代码是很好的。而这是很难做到的。说到底,我们只是人。我们想完成我们的任务,而单元测试通常会产生一些无意义的工作量,我们往往会本能的忽略。代码覆盖率统计工具是一个帮助我们对抗这种情况。 代码覆盖率会告诉你,你的代码有多大一部分被单元测试所覆盖。以我的第一个单元测试为例,检查`getAboutUsLink` 函数: @@ -219,7 +219,7 @@ test("Returns about-us for english language", () => { }); ``` -它检查了英文链接,但西班牙文版本仍未被测试。代码覆盖率为50%。另一个单元测试是彻底检查 `getDisplayName`函数,其代码覆盖率为100%。总之,总的代码覆盖率是67%。我们有3个用例需要测试,但我们的测试只覆盖了其中的两个。 +它检查了英文链接,但西班牙文版本仍未被测试。代码覆盖率为 50%。另一个单元测试是彻底检查 `getDisplayName`函数,其代码覆盖率为 100%。总之,总的代码覆盖率是 67%。我们有 3 个用例需要测试,但我们的测试只覆盖了其中的两个。 要查看代码覆盖率报告,请在终端输入以下命令: @@ -227,25 +227,25 @@ test("Returns about-us for english language", () => { jest --coverage ``` -或者,如果你使用的是带有Jest扩展的Visual Studio Code,你可以运行命令(CTRL+SHIFT+P 组合快捷键调出,然后输入)_Jest。 触发执行 Coverage Overlay_。它将在实现中直接显示哪些代码行没有被测试覆盖。 +或者,如果你使用的是带有 Jest 扩展的 Visual Studio Code,你可以运行命令(CTRL+SHIFT+P 组合快捷键调出,然后输入)_Jest。 触发执行 Coverage Overlay_。它将在实现中直接显示哪些代码行没有被测试覆盖。 ![code-coverage-inline](https://www.freecodecamp.org/news/content/images/2020/03/code-coverage-inline.jpg) -通过运行覆盖率检查,Jest还将创建一个HTML报告。在你的项目文件夹中的`coverage/lcov-report/index.html`下找到它。 +通过运行覆盖率检查,Jest 还将创建一个 HTML 报告。在你的项目文件夹中的`coverage/lcov-report/index.html`下找到它。 ![code-coverage](https://www.freecodecamp.org/news/content/images/2020/03/code-coverage.jpg) -现在,我不用再提了,你应该争取100%的代码覆盖率,对吗? :-) +现在,我不用再提了,你应该争取 100%的代码覆盖率,对吗? :-) ## 总结 -在这篇文章中,我向你展示了如何在JavaScript中开始单元测试。虽然在报告中让你的代码覆盖率达到100%是件好事,但在现实中,并不总是能够(有意义地)达到这个目标。我们的目标是让单元测试帮助你维护你的代码,并确保它总是按照预期工作。它们使你能够: +在这篇文章中,我向你展示了如何在 JavaScript 中开始单元测试。虽然在报告中让你的代码覆盖率达到 100%是件好事,但在现实中,并不总是能够(有意义地)达到这个目标。我们的目标是让单元测试帮助你维护你的代码,并确保它总是按照预期工作。它们使你能够: - 明确定义实现需求。 - 更好地设计你的代码和分离关注点。 - 发现你在较新的提交中尽早发现引入的问题。 - 并让你相信你的代码是正常工作的。 -最好的开始是Jest文档中的 [Getting started(入门)](https://jestjs.io/docs/en/getting-started) 页面,这样你就可以自己尝试这些做法了。 +最好的开始是 Jest 文档中的 [Getting started(入门)](https://jestjs.io/docs/en/getting-started) 页面,这样你就可以自己尝试这些做法了。 你对测试代码有自己的经验吗?我很想听听,请在 [Twitter](https://twitter.com/ondrabus) 上告诉我,或者加入我的 [Twitch streams 直播频道](https://twitch.tv/ondrabus)。 diff --git a/chinese/articles/how-to-stay-motivated-while-learning-to-code.md b/chinese/articles/how-to-stay-motivated-while-learning-to-code.md index 8f693ba90..4b7754487 100644 --- a/chinese/articles/how-to-stay-motivated-while-learning-to-code.md +++ b/chinese/articles/how-to-stay-motivated-while-learning-to-code.md @@ -132,7 +132,7 @@ Pomodoro 学习技巧是那些挣扎于无法长时间集中注意力来学习 设置短期目标并保持专注于一次实现一个目标。 ->成功不会在一夜之间发生。 +> 成功不会在一夜之间发生。 尝试在同一时间实现太多的目标会让人不知所措。所以最好是分解你的目标成为几个小目标,然后集中精力一次实现一个小目标。 @@ -147,7 +147,7 @@ Pomodoro 学习技巧是那些挣扎于无法长时间集中注意力来学习 7. 用你到目前为止学到的 HTML、CSS 和 JavaScript 的基础知识[专注于建立项目](https://www.freecodecamp.org/news/javascript-projects-for-beginners/)。 8. 开始学习高级的 JavaScript 概念,如[面向对象的编程](https://www.freecodecamp.org/news/how-javascript-implements-oop/), 和[异步编程](https://www.freecodecamp.org/news/synchronous-vs-asynchronous-in-javascript/)。 9. 通过建立项目来巩固你对高级概念的知识。 -10. 学习你选择的JavaScript框架。 [React](https://www.freecodecamp.org/news/learn-react-basics/)、 [Vue](https://www.freecodecamp.org/news/vue-js-full-course/)、 和 [Angular](https://www.freecodecamp.org/news/learn-angular-full-course/) 都是流行的选择。 +10. 学习你选择的 JavaScript 框架。 [React](https://www.freecodecamp.org/news/learn-react-basics/)、 [Vue](https://www.freecodecamp.org/news/vue-js-full-course/)、 和 [Angular](https://www.freecodecamp.org/news/learn-angular-full-course/) 都是流行的选择。 11. 最后,你可以使用你所学的框架建立你喜欢的项目。 您可能会认为,按照所述顺序完成上述步骤需要几个月的时间。也可能会花费更长时间,没事儿。 diff --git a/chinese/articles/how-to-style-react-apps-with-css.md b/chinese/articles/how-to-style-react-apps-with-css.md index 8f60d4693..475aef89f 100644 --- a/chinese/articles/how-to-style-react-apps-with-css.md +++ b/chinese/articles/how-to-style-react-apps-with-css.md @@ -5,17 +5,17 @@ ![How to Style Your React App – 5 Ways to Write CSS in 2021](https://www.freecodecamp.org/news/content/images/size/w2000/2021/07/how-to-style-react-apps.png) -当谈到你的React应用程序的 styling ,你有一大堆不同的选择。你会选择哪一种呢? +当谈到你的 React 应用程序的 styling ,你有一大堆不同的选择。你会选择哪一种呢? -我分析了在React应用中编写CSS时,你必须选择的5种主要方式。 +我分析了在 React 应用中编写 CSS 时,你必须选择的 5 种主要方式。 -对于每个项目来说,在React中编写样式并没有第一种方法。每个项目都是不同的,有不同的需求。 +对于每个项目来说,在 React 中编写样式并没有第一种方法。每个项目都是不同的,有不同的需求。 这就是为什么在每个部分的末尾,我将介绍每种方法的优点和缺点,以帮助你在项目中选择最适合你的方法。 让我们开始吧! -> 想成为一名专业的React开发人员,同时建立令人惊叹的项目吗?看看 [**The React Bootcamp**](https://reactbootcamp.com)。 +> 想成为一名专业的 React 开发人员,同时建立令人惊叹的项目吗?看看 [**The React Bootcamp**](https://reactbootcamp.com)。 ## 我们要编程的内容 @@ -23,11 +23,11 @@ ![](https://www.freecodecamp.org/news/content/images/2021/07/Screen-Shot-2021-07-14-at-12.07.40-PM.png) -> 想和这些例子中的每一个一起编程吗?去 [react.new](https://react.new) 即时创建一个新的React应用程序✨ +> 想和这些例子中的每一个一起编程吗?去 [react.new](https://react.new) 即时创建一个新的 React 应用程序✨ ## Inline Styles(内联样式) -内联样式是对任何React应用程序进行样式化的最直接方式。 +内联样式是对任何 React 应用程序进行样式化的最直接方式。 内联元素的样式不需要你创建一个单独的样式表。 @@ -169,11 +169,11 @@ export default function App() { } ``` -尽管有这样的改进,内联样式并不具备任何简单的CSS样式表可以提供的一些基本功能。 +尽管有这样的改进,内联样式并不具备任何简单的 CSS 样式表可以提供的一些基本功能。 例如,你不能编写动画、嵌套元素的样式(即所有子元素、首子、末子)、伪类(即:hover)和伪元素(:首行)等等。 -如果你正在设计一个应用程序的原型,内联样式是很好的。然而,当你进一步制作时,你将需要切换到另一个CSS样式选项,以给你提供基本的CSS功能。 +如果你正在设计一个应用程序的原型,内联样式是很好的。然而,当你进一步制作时,你将需要切换到另一个 CSS 样式选项,以给你提供基本的 CSS 功能。 👍 优点: @@ -183,22 +183,22 @@ export default function App() { 👎 缺点: -- 将普通的CSS转换为内联样式很繁琐 -- 大量的内联样式使JSX无法阅读 -- 你不能使用基本的CSS功能,如动画、选择器等 +- 将普通的 CSS 转换为内联样式很繁琐 +- 大量的内联样式使 JSX 无法阅读 +- 你不能使用基本的 CSS 功能,如动画、选择器等 - 不能很好地扩展 ## Plain CSS -不使用内联样式,而是导入一个CSS样式表来给组件的元素设置样式是很常见的。 +不使用内联样式,而是导入一个 CSS 样式表来给组件的元素设置样式是很常见的。 -在样式表中编写CSS可能是为React应用程序设置样式的最常见和最基本的方法,但它不应该被轻易否定。 +在样式表中编写 CSS 可能是为 React 应用程序设置样式的最常见和最基本的方法,但它不应该被轻易否定。 -在 plain CSS样式表中编写样式一直在变得更好,因为CSS标准中的功能越来越多。 +在 plain CSS 样式表中编写样式一直在变得更好,因为 CSS 标准中的功能越来越多。 -这包括像用于存储动态值的CSS变量、用于精确选择子元素的各种高级选择器,以及像 `:is` 和 `:where` 这样的新伪类。 +这包括像用于存储动态值的 CSS 变量、用于精确选择子元素的各种高级选择器,以及像 `:is` 和 `:where` 这样的新伪类。 -这是我们用 plain CSS写的推荐卡,并在我们的React应用程序的顶部导入。 +这是我们用 plain CSS 写的推荐卡,并在我们的 React 应用程序的顶部导入。 ```css /* src/styles.css */ @@ -277,45 +277,45 @@ export default function App() { 对于我们的推荐卡,请注意,我们正在创建应用于每个单独元素的类。这些类都以相同的名称 `testimonial-` 开始。 -写在样式表中的CSS是你的应用程序的一个很好的首选。与内联样式不同,它几乎可以以任何你所需要的方式为你的应用程序设置样式。 +写在样式表中的 CSS 是你的应用程序的一个很好的首选。与内联样式不同,它几乎可以以任何你所需要的方式为你的应用程序设置样式。 -一个小问题可能是你的命名惯例。一旦你有了一个非常完善的应用程序,你就很难为你的元素想出独特的类名,尤其是当你有5个div互相包裹的时候。 +一个小问题可能是你的命名惯例。一旦你有了一个非常完善的应用程序,你就很难为你的元素想出独特的类名,尤其是当你有 5 个 div 互相包裹的时候。 -如果你没有一个你有信心的命名规则(如BEM),就很容易犯错,再加上创建多个同名的类,这就导致了冲突。 +如果你没有一个你有信心的命名规则(如 BEM),就很容易犯错,再加上创建多个同名的类,这就导致了冲突。 -此外,与SASS/SCSS等新工具相比,编写 plain CSS可能更加繁琐和重复。因此,与SCSS等工具或CSS-in-JS库相比,用CSS写样式可能需要更长的时间。 +此外,与 SASS/SCSS 等新工具相比,编写 plain CSS 可能更加繁琐和重复。因此,与 SCSS 等工具或 CSS-in-JS 库相比,用 CSS 写样式可能需要更长的时间。 -另外,需要注意的是,由于CSS会级联到所有的子元素,如果你把CSS样式表应用到一个组件上,那么它就不仅仅局限于该组件了。它的所有声明规则将被转移到任何作为你的样式表组件的子元素上。 +另外,需要注意的是,由于 CSS 会级联到所有的子元素,如果你把 CSS 样式表应用到一个组件上,那么它就不仅仅局限于该组件了。它的所有声明规则将被转移到任何作为你的样式表组件的子元素上。 -如果你对CSS很有信心,它绝对是你为任何React应用程序设计样式的一个可行的选择。 +如果你对 CSS 很有信心,它绝对是你为任何 React 应用程序设计样式的一个可行的选择。 -说到这里,有一些CSS库为我们提供了CSS的所有功能,但代码更少,并包括许多CSS本身永远不会有的额外功能(如范围内的样式和自动提供前缀)。 +说到这里,有一些 CSS 库为我们提供了 CSS 的所有功能,但代码更少,并包括许多 CSS 本身永远不会有的额外功能(如范围内的样式和自动提供前缀)。 👍 优点: -- 给我们提供了现代CSS的所有工具(变量、高级选择器、新的伪类等等)。 +- 给我们提供了现代 CSS 的所有工具(变量、高级选择器、新的伪类等等)。 - 帮助我们从内联样式中清理我们的组件文件 👎 缺点: - 需要设置统一的前缀,以确保最新的功能对所有用户有效 -- 与其他CSS库(如SASS)相比,需要更多的代码量和模板。 +- 与其他 CSS 库(如 SASS)相比,需要更多的代码量和模板。 - 任何样式表都会级联到组件和所有的子组件;没有范围。 - 必须使用一个可靠的命名规则,以确保样式不冲突。 ## SASS / SCSS -什么是SASS?SASS是一个首字母缩写,代表。Syntactically Awesome Style Sheets。 +什么是 SASS?SASS 是一个首字母缩写,代表。Syntactically Awesome Style Sheets。 -SASS为我们提供了一些强大的工具,其中许多是 plain CSS样式表所不具备的。它包括变量、扩展样式和嵌套等功能。 +SASS 为我们提供了一些强大的工具,其中许多是 plain CSS 样式表所不具备的。它包括变量、扩展样式和嵌套等功能。 ![](https://www.freecodecamp.org/news/content/images/2021/07/Screen-Shot-2021-07-14-at-12.36.47-PM.png) -SASS允许我们用两种不同的样式表编写样式,扩展名为.scss和.sass。 +SASS 允许我们用两种不同的样式表编写样式,扩展名为.scss 和.sass。 -SCSS样式的写法与普通CSS相似,但SASS样式不要求我们在写样式规则时使用开括号和闭括号。 +SCSS 样式的写法与普通 CSS 相似,但 SASS 样式不要求我们在写样式规则时使用开括号和闭括号。 -下面是一个带有一些嵌套样式的SCSS样式表的快速例子: +下面是一个带有一些嵌套样式的 SCSS 样式表的快速例子: ```css /* styles.scss */ @@ -337,7 +337,7 @@ nav { } ``` -将此与写在SASS样式表中的相同代码进行比较: +将此与写在 SASS 样式表中的相同代码进行比较: ```css /* styles.sass */ @@ -358,15 +358,15 @@ nav ``` -因为这不是普通的CSS,所以需要从SASS编译成 plain CSS。为了在我们的React项目中做到这一点,你可以使用一个像node-sass这样的库。 +因为这不是普通的 CSS,所以需要从 SASS 编译成 plain CSS。为了在我们的 React 项目中做到这一点,你可以使用一个像 node-sass 这样的库。 -如果你使用的是Create React App项目,要开始使用.scss和.sass文件,你可以用npm安装node-sass: +如果你使用的是 Create React App 项目,要开始使用.scss 和.sass 文件,你可以用 npm 安装 node-sass: ```bash npm install node-sass ``` -这是我们用SCSS设计的推荐卡的样式: +这是我们用 SCSS 设计的推荐卡的样式: ```css /* src/styles.scss */ @@ -404,7 +404,7 @@ body { 这些样式为我们提供了以下功能:变量、扩展样式和嵌套样式。 -**Variables(变量)**: 你可以通过编写变量来使用动态值,就像在JavaScript中一样,在开头用"$"来声明它们。 +**Variables(变量)**: 你可以通过编写变量来使用动态值,就像在 JavaScript 中一样,在开头用"$"来声明它们。 有两个变量可以在多个规则中使用。`$font-stack`, `$text-color`。 @@ -416,26 +416,26 @@ body { 在`.testimonial-name`中,我们使用一个嵌套的选择器来定位其中的`span`元素。 -你可以找到一个使用SCSS的React应用程序的工作版本 [这里](https://codesandbox.io/s/react-and-scss-forked-2xeu0?file=/src/styles.scss)。 +你可以找到一个使用 SCSS 的 React 应用程序的工作版本 [这里](https://codesandbox.io/s/react-and-scss-forked-2xeu0?file=/src/styles.scss)。 👍 优点: -- 包括许多动态的CSS功能,如扩展、嵌套和混合器 -- 编写CSS样式时,可以比 plain CSS少用很多模板 +- 包括许多动态的 CSS 功能,如扩展、嵌套和混合器 +- 编写 CSS 样式时,可以比 plain CSS 少用很多模板 👎 缺点: -- 和普通CSS一样,样式是全局性的,不属于任何一个组件的范围。 -- CSS样式表开始包括一些SASS独有的功能,如CSS变量(不一定是缺点,但值得注意)。 -- SASS/SCSS通常需要设置,比如安装Node库`node-sass`。 +- 和普通 CSS 一样,样式是全局性的,不属于任何一个组件的范围。 +- CSS 样式表开始包括一些 SASS 独有的功能,如 CSS 变量(不一定是缺点,但值得注意)。 +- SASS/SCSS 通常需要设置,比如安装 Node 库`node-sass`。 ## CSS Modules -CSS Modules 是对CSS或SASS这样的东西的另一种轻量替代。 +CSS Modules 是对 CSS 或 SASS 这样的东西的另一种轻量替代。 -CSS Modules 的好处是,它们可以与普通CSS或SASS一起使用。另外,如果你使用的是Create React App,你就可以开始使用CSS Modules,根本不需要设置。 +CSS Modules 的好处是,它们可以与普通 CSS 或 SASS 一起使用。另外,如果你使用的是 Create React App,你就可以开始使用 CSS Modules,根本不需要设置。 -下面是我们用CSS模块编写的应用程序: +下面是我们用 CSS 模块编写的应用程序: ```css /* src/styles.module.css */ @@ -485,13 +485,13 @@ export default function App() { } ``` -我们的CSS文件在扩展名 `.css` 之前有 `.module` 这个名字。任何CSS模块文件都必须有 `module` 的名字,并以适当的扩展名结尾(如果我们使用的是CSS或SASS/SCSS)。 +我们的 CSS 文件在扩展名 `.css` 之前有 `.module` 这个名字。任何 CSS 模块文件都必须有 `module` 的名字,并以适当的扩展名结尾(如果我们使用的是 CSS 或 SASS/SCSS)。 -如果我们看一下上面的代码,有趣的是,CSS modules 的写法与普通的CSS一样,但被导入和使用时就像它被创建为对象一样(内联样式)。 +如果我们看一下上面的代码,有趣的是,CSS modules 的写法与普通的 CSS 一样,但被导入和使用时就像它被创建为对象一样(内联样式)。 -CSS modules 的好处是,它有助于避免我们与普通CSS的类冲突问题。我们所引用的属性变成了独特的类名,在我们的项目建立时不会相互冲突。 +CSS modules 的好处是,它有助于避免我们与普通 CSS 的类冲突问题。我们所引用的属性变成了独特的类名,在我们的项目建立时不会相互冲突。 -我们生成的HTML元素将看起来像这样: +我们生成的 HTML 元素将看起来像这样: ```html <p class="_styles__testimonial-name_309571057"> @@ -499,35 +499,35 @@ CSS modules 的好处是,它有助于避免我们与普通CSS的类冲突问 </p> ``` -另外,CSS modules 解决了CSS中全局范围的问题。与我们普通的CSS样式表相比,使用模块向单个组件声明的CSS不会层叠到子组件。 +另外,CSS modules 解决了 CSS 中全局范围的问题。与我们普通的 CSS 样式表相比,使用模块向单个组件声明的 CSS 不会层叠到子组件。 -因此,CSS modules 最好在CSS和SASS之上使用,以确保类不冲突,并编写只适用于一个或另一个组件的可预测样式。 +因此,CSS modules 最好在 CSS 和 SASS 之上使用,以确保类不冲突,并编写只适用于一个或另一个组件的可预测样式。 👍 优点: -- 样式的范围是一个或另一个组件(与CSS/SASS不同)。 +- 样式的范围是一个或另一个组件(与 CSS/SASS 不同)。 - 独特的、生成的类名确保没有样式冲突 -- 可以在CRA项目中立即使用,无需设置 -- 可与SASS/CSS一起使用 +- 可以在 CRA 项目中立即使用,无需设置 +- 可与 SASS/CSS 一起使用 👎 缺点: - 引用类名可能比较麻烦 -- 使用CSS样式(如对象属性)可能是一个学习曲线比较高 +- 使用 CSS 样式(如对象属性)可能是一个学习曲线比较高 ## CSS-in-JS -类似于React允许我们用JSX将HTML写成JavaScript,CSS-in-JS也对CSS做了类似的事情。 +类似于 React 允许我们用 JSX 将 HTML 写成 JavaScript,CSS-in-JS 也对 CSS 做了类似的事情。 -CSS-in-JS允许我们直接在我们组件的javascript(.js)文件中编写CSS样式。 +CSS-in-JS 允许我们直接在我们组件的 javascript(.js)文件中编写 CSS 样式。 -它不仅允许你编写CSS样式规则而不需要制作一个.css文件,而且这些样式是针对单个组件的。 +它不仅允许你编写 CSS 样式规则而不需要制作一个.css 文件,而且这些样式是针对单个组件的。 -换句话说,你可以在没有任何意外的情况下添加、改变或删除CSS。改变一个组件的样式不会影响你的应用程序的其他部分的样式。 +换句话说,你可以在没有任何意外的情况下添加、改变或删除 CSS。改变一个组件的样式不会影响你的应用程序的其他部分的样式。 -CSS-in-JS经常使用一种特殊类型的JavaScript函数,称为标签模板字面。这样做的好处是,我们仍然可以直接在JS中编写 plain CSS样式规则 +CSS-in-JS 经常使用一种特殊类型的 JavaScript 函数,称为标签模板字面。这样做的好处是,我们仍然可以直接在 JS 中编写 plain CSS 样式规则 -下面是一个流行的CSS-in-JS库的快速例子,Styled Components: +下面是一个流行的 CSS-in-JS 库的快速例子,Styled Components: ```js import styled from "styled-components"; @@ -558,15 +558,15 @@ export default function App() { 这里要注意几件事: -1. 你可以写普通的CSS样式,但可以包括嵌套样式和伪类(如hover)。 -2. 你可以将样式与任何有效的HTML元素相关联,比如上面的按钮元素(见`styled.button`)。 -3. 你可以用这些关联的样式创建新的组件。请看 `Button` 是如何在我们的App组件中使用的。 +1. 你可以写普通的 CSS 样式,但可以包括嵌套样式和伪类(如 hover)。 +2. 你可以将样式与任何有效的 HTML 元素相关联,比如上面的按钮元素(见`styled.button`)。 +3. 你可以用这些关联的样式创建新的组件。请看 `Button` 是如何在我们的 App 组件中使用的。 -既然这是一个组件,它可以传递 props 吗?是的!我们可以导出这个组件,并在我们喜欢的地方使用它。我们可以导出这个组件,并在我们的应用程序中的任何地方使用它,另外还可以通过props赋予它动态功能。 +既然这是一个组件,它可以传递 props 吗?是的!我们可以导出这个组件,并在我们喜欢的地方使用它。我们可以导出这个组件,并在我们的应用程序中的任何地方使用它,另外还可以通过 props 赋予它动态功能。 比方说,你想要一个倒置的 `Button` 的变体,有一个倒置的背景和文本。没问题。 -将 `inverted` prop传递给我们的第二个按钮,在 `Button` 中,你可以使用 `${}` 语法和内部函数访问传递给该组件的所有 props。 +将 `inverted` prop 传递给我们的第二个按钮,在 `Button` 中,你可以使用 `${}` 语法和内部函数访问传递给该组件的所有 props。 ```js import styled from "styled-components"; @@ -601,36 +601,36 @@ export default function App() { ![](https://www.freecodecamp.org/news/content/images/2021/07/Screen-Shot-2021-07-14-at-11.31.52-AM.png) -使用CSS-in-JS库来设计你的React应用程序还有很多好处(太多了,这里就不多说了),下面我将列出其中的一些。 +使用 CSS-in-JS 库来设计你的 React 应用程序还有很多好处(太多了,这里就不多说了),下面我将列出其中的一些。 -请务必查看两个最受欢迎的React的CSS-in-JS库。Emotion和Styled Components。 +请务必查看两个最受欢迎的 React 的 CSS-in-JS 库。Emotion 和 Styled Components。 -使用CSS-in-JS库的一个缺点是给你的项目增加一个额外的库。然而,我认为这很容易值得你在为你的React应用程序设计样式时,与普通的CSS相比,你有更好的开发者体验。 +使用 CSS-in-JS 库的一个缺点是给你的项目增加一个额外的库。然而,我认为这很容易值得你在为你的 React 应用程序设计样式时,与普通的 CSS 相比,你有更好的开发者体验。 👍 优点: -- CSS-in-JS是可预测的--样式的范围是针对单个组件的 -- 由于我们的CSS现在是JS,我们可以通过props导出、重用甚至扩展我们的样式。 -- CSS-in-JS库通过为你编写的样式生成唯一的类名来确保没有样式冲突。 +- CSS-in-JS 是可预测的--样式的范围是针对单个组件的 +- 由于我们的 CSS 现在是 JS,我们可以通过 props 导出、重用甚至扩展我们的样式。 +- CSS-in-JS 库通过为你编写的样式生成唯一的类名来确保没有样式冲突。 - 不需要关注你的类的命名规则,只需要编写样式即可 👎 缺点: -- 与普通的CSS不同,你需要安装一个或多个第三方的JavaScript库,这将增加你所建项目的重量 +- 与普通的 CSS 不同,你需要安装一个或多个第三方的 JavaScript 库,这将增加你所建项目的重量 ## 结语 请注意,我在这个比较中没有包括组件库。我想把重点主要放在自己编排样式的不同方式上。 -请注意,选择一个有预制组件 style 的库,如Material UI或Ant Design(仅举几例),对你的项目来说是一个完全有效的选择。 +请注意,选择一个有预制组件 style 的库,如 Material UI 或 Ant Design(仅举几例),对你的项目来说是一个完全有效的选择。 -我希望这个指南能让你很好地理解如何为你的React应用程序设计样式,以及为你的下一个项目选择哪种方法。 +我希望这个指南能让你很好地理解如何为你的 React 应用程序设计样式,以及为你的下一个项目选择哪种方法。 -## 想了解更多?加入React Bootcamp +## 想了解更多?加入 React Bootcamp -**[The React Bootcamp](http://bit.ly/join-react-bootcamp)** 将你应该知道的关于学习React的一切,捆绑在一个综合包中,包括视频、手册,以及特别奖金。 +**[The React Bootcamp](http://bit.ly/join-react-bootcamp)** 将你应该知道的关于学习 React 的一切,捆绑在一个综合包中,包括视频、手册,以及特别奖金。 -获得内幕信息,**100** 名开发者已经用它掌握了React,找到了他们梦想的工作,并掌握了他们的未来: +获得内幕信息,**100** 名开发者已经用它掌握了 React,找到了他们梦想的工作,并掌握了他们的未来: [![The React Bootcamp](https://reedbarger.nyc3.digitaloceanspaces.com/react-bootcamp-banner.png)](http://bit.ly/join-react-bootcamp) _点击这里获得开课通知_ diff --git a/chinese/articles/how-to-use-axios-with-react.md b/chinese/articles/how-to-use-axios-with-react.md index 8dbf3417b..81a319123 100644 --- a/chinese/articles/how-to-use-axios-with-react.md +++ b/chinese/articles/how-to-use-axios-with-react.md @@ -5,87 +5,87 @@ ![如何在 React 中使用 Axios:2021 年完全指南](https://www.freecodecamp.org/news/content/images/size/w2000/2021/07/how-to-use-axios-with-react.png) -在本指南中,你将看到如何使用Axios.js和React,并使用大量具有React hook的真实世界的例子。 +在本指南中,你将看到如何使用 Axios.js 和 React,并使用大量具有 React hook 的真实世界的例子。 -你会看到为什么你应该使用Axios作为数据获取库,如何用React设置它,并使用它执行各种类型的HTTP请求。 +你会看到为什么你应该使用 Axios 作为数据获取库,如何用 React 设置它,并使用它执行各种类型的 HTTP 请求。 -然后,我们将触及更多的高级功能,如创建Axios实例以实现可重用性,使用async-await来简化Axios,以及如何将Axios作为一个自定义Hook。 +然后,我们将触及更多的高级功能,如创建 Axios 实例以实现可重用性,使用 async-await 来简化 Axios,以及如何将 Axios 作为一个自定义 Hook。 让我们直接开始吧! ### **想要自己的副本?‬ 📄** -****[点击这里下载PDF格式的小册子](https://reedbarger.com/resources/react-axios-2021)**** (它需要5秒下载完). +****[点击这里下载 PDF 格式的小册子](https://reedbarger.com/resources/react-axios-2021)**** (它需要 5 秒下载完). -它包括这里所有的基本信息,作为一个方便的PDF指南。 +它包括这里所有的基本信息,作为一个方便的 PDF 指南。 ## 目录 -- [什么是Axios?](#what-is-axios) -- [为什么在React中使用Axios](#why-use-axios-in-react) -- [如何用React设置Axios](#how-to-set-up-axios-with-react) -- [如何进行GET请求(检索数据)](#how-to-make-a-get-request) -- [如何进行POST请求(创建数据)](#how-to-make-a-post-request) -- [如何进行PUT请求(更新数据)](#how-to-make-a-put-request) -- [如何提出DELETE请求(删除数据)](#how-to-make-a-delete-request) -- [如何处理Axios的错误](#how-to-handle-errors-with-axios) -- [如何创建一个Axios实例](#how-to-create-an-axios-instance) -- [如何使用Axios的Async-Await语法](#how-to-use-the-async-await-syntax-with-axios) +- [什么是 Axios?](#what-is-axios) +- [为什么在 React 中使用 Axios](#why-use-axios-in-react) +- [如何用 React 设置 Axios](#how-to-set-up-axios-with-react) +- [如何进行 GET 请求(检索数据)](#how-to-make-a-get-request) +- [如何进行 POST 请求(创建数据)](#how-to-make-a-post-request) +- [如何进行 PUT 请求(更新数据)](#how-to-make-a-put-request) +- [如何提出 DELETE 请求(删除数据)](#how-to-make-a-delete-request) +- [如何处理 Axios 的错误](#how-to-handle-errors-with-axios) +- [如何创建一个 Axios 实例](#how-to-create-an-axios-instance) +- [如何使用 Axios 的 Async-Await 语法](#how-to-use-the-async-await-syntax-with-axios) - [如何创建一个自定义的 `useAxios` Hook](#how-to-create-a-custom-useaxios-hook) ## What is Axios? -Axios是一个HTTP客户端库,它允许你向一个给定的端点(endpoint)发出请求。 +Axios 是一个 HTTP 客户端库,它允许你向一个给定的端点(endpoint)发出请求。 ![](https://www.freecodecamp.org/news/content/images/2021/07/Screen-Shot-2021-07-12-at-1.14.41-PM.png) -例如,这可能是一个外部API或你自己的后端Node.js服务器。 +例如,这可能是一个外部 API 或你自己的后端 Node.js 服务器。 -通过提出请求,你希望你的API能根据你提出的请求执行操作。 +通过提出请求,你希望你的 API 能根据你提出的请求执行操作。 -例如,如果你提出一个GET请求,你希望得到的数据能在你的应用程序中显示。 +例如,如果你提出一个 GET 请求,你希望得到的数据能在你的应用程序中显示。 ## Why Use Axios in React -有许多不同的库可以用来提出这些请求,那么为什么选择Axios呢? +有许多不同的库可以用来提出这些请求,那么为什么选择 Axios 呢? -以下 **五个理由** ,为什么你应该使用Axios作为你的客户端来进行HTTP请求: +以下 **五个理由** ,为什么你应该使用 Axios 作为你的客户端来进行 HTTP 请求: -1. 它有很好的默认值来处理JSON数据。与Fetch API等替代品不同,你通常不需要设置你的头文件。或执行繁琐的任务,如将你的请求体转换为JSON字符串 -2. Axios有与任何HTTP方法相匹配的函数名称。要执行一个GET请求,你可以使用`.get()`方法。 -3. Axios用更少的代码做更多的事情。与Fetch API不同,你只需要一个`.then()`回调来访问你请求的JSON数据。 -4. Axios有更好的错误处理。Axios为你抛出400和500范围的错误。不像Fetch API,你必须检查状态代码并自己抛出错误。 -5. Axios既可以在服务器上使用,也可以在客户端使用。如果你正在写一个Node.js应用程序,请注意Axios也可以在独立于浏览器的环境中使用。 +1. 它有很好的默认值来处理 JSON 数据。与 Fetch API 等替代品不同,你通常不需要设置你的头文件。或执行繁琐的任务,如将你的请求体转换为 JSON 字符串 +2. Axios 有与任何 HTTP 方法相匹配的函数名称。要执行一个 GET 请求,你可以使用`.get()`方法。 +3. Axios 用更少的代码做更多的事情。与 Fetch API 不同,你只需要一个`.then()`回调来访问你请求的 JSON 数据。 +4. Axios 有更好的错误处理。Axios 为你抛出 400 和 500 范围的错误。不像 Fetch API,你必须检查状态代码并自己抛出错误。 +5. Axios 既可以在服务器上使用,也可以在客户端使用。如果你正在写一个 Node.js 应用程序,请注意 Axios 也可以在独立于浏览器的环境中使用。 ## How to Set Up Axios with React -在React中使用Axios是一个非常简单的过程。你需要三样东西: +在 React 中使用 Axios 是一个非常简单的过程。你需要三样东西: -1. 一个现有的React项目 -2. 用npm/yarn来安装Axios -3. 一个用于发出请求的API端点(endpoint) +1. 一个现有的 React 项目 +2. 用 npm/yarn 来安装 Axios +3. 一个用于发出请求的 API 端点(endpoint) -创建一个新的React应用程序的最快捷的方法是去 [react.new](https://react.new)网站。 +创建一个新的 React 应用程序的最快捷的方法是去 [react.new](https://react.new)网站。 -如果你有一个现有的React项目,你只需要用npm(或任何其他包管理器)安装Adios。 +如果你有一个现有的 React 项目,你只需要用 npm(或任何其他包管理器)安装 Adios。 ```bash npm install axios ``` -在本指南中,你将使用JSON Placeholder API来获取和改变帖子数据。 +在本指南中,你将使用 JSON Placeholder API 来获取和改变帖子数据。 -下面是你可以提出请求的所有不同路由(routes)的列表,以及每个路线的相应HTTP方法: +下面是你可以提出请求的所有不同路由(routes)的列表,以及每个路线的相应 HTTP 方法: ![](https://www.freecodecamp.org/news/content/images/2021/07/Screen-Shot-2021-07-10-at-12.21.28-PM.png) -下面是一个快速的例子,说明你将使用Axios和你的API端点进行的所有操作--检索、创建、更新和删除帖子: +下面是一个快速的例子,说明你将使用 Axios 和你的 API 端点进行的所有操作--检索、创建、更新和删除帖子: ![](https://www.freecodecamp.org/news/content/images/2021/07/axios-react.gif) ## How to Make a GET Request -要获取数据或检索数据,要提出一个GET请求。 +要获取数据或检索数据,要提出一个 GET 请求。 首先,你要对单个帖子进行请求。如果你看一下端点,你将从`/posts`端点(endpoint)获得第一个帖子。 @@ -115,7 +115,7 @@ export default function App() { } ``` -为了在挂载组件时执行这个请求, 你可以使用`useEffect`Hook。这涉及到导入Axios,使用`.get()`方法向你的端点(endpoint)发出GET请求, 并使用`.then()`回调获得所有的响应数据。 +为了在挂载组件时执行这个请求, 你可以使用`useEffect`Hook。这涉及到导入 Axios,使用`.get()`方法向你的端点(endpoint)发出 GET 请求, 并使用`.then()`回调获得所有的响应数据。 响应被作为一个对象返回。数据(这里是一个带有`id`,`title`和`body`属性的帖子)被放在一个叫做`post`的状态中,在组件中显示。 @@ -123,9 +123,9 @@ export default function App() { ## How to Make a POST Request -要创建新的数据,要发出一个POST请求。 +要创建新的数据,要发出一个 POST 请求。 -根据API,这需要在`/posts`端点(endpoint)上执行。如果你看一下下面的代码,你会发现点击一个按钮可以创建一个帖子。 +根据 API,这需要在`/posts`端点(endpoint)上执行。如果你看一下下面的代码,你会发现点击一个按钮可以创建一个帖子。 ```js import axios from "axios"; @@ -167,15 +167,15 @@ export default function App() { 当你点击按钮时,它会调用`createPost`函数。 -为了用Axios进行POST请求,你使用`.post()`方法。作为第二个参数,你包括一个对象属性,指定你希望新的帖子是什么。 +为了用 Axios 进行 POST 请求,你使用`.post()`方法。作为第二个参数,你包括一个对象属性,指定你希望新的帖子是什么。 再一次,使用`.then()`回调来获取响应数据,用你请求的新帖子替换你得到的第一个帖子。 -这与`.get()`方法非常相似,但你想要创建的新资源是作为API端点(endpoint)之后的第二个参数提供的。 +这与`.get()`方法非常相似,但你想要创建的新资源是作为 API 端点(endpoint)之后的第二个参数提供的。 ## How to Make a PUT Request -要更新一个给定的资源,要提出一个PUT请求。 +要更新一个给定的资源,要提出一个 PUT 请求。 在这种情况下,你将更新第一个帖子。 @@ -219,13 +219,13 @@ export default function App() { } ``` -在上面的代码中,你使用了Axios的PUT方法。和POST方法一样,你包括了你想在更新资源中的属性。 +在上面的代码中,你使用了 Axios 的 PUT 方法。和 POST 方法一样,你包括了你想在更新资源中的属性。 -同样,使用`.then()`回调,你用返回的数据更新JSX。 +同样,使用`.then()`回调,你用返回的数据更新 JSX。 ## How to Make a DELETE Request -最后,要删除一个资源,使用DELETE方法 +最后,要删除一个资源,使用 DELETE 方法 作为一个例子,我们将删除第一个帖子。 @@ -277,11 +277,11 @@ export default function App() { ## How to Handle Errors with Axios -如何处理Axios的错误? +如何处理 Axios 的错误? 如果在发出请求时出现了错误怎么办?例如,你可能传递了错误的数据,向错误的端点(endpoint)发出了请求,或者出现了网络错误。 -为了模拟一个错误,你将向一个不存在的API端点(endpoint)发送一个请求: `/posts/asdf`. +为了模拟一个错误,你将向一个不存在的 API 端点(endpoint)发送一个请求: `/posts/asdf`. 这个请求将返回一个`404`状态代码。 @@ -316,7 +316,7 @@ export default function App() { } ``` -在这种情况下,Axios不会执行`.then()`回调函数,而是抛出一个错误并运行`.catch()`回调函数。 +在这种情况下,Axios 不会执行`.then()`回调函数,而是抛出一个错误并运行`.catch()`回调函数。 在这个函数中,我们正在获取错误数据,并将其放入状态,以提醒我们的用户注意错误。因此,如果我们有一个错误,我们将显示该错误信息。 @@ -326,11 +326,11 @@ export default function App() { ## How to Create an Axios Instance -如果你看一下前面的例子,你会发现有一个`baseURL`,你用它作为Axios执行这些请求的端点(endpoint)的一部分。 +如果你看一下前面的例子,你会发现有一个`baseURL`,你用它作为 Axios 执行这些请求的端点(endpoint)的一部分。 -然而,为每一个请求不断地编写`baseURL`是有点乏味的。你能不能让Axios记住你使用的`baseURL`?因为它总是涉及一个类似的端点。 +然而,为每一个请求不断地编写`baseURL`是有点乏味的。你能不能让 Axios 记住你使用的`baseURL`?因为它总是涉及一个类似的端点。 -事实上,你可以。如果你用`.create()`方法创建一个实例,Axios会记住`baseURL`,以及你可能想为每个请求指定的其他值,包括消息头(header)。 +事实上,你可以。如果你用`.create()`方法创建一个实例,Axios 会记住`baseURL`,以及你可能想为每个请求指定的其他值,包括消息头(header)。 ```js import axios from "axios"; @@ -378,11 +378,11 @@ export default function App() { ## How to Use the Async-Await Syntax with Axios -在JavaScript(包括React应用程序)中使用promises的一大好处是async-await语法。 +在 JavaScript(包括 React 应用程序)中使用 promises 的一大好处是 async-await 语法。 -Async-await允许你不使用`then`和`catch`回调函数的情况下写出更简洁的代码。另外,使用async-await的代码看起来很像同步代码,而且更容易理解。 +Async-await 允许你不使用`then`和`catch`回调函数的情况下写出更简洁的代码。另外,使用 async-await 的代码看起来很像同步代码,而且更容易理解。 -但你如何使用Axios的async-await语法呢? +但你如何使用 Axios 的 async-await 语法呢? 在下面的例子中,获取了帖子,但仍有一个按钮可以删除该帖子: @@ -425,23 +425,23 @@ export default function App() { 然而在`useEffect`中,有一个`async`函数,叫做`getPost`。 -让它成为`async`允许你使用`await`关键字来解决(resolve)GET请求,并在下一行将该数据设置为状态,而不需要`.then()`回调。 +让它成为`async`允许你使用`await`关键字来解决(resolve)GET 请求,并在下一行将该数据设置为状态,而不需要`.then()`回调。 注意,`getPost`函数在被创建后立即被调用。 -此外,`deletePost`函数现在是`async`,这是使用`await`关键字的要求,它可以解决(resolve)它返回的promise(每个Axios方法都会返回一个promise来解决(resolve))。 +此外,`deletePost`函数现在是`async`,这是使用`await`关键字的要求,它可以解决(resolve)它返回的 promise(每个 Axios 方法都会返回一个 promise 来解决(resolve))。 -在使用`await`关键字和DELETE请求后,用户会被提醒帖子被删除,并且帖子被设置为`null`。 +在使用`await`关键字和 DELETE 请求后,用户会被提醒帖子被删除,并且帖子被设置为`null`。 -正如你所看到的,async-await极大地简化了代码,你可以非常容易地将其用于Axios。 +正如你所看到的,async-await 极大地简化了代码,你可以非常容易地将其用于 Axios。 ## How to Create a Custom `useAxios` Hook -Async-await是一个简化代码的好方法,但你可以更进一步。 +Async-await 是一个简化代码的好方法,但你可以更进一步。 -你可以用Axios创建你自己的自定义Hook,作为一个可重用的函数执行同样的操作,而不是使用`useEffect`在组件挂载时获取数据。 +你可以用 Axios 创建你自己的自定义 Hook,作为一个可重用的函数执行同样的操作,而不是使用`useEffect`在组件挂载时获取数据。 -虽然你可以自己制作这个自定义Hook,但是有一个非常好的库可以给你一个自定义的`useAxios`钩子,叫做use-axios-client。 +虽然你可以自己制作这个自定义 Hook,但是有一个非常好的库可以给你一个自定义的`useAxios`钩子,叫做 use-axios-client。 首先,安装该软件包: @@ -449,9 +449,9 @@ Async-await是一个简化代码的好方法,但你可以更进一步。 npm install use-axios-client ``` -要使用Hook本身,请在组件的顶部从use-axios-client导入`useAxios`。 +要使用 Hook 本身,请在组件的顶部从 use-axios-client 导入`useAxios`。 -因为你不再需要`useEffect`,你可以删除React的导入。 +因为你不再需要`useEffect`,你可以删除 React 的导入。 ```js import { useAxios } from "use-axios-client"; @@ -473,27 +473,27 @@ export default function App() { } ``` -现在你可以在应用程序组件的顶部调用`useAxios`,传入你想要请求的URL,钩子会返回一个对象,其中包含你需要处理不同状态的所有值。`loading`, `error`和解决(resolved)的`data`。 +现在你可以在应用程序组件的顶部调用`useAxios`,传入你想要请求的 URL,钩子会返回一个对象,其中包含你需要处理不同状态的所有值。`loading`, `error`和解决(resolved)的`data`。 在执行这个请求的过程中,值`loading`将为真。如果有一个错误,你会想显示这个错误状态。否则,如果你有返回的数据,你可以在用户界面中显示它。 -像这样的自定义Hook的好处是,它确实减少了代码,并从整体上简化了代码 +像这样的自定义 Hook 的好处是,它确实减少了代码,并从整体上简化了代码 -如果你想用Axios获取更简单的数据,可以试试像这样的自定义`useAxios`钩子。 +如果你想用 Axios 获取更简单的数据,可以试试像这样的自定义`useAxios`钩子。 ## What's Next? -恭喜你!你现在知道如何使用一个最强大的HTTP客户端库来支持你的React应用了。你现在知道如何使用最强大的HTTP客户端库之一来支持你的React应用程序。 +恭喜你!你现在知道如何使用一个最强大的 HTTP 客户端库来支持你的 React 应用了。你现在知道如何使用最强大的 HTTP 客户端库之一来支持你的 React 应用程序。 我希望你从本指南中得到了很多。 -[记住,你可以将本指南下载为PDF格式的手册,以备将来参考。](https://reedbarger.com/resources/react-axios-2021) +[记住,你可以将本指南下载为 PDF 格式的手册,以备将来参考。](https://reedbarger.com/resources/react-axios-2021) ## Want Even More? Join The React Bootcamp -**[The React Bootcamp](http://bit.ly/join-react-bootcamp)** 这本书把你应该知道的关于学习React的所有知识,捆绑在一个综合包里,包括视频、手册,还有特别的奖金。 +**[The React Bootcamp](http://bit.ly/join-react-bootcamp)** 这本书把你应该知道的关于学习 React 的所有知识,捆绑在一个综合包里,包括视频、手册,还有特别的奖金。 -获得**已经成为React专家百名开发者**的内幕信息。他们已经找到自己的梦想工作,并掌控他们的未来。 +获得**已经成为 React 专家百名开发者**的内幕信息。他们已经找到自己的梦想工作,并掌控他们的未来。 [![The React Bootcamp](https://reedbarger.nyc3.digitaloceanspaces.com/react-bootcamp-banner.png)](http://bit.ly/join-react-bootcamp) _点击这里,当它开放时,将得到通知_ \ No newline at end of file diff --git a/chinese/articles/how-to-use-flux-in-react-example.md b/chinese/articles/how-to-use-flux-in-react-example.md index 6ffc6f05c..87fec7cbd 100644 --- a/chinese/articles/how-to-use-flux-in-react-example.md +++ b/chinese/articles/how-to-use-flux-in-react-example.md @@ -5,35 +5,35 @@ ![How to Use Flux to Manage State in ReactJS - Explained with an Example](https://www.freecodecamp.org/news/content/images/size/w2000/2020/04/Flux-4.png) -如果你最近开始使用ReactJS,那么你可能想知道如何在React中管理状态以便扩展应用程序。 +如果你最近开始使用 ReactJS,那么你可能想知道如何在 React 中管理状态以便扩展应用程序。 -许多公司就这个问题给出了自己的解决方案,而ReactJS的创始人Facebook给出的解决方案是[**Flux**](https://facebook.github.io/flux/)。 +许多公司就这个问题给出了自己的解决方案,而 ReactJS 的创始人 Facebook 给出的解决方案是[**Flux**](https://facebook.github.io/flux/)。 -如果你曾经研究过**AngularJS**或者**EmberJS**,你可能听说过**Redux**。ReactJS也有一个实现Redux的库。 +如果你曾经研究过**AngularJS**或者**EmberJS**,你可能听说过**Redux**。ReactJS 也有一个实现 Redux 的库。 -但是在学习Redux之前,我建议你先学习并理解Flux。我这样说是因为Redux是Fl​​ux的更高级版本。再清楚了Flux的概念之后,你可以学习redux并将其集成到您的应用程序中。 +但是在学习 Redux 之前,我建议你先学习并理解 Flux。我这样说是因为 Redux 是 Fl​​ux 的更高级版本。再清楚了 Flux 的概念之后,你可以学习 redux 并将其集成到您的应用程序中。 -## 什么是Flux? +## 什么是 Flux? -Flux采用**单向数据流模式**来解决状态管理的复杂性。记住它不是一个框架,而更像是一个解决状态管理问题的模式。 +Flux 采用**单向数据流模式**来解决状态管理的复杂性。记住它不是一个框架,而更像是一个解决状态管理问题的模式。 -你是否会好奇现有的MVC框架出了什么问题?假设你的客户端应用扩展之后,各种模型(model)和视图(view)之间的交互错综复杂,是不是会变得一团糟? +你是否会好奇现有的 MVC 框架出了什么问题?假设你的客户端应用扩展之后,各种模型(model)和视图(view)之间的交互错综复杂,是不是会变得一团糟? ![Screenshot-2020-04-16-at-6.38.14-PM](https://www.freecodecamp.org/news/content/images/2020/04/Screenshot-2020-04-16-at-6.38.14-PM.png) -备注:图片取自Facebook F8 Flux 大会 +备注:图片取自 Facebook F8 Flux 大会 -组件之间的关系变得复杂,导致扩展应用变得麻烦。Facebook曾面临同样的问题,所以他们搭建了**单向数据流**来解决这个问题。 +组件之间的关系变得复杂,导致扩展应用变得麻烦。Facebook 曾面临同样的问题,所以他们搭建了**单向数据流**来解决这个问题。 ![Flux-3](https://www.freecodecamp.org/news/content/images/2020/04/Flux-3.png) -备注:图片取自Facebook的Flux文档 +备注:图片取自 Facebook 的 Flux 文档 -如图所示,Flux包含许多组件,让我们逐一讲解这些组件。 +如图所示,Flux 包含许多组件,让我们逐一讲解这些组件。 -**视图(View):** 这个组件渲染UI。每当视图层上发生任何用户交互(如事件),都会触发渲染。同样,当数据层(store)通知视图层有变化的时候,视图重新渲染。例如,用户点击了**添加**按钮。 +**视图(View):** 这个组件渲染 UI。每当视图层上发生任何用户交互(如事件),都会触发渲染。同样,当数据层(store)通知视图层有变化的时候,视图重新渲染。例如,用户点击了**添加**按钮。 -**动作(Action):** 这部分处理所有的事件。事件由view组件传递。这一层通常被用于进行API调用。 一旦处理完毕,就使用派发器(dispatcher)派发任务。动作(action)可以是添加一个帖子、删除一个帖子等用户交互。 +**动作(Action):** 这部分处理所有的事件。事件由 view 组件传递。这一层通常被用于进行 API 调用。 一旦处理完毕,就使用派发器(dispatcher)派发任务。动作(action)可以是添加一个帖子、删除一个帖子等用户交互。 派发事件的通用负载(payload)结构为: @@ -47,44 +47,44 @@ Flux采用**单向数据流模式**来解决状态管理的复杂性。记住它 } ``` -`actionType`是强制键,dispatcher通过它传递更新到相应的store。通常使用常量来保存`actionType`的值,这样不会出现拼写错误。`data`包含了我们想从Action派发到Store的信息。这个键的名称可以为任意值。 +`actionType`是强制键,dispatcher 通过它传递更新到相应的 store。通常使用常量来保存`actionType`的值,这样不会出现拼写错误。`data`包含了我们想从 Action 派发到 Store 的信息。这个键的名称可以为任意值。 -**派发器(Dispatcher):** 这里是中央枢纽和单例注册表。dispatcher将负载由Action派发到Store,同时也要确保在派发的过程中没有产生[级联效应](https://baike.baidu.com/item/%E7%BA%A7%E8%81%94%E6%95%88%E5%BA%94/15623566)。 它确保数据层在完成处理和存储操作之前没有任何其他操作。 +**派发器(Dispatcher):** 这里是中央枢纽和单例注册表。dispatcher 将负载由 Action 派发到 Store,同时也要确保在派发的过程中没有产生[级联效应](https://baike.baidu.com/item/%E7%BA%A7%E8%81%94%E6%95%88%E5%BA%94/15623566)。 它确保数据层在完成处理和存储操作之前没有任何其他操作。 -可以把这个组件想象成一个系统的交通控制器,它将回调集中到一个清单,调用回调,并且广播由action传递过来的负载。 +可以把这个组件想象成一个系统的交通控制器,它将回调集中到一个清单,调用回调,并且广播由 action 传递过来的负载。 -由于这个组件的存在,数据流变得可以预测。每一个action都由注册在dispatcher的回调更新到对应的store。 +由于这个组件的存在,数据流变得可以预测。每一个 action 都由注册在 dispatcher 的回调更新到对应的 store。 -**数据(Store):** 这里包含了应用的状态,是这个模式中的数据层。不要把它类比为MVC中的模型(model)。一个应用可以有一个或者多个store。store通过注册在dispatcher的回调来更新数据。 +**数据(Store):** 这里包含了应用的状态,是这个模式中的数据层。不要把它类比为 MVC 中的模型(model)。一个应用可以有一个或者多个 store。store 通过注册在 dispatcher 的回调来更新数据。 -Node的EventEmitter(事件发射器)被用来更新store并广播更新到view。view从不直接更新应用程序的状态,它被更新是因为store的变化。 +Node 的 EventEmitter(事件发射器)被用来更新 store 并广播更新到 view。view 从不直接更新应用程序的状态,它被更新是因为 store 的变化。 -这是Flux模型中唯一可以更新数据的组件。store内的接口包括: +这是 Flux 模型中唯一可以更新数据的组件。store 内的接口包括: -1. **EventEmitter(事件发射器)**通知view,store的数据更新了。 +1. **EventEmitter(事件发射器)**通知 view,store 的数据更新了。 2. **addChangeListener**和**removeChangeListener**这类监听器被添加。 3. **emitChange**用于发射更改。 -假设上述的范式有多个store和view,这个模式和数据流还是保持不变。因为和MVC或者双向绑定不同的是,flux模式是单向模式,数据流是可以预见的。 这就提高了**数据的一致性**并且**更容易发现bug**。 +假设上述的范式有多个 store 和 view,这个模式和数据流还是保持不变。因为和 MVC 或者双向绑定不同的是,flux 模式是单向模式,数据流是可以预见的。 这就提高了**数据的一致性**并且**更容易发现 bug**。 ![Flux-Flow-3](https://www.freecodecamp.org/news/content/images/2020/04/Flux-Flow-3.png) -Flux数据流 +Flux 数据流 -因为**单向数据流**的特性,Flux有以下优点: +因为**单向数据流**的特性,Flux 有以下优点: 1. 代码更加简洁且便于理解。 2. 更容易使用单元测试进行测试。 3. 应用可以被扩展。 4. 可预见的数据流。 -> _**注意:** Flux唯一的缺点是我们需要编写一些样板。除去样板,在往应用添加新的组件的时候,我们只需要编写一点点代码。_ +> _**注意:** Flux 唯一的缺点是我们需要编写一些样板。除去样板,在往应用添加新的组件的时候,我们只需要编写一点点代码。_ ## 应用模板 -我们将通过学习创建一个博客页面来学习如何在ReactJS中实现flux。我们将在页面中展现所有文章。应用模板参见这个[commit](https://github.com/Sharvin26/DummyBlog/tree/0d56987b2d461b794e7841302c9337eda1ad0725)。我们将在这个模板的基础上结合Flux。 +我们将通过学习创建一个博客页面来学习如何在 ReactJS 中实现 flux。我们将在页面中展现所有文章。应用模板参见这个[commit](https://github.com/Sharvin26/DummyBlog/tree/0d56987b2d461b794e7841302c9337eda1ad0725)。我们将在这个模板的基础上结合 Flux。 -复制commit的代码: +复制 commit 的代码: ```shell git clone https://github.com/Sharvin26/DummyBlog.git @@ -106,7 +106,7 @@ npm install react-router-dom@5.0.0 bootstrap@4.3.1 虚拟博客 -我们仅通过实现**GET**方法来了解Flux的细节。完成后你会发现**POST**、**EDIT**和**DELETE**的实现过程相同。 +我们仅通过实现**GET**方法来了解 Flux 的细节。完成后你会发现**POST**、**EDIT**和**DELETE**的实现过程相同。 目录结构如下: @@ -132,27 +132,27 @@ npm install react-router-dom@5.0.0 bootstrap@4.3.1 | +-- db.json ``` -> **Note:** 我们添加了`db.json`文件,这是一个虚拟的数据文件。因为我们专注在Flux的讲解而不是如何搭建API,所以我们将从这个文件中获取数据。 +> **Note:** 我们添加了`db.json`文件,这是一个虚拟的数据文件。因为我们专注在 Flux 的讲解而不是如何搭建 API,所以我们将从这个文件中获取数据。 -我们的应用的基础组件是`index.js`。在这个文件中我们渲染了`App.js`,它位于public文件夹内的`index.html`内部,采用了**render**和 **getElementById**方法渲染 `App.js`用来配置路由。 +我们的应用的基础组件是`index.js`。在这个文件中我们渲染了`App.js`,它位于 public 文件夹内的`index.html`内部,采用了**render**和 **getElementById**方法渲染 `App.js`用来配置路由。 同时,我们还添加了**NavBar**组件,让所有组件都可以访问到这个组件。 在**pages**目录中有三个文件=> `Home.js`、 `Posts.js`和`NotFound.js`。 `Home.js`用于展示`Home`组件,如果用户登陆到一个不存在的路由,会渲染`NotFound.js`。 -`Posts.js`是一个父组件,从`db.json`文件获取数据,并将数据传递给`PostLists.js`,它位于**components**目录下。它是一个虚拟组件,仅用于渲染UI。从父组件(`Posts.js`)由props的形式获取数据,并以卡片的形式展示数据。 +`Posts.js`是一个父组件,从`db.json`文件获取数据,并将数据传递给`PostLists.js`,它位于**components**目录下。它是一个虚拟组件,仅用于渲染 UI。从父组件(`Posts.js`)由 props 的形式获取数据,并以卡片的形式展示数据。 -现在我们已经知道了应用的结构,让我们在这个基础上集成Flux。 +现在我们已经知道了应用的结构,让我们在这个基础上集成 Flux。 -## 集成Flux +## 集成 Flux -使用以下命令行安装Flux: +使用以下命令行安装 Flux: ```shell npm install flux@3.1.3 ``` -为了集成Flux,需要把我们的应用分层四个小部分: +为了集成 Flux,需要把我们的应用分层四个小部分: 1. 派发器(Dispatcher) 2. 动作(Actions) @@ -165,7 +165,7 @@ npm install flux@3.1.3 首先,我们在**src**目录下创建两个新的文件夹**actions**和**stores**。然后在同一个目录下创建`appDispatcher.js`。 -**注意:**因为不是ReactJS的组件,现在开始所有和Flux相关的文件命名都为**驼峰式**。 +**注意:**因为不是 ReactJS 的组件,现在开始所有和 Flux 相关的文件命名都为**驼峰式**。 进入`appDispatcher.js`,并复制以下代码: @@ -175,11 +175,11 @@ const dispatcher = new Dispatcher(); export default dispatcher; ``` -这里我们导入了flux库里的Dispatcher,创建了一个新的对象,并且导出这个对象,供action模块使用。 +这里我们导入了 flux 库里的 Dispatcher,创建了一个新的对象,并且导出这个对象,供 action 模块使用。 ### 行为(Actions) -进入**actions**目录,创建`actionTypes.js`和`postActions.js`。 在`actionTypes.js`中,我们将定义被`postActions.js`和store模块引用的常量。 +进入**actions**目录,创建`actionTypes.js`和`postActions.js`。 在`actionTypes.js`中,我们将定义被`postActions.js`和 store 模块引用的常量。 定义常量的原因是我们不想出现任何拼写错误,并不是强制要求这样做,但是这是一个推荐的办法。 @@ -192,7 +192,7 @@ export default { ``` -在`postActions.js`内,我们调用`db.json`数据,并使用dispatcher来派发。 +在`postActions.js`内,我们调用`db.json`数据,并使用 dispatcher 来派发。 ```js //postActions.js @@ -209,7 +209,7 @@ export function getPosts() { } ``` -在上述代码中,我们引入了dispatcher对象,actionTypes常量和data。我们使用dispatch方法将数据发送到store。在我们的例子中的数据将以以下格式被发送: +在上述代码中,我们引入了 dispatcher 对象,actionTypes 常量和 data。我们使用 dispatch 方法将数据发送到 store。在我们的例子中的数据将以以下格式被发送: ```json { @@ -233,9 +233,9 @@ export function getPosts() { ### 数据(Stores) -现在我们需要创建作为**数组层**来存储文章的store。它内部包含一个**事件监听器**来告诉view发生了变化,并且会使用dispatcher的**register**获取action的数据。 +现在我们需要创建作为**数组层**来存储文章的 store。它内部包含一个**事件监听器**来告诉 view 发生了变化,并且会使用 dispatcher 的**register**获取 action 的数据。 -导航到store目录,创建一个文件名为`postStore.js`的文件。首先我们导入Events包的**EventEmitter**,这个是NodeJS默认的方法,同时我们还将导入dispatcher对象和actionTypes常量文件。 +导航到 store 目录,创建一个文件名为`postStore.js`的文件。首先我们导入 Events 包的**EventEmitter**,这个是 NodeJS 默认的方法,同时我们还将导入 dispatcher 对象和 actionTypes 常量文件。 ```js import { EventEmitter } from "events"; @@ -243,7 +243,7 @@ import dispatcher from "../appDispatcher"; import actionTypes from "../actions/actionTypes"; ``` -我们将为**change**事件创建一个常量,还要创建一个变量来存储dispatcher传递过来的文章。 +我们将为**change**事件创建一个常量,还要创建一个变量来存储 dispatcher 传递过来的文章。 ```js const CHANGE_EVENT = "change"; @@ -252,11 +252,11 @@ let _posts = []; 现在我们编写一个由**EventEmitter**扩展而来的类,并在这个类中声明以下方法: -`addChangeListener`: 使用NodeJS的**EventEmitter.on**方法,添加一个change监听器接受回调函数作为参数。 +`addChangeListener`: 使用 NodeJS 的**EventEmitter.on**方法,添加一个 change 监听器接受回调函数作为参数。 -`removeChangeListener`: 使用NodeJS的 **EventEmitter.removeListener**,当你再不需要监听一个事件,就可以使用这个方法。 +`removeChangeListener`: 使用 NodeJS 的 **EventEmitter.removeListener**,当你再不需要监听一个事件,就可以使用这个方法。 -`emitChange`: 使用NodeJS的**EventEmitter.emit**,每当变化发生,就发出这个变化。 +`emitChange`: 使用 NodeJS 的**EventEmitter.emit**,每当变化发生,就发出这个变化。 这个类还将有一个方法叫做`getPosts`,会返回我们在类上面声明的`_posts`变量。 @@ -282,15 +282,15 @@ class PostStore extends EventEmitter { } ``` -现在我们为PostStore类创建`store`对象,并导出这个对象供view使用: +现在我们为 PostStore 类创建`store`对象,并导出这个对象供 view 使用: ```js const store = new PostStore(); ``` -接下来,我们使用dispatcher的**register**方法来接受Actions组件的负载。 +接下来,我们使用 dispatcher 的**register**方法来接受 Actions 组件的负载。 -我们使用`actionTypes`值来判断是什么action,以及需要处理的对应的数据。代码如下: +我们使用`actionTypes`值来判断是什么 action,以及需要处理的对应的数据。代码如下: ```js dispatcher.register((action) => { @@ -312,7 +312,7 @@ export default store; ### 视图(View) -现在我们更新view,一旦文章页面加载,并且从postStore接受到负载就将事件发送到`postActions`。进入**pages**目录的`Posts.js`,你会看到**useEffect**方法内部的代码如下: +现在我们更新 view,一旦文章页面加载,并且从 postStore 接受到负载就将事件发送到`postActions`。进入**pages**目录的`Posts.js`,你会看到**useEffect**方法内部的代码如下: ```js useEffect(() => { @@ -320,9 +320,9 @@ useEffect(() => { }, []); ``` -我们将改变useEffect读取和更新代码的方法,首先我们将使用来自postStore类的`addChangeListener`方法,并传入一个`onChange`回调。 将`posts`的状态设置为`postStore.js`文件中的`getPosts`方法的返回值。 +我们将改变 useEffect 读取和更新代码的方法,首先我们将使用来自 postStore 类的`addChangeListener`方法,并传入一个`onChange`回调。 将`posts`的状态设置为`postStore.js`文件中的`getPosts`方法的返回值。 -一开始store会返回空数组,因为没有可用数据。我们调用`postActions.js`内的`_getPosts_`方法。这个方法会读取和传递数据。然后store发送数据,`addChangeListener`监听到变化,并使用`onChange`回调来更新`posts`的值。 +一开始 store 会返回空数组,因为没有可用数据。我们调用`postActions.js`内的`_getPosts_`方法。这个方法会读取和传递数据。然后 store 发送数据,`addChangeListener`监听到变化,并使用`onChange`回调来更新`posts`的值。 这听起来有点让人困惑,没关系下面的流程图会让一切变得清晰。 @@ -359,12 +359,12 @@ function PostPage() { export default PostPage; ``` -在这段代码中我们删除了原有的导入,并且在回调函数中使用`setPosts`取代了使用useEffect方法获取数据。`return () => postStore.removeChangeListener(onChange);`用来在离开页面时删除监听器。 +在这段代码中我们删除了原有的导入,并且在回调函数中使用`setPosts`取代了使用 useEffect 方法获取数据。`return () => postStore.removeChangeListener(onChange);`用来在离开页面时删除监听器。 -然后我们的博客页面就可以完全运作了。和之前的唯一区别是,之前我们使用**useEffect**获取数据,而现在我们是用action来读取数据,用store来存储数据,以及将数据传输到需要的组件中。 +然后我们的博客页面就可以完全运作了。和之前的唯一区别是,之前我们使用**useEffect**获取数据,而现在我们是用 action 来读取数据,用 store 来存储数据,以及将数据传输到需要的组件中。 -使用实际API时,您会发现应用程序从API加载一次数据并将其存储在store中。当我们重新访问同一页面时,你会发现不需要再次调用API。你可以在 Chrome开发者控制台的源选项卡进行监控。 +使用实际 API 时,您会发现应用程序从 API 加载一次数据并将其存储在 store 中。当我们重新访问同一页面时,你会发现不需要再次调用 API。你可以在 Chrome 开发者控制台的源选项卡进行监控。 -介绍完毕。希望这篇教程可以帮助你对Flux有一个清晰的认识,以及日后你可以将其应用到你的项目中。 +介绍完毕。希望这篇教程可以帮助你对 Flux 有一个清晰的认识,以及日后你可以将其应用到你的项目中。 > 欢迎通过[Twitter](https://twitter.com/sharvinshah26)和[Github](https://github.com/Sharvin26)联系我。 diff --git a/chinese/articles/how-to-use-github-actions-to-deploy-a-next-js-website-to-aws-s3.md b/chinese/articles/how-to-use-github-actions-to-deploy-a-next-js-website-to-aws-s3.md index e0ad31718..1599fe036 100644 --- a/chinese/articles/how-to-use-github-actions-to-deploy-a-next-js-website-to-aws-s3.md +++ b/chinese/articles/how-to-use-github-actions-to-deploy-a-next-js-website-to-aws-s3.md @@ -5,51 +5,51 @@ ![如何使用Github Actions将Next.js网站部署到AWS S3](https://www.freecodecamp.org/news/content/images/size/w2000/2020/10/actions-s3.jpg) -Next.js和静态 web应用的好处是,你将它们储存在对象储存里,几乎可以在任何地方运行, 比如AWS S3。但是每次手动更新这些文件可能是一件痛苦的事. +Next.js 和静态 web 应用的好处是,你将它们储存在对象储存里,几乎可以在任何地方运行, 比如 AWS S3。但是每次手动更新这些文件可能是一件痛苦的事. -我们如何用Github Actions来自动持续部署我们的应用程序到S3? +我们如何用 Github Actions 来自动持续部署我们的应用程序到 S3? -- [什么是GitHub Actions?](#什么是Gtihub-Actions) +- [什么是 GitHub Actions?](#什么是Gtihub-Actions) - [什么是持续部署?](#什么使持续部署) - [我们怎么去构建?](#我们怎样去构建) -- [第0步: 在Gihub上建立一个新的Next.js项目](#第0步:在Github上创建一个新的Next.js项目) -- [第1步: 手工创建一个用于部署next.js项目的新S3桶 ](#第1步:手动创建新的S3桶,并将Next.js项目部署到上面) -- [第2步: 创建一个新的Github Action工作流来自动化构建一个Next.js项目](#第三2步:创建一个新的Github-Action工作流来自动构建一个Next.js项目) -- [第3步: 配置一个Github Action,部署静态网站到S3上](#第3步:配置一个Github-Action,将静态网站部署到S3上) +- [第 0 步: 在 Gihub 上建立一个新的 Next.js 项目](#第0步:在Github上创建一个新的Next.js项目) +- [第 1 步: 手工创建一个用于部署 next.js 项目的新 S3 桶](#第1步:手动创建新的S3桶,并将Next.js项目部署到上面) +- [第 2 步: 创建一个新的 Github Action 工作流来自动化构建一个 Next.js 项目](#第三2步:创建一个新的Github-Action工作流来自动构建一个Next.js项目) +- [第 3 步: 配置一个 Github Action,部署静态网站到 S3 上](#第3步:配置一个Github-Action,将静态网站部署到S3上) -## 什么是Gtihub Actions? +## 什么是 Gtihub Actions? -Github Actions是Github的一项免费服务,它允许我们用代码实现任务自动化。 +Github Actions 是 Github 的一项免费服务,它允许我们用代码实现任务自动化。 -我[之前写个](/news/what-are-github-actions-and-how-can-you-automate-tests-and-slack-notifications/) 我们任何用它来自动化任务,比如在运行代码中的测试,并向Slack发送通知。 +我[之前写个](/news/what-are-github-actions-and-how-can-you-automate-tests-and-slack-notifications/) 我们任何用它来自动化任务,比如在运行代码中的测试,并向 Slack 发送通知。 它们提供一种灵活的方式,在我们现有的工作流基础上为自动化运行代码。这提供了很多的可能性,比如部署我们的网站。 ## 什么是 AWS S3? -[S3](https://aws.amazon.com/s3/) (简单存储服务)是AWS的一个对象存储服务。它允许你在云上轻松存储文件,使它们在世界各地都可以使用。 +[S3](https://aws.amazon.com/s3/) (简单存储服务)是 AWS 的一个对象存储服务。它允许你在云上轻松存储文件,使它们在世界各地都可以使用。 -它还允许你将这些文件作为一个网站使用。因为我们可以把HTML文件作为一个对象上传,我们也可以配置S3,让一个HTTP请求访问该文件。 这意味着,我们可以[在S3中直接托管整个网站](/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/). +它还允许你将这些文件作为一个网站使用。因为我们可以把 HTML 文件作为一个对象上传,我们也可以配置 S3,让一个 HTTP 请求访问该文件。 这意味着,我们可以[在 S3 中直接托管整个网站](/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/). ## 什么使持续部署? 持续部署(Continuous Deployment),通常是指将代码处在可发布的状态,自动化部署代码,缩短部署部署时间。 -特别是在我们的情况中,我们将配置我们的项目,以便任何有更新的更新被推送或者合并到git主分支,我们的网站将被部署。 +特别是在我们的情况中,我们将配置我们的项目,以便任何有更新的更新被推送或者合并到 git 主分支,我们的网站将被部署。 ## 我们怎样去构建? -我们首先要使用默认的Next.js起始模板初始化一个简单的[Next.js](https://nextjs.org/)应用,并配置将其编译成静态文件。 +我们首先要使用默认的 Next.js 起始模板初始化一个简单的[Next.js](https://nextjs.org/)应用,并配置将其编译成静态文件。 -如果你不向创建一个Next.js项目,你甚至用一个简单的HTML文件跟着做,并不运行任何构建命令。但Next.js是构建动态网站应用的一种现代化方式,所以我将从这里开始。 +如果你不向创建一个 Next.js 项目,你甚至用一个简单的 HTML 文件跟着做,并不运行任何构建命令。但 Next.js 是构建动态网站应用的一种现代化方式,所以我将从这里开始。 -随着我们的网站文件准备就绪,我们将在AWS中创建和配置一个S3桶,在S3桶上托管我们的网站。 +随着我们的网站文件准备就绪,我们将在 AWS 中创建和配置一个 S3 桶,在 S3 桶上托管我们的网站。 -最后,我们将创建一个新的Github Action工作流,当我们的主分支(`main`)发生新的变化时,它将自动更新S3中网站文件。 +最后,我们将创建一个新的 Github Action 工作流,当我们的主分支(`main`)发生新的变化时,它将自动更新 S3 中网站文件。 -## 第0步:在Github上创建一个新的Next.js项目 +## 第 0 步:在 Github 上创建一个新的 Next.js 项目 -我们将从Next.js的默认模板开始。 +我们将从 Next.js 的默认模板开始。 在创建你像创建项目的目录后,运行: @@ -82,7 +82,7 @@ New Next.js App "build": "next build && next export", ``` -这样做的目的时告诉Next将网站导出为静态文件,我们将它用于托管网站。 +这样做的目的时告诉 Next 将网站导出为静态文件,我们将它用于托管网站。 ``` yarn build @@ -96,67 +96,67 @@ npm run build Next.js 的静态输出 -最后,我们要把它推送到github上。 +最后,我们要把它推送到 github 上。 -在你的Github账号中 [创建一个新的仓库](https://docs.github.com/en/free-pro-team@latest/github/getting-started-with-github/create-a-repo)。然后会提供如何 [添加现有项目](https://docs.github.com/en/free-pro-team@latest/github/importing-your-projects-to-github/adding-an-existing-project-to-github-using-the-command-line)到该仓库的说明。 +在你的 Github 账号中 [创建一个新的仓库](https://docs.github.com/en/free-pro-team@latest/github/getting-started-with-github/create-a-repo)。然后会提供如何 [添加现有项目](https://docs.github.com/en/free-pro-team@latest/github/importing-your-projects-to-github/adding-an-existing-project-to-github-using-the-command-line)到该仓库的说明。 -一旦把你的项目推送到Github上,我们就做好了建立我的新网站项目的准备。 +一旦把你的项目推送到 Github 上,我们就做好了建立我的新网站项目的准备。 ![](https://www.freecodecamp.org/news/content/images/2020/10/project-on-github.jpg) -Github中的新repo +Github 中的新 repo 有下面的提交内容 -- [添加初始Next.js项目](https://github.com/colbyfayock/my-static-website/commit/ca9e4bca3c37fbd8553b0b183890c32836c35296) 通过 [创建Next引用](https://nextjs.org/docs/api-reference/create-next-app) -- [配置Next.js导出](https://github.com/colbyfayock/my-static-website/commit/7907f4a0fac5f0aed2922202c5f0070dfc055f83) +- [添加初始 Next.js 项目](https://github.com/colbyfayock/my-static-website/commit/ca9e4bca3c37fbd8553b0b183890c32836c35296) 通过 [创建 Next 引用](https://nextjs.org/docs/api-reference/create-next-app) +- [配置 Next.js 导出](https://github.com/colbyfayock/my-static-website/commit/7907f4a0fac5f0aed2922202c5f0070dfc055f83) -## 第1步: 手动创建新的S3桶,并将Next.js项目部署到上面。 +## 第 1 步: 手动创建新的 S3 桶,并将 Next.js 项目部署到上面 -要开始使用我们的新S3桶,首先登录你的AWS账号,并进入到S3服务。 +要开始使用我们的新 S3 桶,首先登录你的 AWS 账号,并进入到 S3 服务。 ![](https://www.freecodecamp.org/news/content/images/2020/10/aws-s3-console.jpg) 发现没有桶 -我们要创建一个新桶,使用我们选择的名字命名,用于我们网址托管的S3,我们还要配置我们的S3桶,使其能够托管一个网站。 +我们要创建一个新桶,使用我们选择的名字命名,用于我们网址托管的 S3,我们还要配置我们的 S3 桶,使其能够托管一个网站。 -_注意: 本教程步会指导你如何在S3上托管网站,但是你可以查看我的另一个教程,该教程将一步步地 [指导你在S3上托管网站](/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/)。_ +_注意: 本教程步会指导你如何在 S3 上托管网站,但是你可以查看我的另一个教程,该教程将一步步地 [指导你在 S3 上托管网站](/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/)。_ ![](https://www.freecodecamp.org/news/content/images/2020/10/s3-bucket-website-hosting.jpg) -静态网站在AWS S3上托管 +静态网站在 AWS S3 上托管 -当我们把S3桶配置成一个网站,我们就可以回到Next.js项目文件夹,运行我们的构建命令,然后把`out`文件夹中的所有文件上传到我们的新建的S3桶。 +当我们把 S3 桶配置成一个网站,我们就可以回到 Next.js 项目文件夹,运行我们的构建命令,然后把`out`文件夹中的所有文件上传到我们的新建的 S3 桶。 ![](https://www.freecodecamp.org/news/content/images/2020/10/website-files-in-s3.jpg) -S3桶上的静态应用 +S3 桶上的静态应用 -当这些文件被上传,并且我们已经为网站托管配置了S3桶,我们现在应该能看到我们的项目在网络上线运行! +当这些文件被上传,并且我们已经为网站托管配置了 S3 桶,我们现在应该能看到我们的项目在网络上线运行! ![](https://www.freecodecamp.org/news/content/images/2020/10/nextjs-s3-website.jpg) -AWS S3托管Next.js应用程序 +AWS S3 托管 Next.js 应用程序 -## 第2步: 创建一个新的Github Action工作流来自动构建一个Next.js项目 +## 第 2 步: 创建一个新的 Github Action 工作流来自动构建一个 Next.js 项目 首先,我们需要创建一个新的工作流程(workflow)。 -如果你熟悉Github Actions,你可以手动创建一个,单我们将通过用户界面快速创建一个。 +如果你熟悉 Github Actions,你可以手动创建一个,单我们将通过用户界面快速创建一个。 -进入Github的仓库中的`Action`标签,点击`set up a workflow yourself`,来自行设置工作流。 +进入 Github 的仓库中的`Action`标签,点击`set up a workflow yourself`,来自行设置工作流。 ![](https://www.freecodecamp.org/news/content/images/2020/10/github-actions-new-workflow.jpg) -新的Github Action工作流 +新的 Github Action 工作流 -Github提供了一个模板,我们可以在工作流程中使用,不过我们要做一些修改。 +Github 提供了一个模板,我们可以在工作流程中使用,不过我们要做一些修改。 让我们做以下工作。 -- 可选: 将文件重名为deploy.yml -- 可选: 将workflow重名为CD (因为它与CI不同) +- 可选: 将文件重名为 deploy.yml +- 可选: 将 workflow 重名为 CD (因为它与 CI 不同) - 可选: 删除所有的注释,使其更容易阅读 - 删除`on` 属性中的`pull_request` - 删除所有的 `steps` 除了`uses: actions/checkout@v2` @@ -177,13 +177,13 @@ jobs: - uses: actions/checkout@v2 ``` -仅仅这段代码会触发一个流程,会启动一个新的Ubuntu实例,并在Github上有新的改动推送到主分支后,拉取代码到Ubuntu上。 +仅仅这段代码会触发一个流程,会启动一个新的 Ubuntu 实例,并在 Github 上有新的改动推送到主分支后,拉取代码到 Ubuntu 上。 -接下来, 当我们获取我们的代码后,我们要构建它。然后将输出文件同步到S3。 +接下来, 当我们获取我们的代码后,我们要构建它。然后将输出文件同步到 S3。 -这一步将不同,取决于你的项目使用yarn还是npm。 +这一步将不同,取决于你的项目使用 yarn 还是 npm。 -如果你使用yarn,在 `steps`定义下,添加以下内容。 +如果你使用 yarn,在 `steps`定义下,添加以下内容。 ```yaml - uses: actions/setup-node@v1 @@ -194,7 +194,7 @@ jobs: - run: yarn build ``` -如果是使用npm,添加以下内容: +如果是使用 npm,添加以下内容: ```yaml - uses: actions/setup-node@v1 @@ -206,38 +206,38 @@ jobs: 在这两个步骤之间,我们要做的是: -- 设置 node: 这是为了我们能够使用npm 和node 来安装和运行的脚本 -- 安装Yarn (仅对使用Yarn): 如果我们使用Yarn,我们将为其安装全局依赖,以便我们使用它 +- 设置 node: 这是为了我们能够使用 npm 和 node 来安装和运行的脚本 +- 安装 Yarn (仅对使用 Yarn): 如果我们使用 Yarn,我们将为其安装全局依赖,以便我们使用它 - 安装依赖: 我们安装我们的依赖,我们使用一个特定命令,确保我们使用`lock`文件,以避免任何意外的软件包升级 -- 构建: 最后, 我们运行我们的构建命令,将我们的Next.js项目编译到`out`目录中。 +- 构建: 最后, 我们运行我们的构建命令,将我们的 Next.js 项目编译到`out`目录中。 -现在我们可以将该该文件直接提交到我们的`main`分支,这触发我们的workflow的运行,我们可以子啊`Actions`标签里看到。 +现在我们可以将该该文件直接提交到我们的`main`分支,这触发我们的 workflow 的运行,我们可以子啊`Actions`标签里看到。 ![](https://www.freecodecamp.org/news/content/images/2020/10/github-action-run-workflow.jpg) -在Github Actions中新的workflow +在 Github Actions 中新的 workflow 为了看到它的运行状态,我们进入运行的`workflow`,选择我们的`workflow`,看到所有我们的项目包含的步骤。 ![](https://www.freecodecamp.org/news/content/images/2020/10/github-action-successful-build.jpg) -Github Action成功构建日志 +Github Action 成功构建日志 [随着提交!](https://github.com/colbyfayock/my-static-website/commit/59e0a5158d6afbf54793d826d05455f5205c98fb) -## 第3步: 配置一个Github Action,将静态网站部署到S3上 +## 第 3 步: 配置一个 Github Action,将静态网站部署到 S3 上 -现在我们正在自动构建我们的项目,我们想在S3中自动更新我们的网站。 +现在我们正在自动构建我们的项目,我们想在 S3 中自动更新我们的网站。 - 为了做到这一点,我们将使用Github Action [aws-actions/configure-aws-credentials(配置aws凭证)](https://github.com/aws-actions/configure-aws-credentials) 和 the AWS CLI(AWS提供的命令行)。 + 为了做到这一点,我们将使用 Github Action [aws-actions/configure-aws-credentials(配置 aws 凭证)](https://github.com/aws-actions/configure-aws-credentials) 和 the AWS CLI(AWS 提供的命令行)。 -我们使用Github Action 将接收我们的AWS凭证和配置,并在workflow的生命周期内使用。 +我们使用 Github Action 将接收我们的 AWS 凭证和配置,并在 workflow 的生命周期内使用。 -目前,Github Action中的Ubuntu实例允许使用AWS CLI,因为它包含在其中。因此,我们将能够在workflow中使用CLI命令。 +目前,Github Action 中的 Ubuntu 实例允许使用 AWS CLI,因为它包含在其中。因此,我们将能够在 workflow 中使用 CLI 命令。 -另外,我们也可以使用[S3 Sync action](https://github.com/jakejarvis/s3-sync-action)。但是通过使用AWS CLI,我们可以获得更多的灵活性来定制我们的设置,我们可以使用它来获得额外的CLI命令,一般来说,熟悉AWS CLI也是不错的。 +另外,我们也可以使用[S3 Sync action](https://github.com/jakejarvis/s3-sync-action)。但是通过使用 AWS CLI,我们可以获得更多的灵活性来定制我们的设置,我们可以使用它来获得额外的 CLI 命令,一般来说,熟悉 AWS CLI 也是不错的。 -为了开始,让我们在workflow添加以下片段作为附加步骤。 +为了开始,让我们在 workflow 添加以下片段作为附加步骤。 ```yaml - uses: aws-actions/configure-aws-credentials@v1 @@ -247,31 +247,31 @@ Github Action成功构建日志 aws-region: us-east-1 ``` -上面要做的是使用AWS凭证配置action,根据我们的设置来设置我们的AWS的Access Key和Secret Key还有region(区域)。 +上面要做的是使用 AWS 凭证配置 action,根据我们的设置来设置我们的 AWS 的 Access Key 和 Secret Key 还有 region(区域)。 -AWS Region可以自定义为你通常使用的AWS账号的任何区域,我在美国东北部,所以我设置为`us-east-1`。 +AWS Region 可以自定义为你通常使用的 AWS 账号的任何区域,我在美国东北部,所以我设置为`us-east-1`。 -Access Key和Secret Key是你需要你的AWS账号生成的凭证。我们的代码设置方式是,我们将这些值存储在Github Secrets里,要防止这些密钥被泄。当action运行时,Github会将这些值改为星星(`***`),这样人们就无法访问这些密钥。 +Access Key 和 Secret Key 是你需要你的 AWS 账号生成的凭证。我们的代码设置方式是,我们将这些值存储在 Github Secrets 里,要防止这些密钥被泄。当 action 运行时,Github 会将这些值改为星星(`***`),这样人们就无法访问这些密钥。 -为了设置这些secrets,我们首先要在AWS生成 `Access Keys`。 +为了设置这些 secrets,我们首先要在 AWS 生成 `Access Keys`。 -进入了AWS控制台。在用户菜单下,选择 **My Security Credentials**,然后选择 **Create access key**。 +进入了 AWS 控制台。在用户菜单下,选择 **My Security Credentials**,然后选择 **Create access key**。 ![](https://www.freecodecamp.org/news/content/images/2020/10/aws-console-create-access-key.jpg) -在AWS创建一个 `Access Key` +在 AWS 创建一个 `Access Key` -这会生成两个值 **Access key ID** 和**Secret access key**。必须保存好这些值,因为你将无法再次访问`Secret key ID `。 +这会生成两个值 **Access key ID** 和**Secret access key**。必须保存好这些值,因为你将无法再次访问`Secret key ID`。 ![](https://www.freecodecamp.org/news/content/images/2020/10/aws-secret-access-keys.jpg) -在AWS中寻找 `Secret Key` 和 `Access Key` +在 AWS 中寻找 `Secret Key` 和 `Access Key` -_注意: 记住不要再你的代码中包含`Access Key`和`Secret Key`。这可能导致有人破坏你的AWS凭证。_ +_注意: 记住不要再你的代码中包含`Access Key`和`Secret Key`。这可能导致有人破坏你的 AWS 凭证。_ -下一步, 再Github repo中, 进入到 Settings -> Secrets, 然后选择 `New secret`。 +下一步, 再 Github repo 中, 进入到 Settings -> Secrets, 然后选择 `New secret`。 -在这里,我们要使用AWS keys添加到下面的secrets: +在这里,我们要使用 AWS keys 添加到下面的 secrets: - AWS\_ACCESS\_KEY\_ID: your AWS Access key ID - AWS\_SECRET\_ACCESS\_KEY: your AWS Secret key @@ -280,32 +280,32 @@ _注意: 记住不要再你的代码中包含`Access Key`和`Secret Key`。这 ![](https://www.freecodecamp.org/news/content/images/2020/10/github-secrets-access-keys.jpg) -在Github中创建`Secrets` +在 Github 中创建`Secrets` -现在我们已经配置好了我们的凭证,我们应该为运行命令,将我们的项目同步到S3,做好准备。 +现在我们已经配置好了我们的凭证,我们应该为运行命令,将我们的项目同步到 S3,做好准备。 -在Github Action,添加以下步骤: +在 Github Action,添加以下步骤: ```yaml - run: aws s3 sync ./out s3://[bucket-name] ``` -_注意: 请确保`[bucket-name]` 替换为你的S3桶的名称。_ +_注意: 请确保`[bucket-name]` 替换为你的 S3 桶的名称。_ -这个命令会触发与我们的S3桶的同步(sync),使用`out`目录的文件,也就是我们项目构建的地方。 +这个命令会触发与我们的 S3 桶的同步(sync),使用`out`目录的文件,也就是我们项目构建的地方。 -现在,如果我们提交我们的修改,我们可以看到,一旦提交到`main`分支,我们的actions会自动触发,我们构建我们的项目并同步到S3! +现在,如果我们提交我们的修改,我们可以看到,一旦提交到`main`分支,我们的 actions 会自动触发,我们构建我们的项目并同步到 S3! ![](https://www.freecodecamp.org/news/content/images/2020/10/github-action-sync-s3-bucket.jpg) -成功通过GitHub Action workflow 同步到AWS S3 +成功通过 GitHub Action workflow 同步到 AWS S3 -_注意: 请确保在设置这个action之前,你已经将S3桶配置为网站托管(包括解除S3桶权限) --否则这个action可能失败。_ +_注意: 请确保在设置这个 action 之前,你已经将 S3 桶配置为网站托管(包括解除 S3 桶权限) --否则这个 action 可能失败。_ 在这一点上,我们的项目可能看起来是一样的,因为我们对代码进行任何修改。 ![](https://www.freecodecamp.org/news/content/images/2020/10/nextjs-s3-website.jpg) -AWS S3的Next.js应用程序 +AWS S3 的 Next.js 应用程序 但如果你做了一个代码修改,比如在`pages/index.js`中改变主页的标题,并提交该修改: @@ -315,50 +315,50 @@ AWS S3的Next.js应用程序 </h1> ``` -我们可以看到,我们的修改触发了workflow的启动: +我们可以看到,我们的修改触发了 workflow 的启动: ![](https://www.freecodecamp.org/news/content/images/2020/10/github-action-commit-workflow.jpg) -新的Github Action workflow的触发来自代码改变 +新的 Github Action workflow 的触发来自代码改变 -一旦我们的workflow完成,我们可以看到我们的内容已经在我们的网站上自动更新。 +一旦我们的 workflow 完成,我们可以看到我们的内容已经在我们的网站上自动更新。 ![](https://www.freecodecamp.org/news/content/images/2020/10/updated-nextjs-site-title.jpg) -AWS S3托管的应用程序,代码已经更新 +AWS S3 托管的应用程序,代码已经更新 随着内容的提交 -- [添加ASW的配置和S3 sync命令](https://github.com/colbyfayock/my-static-website/commit/f891412b827aca4b06e9bf3de8e4e5b4c5704fc8) -- [测试workflow的标题的更新](https://github.com/colbyfayock/my-static-website/commit/bb9b981416645e35c6d3442e02d6b61f2ba032d2) +- [添加 ASW 的配置和 S3 sync 命令](https://github.com/colbyfayock/my-static-website/commit/f891412b827aca4b06e9bf3de8e4e5b4c5704fc8) +- [测试 workflow 的标题的更新](https://github.com/colbyfayock/my-static-website/commit/bb9b981416645e35c6d3442e02d6b61f2ba032d2) ## 我们还能做什么? -### 设置CloudFront +### 设置 CloudFront -这个篇文章的目的不是要经历AWS配置网站的整个过程,但是你在S3上运行网站服务,你可能在之前考虑过CloudFront。 +这个篇文章的目的不是要经历 AWS 配置网站的整个过程,但是你在 S3 上运行网站服务,你可能在之前考虑过 CloudFront。 -你可以查看以下[我的另一个指南](/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/),它指导你如何设置CloudFront,以及如何在S3中创建网站的手把手指南。 +你可以查看以下[我的另一个指南](/news/how-to-host-and-deploy-a-static-website-or-jamstack-app-to-s3-and-cloudfront/),它指导你如何设置 CloudFront,以及如何在 S3 中创建网站的手把手指南。 -### CloudFront的缓存失效 +### CloudFront 的缓存失效 -如果你的S3网站在CloudFront后面,有可能你会确保CloudFront没有缓存新的变化。 +如果你的 S3 网站在 CloudFront 后面,有可能你会确保 CloudFront 没有缓存新的变化。 -通过AWS CLI,我们也可以触发CloudFront的缓存失效,以确保它正在抓取最新的变化。 +通过 AWS CLI,我们也可以触发 CloudFront 的缓存失效,以确保它正在抓取最新的变化。 [请看这里的文档](https://docs.aws.amazon.com/cli/latest/reference/cloudfront/create-invalidation.html)学习更多的知识. -### pull request部署 +### pull request 部署 -如果你不断地在pull request中的网站修改,有时候更容易看到网站的修改。 +如果你不断地在 pull request 中的网站修改,有时候更容易看到网站的修改。 -你可以设置一个新的workflow,只在pull request上运行,workflow可以根据分支或者环境动态创建一个新的桶,并在pull request上添加一个带有该URL的comment。 +你可以设置一个新的 workflow,只在 pull request 上运行,workflow 可以根据分支或者环境动态创建一个新的桶,并在 pull request 上添加一个带有该 URL 的 comment。 -你也许能找到一个GitHub Action 作为你管理你pull request上带的comments,你可以查询[GitHub Actions文档](https://docs.github.com/en/free-pro-team@latest/rest/reference/actions). +你也许能找到一个 GitHub Action 作为你管理你 pull request 上带的 comments,你可以查询[GitHub Actions 文档](https://docs.github.com/en/free-pro-team@latest/rest/reference/actions). [![关注我,了解更多的Javascript、UX和其他有趣的事情!](https://res.cloudinary.com/fay/image/upload/w_2000,h_400,c_fill,q_auto,f_auto/w_1020,c_fit,co_rgb:007079,g_north_west,x_635,y_70,l_text:Source%20Sans%20Pro_64_line_spacing_-10_bold:Colby%20Fayock/w_1020,c_fit,co_rgb:383f43,g_west,x_635,y_6,l_text:Source%20Sans%20Pro_44_line_spacing_0_normal:Follow%20me%20for%20more%20JavaScript%252c%20UX%252c%20and%20other%20interesting%20things!/w_1020,c_fit,co_rgb:007079,g_south_west,x_635,y_70,l_text:Source%20Sans%20Pro_40_line_spacing_-10_semibold:colbyfayock.com/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_68,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_145,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_222,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/w_300,c_fit,co_rgb:7c848a,g_north_west,x_1725,y_295,l_text:Source%20Sans%20Pro_40_line_spacing_-10_normal:colbyfayock/v1/social-footer-card)](https://twitter.com/colbyfayock) - [🐦 在推特上关注我](https://twitter.com/colbyfayock) - [🎥 在油管上订阅我](https://youtube.com/colbyfayock) -- [✉️ 订阅我的Newsletter](https://www.colbyfayock.com/newsletter/) +- [✉️ 订阅我的 Newsletter](https://www.colbyfayock.com/newsletter/) - [💝 赞助我](https://github.com/sponsors/colbyfayock) \ No newline at end of file diff --git a/chinese/articles/how-to-use-javascript-map-method-to-make-a-breath-first-and-depth-first-search.md b/chinese/articles/how-to-use-javascript-map-method-to-make-a-breath-first-and-depth-first-search.md index 0010ee55f..84c1f98ea 100644 --- a/chinese/articles/how-to-use-javascript-map-method-to-make-a-breath-first-and-depth-first-search.md +++ b/chinese/articles/how-to-use-javascript-map-method-to-make-a-breath-first-and-depth-first-search.md @@ -5,17 +5,17 @@ ![How to Use JavaScript's Map() Method to Solve Breadth-First and Depth-First Search Problems](https://www.freecodecamp.org/news/content/images/size/w2000/2022/08/pexels-porapak-apichodilok-346696.jpg) -JavaScript的[`map()`方法](https://www.freecodecamp.org/news/array-map-tutorial/)是一个包含键值对的[对象](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)。对象的键和值通过冒号 (:) 连接,而map是通过箭头符号 (=>)连接。 +JavaScript 的[`map()`方法](https://www.freecodecamp.org/news/array-map-tutorial/)是一个包含键值对的[对象](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)。对象的键和值通过冒号 (:) 连接,而 map 是通过箭头符号 (=>)连接。 -以下是JavaScript中的对象: +以下是 JavaScript 中的对象: ```javascript { a: [ 1, 2, 3 ], b: 2, c: 3 } ``` -一个JavaScript对象 +一个 JavaScript 对象 -以下是JavaScript中的map: +以下是 JavaScript 中的 map: ```javascript { 'a' => [ 1, 2, 3 ], 'b' => 2, 'c' => 3 } @@ -48,12 +48,12 @@ JavaScript的[`map()`方法](https://www.freecodecamp.org/news/array-map-tutoria ## 问题 -尼日利亚有 36 个州。游客可以通过公路、空中和水运(路线routes)从一个州移动到另一个州。我们要做的是以下编程: +尼日利亚有 36 个州。游客可以通过公路、空中和水运(路线 routes)从一个州移动到另一个州。我们要做的是以下编程: 1. 用图展示每个州和其他州之间的连接。 2. 检查两个州之间是否有连接。 -我们从尼日利亚的36个州中挑选了11州来进行创建: +我们从尼日利亚的 36 个州中挑选了 11 州来进行创建: ```js ENUGU, ABIA, SOKOTO, NIGER, LAGOS, OGUN, BAYELSA, AKWAIBOM, ANAMBRA, IMO, EBONYI @@ -79,13 +79,13 @@ ENUGU, ABIA, SOKOTO, NIGER, LAGOS, OGUN, BAYELSA, AKWAIBOM, ANAMBRA, IMO, EBONYI 上一章节我们讲解了问题,现在我们来解决问题,在这篇教程中我将使用[Replit](https://replit.com/)。 -Replit是一个配置完全的IDE,方便你可以快速地编写和检查程序。 +Replit 是一个配置完全的 IDE,方便你可以快速地编写和检查程序。 ### 如何创建 Map 我们首先要解决的问题是如何展现每一个州和其他州之间的连接。 -我们先定义这11个州和路线,输入以下代码: +我们先定义这 11 个州和路线,输入以下代码: ```javascript @@ -119,7 +119,7 @@ states.forEach((state) => { }); ``` -这段代码迭代所有 **states**。每次迭代,都将当前state作为`connections`图中的键,并设置初始值为空数组 (`[]`)。 +这段代码迭代所有 **states**。每次迭代,都将当前 state 作为`connections`图中的键,并设置初始值为空数组 (`[]`)。 如果你在控制台打印 `connections` ,会得到以下结果: @@ -159,7 +159,7 @@ routes.forEach(route => { 上述代码将州添加到空数组,作为前一步键对应的值。代码循环迭代路线(routes)数组,并提取出每一个路线。 -departure(出发)州为每一个路线数组的第一个值,而destination(目的地)州为每一个路线数组的第二个值。 +departure(出发)州为每一个路线数组的第一个值,而 destination(目的地)州为每一个路线数组的第二个值。 然后将目的地州添加到出发州对应的值数组,最后将出发州添加到目的地州的值数组中。 @@ -271,15 +271,15 @@ console.log(connections) [BFS](https://www.freecodecamp.org/news/breadth-first-search-in-javascript-e655cd824fa4/) 是一种用于检查树或者图的算法,通过探索一个父(节点)的所有直系子(边)之后再探索孙节点,以这样的方式一直持续到树或者图的底部。 -例如,在我们的例子中,我们想检查ENUGU和ABIA之间有没有连接。 +例如,在我们的例子中,我们想检查 ENUGU 和 ABIA 之间有没有连接。 -BFS会从检查Enugu的直线路线(LAGOS和NIGER)开始。因为ABIA并非直接和ENUGU联系的,算法会接着检查和LAGOS相连的州。 +BFS 会从检查 Enugu 的直线路线(LAGOS 和 NIGER)开始。因为 ABIA 并非直接和 ENUGU 联系的,算法会接着检查和 LAGOS 相连的州。 -接着,算法会检查和NIGER相连的州,这个过程会一直持续到算法找到ABIA或者走到头。程序就会终止。 +接着,算法会检查和 NIGER 相连的州,这个过程会一直持续到算法找到 ABIA 或者走到头。程序就会终止。 -BFS使用[队列](https://www.freecodecamp.org/news/queue-data-structure-definition-and-java-example-code/)数据结构。也就是说,项目会在数组的一端被添加,然后在另一端被删除。在我们的例子中,队列会保存所有还未被访问的州,我们会在编程中看到详细行为: +BFS 使用[队列](https://www.freecodecamp.org/news/queue-data-structure-definition-and-java-example-code/)数据结构。也就是说,项目会在数组的一端被添加,然后在另一端被删除。在我们的例子中,队列会保存所有还未被访问的州,我们会在编程中看到详细行为: -我们从创建一个命名为 `bfs`函数开始编写BFS: +我们从创建一个命名为 `bfs`函数开始编写 BFS: ```javascript function bfs(departureState, destinationState) { @@ -295,7 +295,7 @@ function bfs(departureState, destinationState) { const queue = [departureState]; ``` -我们将 `departureState`添加至`queue`数组,是因为它保存了所有尚未被访问的节点。(译者注:除了起始点以外,广度优先搜索的无向图中所有节点都是自己顶点-边关系的顶点以及其他顶点-边关系中的边,这里queue数组记录的是所有尚未作为顶点被访问的节点,而后文的visited集合记录的是所有作为边已被访问的节点)。 +我们将 `departureState`添加至`queue`数组,是因为它保存了所有尚未被访问的节点。(译者注:除了起始点以外,广度优先搜索的无向图中所有节点都是自己顶点-边关系的顶点以及其他顶点-边关系中的边,这里 queue 数组记录的是所有尚未作为顶点被访问的节点,而后文的 visited 集合记录的是所有作为边已被访问的节点)。 接下来,定义一个空`[Set()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/Set)`,并命名为`visited`: @@ -382,7 +382,7 @@ function bfs(departureState, destinationState) { } ``` -检查两个州之间是否有连接,比方说ENUGU和SOKOTO,调用`bfs`函数: +检查两个州之间是否有连接,比方说 ENUGU 和 SOKOTO,调用`bfs`函数: ```javascript bfs("ENUGU", "SOKOTO") @@ -431,11 +431,11 @@ Found => SOKOTO [DFS](https://www.freecodecamp.org/news/dfs-for-your-next-tech-giant-interview/)算法一次只取一个节点的一个子节点。它继续向下搜索该节点的一个子节点,直到走到死胡同,然后再回溯并尝试另一个子节点。 -在我们的例子中,假设我们需要检查ENUGU和ABIA之间是否有连接。 +在我们的例子中,假设我们需要检查 ENUGU 和 ABIA 之间是否有连接。 -DFS会从Enugu开始,并检查第一个与之相连的州(LAGOS)。因为LAGOS不是ABIA,搜索会继续检查LAGOS紧密连接的下一个州。搜索会一直持续到找到ABIA或者走到死胡同。然后回溯并尝试另一个节点。 +DFS 会从 Enugu 开始,并检查第一个与之相连的州(LAGOS)。因为 LAGOS 不是 ABIA,搜索会继续检查 LAGOS 紧密连接的下一个州。搜索会一直持续到找到 ABIA 或者走到死胡同。然后回溯并尝试另一个节点。 -DFS使用栈来记录访问过的项目(在我们的例子中就是州)。当栈为空的时候,搜索终止。我们将使用递归来编写DFS算法,让我们开始吧! +DFS 使用栈来记录访问过的项目(在我们的例子中就是州)。当栈为空的时候,搜索终止。我们将使用递归来编写 DFS 算法,让我们开始吧! 首先创建一个名为`dfs`的函数, 该函数将接受三个参数(`departureState`, `destinationState`和 `visited`): @@ -482,7 +482,7 @@ function dfs(departureState, destinationState, visited = new Set()) { } ``` -`destinationState`保持不变,`visited`(访问过的)`Set()` 不再为空数组。此时,位于循环中,但并不在visited集合中的destination将被存储到栈。 +`destinationState`保持不变,`visited`(访问过的)`Set()` 不再为空数组。此时,位于循环中,但并不在 visited 集合中的 destination 将被存储到栈。 `dfs`函数如下: @@ -506,7 +506,7 @@ function dfs(departureState, destinationState, visited = new Set()) { } ``` -检查两个州之间是否连接,如ENUGU和SOKOTO, 调用`dfs`函数: +检查两个州之间是否连接,如 ENUGU 和 SOKOTO, 调用`dfs`函数: ```javascript dfs("ENUGU", "SOKOTO") @@ -545,15 +545,15 @@ Found => SOKOTO Found => SOKOTO ``` -虽然上面的输出与我们运行bfs函数时得到的输出相同,但得出此结果的过程有所不同。尝试在不同的点分解代码以查看程序的流程。 +虽然上面的输出与我们运行 bfs 函数时得到的输出相同,但得出此结果的过程有所不同。尝试在不同的点分解代码以查看程序的流程。 <iframe width="546" height="307" src="https://www.youtube.com/embed/yl8GjfOSNq0?list=PLOvIwkWvHysOUVGqOwb_4j5mq8ir0fZ1O" title="7 Depth First Search Algorithm on a graph" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> ## 总结 -数据结构和算法逐渐成为软件工程师面试中重要的一环。 `map()` 是一个强大的JavaScript工具,能够帮助我们更容易地解决复杂问题。 +数据结构和算法逐渐成为软件工程师面试中重要的一环。 `map()` 是一个强大的 JavaScript 工具,能够帮助我们更容易地解决复杂问题。 -我们首先使用 `map()` 方法构建了一个图。 然后我们使用了BFS和DFS算法搜索了两个州之间是否有连接。 +我们首先使用 `map()` 方法构建了一个图。 然后我们使用了 BFS 和 DFS 算法搜索了两个州之间是否有连接。 你可以在[这里](https://replit.com/@EBEREGIT/MappingWithStates#index.js)查看我的代码。 diff --git a/chinese/articles/how-to-use-proptypes-in-react.md b/chinese/articles/how-to-use-proptypes-in-react.md index 704621043..d76be3b4f 100644 --- a/chinese/articles/how-to-use-proptypes-in-react.md +++ b/chinese/articles/how-to-use-proptypes-in-react.md @@ -5,23 +5,23 @@ ![How to Use PropTypes in React](https://www.freecodecamp.org/news/content/images/size/w2000/2022/02/props.png) -当调试你的应用程序时,PropTypes是一个很好的第一道防线。但在详细了解PropTypes之前,我们必须先了解props的概念。 +当调试你的应用程序时,PropTypes 是一个很好的第一道防线。但在详细了解 PropTypes 之前,我们必须先了解 props 的概念。 -[Props](https://tekolio.com/what-are-props-in-react-and-how-to-use-them/) 是只读属性,在组件之间共享,使React的单向流动具有动态性(dynamic touch)。它们主要是从父组件到子组件的共享,但反过来也可以(尽管不推荐)。 +[Props](https://tekolio.com/what-are-props-in-react-and-how-to-use-them/) 是只读属性,在组件之间共享,使 React 的单向流动具有动态性(dynamic touch)。它们主要是从父组件到子组件的共享,但反过来也可以(尽管不推荐)。 -在这篇博客中,我们将讨论如何验证或检查我们所传递的props,以避免在后期进行复杂的调试。 +在这篇博客中,我们将讨论如何验证或检查我们所传递的 props,以避免在后期进行复杂的调试。 -## 什么是PropTypes? +## 什么是 PropTypes? -PropTypes只是一种机制,确保传递的值是正确的数据类型。这确保了我们不会在应用的最后阶段收到控制台的错误,这可能不容易处理。 +PropTypes 只是一种机制,确保传递的值是正确的数据类型。这确保了我们不会在应用的最后阶段收到控制台的错误,这可能不容易处理。 我不建议在自我练习的项目等短期应用中使用它们,但这完全取决于你。对于像客户这样的大型项目,使用它们往往是一个明智的选择和良好的做法。 -有许多不同类型的PropTypes,它们都有自己独特的ES6类,我们可以使用。我们将在本文中讨论每一种类型。 +有许多不同类型的 PropTypes,它们都有自己独特的 ES6 类,我们可以使用。我们将在本文中讨论每一种类型。 -## 如何使用PropTypes +## 如何使用 PropTypes -在React 15.5.0发布之前,PropTypes在React包中包含的,但现在我们必须在我们的项目中添加prop-types库。 +在 React 15.5.0 发布之前,PropTypes 在 React 包中包含的,但现在我们必须在我们的项目中添加 prop-types 库。 我们可以通过在终端运行以下命令来做到这一点: @@ -29,7 +29,7 @@ PropTypes只是一种机制,确保传递的值是正确的数据类型。这 npm install prop-types --save ``` -我们可以使用PropTypes来验证我们从propps接收的任何数据。但在使用它之前,我们必须在我们的应用程序中导入它: +我们可以使用 PropTypes 来验证我们从 propps 接收的任何数据。但在使用它之前,我们必须在我们的应用程序中导入它: ```javascript import PropTypes from 'prop-types'; @@ -56,9 +56,9 @@ Count.propTypes = { export default Count; ``` -PropTypes也是具有键值对的对象,其中 `键`(key)是prop的名称,而值(value)代表它们被定义的类型(type)或类(class)。 +PropTypes 也是具有键值对的对象,其中 `键`(key)是 prop 的名称,而值(value)代表它们被定义的类型(type)或类(class)。 -由于在组件上定义PropTypes并不取决于组件的实现,我们将在下面的所有例子中省略组件本身的代码。上面的代码可以简化为以下内容: +由于在组件上定义 PropTypes 并不取决于组件的实现,我们将在下面的所有例子中省略组件本身的代码。上面的代码可以简化为以下内容: ```javascript Count.propTypes = { @@ -66,17 +66,17 @@ Count.propTypes = { } ``` -让我们先讨论一下有多少种PropTypes,然后再通过一个例子来理解它们。 +让我们先讨论一下有多少种 PropTypes,然后再通过一个例子来理解它们。 -## PropTypes的基本类型 +## PropTypes 的基本类型 -我们检查prop类型的最基本方法是检查它是否属于JavaScript中的原始类型,如布尔型、字符串、对象等。 +我们检查 prop 类型的最基本方法是检查它是否属于 JavaScript 中的原始类型,如布尔型、字符串、对象等。 -下面是所有被认为是原始的或基本的数据类型的列表,以及我们可以用来检查props的类。 +下面是所有被认为是原始的或基本的数据类型的列表,以及我们可以用来检查 props 的类。 <table style="border:none;border-collapse:collapse;table-layout:fixed;width:548.7755905511812pt"><colgroup><col><col><col></colgroup><tbody><tr style="height:0pt"><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Type</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Class</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Example</span></p></td></tr><tr style="height:0pt"><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">String</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">PropType.string</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">“helllo”</span></p></td></tr><tr style="height:0pt"><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Object</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">PropType.object</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">{name: “Rohit”}</span></p></td></tr><tr style="height:0pt"><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Number</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">PropType.number</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">10</span></p></td></tr><tr style="height:0pt"><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Boolean</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">PropType.bool</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">true/false</span></p></td></tr><tr style="height:0pt"><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Function</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">PropType.func</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">const say = {console.log(“hello”)}</span></p></td></tr><tr style="height:0pt"><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Symbol</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">PropType.symbol</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Symbol(“m”)</span></p></td></tr></tbody></table> -下面是一个例子,告诉我们如何在我们的应用程序中使用这些PropTypes进行类型检查。正如我们已经讨论过的,它们被定义为具有一个键值对的对象,其中键是对象的名称,而值包含将用于类型检查的类。 +下面是一个例子,告诉我们如何在我们的应用程序中使用这些 PropTypes 进行类型检查。正如我们已经讨论过的,它们被定义为具有一个键值对的对象,其中键是对象的名称,而值包含将用于类型检查的类。 ```javascript Count.propTypes = { @@ -87,13 +87,13 @@ Count.propTypes = { }; ``` -在上面的代码中,name应该是一个字符串,age是一个数字,address是一个对象,friends是一个数组。如果任何其他的值被用作相同的props值,它将在控制台显示一个错误,像这样: +在上面的代码中,name 应该是一个字符串,age 是一个数字,address 是一个对象,friends 是一个数组。如果任何其他的值被用作相同的 props 值,它将在控制台显示一个错误,像这样: ![NoiuFl2D3WofbIe7_CsqbNkolVLFzXyPSvvADV3LvFug2jp2oMhBXFl42Qy79e4LkGAOio5RD5rAhlUOBJEoSP3oDUuWNwxb1wCfQYdYQpWvdtDbKQQDkwt0rMSD9dlQAhXozKKC](https://lh3.googleusercontent.com/NoiuFl2D3WofbIe7_CsqbNkolVLFzXyPSvvADV3LvFug2jp2oMhBXFl42Qy79e4LkGAOio5RD5rAhlUOBJEoSP3oDUuWNwxb1wCfQYdYQpWvdtDbKQQDkwt0rMSD9dlQAhXozKKC) -控制台输出有关PropTypes的错误 +控制台输出有关 PropTypes 的错误 -我们可以用`isRequired`配置上述任何一个,以确保在没有提供props的情况下显示一个警告。 +我们可以用`isRequired`配置上述任何一个,以确保在没有提供 props 的情况下显示一个警告。 ```javascript Count.propTypes = { @@ -109,9 +109,9 @@ Count.propTypes = { ## Collective Type -我们已经看到如何验证或检查props属于哪一类基本数据类型。但是还有很多方法可以传递和使用props,比如集合类型(collective types),如数字数组、字符串等等。那么,它们呢? +我们已经看到如何验证或检查 props 属于哪一类基本数据类型。但是还有很多方法可以传递和使用 props,比如集合类型(collective types),如数字数组、字符串等等。那么,它们呢? -我们也可以为它们检查props。下面是数据类型可以组合和使用的各种方式。 +我们也可以为它们检查 props。下面是数据类型可以组合和使用的各种方式。 ### Array Types @@ -151,11 +151,11 @@ Count.propTypes = { ## 高级类型检查 -除了基本的类型检查外,我们还有很多方法可以用来检查我们的props。这种方法主要关注React代码而不是数据类型。 +除了基本的类型检查外,我们还有很多方法可以用来检查我们的 props。这种方法主要关注 React 代码而不是数据类型。 -### 如何检查React组件 +### 如何检查 React 组件 -如果你只想检查一个道具是否是React组件,你可以使用**PropTypes.element**。这对于确保一个组件只有一个子组件是很有用的。 +如果你只想检查一个道具是否是 React 组件,你可以使用**PropTypes.element**。这对于确保一个组件只有一个子组件是很有用的。 <table style="border:none;border-collapse:collapse;table-layout:fixed;width:548.7755905511812pt"><colgroup><col><col><col></colgroup><tbody><tr style="height:0pt"><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Type</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Class</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Example</span></p></td></tr><tr style="height:0pt"><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:700;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">Element</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">PropTypes.element</span></p></td><td style="border-left:solid #000000 1pt;border-right:solid #000000 1pt;border-bottom:solid #000000 1pt;border-top:solid #000000 1pt;vertical-align:top;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.2;text-align: center;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:13.999999999999998pt;font-family:Arial;color:#000000;background-color:transparent;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"><Title /></span></p></td></tr></tbody></table> @@ -165,9 +165,9 @@ Count.propTypes = { }; ``` -### 如何检查React组件的名称 +### 如何检查 React 组件的名称 -最后,我们可以通过使用**PropTypes.elementType**来检查该道具是否是React组件的名称。 +最后,我们可以通过使用**PropTypes.elementType**来检查该道具是否是 React 组件的名称。 ```javascript Component.propTypes = { @@ -181,7 +181,7 @@ Component.propTypes = { ## 自定义类型 -我们也可以有一个自定义的验证器(validator)或props的类型检查。但是它需要一个错误对象,当验证失败时。 +我们也可以有一个自定义的验证器(validator)或 props 的类型检查。但是它需要一个错误对象,当验证失败时。 你可以对数组和对象使用这个方法,但错误对象将为数组或对象中的每个键(key)被调用。验证器的前两个参数是数组或对象本身和当前项目的键(key)。 @@ -230,9 +230,9 @@ Count.propTypes = { // array function ## 默认 PropTypes -有时,我们希望能够为一个prop设置一个默认值。例如,我们的父组件可能不需要传递标题。但我们仍然希望有一个标题被呈现出来。 +有时,我们希望能够为一个 prop 设置一个默认值。例如,我们的父组件可能不需要传递标题。但我们仍然希望有一个标题被呈现出来。 -在这种情况下,我们可以为我们的标题设置一个默认值,如果标题没有被作为一个prop从我们的父组件中传递出来,它将自动被呈现出来。 +在这种情况下,我们可以为我们的标题设置一个默认值,如果标题没有被作为一个 prop 从我们的父组件中传递出来,它将自动被呈现出来。 ```javascript Header.defaultProp = { @@ -244,7 +244,7 @@ Header.defaultProp = { ## 举个例子 -让我们通过一些简单的React代码来了解这一切是如何运作的。 +让我们通过一些简单的 React 代码来了解这一切是如何运作的。 我们将制作两个可重用的组件,**About.js**和**Count.js**。**About**组件是父组件,**Count**组件是子组件,如图所示: @@ -252,7 +252,7 @@ Header.defaultProp = { ![2RbGh5-GHCcP57-3GG9ysJ-9p7xFIOKRzg2Z_TzzJFObcqalPbUe_8MDe1iyckfD0rKxg6Kfcksd8V9uNx9SHV6sUr8yM37Z2NP1k7YS_e7WLIz-OXtq-jOS7DsRTjfj-C0PBPBp](https://lh3.googleusercontent.com/2RbGh5-GHCcP57-3GG9ysJ-9p7xFIOKRzg2Z_TzzJFObcqalPbUe_8MDe1iyckfD0rKxg6Kfcksd8V9uNx9SHV6sUr8yM37Z2NP1k7YS_e7WLIz-OXtq-jOS7DsRTjfj-C0PBPBp) -如果我们把 age prop的值从数字改为字符串,而不改变其类型(PropTypes),会怎么样? +如果我们把 age prop 的值从数字改为字符串,而不改变其类型(PropTypes),会怎么样? ```javascript import React from "react"; @@ -274,16 +274,16 @@ export default About; ![NoiuFl2D3WofbIe7_CsqbNkolVLFzXyPSvvADV3LvFug2jp2oMhBXFl42Qy79e4LkGAOio5RD5rAhlUOBJEoSP3oDUuWNwxb1wCfQYdYQpWvdtDbKQQDkwt0rMSD9dlQAhXozKKC](https://lh3.googleusercontent.com/NoiuFl2D3WofbIe7_CsqbNkolVLFzXyPSvvADV3LvFug2jp2oMhBXFl42Qy79e4LkGAOio5RD5rAhlUOBJEoSP3oDUuWNwxb1wCfQYdYQpWvdtDbKQQDkwt0rMSD9dlQAhXozKKC) -它明确指出,传递的age prop的值与预期的值不一致(PropTypes)。 +它明确指出,传递的 age prop 的值与预期的值不一致(PropTypes)。 -从上面的例子中,现在应该清楚我们如何使用PropTypes。你还可以看到,当应用程序太大,仅用传统方法无法找到错误时,它们对调试你的应用程序是多么有用。 +从上面的例子中,现在应该清楚我们如何使用 PropTypes。你还可以看到,当应用程序太大,仅用传统方法无法找到错误时,它们对调试你的应用程序是多么有用。 ## 结语 -PropTypes是一种在运行时捕捉错误的好方法,可以作为你的应用程序的第一道防线。它们不像TypeScript那样类型安全,但它们更容易设置和使用。 +PropTypes 是一种在运行时捕捉错误的好方法,可以作为你的应用程序的第一道防线。它们不像 TypeScript 那样类型安全,但它们更容易设置和使用。 你也可以通过我的一些其他博文: -1. [React中的**虚拟DOM**是什么意思?](https://tekolio.com/react-virtual-dom-explained-in-simple-words/) -2. [React中的Hooks是什么?](https://tekolio.com/what-are-hooks-in-react/) -3. [如何在React中制作一个作品集](https://tekolio.com/how-i-made-my-portfolio-in-react/) +1. [React 中的**虚拟 DOM**是什么意思?](https://tekolio.com/react-virtual-dom-explained-in-simple-words/) +2. [React 中的 Hooks 是什么?](https://tekolio.com/what-are-hooks-in-react/) +3. [如何在 React 中制作一个作品集](https://tekolio.com/how-i-made-my-portfolio-in-react/) diff --git a/chinese/articles/how-to-use-rest-api.md b/chinese/articles/how-to-use-rest-api.md index 343d3f241..b5b2df285 100644 --- a/chinese/articles/how-to-use-rest-api.md +++ b/chinese/articles/how-to-use-rest-api.md @@ -7,25 +7,25 @@ 应用程序编程接口(API)是一个需要掌握的重要编程概念。如果花时间学习这些接口,你就可以更轻松地管理任务。 -最常见的API之一就是REST API。如果你曾经尝试过从另一个网站(如:Twitter或Github)上获取数据,你可能已经使用过这个API。 +最常见的 API 之一就是 REST API。如果你曾经尝试过从另一个网站(如:Twitter 或 Github)上获取数据,你可能已经使用过这个 API。 -那为什么理解REST API对你有帮助?REST API是如何确保现代业务的连通性的? +那为什么理解 REST API 对你有帮助?REST API 是如何确保现代业务的连通性的? -在创建或者运行一个API(这里特指REST API)之前,你得先学习什么是API。这篇文章会带着你一步一步理解REST API的基本原则,以及这些原则如何使得REST API成为强大的应用。 +在创建或者运行一个 API(这里特指 REST API)之前,你得先学习什么是 API。这篇文章会带着你一步一步理解 REST API 的基本原则,以及这些原则如何使得 REST API 成为强大的应用。 -## **API是如何工作的,我们为什么需要它?** +## **API 是如何工作的,我们为什么需要它?** -API代表一组定义和协议。你需要使用API来开发和集成应用,因为API可以协调两个软件之间的数据交换,如信息供应商(服务器)和用户之间。 +API 代表一组定义和协议。你需要使用 API 来开发和集成应用,因为 API 可以协调两个软件之间的数据交换,如信息供应商(服务器)和用户之间。 -客户向生产商发出调用,生产商返回响应。API规定了返回的可访问内容。 +客户向生产商发出调用,生产商返回响应。API 规定了返回的可访问内容。 -程序使用API进行通信、检索信息或执行功能。API允许用户通过这一系统返回想要的结果。 +程序使用 API 进行通信、检索信息或执行功能。API 允许用户通过这一系统返回想要的结果。 -简言之,API就是用户(客户)和资源(服务器)之间的中间人。 +简言之,API 就是用户(客户)和资源(服务器)之间的中间人。 -当用户发出API请求或者浏览一个在线商城,会期望快速得到反馈,所以作为开发者你需要优化加载时间(参考——[优化麦进斗TTFB (Time To First Byte)](https://onilab.com/blog/magento-ttfb-optimization/))或者使用其他更适合你的内容管理系统(CMS)的性能提升策略。 +当用户发出 API 请求或者浏览一个在线商城,会期望快速得到反馈,所以作为开发者你需要优化加载时间(参考——[优化麦进斗 TTFB (Time To First Byte)](https://onilab.com/blog/magento-ttfb-optimization/))或者使用其他更适合你的内容管理系统(CMS)的性能提升策略。 -集成API的原因包括: +集成 API 的原因包括: - 精简资源、信息共享 - 通过[验证和定义权利](https://www.freecodecamp.org/news/authenticate-and-authorize-apis-in-dotnet5/)的手段来控制特定人群访问特定内容 @@ -33,64 +33,64 @@ API代表一组定义和协议。你需要使用API来开发和集成应用, - 无需了解软件细节 - 即便使用不同的技术,服务间也可以保持持续通信 -## **REST API概览** +## **REST API 概览** ![DUwmoHyRnoD1WovETSrQdSaIv8rh5WUVPxVjPN9_cvVokx7E4fZxzGyCY0_XMRA2cikjPkWIUDlXmtDqqGDX-KCzya5EVEEgxi8sEVwpVTeiHBNsqCULC-78QCE4dJ0_ieC1mQzn](https://lh3.googleusercontent.com/DUwmoHyRnoD1WovETSrQdSaIv8rh5WUVPxVjPN9_cvVokx7E4fZxzGyCY0_XMRA2cikjPkWIUDlXmtDqqGDX-KCzya5EVEEgxi8sEVwpVTeiHBNsqCULC-78QCE4dJ0_ieC1mQzn) -RESTful指的是一种软件架构,即“表现层状态转移”(Representational State Transfer)。你可能在有关制定信息交换系统(web服务)标准的内容中听到过这个表达。 +RESTful 指的是一种软件架构,即“表现层状态转移”(Representational State Transfer)。你可能在有关制定信息交换系统(web 服务)标准的内容中听到过这个表达。 -web服务通过无状态协议使得在线资源由文本的方式呈现,并且可以读取和处理这些在线资源。客户端通过著名的HTTP协议进行数据获取、更新和删除。 +web 服务通过无状态协议使得在线资源由文本的方式呈现,并且可以读取和处理这些在线资源。客户端通过著名的 HTTP 协议进行数据获取、更新和删除。 -REST于2000年被首次提出,目的是通过对API采取特定的规范来提升API的性能、可扩展性并简化API。 +REST 于 2000 年被首次提出,目的是通过对 API 采取特定的规范来提升 API 的性能、可扩展性并简化 API。 -由于可以广泛兼容各种设备和应用,REST API变得越来越受欢迎。下图列举了使用REST API的一些场景: +由于可以广泛兼容各种设备和应用,REST API 变得越来越受欢迎。下图列举了使用 REST API 的一些场景: ![Jk2xFwUgtgRzOuJuSa9kiWPPe51CN0qLd2hXMJ3F2SyW6MM10Gzq2qIY36dDQQj6fPJPG7Axl3q431QumWwi3WtYyFC1FA5TcI1i7i5PeQOO38tpdSCgIF0dJktnVhoWvVjAwFOK](https://lh4.googleusercontent.com/Jk2xFwUgtgRzOuJuSa9kiWPPe51CN0qLd2hXMJ3F2SyW6MM10Gzq2qIY36dDQQj6fPJPG7Axl3q431QumWwi3WtYyFC1FA5TcI1i7i5PeQOO38tpdSCgIF0dJktnVhoWvVjAwFOK) -### 1\. Web使用 +### 1. Web 使用 -REST并没有限制客户端技术,因此适用于各种各样的项目,如: +REST 并没有限制客户端技术,因此适用于各种各样的项目,如: -- web开发 -- iOS应用 -- IoT设备 -- Windows手机应用 +- web 开发 +- iOS 应用 +- IoT 设备 +- Windows 手机应用 -因为不必拘泥于某一种客户端技术栈,所以你可以使用REST为公司搭建任意基础设施。 +因为不必拘泥于某一种客户端技术栈,所以你可以使用 REST 为公司搭建任意基础设施。 ### 2\. 云应用 -由于无状态特性,调用REST API对于云应用来说是理想的解决方案。一旦出现问题,你可以重新部署无状态组件,组件会管理流量转移。 +由于无状态特性,调用 REST API 对于云应用来说是理想的解决方案。一旦出现问题,你可以重新部署无状态组件,组件会管理流量转移。 ### 3\. 云计算 -与服务连接的API需要控制URL的解码方式,所以REST在云服务中作用巨大。 +与服务连接的 API 需要控制 URL 的解码方式,所以 REST 在云服务中作用巨大。 -云计算和微服务的发展使得RESTful API架构在将来会成为一种常态。 +云计算和微服务的发展使得 RESTful API 架构在将来会成为一种常态。 -## REST API是如何运作的? +## REST API 是如何运作的? -数据(如:图像、视频和文本)实体化了REST的资源。客户浏览一个特定的URL,并向服务器发送请求以获得响应。 +数据(如:图像、视频和文本)实体化了 REST 的资源。客户浏览一个特定的 URL,并向服务器发送请求以获得响应。 ![HwYHNtAz8M84Tggswzk662nm_dyGUA77st12KGsiqw4rVBGqhJM2gQ5wgL2sL8ZhWmwOGsoEJx6Uqt7TdxU4Bkbg_uccr2UVTXtWsxnR495yZReGoY_reZEd9rq5_9vnjiaUUBs2](https://lh4.googleusercontent.com/HwYHNtAz8M84Tggswzk662nm_dyGUA77st12KGsiqw4rVBGqhJM2gQ5wgL2sL8ZhWmwOGsoEJx6Uqt7TdxU4Bkbg_uccr2UVTXtWsxnR495yZReGoY_reZEd9rq5_9vnjiaUUBs2) -### **REST API背后的概念** +### **REST API 背后的概念** -一个请求(你访问的URL)包含以下四个方面: +一个请求(你访问的 URL)包含以下四个方面: -- **终点(路径)**, 即以 `root-endpoint/?`为结构的URL +- **终点(路径)**, 即以 `root-endpoint/?`为结构的 URL - **请求方式**, 有五种请求方式: GET, POST, PUT, PATCH, DELETE -- **请求头**, 包含各种功能,如信息验证以及请求体的内容(可以使用 `-H`或`--header`来发送HTTP请求头) -- **数据(请求体)**, 是你通过 `-d`或`--data`向服务器发送的POST, PUT, PATCH或DELETE请求。 +- **请求头**, 包含各种功能,如信息验证以及请求体的内容(可以使用 `-H`或`--header`来发送 HTTP 请求头) +- **数据(请求体)**, 是你通过 `-d`或`--data`向服务器发送的 POST, PUT, PATCH 或 DELETE 请求。 -HTTP请求允许你使用以下方式处理数据,如: +HTTP 请求允许你使用以下方式处理数据,如: -- POST请求创建记录 -- GET请求从服务器读取或获取资源(如图像文件或者其他资源合集) -- PUT和PATCH请求更新记录 -- DELETE请求服务器删除某个资源 +- POST 请求创建记录 +- GET 请求从服务器读取或获取资源(如图像文件或者其他资源合集) +- PUT 和 PATCH 请求更新记录 +- DELETE 请求服务器删除某个资源 -这四种方式可以总结为CRUD(增删查改):建立(Create)、读取(Read)、改正(Update)和删除(Delete)。 +这四种方式可以总结为 CRUD(增删查改):建立(Create)、读取(Read)、改正(Update)和删除(Delete)。 ![Quydyrq2Zw2Mh3uJj4G9LE40DhjJyWLjRCU9-hqs0uKt-hGCgoyGVP9eiU_6IBnb6GwxsILeu9kqjO5LQ6s7LBmHDtbksnqb13YtPoCKRq062zXi1Pz4wf0GAO27maHMlhamixAz](https://lh5.googleusercontent.com/Quydyrq2Zw2Mh3uJj4G9LE40DhjJyWLjRCU9-hqs0uKt-hGCgoyGVP9eiU_6IBnb6GwxsILeu9kqjO5LQ6s7LBmHDtbksnqb13YtPoCKRq062zXi1Pz4wf0GAO27maHMlhamixAz) 服务器使用以下格式向客服端发送数据: @@ -102,43 +102,43 @@ HTTP请求允许你使用以下方式处理数据,如: - Python - 纯文本 -## 为什么使用REST API? +## 为什么使用 REST API? -选择REST而不是其他的API,如SOA是出于一系列原因,比如:REST易于扩展、操作灵活、可移植性和独立性。 +选择 REST 而不是其他的 API,如 SOA 是出于一系列原因,比如:REST 易于扩展、操作灵活、可移植性和独立性。 ![yJ2QDrpGbA-RpzwhXOXr1yl9aGTvVHXeiuyBvFxsMtE5KQu2wRmNLwlCX7cNGOlp1TjRK-P9VsBsFaGRNkxZw-QWvggxqXLYFtLg-THClHzB-5GJlMX6hGkY3DQnFh1YpzkHt2iE](https://lh4.googleusercontent.com/yJ2QDrpGbA-RpzwhXOXr1yl9aGTvVHXeiuyBvFxsMtE5KQu2wRmNLwlCX7cNGOlp1TjRK-P9VsBsFaGRNkxZw-QWvggxqXLYFtLg-THClHzB-5GJlMX6hGkY3DQnFh1YpzkHt2iE) ### 不依赖项目架构 -独立运行的客户端和服务器意味着开发者不受任何项目部分的约束。由于REST API的自适应,开发者可以分别开发各个部分,互不打扰。 +独立运行的客户端和服务器意味着开发者不受任何项目部分的约束。由于 REST API 的自适应,开发者可以分别开发各个部分,互不打扰。 ### 可移植性和适应性 -REST API仅当某个请求的数据发送成功时运作。你可以从一个服务器迁移到另一个服务器,并且随时更新数据。 +REST API 仅当某个请求的数据发送成功时运作。你可以从一个服务器迁移到另一个服务器,并且随时更新数据。 ### 可在未来扩展项目 由于客户端和服务器相互独立,开发者可以迅速开发产品。 -## **RESTful架构的风格特征** +## **RESTful 架构的风格特征** -若使用SOAP、XML-RPC这类API,开发者必须构思出严谨的架构。但是REST API与众不同,REST广泛支持数据类型,也可以使用几乎任何编程语言编写。 +若使用 SOAP、XML-RPC 这类 API,开发者必须构思出严谨的架构。但是 REST API 与众不同,REST 广泛支持数据类型,也可以使用几乎任何编程语言编写。 -六种REST架构限制是设计解决方案的原则,具体如下: +六种 REST 架构限制是设计解决方案的原则,具体如下: ![XRsmwgFoTf1sCI3hZf6n5DxHXDqHclunxf6ocqxjUVgWPss5KHiz8wm4fXYzCJ9mkijpfwhGc-YzSO_R1fm9JtOej1T1SQJwngs-wK_Lz0DhUwI2LfCOQWsZvm88nVlkGkmBgV-E](https://lh5.googleusercontent.com/XRsmwgFoTf1sCI3hZf6n5DxHXDqHclunxf6ocqxjUVgWPss5KHiz8wm4fXYzCJ9mkijpfwhGc-YzSO_R1fm9JtOej1T1SQJwngs-wK_Lz0DhUwI2LfCOQWsZvm88nVlkGkmBgV-E) ### **1\. 统一接口(一致的用户接口)** -这个概念规定不论源头是哪里,所有请求同一个资源的API必须一致,即使用同一种语言。统一标识符(URI)和关联数据一一对应,如用户名或者电子邮件地址。 +这个概念规定不论源头是哪里,所有请求同一个资源的 API 必须一致,即使用同一种语言。统一标识符(URI)和关联数据一一对应,如用户名或者电子邮件地址。 -统一接口原则的另一个要求是信息必须是自我描述的。即信息必须是服务器可以理解并决定如何处理的(如,请求类型、MIME类型等)。 +统一接口原则的另一个要求是信息必须是自我描述的。即信息必须是服务器可以理解并决定如何处理的(如,请求类型、MIME 类型等)。 ### **2\. 客户端和服务器分离** -REST架构风格采取了特殊的方式实现客户端和服务器。也就是说,客户端和服务器可以在不知道彼此的情况下实现。 +REST 架构风格采取了特殊的方式实现客户端和服务器。也就是说,客户端和服务器可以在不知道彼此的情况下实现。 -例如,客户端仅使用统一标识符(URI)请求资源,并不能使用其他方式和服务器通信。同时,服务器无法影响客户端,仅通过HTTP协议传输必要的数据。 +例如,客户端仅使用统一标识符(URI)请求资源,并不能使用其他方式和服务器通信。同时,服务器无法影响客户端,仅通过 HTTP 协议传输必要的数据。 这意味着你可以随时修改客户端代码,完全不影响服务器的运行。 @@ -148,15 +148,15 @@ REST架构风格采取了特殊的方式实现客户端和服务器。也就是 将用户接口问题和数据存储限制分离的好处是什么呢?我们提高了接口的灵活性,可以跨不同平台使用,并且提高了扩展的可能。 -另外,每一个组件从分离受益,因为组件可以独立进化。一个REST接口可以帮助不同的客户端: +另外,每一个组件从分离受益,因为组件可以独立进化。一个 REST 接口可以帮助不同的客户端: -- 访问相同的REST终点 +- 访问相同的 REST 终点 - 执行相同的活动 - 获得相同的响应 ### **3\. 客户端和服务器之间的无状态通信** -基于REST的系统是无状态的,意味着客户端状态对于服务器来说未知,反之亦然。这样的限制可以确保服务器和客户端之间理解每条信息,即便是上一条信息不知情的情况下。 +基于 REST 的系统是无状态的,意味着客户端状态对于服务器来说未知,反之亦然。这样的限制可以确保服务器和客户端之间理解每条信息,即便是上一条信息不知情的情况下。 为了加强对无状态的限制,你必须使用资源而非命令。资源是网络的名词。使用名词的目的是描述你想要从其他服务获取或者通信的对象。 @@ -164,13 +164,13 @@ REST架构风格采取了特殊的方式实现客户端和服务器。也就是 - 稳定性 - 速度 -- RESTful应用的可扩展性 +- RESTful 应用的可扩展性 注意每一个请求必须包括你想要的所有信息,这个请求才得以完成。客户端必须保存会话状态,因为服务端不会存储和请求相关的任何数据。 ### **4\. 可缓存数据** -REST要求在可能的情况下缓存服务端和客户端的资源。网络发展到今天,数据和响应的缓存对于客户端性能提升至关重要。 +REST 要求在可能的情况下缓存服务端和客户端的资源。网络发展到今天,数据和响应的缓存对于客户端性能提升至关重要。 这对用户有什么影响?一个管理良好的缓存可以减少客户端的通信。 @@ -180,45 +180,45 @@ REST要求在可能的情况下缓存服务端和客户端的资源。网络发 ![DBk2dcqnTMZdz-dBA0sFDUe5cQu71VxMqG8pW-ux4rqNvkVcsixRNR_ZyuY1z6UeWWZ5NRV11FPIv8XYK86EGr2G-Nnb7O_njC9PER6a5TdmfpZ2qmRTI7f9P--S7QU50cYwD9EC](https://lh3.googleusercontent.com/DBk2dcqnTMZdz-dBA0sFDUe5cQu71VxMqG8pW-ux4rqNvkVcsixRNR_ZyuY1z6UeWWZ5NRV11FPIv8XYK86EGr2G-Nnb7O_njC9PER6a5TdmfpZ2qmRTI7f9P--S7QU50cYwD9EC) -RESTful分层架构也是我们要讨论的一个限制。这个原则是将特定功能的层分到一组。 +RESTful 分层架构也是我们要讨论的一个限制。这个原则是将特定功能的层分到一组。 -REST API的层各司其职,并且按层次顺序排列。例如,第一层是负责从服务器存储数据的,第二层就负责从另一个服务器部署API,第三层就负责从再一个服务器验证请求。 +REST API 的层各司其职,并且按层次顺序排列。例如,第一层是负责从服务器存储数据的,第二层就负责从另一个服务器部署 API,第三层就负责从再一个服务器验证请求。 这些分层像中间人一样防止服务器和客户端直接通信。所以,客户端并不知道他们的请求发送给了哪一个服务器或者组件。 -在传输信息前每一层各司其职意味着什么?这样可以提高API整体的安全性和灵活性,因为增加、修改或者删除API都不会影响其他接口组件。 +在传输信息前每一层各司其职意味着什么?这样可以提高 API 整体的安全性和灵活性,因为增加、修改或者删除 API 都不会影响其他接口组件。 ### **6\. 按需编码(非强制性)** -使用REST API最常见的场景是传输如XML或者JSON格式的静态资源。 +使用 REST API 最常见的场景是传输如 XML 或者 JSON 格式的静态资源。 -这样的架构风格方便用户以Java小程序或脚本(JavaScript)的形式来下载并运行代码。例如,客户端可以调用API来检索和渲染UI插件的代码。 +这样的架构风格方便用户以 Java 小程序或脚本(JavaScript)的形式来下载并运行代码。例如,客户端可以调用 API 来检索和渲染 UI 插件的代码。 -## **使用REST API面临的挑战** +## **使用 REST API 面临的挑战** -了解REST API的设计和架构限制后,你需要了解使用这种架构风格将迎接的问题: +了解 REST API 的设计和架构限制后,你需要了解使用这种架构风格将迎接的问题: ![FnzdrS-v1CIkyY6lWVBZymkIbLGDOQb4ZFAPqcJD6_EDL9QL1Xd3KGwd2SP24GfYO2CTwO4-9ra4a8Dc8gOvokndr3uO7Zt0-VOjQjR6bdcLrSH3SWK0vmAeg5mZlEavHkgpsIhh](https://lh3.googleusercontent.com/FnzdrS-v1CIkyY6lWVBZymkIbLGDOQb4ZFAPqcJD6_EDL9QL1Xd3KGwd2SP24GfYO2CTwO4-9ra4a8Dc8gOvokndr3uO7Zt0-VOjQjR6bdcLrSH3SWK0vmAeg5mZlEavHkgpsIhh) -### REST终点的一致性 +### REST 终点的一致性 -无论URL如何构造,API都应该保持一致。但随着可用组合方法数量的增加,保持大型代码库的一致性变得越来越难。 +无论 URL 如何构造,API 都应该保持一致。但随着可用组合方法数量的增加,保持大型代码库的一致性变得越来越难。 -### REST API的特性版本 +### REST API 的特性版本 -API需要定期[更新或控制版本](https://www.freecodecamp.org/news/how-to-version-a-rest-api/)以防止兼容性问题。旧版本的终点保持运行常常会增加工作量。 +API 需要定期[更新或控制版本](https://www.freecodecamp.org/news/how-to-version-a-rest-api/)以防止兼容性问题。旧版本的终点保持运行常常会增加工作量。 ### 大量认证方式 你可以限制特定用户访问特定资源。比方说,你可以决定哪一个第三方服务器可以访问顾客的电子邮箱地址或者其他的敏感信息,以及服务器可以对这个信息做什么。 -但20个各不相同的认证方式会导致初始化API调用变得十分复杂。这一初始化难题使得开发者不愿意推进项目进展。 +但 20 个各不相同的认证方式会导致初始化 API 调用变得十分复杂。这一初始化难题使得开发者不愿意推进项目进展。 -### REST API的安全弱点 +### REST API 的安全弱点 -尽管RESTful API是分层结构,仍存在安全隐患。例如一个应用因为缺乏加密而不够安全,就会泄露敏感数据。 +尽管 RESTful API 是分层结构,仍存在安全隐患。例如一个应用因为缺乏加密而不够安全,就会泄露敏感数据。 -又比如黑客每秒发送成千上万的API请求,导致DDoS攻击,或者采用其他滥用API服务的行为,使服务器崩溃。 +又比如黑客每秒发送成千上万的 API 请求,导致 DDoS 攻击,或者采用其他滥用 API 服务的行为,使服务器崩溃。 ### 过量的数据收集和请求 @@ -226,8 +226,8 @@ API需要定期[更新或控制版本](https://www.freecodecamp.org/news/how-to- ## **总结** -毫不意外API会在未来简化web通信。API的目的就是助力web应用的通信和数据的共享。 +毫不意外 API 会在未来简化 web 通信。API 的目的就是助力 web 应用的通信和数据的共享。 -通过开发强大且富有创造性的系统,API帮助在线业务的发展。随着API架构的进化,会出现更加轻量、更灵活的变体,这对于手机应用和分散网络的发展至关重要。 +通过开发强大且富有创造性的系统,API 帮助在线业务的发展。随着 API 架构的进化,会出现更加轻量、更灵活的变体,这对于手机应用和分散网络的发展至关重要。 -在这篇文章中你学习了REST API的基础。 +在这篇文章中你学习了 REST API 的基础。 diff --git a/chinese/articles/how-to-use-small-sustainable-habits-to-get-your-first-dev-job.md b/chinese/articles/how-to-use-small-sustainable-habits-to-get-your-first-dev-job.md index bf31c39fc..2747ce941 100644 --- a/chinese/articles/how-to-use-small-sustainable-habits-to-get-your-first-dev-job.md +++ b/chinese/articles/how-to-use-small-sustainable-habits-to-get-your-first-dev-job.md @@ -15,7 +15,7 @@ 但是一个关键的部分,不要在达到目标时衡量你的成功,而是在每次成功执行在你的系统内。 -这意味着你处在持续成功的状态,而不是在99%时间内处于失败状态,只有短暂的成功。 +这意味着你处在持续成功的状态,而不是在 99%时间内处于失败状态,只有短暂的成功。 这不仅在心理上更好,而且提高了你实现目标的可能性。 @@ -41,11 +41,11 @@ Fogg 认为在进行持续的改变时,动力是可怜的工具。它让我们 相反,你应该在实现你的目标的过程中,通过尽可能小的行动来实现。 -因此,与其试图养成每天做50个俯卧撑的习惯, 不如设定一天做两个的习惯。 +因此,与其试图养成每天做 50 个俯卧撑的习惯, 不如设定一天做两个的习惯。 同样,这个想法对习惯的依赖是非常非常小的,关键是做这些事情几乎不需要任何努力。然后你灌输这个习惯,并逐渐增加你所做的数量。 -一般来说,这个习惯应该在30秒内完成。 +一般来说,这个习惯应该在 30 秒内完成。 另一个关键的概念是要立即庆祝你的胜利,你要在你大脑中建立起成功习惯的和积极情绪的联系。 @@ -69,7 +69,7 @@ Fogg 认为在进行持续的改变时,动力是可怜的工具。它让我们 在刷完牙后,我会用牙线清洁一颗牙齿。 -我再次推荐你看Fogg的书《小习惯》,会对这种方法有更深入的探索。 +我再次推荐你看 Fogg 的书《小习惯》,会对这种方法有更深入的探索。 现在,让我们看看如何将这种方法用于成功转型为一名开发者。 @@ -157,4 +157,4 @@ Fogg 认为在进行持续的改变时,动力是可怜的工具。它让我们 但是,当我们实施小习惯的方法时,我们可能采取的是微型的小步骤,但是我们每天都在采取这些步骤,而且一直在前进。 - 最后,如果你想一步步了解如何找到第一份`web`开发工作,我有一个免费10电子邮件课程[LaunchYourDevCareer.com](https://LaunchYourDevCareer.com).它涵盖我们在这里谈到的很多内容,以及更多内容,为你提供找到第一份工作的路径。 \ No newline at end of file + 最后,如果你想一步步了解如何找到第一份`web`开发工作,我有一个免费 10 电子邮件课程[LaunchYourDevCareer.com](https://LaunchYourDevCareer.com).它涵盖我们在这里谈到的很多内容,以及更多内容,为你提供找到第一份工作的路径。 \ No newline at end of file diff --git a/chinese/articles/how-to-write-a-good-technical-tutorial.md b/chinese/articles/how-to-write-a-good-technical-tutorial.md index e3b6aeb63..7fc31c44e 100644 --- a/chinese/articles/how-to-write-a-good-technical-tutorial.md +++ b/chinese/articles/how-to-write-a-good-technical-tutorial.md @@ -17,9 +17,9 @@ 你也不希望读者在阅读中途就放弃了。有时,你只需要写必要信息,不用什么都写。 -参考这篇文章 [如何更改HTML中的背景色](https://www.freecodecamp.org/news/html-background-color-change-bg-color-tutorial/),读者想要知道更改HTML中背景色的方法。 +参考这篇文章 [如何更改 HTML 中的背景色](https://www.freecodecamp.org/news/html-background-color-change-bg-color-tutorial/),读者想要知道更改 HTML 中背景色的方法。 -作者直接列出了不同的设置方法,而并没有从“什么是HTML和CSS”写起。 +作者直接列出了不同的设置方法,而并没有从“什么是 HTML 和 CSS”写起。 还要记住,教程是写给入门人员看的还是资深人员看的——要尽量写明(可以在必要条件部分写明,或者只提一句教程是写给谁看的)。这将有助于确定你如何阐述内容以及预设读者的背景知识等。 @@ -63,7 +63,7 @@ 如果你要描述一系列事情,可以试着使用标题或副标题来展示该系列中的每个元素,然后使用短段来做简要说明。 -使用标题也会有助于SEO(搜索引擎优化),而且教程会看起来更有说服力,更容易实践。 +使用标题也会有助于 SEO(搜索引擎优化),而且教程会看起来更有说服力,更容易实践。 ## 提供代码片段 diff --git a/chinese/articles/how-to-write-a-great-technical-blog-post-414c414b67f6.md b/chinese/articles/how-to-write-a-great-technical-blog-post-414c414b67f6.md index 19a0ac75b..73d2b953e 100644 --- a/chinese/articles/how-to-write-a-great-technical-blog-post-414c414b67f6.md +++ b/chinese/articles/how-to-write-a-great-technical-blog-post-414c414b67f6.md @@ -7,11 +7,11 @@ #### 五步法从构思到打磨出终稿 -我在开源社区工作了将近5年,建立并推广了包括 [Meteor](https://github.com/meteor/) 和 [Apollo](https://github.com/apollographql/) 在内的开发者工具。在这段时间里,我发现写博客是传播思想最有效的方式之一。 +我在开源社区工作了将近 5 年,建立并推广了包括 [Meteor](https://github.com/meteor/) 和 [Apollo](https://github.com/apollographql/) 在内的开发者工具。在这段时间里,我发现写博客是传播思想最有效的方式之一。 一篇博客文章不需要像视频或会议演讲那样花很长时间来准备,它很容易上手,而且受众面很广。我个人也从写作中受益颇多:帮助我整理思路以及向他人传授我喜欢的技术的同时,也让我被更多人认识。 -自从2014年发布[我的第一篇博文](https://blog.meteor.com/collaborative-3d-scene-builder-in-50-lines-of-code-3c8ac717c658)以来,到目前为止,我已经在Medium上写了[68篇文章](https://medium.com/@stubailo/latest)。有些文章的浏览量超过5万,我的粉丝数也超过1000人。我也为我的朋友和同事编辑过许多文章。在这段时间里,我掌握了一些从提出概念到最终完稿的方法策略。 +自从 2014 年发布[我的第一篇博文](https://blog.meteor.com/collaborative-3d-scene-builder-in-50-lines-of-code-3c8ac717c658)以来,到目前为止,我已经在 Medium 上写了[68 篇文章](https://medium.com/@stubailo/latest)。有些文章的浏览量超过 5 万,我的粉丝数也超过 1000 人。我也为我的朋友和同事编辑过许多文章。在这段时间里,我掌握了一些从提出概念到最终完稿的方法策略。 在本篇文章中,我将介绍我写文章时涉及的五个步骤: @@ -32,11 +32,11 @@ 另一个办法是在一个你认为缺乏内容的领域写作。比如,现在关于如何申请技术会议的文章很少,那么写这方面内容就可以填补技术社区的空白。 -这里有一些具体的文章类型,你可以去看看。例子来自Apollo博客上GraphQL相关的文章: +这里有一些具体的文章类型,你可以去看看。例子来自 Apollo 博客上 GraphQL 相关的文章: -1. 实现特定目标的分步操作指南:[“在React Native中使用Flatlist创建可滚动列表”](https://blog.apollographql.com/loading-data-into-react-natives-flatlist-9646fa9a199b) 或者 [“通过Apollo和Recompose简化React组件”](https://blog.apollographql.com/simplify-your-react-components-with-apollo-and-recompose-8b9e302dea51)。这类文章很适合那些希望读懂就上手,然后迅速离开的读者。 -2. 关于特定主题的深入调查:[“在GraphQL中使用nullability”](https://blog.apollographql.com/using-nullability-in-graphql-2254f84c4ed7) 或者 [“GraphQL查询的剖析”](https://blog.apollographql.com/the-anatomy-of-a-graphql-query-6dffa9e9e747)。这类文章适合那些有时间慢慢深入学习的读者。 -3. 就一个常见主题列出多项实用因素:[“调用GraphQL API的4种简单方法”](https://blog.apollographql.com/4-simple-ways-to-call-a-graphql-api-a6807bcdb355) 或者[“静态GraphQL查询的5个好处”](https://blog.apollographql.com/5-benefits-of-static-graphql-queries-b7fa90b0b69a)。这类有趣又轻量的内容,适合碎片式阅读,读者无需阅读全部内容。 +1. 实现特定目标的分步操作指南:[“在 React Native 中使用 Flatlist 创建可滚动列表”](https://blog.apollographql.com/loading-data-into-react-natives-flatlist-9646fa9a199b) 或者 [“通过 Apollo 和 Recompose 简化 React 组件”](https://blog.apollographql.com/simplify-your-react-components-with-apollo-and-recompose-8b9e302dea51)。这类文章很适合那些希望读懂就上手,然后迅速离开的读者。 +2. 关于特定主题的深入调查:[“在 GraphQL 中使用 nullability”](https://blog.apollographql.com/using-nullability-in-graphql-2254f84c4ed7) 或者 [“GraphQL 查询的剖析”](https://blog.apollographql.com/the-anatomy-of-a-graphql-query-6dffa9e9e747)。这类文章适合那些有时间慢慢深入学习的读者。 +3. 就一个常见主题列出多项实用因素:[“调用 GraphQL API 的 4 种简单方法”](https://blog.apollographql.com/4-simple-ways-to-call-a-graphql-api-a6807bcdb355) 或者[“静态 GraphQL 查询的 5 个好处”](https://blog.apollographql.com/5-benefits-of-static-graphql-queries-b7fa90b0b69a)。这类有趣又轻量的内容,适合碎片式阅读,读者无需阅读全部内容。 现在,我想消除一些你们常见的担忧。 @@ -47,7 +47,7 @@ ![49vpFzkVXA9fHTQoMGGDKEnqVDBIY4-eZ8d7](https://cdn-media-1.freecodecamp.org/images/49vpFzkVXA9fHTQoMGGDKEnqVDBIY4-eZ8d7) -灯泡-常用来象征思想 [James Pond来自Unsplash](https://unsplash.com/photos/1qkyck-UL3g) +灯泡-常用来象征思想 [James Pond 来自 Unsplash](https://unsplash.com/photos/1qkyck-UL3g) ### 2\. 确定你的写作目标和读者对象 @@ -58,9 +58,9 @@ - **读者对象:** 想开始写博客的人,尤其是想写技术博客却还没写过的人。 - **写作目标:** 提供一套具体的写作步骤和指导方法,便于他们开始行动起来。 -有了这些以后,就要专注于文章的重点了,删掉没有意义的内容,不要增加看起来有关联但很多余的细节。我发现那种阅读时间在5至10分钟,相对简洁的文章是最好的。 +有了这些以后,就要专注于文章的重点了,删掉没有意义的内容,不要增加看起来有关联但很多余的细节。我发现那种阅读时间在 5 至 10 分钟,相对简洁的文章是最好的。 -了解读者背景可以帮助你基于读者现有知识水平去定制化写作。而且可以帮助你决定如何去发布和推广你的内容。比如,我就希望在freeCodeCamp上发表这篇文章,因为在我的目标读者中,可能已经有很多人读过此类文章了。 +了解读者背景可以帮助你基于读者现有知识水平去定制化写作。而且可以帮助你决定如何去发布和推广你的内容。比如,我就希望在 freeCodeCamp 上发表这篇文章,因为在我的目标读者中,可能已经有很多人读过此类文章了。 ### 3\. 文章有开头、中间、结尾 @@ -83,7 +83,7 @@ ![op8670wN9BlN6an-m9qZpZcFbitHwZnDFief](https://cdn-media-1.freecodecamp.org/images/op8670wN9BlN6an-m9qZpZcFbitHwZnDFief) -就像道路标识一样,文章的结构可以引导读者阅读。[Bart Anestin来自Unsplash](https://unsplash.com/photos/bXMbMw560C8) +就像道路标识一样,文章的结构可以引导读者阅读。[Bart Anestin 来自 Unsplash](https://unsplash.com/photos/bXMbMw560C8) ### 4\. 获取反馈并迭代文章 @@ -94,7 +94,7 @@ 你应该问你的文章审阅者一些什么问题呢?我的主要建议是尽可能地不设限。尽量不要提前解释你的目标。把草稿原封不动地交给审阅者,然后问他们从中得到了什么或者有什么修改建议。当读者在网上看到你的文章时,他们就因为已经被考虑进目标读者中而自然认可你的文章了。 -反馈主要是要验证一件事:这篇文章能否实现你在第2步中决定的目标?如果不能,那就一直迭代,直到你确信目标能实现为止。 +反馈主要是要验证一件事:这篇文章能否实现你在第 2 步中决定的目标?如果不能,那就一直迭代,直到你确信目标能实现为止。 ### 5\. 添加点睛之笔:包装、发布和推广 @@ -103,30 +103,30 @@ #### 包装 -想一个出色的标题和副标题,并确保文章至少有一张配图。文章分享到Twitter或Facebook时,读者会看到这些标题和图片的。这也是吸引读者阅读的机会。 +想一个出色的标题和副标题,并确保文章至少有一张配图。文章分享到 Twitter 或 Facebook 时,读者会看到这些标题和图片的。这也是吸引读者阅读的机会。 同样重要的是,你的文章要让人觉得专业,这样你的内容才能真正出彩。文章中应该避免拼写错误、语法错误或者奇怪的格式。如果你有善于发现细节的朋友,可以请他们先阅读一遍文章,然后你再发布出去。 -[freeCodeCamp上关于文章的发布](https://medium.freecodecamp.org/how-to-get-published-in-the-freecodecamp-medium-publication-9b342a22400e) 也有一些文章,提到了写作风格和样式的技巧。既然你已经在文章中投入了这么多精力,那么额外多付出一些努力去打磨它,让它有更大的影响力也是值得的。 +[freeCodeCamp 上关于文章的发布](https://medium.freecodecamp.org/how-to-get-published-in-the-freecodecamp-medium-publication-9b342a22400e) 也有一些文章,提到了写作风格和样式的技巧。既然你已经在文章中投入了这么多精力,那么额外多付出一些努力去打磨它,让它有更大的影响力也是值得的。 最后,请务必注明你引用了谁的作品或者谁帮助过审阅和编辑你的文章。 #### 发布 -你就快大功告成了!选择最利于触达读者的平台来发布文章。通常,Medium是一个发表技术文章很好的平台,在这儿你的文章很容易被读者发现。 +你就快大功告成了!选择最利于触达读者的平台来发布文章。通常,Medium 是一个发表技术文章很好的平台,在这儿你的文章很容易被读者发现。 -为了效果更好,试试将你的文章发表在有助于分享你内容的平台上-基于这种考虑,我选择了freeCodeCamp。因为我认为这个建议是考虑了freeCodeCamp上的读者人群。如果你也想这样做,[这里有他们给出的提交文章的指南](https://medium.freecodecamp.org/how-to-get-published-in-the-freecodecamp-medium-publication-9b342a22400e)。你感兴趣的发布平台也可能正在寻找相应的文章,所以大胆去联系吧! +为了效果更好,试试将你的文章发表在有助于分享你内容的平台上-基于这种考虑,我选择了 freeCodeCamp。因为我认为这个建议是考虑了 freeCodeCamp 上的读者人群。如果你也想这样做,[这里有他们给出的提交文章的指南](https://medium.freecodecamp.org/how-to-get-published-in-the-freecodecamp-medium-publication-9b342a22400e)。你感兴趣的发布平台也可能正在寻找相应的文章,所以大胆去联系吧! #### 推广 -即使你真正发布了文章,工作也还没有结束!如果你想要读者看到你写的东西,并从中受益,那就要把文章分享到读者经常阅读的地方。像包括Facebook群组、Reddit、Hacker News、LinkedIn或者其它任何平台。另外,也要在你自己的社交媒体账户分享你的创作,比如Twitter。你的朋友会很乐意阅读、分享和点赞你的文章。 +即使你真正发布了文章,工作也还没有结束!如果你想要读者看到你写的东西,并从中受益,那就要把文章分享到读者经常阅读的地方。像包括 Facebook 群组、Reddit、Hacker News、LinkedIn 或者其它任何平台。另外,也要在你自己的社交媒体账户分享你的创作,比如 Twitter。你的朋友会很乐意阅读、分享和点赞你的文章。 现在,你已经大功告成了!去喝杯咖啡或者散散步吧-把一篇博客文章从头到尾写完可不是一件小事。阅读来自社区的所有反馈和回复。当你有了另一个想法时,又继续这样来一遍吧! ![lMYImUN-AHrgvCD74GhFaHyXGkIa6L0zP15z](https://cdn-media-1.freecodecamp.org/images/lMYImUN-AHrgvCD74GhFaHyXGkIa6L0zP15z) -精美的包装对美味的甜甜圈来说是锦上添花。[Zach Miles来自Unsplash](https://unsplash.com/photos/BE9AifuJfD4) +精美的包装对美味的甜甜圈来说是锦上添花。[Zach Miles 来自 Unsplash](https://unsplash.com/photos/BE9AifuJfD4) ### 除了实践,没有别的选择 @@ -136,4 +136,4 @@ 所以,即使你的第一篇、第二篇或者第三篇文章没有成功,也要继续尝试写新的东西。把你的想法写出来,并不断优化。世界想听听你要说点什么,去告诉他们吧! -_非常感谢[Anvisha Pai](https://www.freecodecamp.org/news/how-to-write-a-great-technical-blog-post-414c414b67f6/undefined)、[Angela Zhang](https://www.freecodecamp.org/news/how-to-write-a-great-technical-blog-post-414c414b67f6/undefined)、[Katie Siegel](https://www.freecodecamp.org/news/how-to-write-a-great-technical-blog-post-414c414b67f6/undefined)和freeCodeCamp的编辑人员,帮助审阅这篇文章。_ +_非常感谢[Anvisha Pai](https://www.freecodecamp.org/news/how-to-write-a-great-technical-blog-post-414c414b67f6/undefined)、[Angela Zhang](https://www.freecodecamp.org/news/how-to-write-a-great-technical-blog-post-414c414b67f6/undefined)、[Katie Siegel](https://www.freecodecamp.org/news/how-to-write-a-great-technical-blog-post-414c414b67f6/undefined)和 freeCodeCamp 的编辑人员,帮助审阅这篇文章。_ diff --git a/chinese/articles/how-to-write-api-documentation-like-a-pro.md b/chinese/articles/how-to-write-api-documentation-like-a-pro.md index f89eee4c7..c6148a5e1 100644 --- a/chinese/articles/how-to-write-api-documentation-like-a-pro.md +++ b/chinese/articles/how-to-write-api-documentation-like-a-pro.md @@ -9,59 +9,59 @@ 谢天谢地,你有一本方便的设备手册来帮助你。你只需要按照手册中详细说明的步骤进行操作,然后就可以了。你的家庭影院系统已经准备好播放你喜欢的歌曲了。 -就像设备手册如何指导你进行设置和安装一样,API文档可以帮助指导你完成配置API。 +就像设备手册如何指导你进行设置和安装一样,API 文档可以帮助指导你完成配置 API。 -## 什么是API文档? +## 什么是 API 文档? -在深入研究API文档之前,让我简单解释一下什么是API以及它的基本功能。 +在深入研究 API 文档之前,让我简单解释一下什么是 API 以及它的基本功能。 -API是应用编程接口的首字母缩写。 +API 是应用编程接口的首字母缩写。 ![7020614](https://www.freecodecamp.org/news/content/images/2022/04/7020614.jpg) -通过API将设备连接到数据库。 +通过 API 将设备连接到数据库。 无论你是初级编码员还是高级开发人员,你都会在你的软件开发旅程中经常遇到这个术语。它是你的计算机、手机或应用程序与外部资源之间的桥梁。 -换句话说,API赋予你的软件与其他软件程序、数据库或资源互动的能力。与其为你的应用程序的某一特定功能编写程序,你可以使用类似功能的现成的API。 +换句话说,API 赋予你的软件与其他软件程序、数据库或资源互动的能力。与其为你的应用程序的某一特定功能编写程序,你可以使用类似功能的现成的 API。 -许多API是公共的(免费的),而其他的则是私有的,需要付费购买一个让你访问API的私钥。有不同类型的API,如REST(Representational State Transfer),SOAP(Simple Object Access Protocol),以及其他。 +许多 API 是公共的(免费的),而其他的则是私有的,需要付费购买一个让你访问 API 的私钥。有不同类型的 API,如 REST(Representational State Transfer),SOAP(Simple Object Access Protocol),以及其他。 -那么什么是API文档?嗯,它是一个书面指南,说明API的功能,如何将其整合到你的程序中,以及API的使用案例,同时还有例子。 +那么什么是 API 文档?嗯,它是一个书面指南,说明 API 的功能,如何将其整合到你的程序中,以及 API 的使用案例,同时还有例子。 -请记住,API文档是技术内容。这意味着它将包含一些技术术语,但仍然应该是可读的和容易理解的。 +请记住,API 文档是技术内容。这意味着它将包含一些技术术语,但仍然应该是可读的和容易理解的。 -## 谁应该写API文档? +## 谁应该写 API 文档? -API是由软件开发人员编写的。由于软件开发人员直接参与了API的建设和使用,所以他们更容易创建文档。 +API 是由软件开发人员编写的。由于软件开发人员直接参与了 API 的建设和使用,所以他们更容易创建文档。 -软件开发人员编写API文档的缺点是,他们从一个非常技术性的角度来写,这可能使文档相当难以理解。另一个问题是,API开发者在开发API的同时,还要花费更多的时间来创建文档。 +软件开发人员编写 API 文档的缺点是,他们从一个非常技术性的角度来写,这可能使文档相当难以理解。另一个问题是,API 开发者在开发 API 的同时,还要花费更多的时间来创建文档。 -因此,一个好的选择是将API文档的任务分配给技术作家。技术作家是将内容写作和技术知识的专业知识结合起来的人,他制作的文档不仅是技术性的,而且是信息丰富的,可以理解的。 +因此,一个好的选择是将 API 文档的任务分配给技术作家。技术作家是将内容写作和技术知识的专业知识结合起来的人,他制作的文档不仅是技术性的,而且是信息丰富的,可以理解的。 -技术撰稿人从API开发者那里了解API,然后创建教程、例子和其他内容,用于编写文档。 +技术撰稿人从 API 开发者那里了解 API,然后创建教程、例子和其他内容,用于编写文档。 -同时,API开发者给技术撰稿人提供意见,以确保书面文档的准确性,必要时他们可以向撰稿人提供更多信息。 +同时,API 开发者给技术撰稿人提供意见,以确保书面文档的准确性,必要时他们可以向撰稿人提供更多信息。 -我们的目标是让每个人一起工作,制作出能够充分解释API并引导用户的文档,而不至于出现混乱。 +我们的目标是让每个人一起工作,制作出能够充分解释 API 并引导用户的文档,而不至于出现混乱。 -如果你对编写API的文档感兴趣,但不知道从哪里开始,也不知道如何开始,这篇文章将帮助你开始。 +如果你对编写 API 的文档感兴趣,但不知道从哪里开始,也不知道如何开始,这篇文章将帮助你开始。 我可以从这里感受到你的兴奋,所以让我们开始行动吧! -## 如何开始编写API文档 +## 如何开始编写 API 文档 -在编写API文档时,首先要创建几个大纲。这将使你对你打算写的东西有一个概述。 +在编写 API 文档时,首先要创建几个大纲。这将使你对你打算写的东西有一个概述。 -接下来就是为你创建的每个大纲收集信息。这可以通过从API开发者那里获得API描述、使用的语言、其他参考资料和样本案例来实现。你也可以查看API的现场演示,这样你就有了关于它如何工作的第一手经验。 +接下来就是为你创建的每个大纲收集信息。这可以通过从 API 开发者那里获得 API 描述、使用的语言、其他参考资料和样本案例来实现。你也可以查看 API 的现场演示,这样你就有了关于它如何工作的第一手经验。 最后,把你收集到的细节结合起来,按逻辑顺序排列。 -记得校对你的文件,并在公开前与API开发者分享,以便进行任何修正或补充。 +记得校对你的文件,并在公开前与 API 开发者分享,以便进行任何修正或补充。 现在你知道从哪里开始,你如何把这些零碎的东西放在一起,使它们成为一个有意义的整体? -## API文档中应包括哪些内容 +## API 文档中应包括哪些内容 ![API-Doc](https://www.freecodecamp.org/news/content/images/2022/04/API-Doc.png) @@ -69,39 +69,39 @@ API是由软件开发人员编写的。由于软件开发人员直接参与了AP 这类似于项目报告的摘要页。 -概述应该包含API的摘要和它所解决的问题。它还可以包括使用这个特定API比其他类似API的好处。 +概述应该包含 API 的摘要和它所解决的问题。它还可以包括使用这个特定 API 比其他类似 API 的好处。 ### 2\. 教程 这是文档的主要部分。 -它应该包括你所使用的不同的内容格式,向用户解释API的概念。它还可以包括供参考的链接,以及整合API和使用它的步骤指南,以便它能正常运作。 +它应该包括你所使用的不同的内容格式,向用户解释 API 的概念。它还可以包括供参考的链接,以及整合 API 和使用它的步骤指南,以便它能正常运作。 ### 3\. 例子 -当你解释了API的工作原理和/或提供了分项步骤,展示调用、响应、错误处理和其他与开发者如何与API互动有关的操作的例子是个好主意。 +当你解释了 API 的工作原理和/或提供了分项步骤,展示调用、响应、错误处理和其他与开发者如何与 API 互动有关的操作的例子是个好主意。 ### 4\. 词汇表 -虽然这是可选的,但我建议为你的API文档添加一个词汇表页面。 +虽然这是可选的,但我建议为你的 API 文档添加一个词汇表页面。 为了避免用户被冗长的文本块所烦扰,你在整个文档中使用的各种术语、模式、图像等的解释都可以集中到词汇表中。然后你可以在文档中引用这些东西,并链接到词汇表。 ![A PC and a notepad](https://www.freecodecamp.org/news/content/images/2022/04/Talk2Her-Foundation--15-.png) -## 如何编写有用的API文档 +## 如何编写有用的 API 文档 ### 了解 API -正如我们刚才所讨论的,你应该对你所记录的API有第一手的知识。记住,你的目标是引导那些可能对API没有任何了解的潜在用户。你不会想让他们感到困惑,对吗? +正如我们刚才所讨论的,你应该对你所记录的 API 有第一手的知识。记住,你的目标是引导那些可能对 API 没有任何了解的潜在用户。你不会想让他们感到困惑,对吗? -如果你对产品的架构、功能和其他重要信息有扎实的了解,你就能有效地编写API的产品描述部分,而不需要做任何猜测。 +如果你对产品的架构、功能和其他重要信息有扎实的了解,你就能有效地编写 API 的产品描述部分,而不需要做任何猜测。 -如果你对你要写的API没有充分的了解,那就花点时间做研究,尽可能多地收集信息。自己使用该API,这样你就能对它的工作原理有重要的了解。 +如果你对你要写的 API 没有充分的了解,那就花点时间做研究,尽可能多地收集信息。自己使用该 API,这样你就能对它的工作原理有重要的了解。 ### 使用相关的内容 -API文档不只限于书面指南。你可以使用简短的视频或PPT幻灯片来说明API的整合情况。 +API 文档不只限于书面指南。你可以使用简短的视频或 PPT 幻灯片来说明 API 的整合情况。 在写文档的时候说明不同的用例。这将有助于读者认识到哪一个与他们的相似,或者找到一个他们可以很容易联系到的。 @@ -109,11 +109,11 @@ API文档不只限于书面指南。你可以使用简短的视频或PPT幻灯 ### 要清楚,即使你需要技术性的 -API是软件或硬件的指南,所以你在写文档时需要使用一些技术术语。如果你想成为一名技术作家,请抵制含糊其辞。 +API 是软件或硬件的指南,所以你在写文档时需要使用一些技术术语。如果你想成为一名技术作家,请抵制含糊其辞。 一份好的文件不是有复杂的语法结构,而是可亲的、直接的、清晰的。只有用简单易懂的语言来写,才会有亲和力。 -你的API文档应该以最简单的形式出现,但它不应该遗漏任何重要的细节。另外,确保你在第一次使用缩略语和技术术语时对其进行解释,或在文档的最后将其放在词汇表中。 +你的 API 文档应该以最简单的形式出现,但它不应该遗漏任何重要的细节。另外,确保你在第一次使用缩略语和技术术语时对其进行解释,或在文档的最后将其放在词汇表中。 ### 指南分项说明 @@ -131,33 +131,33 @@ API是软件或硬件的指南,所以你在写文档时需要使用一些技 一个彻底的检查过程可以帮助你尽量减少任何错误并产生清晰的文件。 -## API文档的最佳工具 +## API 文档的最佳工具 -编写API文档可能相当耗费时间,而且难以维护。但是一个好的文档工具可以缓解大部分,甚至是所有的问题。 +编写 API 文档可能相当耗费时间,而且难以维护。但是一个好的文档工具可以缓解大部分,甚至是所有的问题。 -现在有许多工具可以让你的API文档之旅变得更容易。使用工具的好处是这些工具提供的协作功能和标准模板,而不是从头开始。 +现在有许多工具可以让你的 API 文档之旅变得更容易。使用工具的好处是这些工具提供的协作功能和标准模板,而不是从头开始。 下面是一些流行的工具及其优点的清单。 ### Postman -[Postman](https://www.postman.com/)是一个用于构建和维护API的平台,具有创建API文档的功能。 +[Postman](https://www.postman.com/)是一个用于构建和维护 API 的平台,具有创建 API 文档的功能。 -Postman使用其机器可读的文档工具,使API文档过程更容易和更快。你可以免费注册Postman并将其安装在你的PC上。 +Postman 使用其机器可读的文档工具,使 API 文档过程更容易和更快。你可以免费注册 Postman 并将其安装在你的 PC 上。 -尽管Postman对其制作的所有API文档自动提供更新,但其用户界面一开始可能难以理解。 +尽管 Postman 对其制作的所有 API 文档自动提供更新,但其用户界面一开始可能难以理解。 ### DapperDox -[DapperDox](http://dapperdox.io/) 是一个开源的API文档工具,提供各种主题来创建你的文档。这个工具结合了图表、规范和其他内容类型,为你提供更好的文档。 +[DapperDox](http://dapperdox.io/) 是一个开源的 API 文档工具,提供各种主题来创建你的文档。这个工具结合了图表、规范和其他内容类型,为你提供更好的文档。 它的优点是允许作者用 GitHub 风格的 markdown 编写,但这个工具的更新是不定期的。 ### SwaggerHub -[SwaggerHub](https://swagger.io/tools/swaggerhub/) 是在许多技术作者中一个流行的API文档工具,因为它是互动的,易于使用。 +[SwaggerHub](https://swagger.io/tools/swaggerhub/) 是在许多技术作者中一个流行的 API 文档工具,因为它是互动的,易于使用。 -虽然它对初学者很友好,但除了个人使用外,它需要付费。因此,如果你是一个组织的一部分,想使用SwaggerHub,你的组织将不得不为它付费。 +虽然它对初学者很友好,但除了个人使用外,它需要付费。因此,如果你是一个组织的一部分,想使用 SwaggerHub,你的组织将不得不为它付费。 无论你是选择这里列出的工具还是选择其他工具,你都应该考虑以下几点: @@ -165,17 +165,17 @@ Postman使用其机器可读的文档工具,使API文档过程更容易和更 - 你的技术水平如何?你是初学者还是专家? - 用户界面和用户体验如何? -## 一些值得学习的API文档的例子 +## 一些值得学习的 API 文档的例子 -下面是一些API文档,它们给你开始编写优秀的API文档的灵感。这些文件中的每一个都以简单的步骤和可理解的术语向开发者详细介绍了产品API的用法。 +下面是一些 API 文档,它们给你开始编写优秀的 API 文档的灵感。这些文件中的每一个都以简单的步骤和可理解的术语向开发者详细介绍了产品 API 的用法。 ### GitHub API Docs -GitHub提供了非常有用的文档--这并不奇怪。在这里查看他们的API文档: +GitHub 提供了非常有用的文档--这并不奇怪。在这里查看他们的 API 文档: [GitHub API Docs](https://docs.github.com/en/rest/guides/getting-started-with-the-rest-api) -REST API是开发人员用来访问网络或数据库数据的常用API。Github的这个文档包括概述、指南,甚至是如何在你的程序中使用REST API的代码。 +REST API 是开发人员用来访问网络或数据库数据的常用 API。Github 的这个文档包括概述、指南,甚至是如何在你的程序中使用 REST API 的代码。 这些文档的有趣之处在于,无论你的技术水平如何,你都可以轻松地理解它。 @@ -183,20 +183,20 @@ REST API是开发人员用来访问网络或数据库数据的常用API。Github [Paystack API Docs](https://paystack.com/docs/) -你是否正在构建一个需要支付的应用程序?Paystack是一个用于支付的金融技术解决方案。他们的团队为开发者提供详细的信息,说明如何在你的程序中使用Paystack的API。这更像是提供一本关于使用API的手册,以避免在将API消耗到你的程序中时出现混乱。 +你是否正在构建一个需要支付的应用程序?Paystack 是一个用于支付的金融技术解决方案。他们的团队为开发者提供详细的信息,说明如何在你的程序中使用 Paystack 的 API。这更像是提供一本关于使用 API 的手册,以避免在将 API 消耗到你的程序中时出现混乱。 ### Twitter API Docs -[推特API文档](https://developer.twitter.com/en/docs/twitter-api) +[推特 API 文档](https://developer.twitter.com/en/docs/twitter-api) -Twitter的API文档解释了开发者如何与App互动。这些文件清楚地详述了不同的部分(用户、推文、直接信息等)和它们的操作。 +Twitter 的 API 文档解释了开发者如何与 App 互动。这些文件清楚地详述了不同的部分(用户、推文、直接信息等)和它们的操作。 虽然更多的信息需要权限访问,但你只需点击一下链接,就可以访问基本的信息。 ## 结语 -文档阐述了一个工具是如何工作的,以便其他人能够正确使用它。创建API文档并不容易,但创建有用的文档并不像你想象的那么难。 +文档阐述了一个工具是如何工作的,以便其他人能够正确使用它。创建 API 文档并不容易,但创建有用的文档并不像你想象的那么难。 只要记住:从写你的初稿开始,每天改进它,当你遇到困难时,向导师或资深同事寻求帮助。 -现在就去写那些将与下一个世界级产品一起使用的API文档吧。 +现在就去写那些将与下一个世界级产品一起使用的 API 文档吧。 diff --git a/chinese/articles/how-to-write-clean-code.md b/chinese/articles/how-to-write-clean-code.md index 4da28b395..5fc02da7d 100644 --- a/chinese/articles/how-to-write-clean-code.md +++ b/chinese/articles/how-to-write-clean-code.md @@ -16,10 +16,10 @@ - [写出“clean code”意味着什么以及我为什么要注意它?](##what-does-it-mean-to-write-clean-code-and-why-should-i-care) - [我如何评估一个代码库是否整洁?](##how-can-i-assess-whether-a-codebase-is-clean-or-not) - [写出整洁的代码的一些技巧和惯例](##tips-and-conventions-to-write-cleaner-code) - - [有效、效率和简单 ](###effectiveness-efficiency-and-simplicity) + - [有效、效率和简单](###effectiveness-efficiency-and-simplicity) - [格式和语法](###format-and-syntax) - [命名](###naming) - - [简洁VS清晰](###conciseness-versus-clarity) + - [简洁 VS 清晰](###conciseness-versus-clarity) - [复用性](###re-usability) - [清晰的执行流程](###clear-flow-of-execution) - [单一制责原则](###single-responsibility-principle) @@ -94,7 +94,7 @@ function sumArrayEfficient(array) { } ``` -在这里, `sumArrayEfficient` 函数使用`reduce` 方法去计算数组里的数字的总和。 `reduce` 方法将函数应用于数组的每个元素,并累加结果。在这种情况下,函数只需将每个元素添加到从0开始的累加器中。 +在这里, `sumArrayEfficient` 函数使用`reduce` 方法去计算数组里的数字的总和。 `reduce` 方法将函数应用于数组的每个元素,并累加结果。在这种情况下,函数只需将每个元素添加到从 0 开始的累加器中。 这是一个更有效的解决方案,因为它只需要对数组进行一次迭代,并在进行时对每个元素执行求和操作。 @@ -173,9 +173,9 @@ const my_name = 'John' <h3 id="naming">命名</h3> -清晰而有描述性地命名变量和函数,对于书写clean code也是十分钟重要的一个方面。它可以帮助提高代码的可读性和维护能力。当命名选择良好时,其他开发人员可以快速理解变量或函数在做什么,以及它与代码的其余部分有何关系。 +清晰而有描述性地命名变量和函数,对于书写 clean code 也是十分钟重要的一个方面。它可以帮助提高代码的可读性和维护能力。当命名选择良好时,其他开发人员可以快速理解变量或函数在做什么,以及它与代码的其余部分有何关系。 -下面是Javascript中的两个例子,用来展示清晰且有描述性地命名的重要性: +下面是 Javascript 中的两个例子,用来展示清晰且有描述性地命名的重要性: ```javascript // Example 1: Poor Naming @@ -188,7 +188,7 @@ function ab(a, b) { ab(5, 3); ``` -在本例中,我们有一个函数,它接受两个参数,将它们与常量值10相加,并将结果记录到控制台。函数名称和变量名选择不当,没有给出任何指示函数的作用或变量代表什么。 +在本例中,我们有一个函数,它接受两个参数,将它们与常量值 10 相加,并将结果记录到控制台。函数名称和变量名选择不当,没有给出任何指示函数的作用或变量代表什么。 ```javascript // Example 1: Good Naming @@ -269,7 +269,7 @@ console.log(circleArea, rectangleArea, triangleArea); 这个例子定义了三个函数,分别计算圆、矩形和三角形的面积。每个函数执行一个特定的任务,但它们都不能重复用于其他类似任务。 -此外,如果将来需要更改值,使用直接赋值的PI值可能会导致错误。该代码效率低下,因为它多次重复相同的逻辑。 +此外,如果将来需要更改值,使用直接赋值的 PI 值可能会导致错误。该代码效率低下,因为它多次重复相同的逻辑。 ```javascript // Example 2: Implementing re-usability diff --git a/chinese/articles/how-to-write-helpful-error-messages-to-improve-your-apps-ux.md b/chinese/articles/how-to-write-helpful-error-messages-to-improve-your-apps-ux.md index 94614a91d..d4b5cc2e0 100644 --- a/chinese/articles/how-to-write-helpful-error-messages-to-improve-your-apps-ux.md +++ b/chinese/articles/how-to-write-helpful-error-messages-to-improve-your-apps-ux.md @@ -140,7 +140,7 @@ _出离愤怒退出程序前的截图_ - 前端应用所有与上下文相关的信息 - 前端将这些信息显示给终端用户。 -你可以在 [CodeSandbox](https://codesandbox.io/s/amazing-platform-dxtjc?file=/src/App.js) 亲自上手体验一下。 CodeSandbox 向一个虚拟服务器发出请求,随机返回12个错误代码中的一个。 +你可以在 [CodeSandbox](https://codesandbox.io/s/amazing-platform-dxtjc?file=/src/App.js) 亲自上手体验一下。 CodeSandbox 向一个虚拟服务器发出请求,随机返回 12 个错误代码中的一个。 客户端将使用错误代码,从报错信息库中检索一个合理的报错信息。接着,客户端向用户显示报错信息。如果代码中没有指定的信息,就会显示通用报错信息(它们通常都很糟糕)。 @@ -151,7 +151,7 @@ _出离愤怒退出程序前的截图_ ## 如何设置你的报错信息 -注:你可以在这里找到 [repo](https://github.com/andrico1234/sane-error-messages#readme)。如果你在教程过程中遇到任何问题,可以在GitHub上提出。 +注:你可以在这里找到 [repo](https://github.com/andrico1234/sane-error-messages#readme)。如果你在教程过程中遇到任何问题,可以在 GitHub 上提出。 开始时运行 @@ -243,7 +243,7 @@ function riskyFunction() { ### 为什么服务器端不能直接返回报错信息? -服务器不应涉及任何面向客户端的逻辑。但如果你够走运,与每次请求失败时都给出有用错误代码的API协同工作,你将很快成功。 +服务器不应涉及任何面向客户端的逻辑。但如果你够走运,与每次请求失败时都给出有用错误代码的 API 协同工作,你将很快成功。 ### 我是否需要为每个 API 消费者创建一个报错信息实例? @@ -259,7 +259,7 @@ function riskyFunction() { ****《微副本:完整指南》**** 在上文我已经提到过这本书。在讨论如何让面向用户的产品更加人性化方面,它是我最喜欢的书之一。 -这本书的作者 Kinneret Yifrah 慷慨地提供了一张9折的优惠券,你可以在[这里](https://www.microcopybook.com/)购买。 +这本书的作者 Kinneret Yifrah 慷慨地提供了一张 9 折的优惠券,你可以在[这里](https://www.microcopybook.com/)购买。 电子书折扣码:andrico-ebook diff --git a/chinese/articles/how-to-write-unit-tests-in-react.md b/chinese/articles/how-to-write-unit-tests-in-react.md index 213ed6449..ca934f447 100644 --- a/chinese/articles/how-to-write-unit-tests-in-react.md +++ b/chinese/articles/how-to-write-unit-tests-in-react.md @@ -43,7 +43,7 @@ Any test in React, no matter how complicated, follows this structure: ## How to Set Up Our Project -First, create the app with `create-react-app`. We’ll be using [Jest](https://jestjs.io/docs/getting-started) and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/). Both of them come pre - installed with `create-react-app`. You can see what it looks like here: +First, create the app with `create-react-app`. We’ll be using [Jest](https://jestjs.io/docs/getting-started) and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/). Both of them come pre-installed with `create-react-app`. You can see what it looks like here: ```javascript "dependencies": { diff --git a/chinese/articles/how-to-write-your-first-technical-book.md b/chinese/articles/how-to-write-your-first-technical-book.md index dd6c4bb89..622c9180b 100644 --- a/chinese/articles/how-to-write-your-first-technical-book.md +++ b/chinese/articles/how-to-write-your-first-technical-book.md @@ -11,19 +11,19 @@ 在这篇帖子里,我会尽量记录我的整个写作历程。所有我都会谈到,包括我的动机、遇到的难关以及用到的工具、技术和资源。 -我的书重点讲的是[Hyperledger Composer区块链](https://schadokar.dev/ebooks/)工具。这个工具是完全免费的,目前仅支持PDF格式。 +我的书重点讲的是[Hyperledger Composer 区块链](https://schadokar.dev/ebooks/)工具。这个工具是完全免费的,目前仅支持 PDF 格式。 帖子里提到的所有要点也同样有助于技术博客写作。所以,咱们开始吧,一起深入了解我到底学到了什么。 # 动机 -从2018年年底我就开始写技术文章和教程了。到现在,我已经对写文章或教程的流程得心应手了。我知道自己该如何写好一篇文章,以及应该使用哪种工具来写作。 +从 2018 年年底我就开始写技术文章和教程了。到现在,我已经对写文章或教程的流程得心应手了。我知道自己该如何写好一篇文章,以及应该使用哪种工具来写作。 但,如果说到写一本书—尤其是技术书—这是很不同以往的。 我写这本书的动机是好奇心。我想知道那些作者是如何写书的。他们的思考过程是怎样的?他们会用到什么工具?当然,我还想知道写一本书是什么样的体验?? -我是一名软件工程师,从2018年起我就一直从事区块链相关工作。我了解过不同的区块链,比如Ethereum和Hyperledger Fabric。我也用过很多工具,比如[truffle](https://www.trufflesuite.com/)、[remix](https://remix.ethereum.org/)和[hyperledger composer](https://hyperledger.github.io/composer/)。 +我是一名软件工程师,从 2018 年起我就一直从事区块链相关工作。我了解过不同的区块链,比如 Ethereum 和 Hyperledger Fabric。我也用过很多工具,比如[truffle](https://www.trufflesuite.com/)、[remix](https://remix.ethereum.org/)和[hyperledger composer](https://hyperledger.github.io/composer/)。 有好多不同的东西我都想写,像**Ethereum**或者**Hyperledger Fabric**。 @@ -33,7 +33,7 @@ 在开始之前,我犹豫该用哪种工具或编辑器来写书。 -我应该用Word、Google Docs还是其它什么工具呢? +我应该用 Word、Google Docs 还是其它什么工具呢? 主要的问题是如何正确排版代码片段。这些编辑器都不是为技术写作设计的。 有不同的添加代码方法,但那需要额外的排版工作。 @@ -46,19 +46,19 @@ 我用我最喜欢的代码编辑器来写书。是的,**VS Code**?。 -我花了几天时间在网上搜索,没有一篇文章建议说你需要任何特定的工具或编辑器来写技术书籍。VS Code或者Atom就足够了。 +我花了几天时间在网上搜索,没有一篇文章建议说你需要任何特定的工具或编辑器来写技术书籍。VS Code 或者 Atom 就足够了。 -我在**VS Code**里用我最喜欢的Markdown格式写完了整本书。为了让写作更容易,我用到了一些Markdown插件,比如:**Markdown All in One**和**Markdown Preview Enhanced**。 +我在**VS Code**里用我最喜欢的 Markdown 格式写完了整本书。为了让写作更容易,我用到了一些 Markdown 插件,比如:**Markdown All in One**和**Markdown Preview Enhanced**。 -第一个插件帮助写作,第二个插件帮助预览。可以看到Markdown转换为HTML或其它格式后是什么样子,以及会有什么变化。 +第一个插件帮助写作,第二个插件帮助预览。可以看到 Markdown 转换为 HTML 或其它格式后是什么样子,以及会有什么变化。 -**Markdown All in One**也有预览模式,但**Markdown Preview Enhanced**有多个主题和选项,可以将Markdown文件以HTML、PDF和其它可读格式如epub或Mobi导出。 +**Markdown All in One**也有预览模式,但**Markdown Preview Enhanced**有多个主题和选项,可以将 Markdown 文件以 HTML、PDF 和其它可读格式如 epub 或 Mobi 导出。 提醒一下—想导出其它文档格式需要你在电脑上安装**Pandoc**。 -> 我是Windows系统用户,对于Mac系统用户,我发现还可以选择很多很棒的编辑器,比如[bear](https://bear.app/)和[ulysses](https://ulysses.app/)等等。 +> 我是 Windows 系统用户,对于 Mac 系统用户,我发现还可以选择很多很棒的编辑器,比如[bear](https://bear.app/)和[ulysses](https://ulysses.app/)等等。 -最近,我发现了很多在**Windows系统**和**MacOS系统**都能用来写书的Markdown编辑器。搜一搜[Notion](https://www.notion.so/)、[Typora](https://typora.io/)、[iA Writer](https://ia.net/writer)和[SimpleNote](https://simplenote.com/)。 +最近,我发现了很多在**Windows 系统**和**MacOS 系统**都能用来写书的 Markdown 编辑器。搜一搜[Notion](https://www.notion.so/)、[Typora](https://typora.io/)、[iA Writer](https://ia.net/writer)和[SimpleNote](https://simplenote.com/)。 划重点:**不要浪费太多时间去寻找完美的编辑器**。选择了一款编辑器就开始写作。慢慢你就会懂得的。 @@ -68,7 +68,7 @@ 简而言之,我想知道我到底该如何写这本书,这样读者就能从书中得到最大的收获。 -这些问题让我抓耳挠腮了很久。一开始,我改了有4、5次办法。 +这些问题让我抓耳挠腮了很久。一开始,我改了有 4、5 次办法。 在这一点上,我的建议是花一点时间真正思考你的方法。因为一旦你写到中途,想要做更改就不容易了。 @@ -95,9 +95,9 @@ 我把所有想到的关于这本书的想法都尽可能加到清单里。 -我建议创建2份同样的待办事项清单:一份纸质档,一份电子档。 +我建议创建 2 份同样的待办事项清单:一份纸质档,一份电子档。 -首先,在纸上记下所有要点。所有内容都记下后,再阅读2至3遍。然后,不管脑子里闪现什么新的点子,都记到纸上。 +首先,在纸上记下所有要点。所有内容都记下后,再阅读 2 至 3 遍。然后,不管脑子里闪现什么新的点子,都记到纸上。 例如,如果你想到了将如何解释某个特殊的主题,就把想法记下来。这会让你的写作变得更容易。后面在你开始写这个主题时,你就可以参考这些笔记。 @@ -112,14 +112,14 @@ - \[x\] 标题 - \[x\] 副标题 - \[x\] 前言 -- \[x\] 什么是Blockchain和Hyperledger Fabric? -- \[x\] 介绍Hyperledger Composer +- [x] 什么是 Blockchain 和 Hyperledger Fabric? +- [x] 介绍 Hyperledger Composer - \[x\] 环境要求和设置 - \[x\] Azure - \[x\] AWS - \[x\] GCP - \[x\] 项目目标 -- \[x\] Composer中的项目设置 +- [x] Composer 中的项目设置 - \[x\] 模型文件 - \[x\] 定义 - \[x\] 建模语言 @@ -132,14 +132,14 @@ - \[x\] 定义 - \[x\] 查询语言 - \[x\] 项目代码 -- \[x\] ACL文件 +- [x] ACL 文件 - \[x\] 定义 - \[x\] 语法 - \[x\] 项目代码 -- \[x\] 在Composer Playground中部署 -- \[x\] 在Composer Playground中测试 +- [x] 在 Composer Playground 中部署 +- [x] 在 Composer Playground 中测试 - \[x\] 导出.bna -- \[x\] Composer Rest服务器 +- [x] Composer Rest 服务器 - \[x\] 前端 - \[x\] 总结 - \[x\] 参考文献 @@ -148,14 +148,14 @@ - \[x\] 复查语法 - \[x\] 阅读草稿 - \[x\] 阅读最后的草稿 -- \[x\] PDF格式 -- \[x\] 在PDF中添加页码 +- [x] PDF 格式 +- [x] 在 PDF 中添加页码 - \[x\] 新章节另起一页 - \[x\] 感谢信 - \[x\] 许可协议 - \[x\] 封底 -我是用的Markdown格式写的**待办事项**你可以选择任何自己觉得最方便的格式来写。 +我是用的 Markdown 格式写的**待办事项**你可以选择任何自己觉得最方便的格式来写。 ## 从小处着手,但一定要开始 @@ -163,7 +163,7 @@ 另外,你也不必一次性写完所有主题。哪个主题你觉得写来顺畅,就从哪个主题开始写。 -你的目标应该是开始行动起来写书。计划在几周内写10%-20%的量。一旦开始了,就会有声音不断提醒你,你必须写完这本书。久而久之,你就会发现,这变成了一个巨大的动力。 +你的目标应该是开始行动起来写书。计划在几周内写 10%-20%的量。一旦开始了,就会有声音不断提醒你,你必须写完这本书。久而久之,你就会发现,这变成了一个巨大的动力。 如果主题不熟悉,不用担心。尽管去互联网上寻求帮助。读一读别人是怎么阐述的,从中获得灵感,然后用你自己的方式写出来。 @@ -181,11 +181,11 @@ 另一个问题是如果你在章节之间新增了一个章节,你就不得不给后面所有的章节重新命名。 -我发现了2种有用的命名规范,但也各有缺点。 +我发现了 2 种有用的命名规范,但也各有缺点。 -一种选择是使用**章节编号-主题**:文件命名为一个章节编号跟上该章节的主题。比如这样**第10章-区块链介绍**。 +一种选择是使用**章节编号-主题**:文件命名为一个章节编号跟上该章节的主题。比如这样**第 10 章-区块链介绍**。 -章节编号用2位数。这有助于在不同文件中为同一章节增加子章节。比如这样**第11章-区块链的历史**。 +章节编号用 2 位数。这有助于在不同文件中为同一章节增加子章节。比如这样**第 11 章-区块链的历史**。 这种命名方式的另一个好处是它将按你书籍章节的顺序显示所有文件。 @@ -193,13 +193,13 @@ 第二个选择是使用**文件名作为主题**:将所有文件以主题名命名。这样你可以按任意顺序写主题。而且你可以保持书中的顺序与你待办事项清单中的顺序一致。 -**缺点:** 所有文件都将按字母顺序排列。文件数达10至15个之后,就会变得难以追踪所有文件,而且也更难将这些文件组合在一起形成草稿。 +**缺点:** 所有文件都将按字母顺序排列。文件数达 10 至 15 个之后,就会变得难以追踪所有文件,而且也更难将这些文件组合在一起形成草稿。 最后,我选择了第二种方式。对我来说还效果还不错。 -为了创建草稿,我创建了一个Node.js脚本。在这个脚本里,我将所有主题输入一个数组中。然后我创建了一个草稿文件,并在其中追加了所有主题。当然,要先通过阅读主题?。作为一名软件工程师的一些优势? +为了创建草稿,我创建了一个 Node.js 脚本。在这个脚本里,我将所有主题输入一个数组中。然后我创建了一个草稿文件,并在其中追加了所有主题。当然,要先通过阅读主题?。作为一名软件工程师的一些优势? -这个脚本是我编辑时的一个救星。很多次,我都是在脚本里更新主题或图片,以及修改语法错误。这里要说Grammarly(译者注:Grammarly是一种在线语法纠正和校对工具)真的拯救了我……但并没能完全拯救,因为我用的是免费版。? +这个脚本是我编辑时的一个救星。很多次,我都是在脚本里更新主题或图片,以及修改语法错误。这里要说 Grammarly(译者注:Grammarly 是一种在线语法纠正和校对工具)真的拯救了我……但并没能完全拯救,因为我用的是免费版。? ## 书籍之旅记录 @@ -211,22 +211,22 @@ 对于不知道**Git**是什么的读者可看以下释义: -> Git是一个分布式版本控制系统,用于记录软件开发过程中的源码变更。Git为程序员之间协同工作而设计,但也能用于记录所有文件变更。--[维基百科](https://en.wikipedia.org/wiki/Git) +> Git 是一个分布式版本控制系统,用于记录软件开发过程中的源码变更。Git 为程序员之间协同工作而设计,但也能用于记录所有文件变更。--[维基百科](https://en.wikipedia.org/wiki/Git) 你不必等学完**Git**的一切才开始用它来写作。掌握基础的命令,如**init**、**add**、**commit**、**logs**和**checkout**就远远足够你维护文档版本以及保证文本的可访问性和安全性了。 -可用的Git代码托管平台有很多,比如:[GitHub](https://github.com/)和[GitLab](https://about.gitlab.com/) 等。要将你的书籍托管在这些平台上,可采用以下步骤: +可用的 Git 代码托管平台有很多,比如:[GitHub](https://github.com/)和[GitLab](https://about.gitlab.com/) 等。要将你的书籍托管在这些平台上,可采用以下步骤: 1. 创建一个账号。我个人选择的是用**GitHub**平台。 2. 创建一个私人仓库,保持默认选项。你可以在将来把这个仓库可见性设置为公共。 3. 仓库创建完毕后,根据提示进行操作。基本上,这一步是将你本地**Git**连接到你的托管仓库。 -4. 再学习2条命令:**push**和**pull**。**push**命令是将本地变更上传至云仓库,**pull**命令是从云端获取内容。 +4. 再学习 2 条命令:**push**和**pull**。**push**命令是将本地变更上传至云仓库,**pull**命令是从云端获取内容。 -之后,无论何时你做任何变更,只需**add**、**commit**和**push**3条命令搞定。是不是很简单?? +之后,无论何时你做任何变更,只需**add**、**commit**和**push**3 条命令搞定。是不是很简单?? 提交几次之后,你就会对**Git**运用自如了。 -> 阅读这篇精彩文章[一小时掌握Git和版本控制](https://www.freecodecamp.org/news/learn-git-and-version-control-in-an-hour/),学习更多Git知识。 +> 阅读这篇精彩文章[一小时掌握 Git 和版本控制](https://www.freecodecamp.org/news/learn-git-and-version-control-in-an-hour/),学习更多 Git 知识。 # 我用到的工具和资源 @@ -234,25 +234,25 @@ ## 写作 -对于写作,如我前文所述,我用的是VS Code编辑器和几个Markdown插件。 +对于写作,如我前文所述,我用的是 VS Code 编辑器和几个 Markdown 插件。 -表情符号,我用的是[复制粘贴emojis](https://getemoji.com/)。 +表情符号,我用的是[复制粘贴 emojis](https://getemoji.com/)。 ## 编辑 -我用的是免费版Grammarly来纠正语法错误。免费版可以纠正所有的基础错误,比如错误的或遗漏的冠词、介词、逗号等等。 +我用的是免费版 Grammarly 来纠正语法错误。免费版可以纠正所有的基础错误,比如错误的或遗漏的冠词、介词、逗号等等。 -我用的是[在线PDF编辑器](https://www.ilovepdf.com/add_pdf_page_number) 给书籍添加页码。 +我用的是[在线 PDF 编辑器](https://www.ilovepdf.com/add_pdf_page_number) 给书籍添加页码。 ## 排版 -在VS Code中,我用Preview插件将Markdown生成PDF格式。我用的是默认的Git Markdown样式,你也可以在设置里更改样式。 +在 VS Code 中,我用 Preview 插件将 Markdown 生成 PDF 格式。我用的是默认的 Git Markdown 样式,你也可以在设置里更改样式。 -### PDF中的分页符 +### PDF 中的分页符 -因为我是用Markdown格式进行写作的,在输出为PDF格式时会产生差异。比如,新建的主题是接着上一页排版的,而没有另起一页。 +因为我是用 Markdown 格式进行写作的,在输出为 PDF 格式时会产生差异。比如,新建的主题是接着上一页排版的,而没有另起一页。 -为解决这个问题,我在每个主题最后都使用了用于分页的HTML代码。 +为解决这个问题,我在每个主题最后都使用了用于分页的 HTML 代码。 ```html <div style="page-break-after:always;"></div> @@ -269,7 +269,7 @@ 在我的书中 **关于我**部分,我把内容分为了两栏:一栏是关于我的简介,另一栏是一张个人照片。 -我花了一段时间才了解Markdown格式的全部功能。我们可以在Markdown里添加普通的`html`代码。下面是我的 "关于我 "页面的内容: +我花了一段时间才了解 Markdown 格式的全部功能。我们可以在 Markdown 里添加普通的`html`代码。下面是我的 "关于我 "页面的内容: ```html <div > @@ -291,7 +291,7 @@ If you have any queries or questions, please feel free to drop me an email. <img src="https://github.githubassets.com/images/icons/emoji/octocat.png" style="width:20px;" />[github.com/schadokar](https://github.com/schadokar) ``` -对于Octocat(译者注:GitHub的吉祥物章鱼猫),我用的是`img`标签。 +对于 Octocat(译者注:GitHub 的吉祥物章鱼猫),我用的是`img`标签。 看起来像这样。 @@ -299,7 +299,7 @@ If you have any queries or questions, please feel free to drop me an email. ### 感谢页 -我添加了一个感谢页,以此感谢**Hyperledger Composer社区**的工作付出。我试着将内容加在页面中间位置。 +我添加了一个感谢页,以此感谢**Hyperledger Composer 社区**的工作付出。我试着将内容加在页面中间位置。 ```html <div style="padding-top:40%; text-align: center; font-size:35px;"> @@ -328,7 +328,7 @@ wrapping a complex Hyperledger Fabric into the easy to use Hyperledger Composer. 副标题用一句话是合适的,不应超过两句。不要过长—让读者自己阅读书里的内容。副标题的目的是让读者通过一句话了解整本书的格调,但仍觉语意未尽?。 -我的书的标题是 **《玩转Hyperledger Composer》**,副标题是 **《使用Hyperledger Composer在区块链中创建一个供应链管理项目》**。 +我的书的标题是 **《玩转 Hyperledger Composer》**,副标题是 **《使用 Hyperledger Composer 在区块链中创建一个供应链管理项目》**。 在开始写书时,不要花太多时间在书的标题上。当你写完了,你能站在一个更好的视角去想标题。所有内容都写完了,你知道写了些什么,读者能从中收获什么。 @@ -341,15 +341,15 @@ wrapping a complex Hyperledger Fabric into the easy to use Hyperledger Composer. 封面尽量保持简单且信息翔实。不要过度设计。一个简单的标题和一个简短的副标题加上一两张图片就足够了。 -我开始设计书的封面时,参考了其他书,并试着Paint中进行编辑。结果完全是一场灾难,我想不出什么好的设计了。 +我开始设计书的封面时,参考了其他书,并试着 Paint 中进行编辑。结果完全是一场灾难,我想不出什么好的设计了。 然后我意识到,设计不是我的长处。我决定找一个自由职业者来做这件事,所以我查看了**UpWork**和**Fiverr**等自由职业网站。 然后,我发现了[**Canva**](https://canva.com)。这真是一个很棒的工具。太神奇了!?? ? ? ? -> Canva是一个图形设计平台,支持用户创建社交媒体图形、演示文稿、海报和其他视觉内容。它可以在网页端和移动端使用,并集成了数百万张图片、字体、模板和插图。[维基百科](https://en.wikipedia.org/wiki/Canva) +> Canva 是一个图形设计平台,支持用户创建社交媒体图形、演示文稿、海报和其他视觉内容。它可以在网页端和移动端使用,并集成了数百万张图片、字体、模板和插图。[维基百科](https://en.wikipedia.org/wiki/Canva) -我通过Canva上书籍封面版块中的一个模板,创建了我的书籍封面。还不错吧?? +我通过 Canva 上书籍封面版块中的一个模板,创建了我的书籍封面。还不错吧?? ![book-cover](https://www.freecodecamp.org/news/content/images/2020/09/book-cover.png) @@ -357,7 +357,7 @@ wrapping a complex Hyperledger Fabric into the easy to use Hyperledger Composer. 我写这本书是出于好奇心和乐趣。所以,我希望它是免费的,而且是开源的,但我不希望有人利用这本书来谋利。没有许可协议,就没有约束。 -我搜索了一段时间,在StackOverflow上发现了一个关于免费许可协议的很好回答,即[知识共享版权许可协议(Creative Commons Licenses)](https://creativecommons.org/licenses/)。 +我搜索了一段时间,在 StackOverflow 上发现了一个关于免费许可协议的很好回答,即[知识共享版权许可协议(Creative Commons Licenses)](https://creativecommons.org/licenses/)。 > **知识共享基金会(Creative Commons)是一个非营利组织,帮助解决知识分享和创造力有关的法律困境,应对世界上(译者注:版权方面的)紧迫的挑战。** @@ -375,13 +375,13 @@ wrapping a complex Hyperledger Fabric into the easy to use Hyperledger Composer. 另一个选择是自助出版。是的,我们可以自行出版自己的书。亚马逊的自行出版系统[Kindle Direct Publishing](https://kdp.amazon.com/en_US/)(KDP)提供了一个很好的平台。它是免费的,而且可以在全世界范围内出版书籍。 -你将从每笔销售中获得70%的利润。KDP负责所有的出版过程。你只需将书籍写好、上传并排版。 +你将从每笔销售中获得 70%的利润。KDP 负责所有的出版过程。你只需将书籍写好、上传并排版。 你只需填写你想收取的价格,以及你的书和你自己的一些基本信息。你可以阅读他们的教程了解更多信息—他们的教程写得非常棒。 但我希望我的书是免费的,而且我没有耐心操作上述过程。所以,我并没有依靠任何第三方就自行出版了我的书。 -我只是把书转换成PDF格式,并把它存在AWS S3 Bucket上,这样任何人都可以下载。然后我把这本书托管在我的网站上。简单吧。? +我只是把书转换成 PDF 格式,并把它存在 AWS S3 Bucket 上,这样任何人都可以下载。然后我把这本书托管在我的网站上。简单吧。? # 分享你的作品 @@ -392,17 +392,17 @@ wrapping a complex Hyperledger Fabric into the easy to use Hyperledger Composer. ## 领英(LinkdeIn) -LinkedIn是一个专业平台,上面有许多技术领域不同专业的开发人员。凡是你能说得出来的职业,在LinkedIn上你也都能找到从事该职业的人。 +LinkedIn 是一个专业平台,上面有许多技术领域不同专业的开发人员。凡是你能说得出来的职业,在 LinkedIn 上你也都能找到从事该职业的人。 -与他们分享你的作品,并询问反馈。90%的可能你都会得到回复。我向Dan Selmon分享了我的书,他是Hyperledger Composer的一名贡献者。还有Srinivas Mahankali,他写了很多关于区块链的书。 +与他们分享你的作品,并询问反馈。90%的可能你都会得到回复。我向 Dan Selmon 分享了我的书,他是 Hyperledger Composer 的一名贡献者。还有 Srinivas Mahankali,他写了很多关于区块链的书。 -他们都非常乐于助人,并给出了他们真诚的反馈。我很感谢Dan,他甚至提出会在他的社交网络LinkedIn和Twitter上分享我的书。? +他们都非常乐于助人,并给出了他们真诚的反馈。我很感谢 Dan,他甚至提出会在他的社交网络 LinkedIn 和 Twitter 上分享我的书。? ## Reddit -Reddit是一个社区枢纽。你会发现这里有许多关于各种主题的活跃社区。你只需加入与你工作相关的社区,然后在那里分享你的作品。 +Reddit 是一个社区枢纽。你会发现这里有许多关于各种主题的活跃社区。你只需加入与你工作相关的社区,然后在那里分享你的作品。 -你会发现Reddit上有很多活跃的成员,在这些群体中,他们并不羞于分享自己的观点。如果你的作品有提升空间,他们中的一些人可能会提供帮助。 +你会发现 Reddit 上有很多活跃的成员,在这些群体中,他们并不羞于分享自己的观点。如果你的作品有提升空间,他们中的一些人可能会提供帮助。 _但在分享你的作品之前,请务必阅读指南。如果你违反了其中任何一条规则,他们将删除你的帖子_。 @@ -410,9 +410,9 @@ _但在分享你的作品之前,请务必阅读指南。如果你违反了其 推特不仅仅是一个人们分享自己观点的社交平台。所以不要低估它。 -如果你喜欢事实和数字,可以看到:Twitter上有13亿多个账户,3.3亿月活跃用户,1.52亿日活跃用户,每天有5亿条推文。这个数字是很庞大的。 +如果你喜欢事实和数字,可以看到:Twitter 上有 13 亿多个账户,3.3 亿月活跃用户,1.52 亿日活跃用户,每天有 5 亿条推文。这个数字是很庞大的。 -你只需要在有限的280个字符内,精心设计你的信息,选择合适的关键词,你就有可能接触到大量的受众。 +你只需要在有限的 280 个字符内,精心设计你的信息,选择合适的关键词,你就有可能接触到大量的受众。 ## 博客 @@ -435,13 +435,13 @@ _但在分享你的作品之前,请务必阅读指南。如果你违反了其 # 我所使用工具的最终清单 -- **编辑器**:[Visual Studio Code](https://code.visualstudio.com/)及2个Markdown插件 -- **版本控制工具**:Git和[GitHub](https://github.com) -- **表情符号**:[复制粘贴emojis](https://getemoji.com/) +- **编辑器**:[Visual Studio Code](https://code.visualstudio.com/)及 2 个 Markdown 插件 +- **版本控制工具**:Git 和[GitHub](https://github.com) +- **表情符号**:[复制粘贴 emojis](https://getemoji.com/) - **语法检查**:[Grammarly](https://app.grammarly.com/) - **许可协议**:[Creative Commons Licenses](https://creativecommons.org/licenses/) - **封面设计**:[Canva](https://www.canva.com/) -- **PDF页码编辑器**:[在线PDF编辑器](https://www.ilovepdf.com/add_pdf_page_number) +- **PDF 页码编辑器**:[在线 PDF 编辑器](https://www.ilovepdf.com/add_pdf_page_number) - **电子书存储**:[AWS S3 bucket](https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html). - **书籍托管**:[我的博客](https://schadokar.dev/ebooks/) diff --git a/chinese/articles/how-writing-can-lead-to-better-product-design.md b/chinese/articles/how-writing-can-lead-to-better-product-design.md index ed69ac520..5b934e6b8 100644 --- a/chinese/articles/how-writing-can-lead-to-better-product-design.md +++ b/chinese/articles/how-writing-can-lead-to-better-product-design.md @@ -5,9 +5,9 @@ ![写作为何有助于产品设计](https://cdn-media-2.freecodecamp.org/w1280/604adcf9a7946308b7687147.jpg) -在我创建和设计产品,如APP应用程序、网站和[图形制作工具](http://gfxmaker.com/)时,通常会遇到两类人:一类是偏好定性叙述的人,另一类是偏好数据和分析的人。 +在我创建和设计产品,如 APP 应用程序、网站和[图形制作工具](http://gfxmaker.com/)时,通常会遇到两类人:一类是偏好定性叙述的人,另一类是偏好数据和分析的人。 -第一类人习惯基于用户主观反馈和观点去构建产品开发框架。第二类人对第一类人的方法持谨慎态度。第二类人希望应用“excel模型”根据数字来倒推工作。 +第一类人习惯基于用户主观反馈和观点去构建产品开发框架。第二类人对第一类人的方法持谨慎态度。第二类人希望应用“excel 模型”根据数字来倒推工作。 为便于区分,我们就称第一类人为“storyteller”(叙述者),第二类人为“quant”(量化员)。 @@ -15,7 +15,7 @@ 另外,他们收集、分析和部署这些洞察的方式也不同。 -暂停片刻,问问自己你更像哪类人呢?storyteller还是quant?或许会对你有用。 +暂停片刻,问问自己你更像哪类人呢?storyteller 还是 quant?或许会对你有用。 无论你的答案是什么,我相信有一种将两者统一结合起来的方法,帮助他们在追求创建更优质更构思周密产品的路途中找到共通点。 @@ -39,7 +39,7 @@ 亚马逊创始人杰夫·贝佐斯(Jeff Bezos)曾经写道: -> “Powerpoint式的演讲文稿在某种程度上允许掩饰观点,抹平任何相对重要性的感觉,忽略观点之间的关联性。” +> “Powerpoint 式的演讲文稿在某种程度上允许掩饰观点,抹平任何相对重要性的感觉,忽略观点之间的关联性。” 写作促使我们明白真正重要的东西:一个论点的力度和条理性,以及论点如何被论据支持或证实。 @@ -47,15 +47,15 @@ 软硬件都是如此。我们认为理所当然的日常物品中就存在着巨大的复杂性。 -汉莎航空声称,波音制造747-8需要600万个零件。 +汉莎航空声称,波音制造 747-8 需要 600 万个零件。 -一辆简单的汽车整车可能有30,000个零件。 +一辆简单的汽车整车可能有 30,000 个零件。 -据估计,Android操作系统的代码量有1200-1500万行。 +据估计,Android 操作系统的代码量有 1200-1500 万行。 -大型强子对撞机使用了5000万条线。 +大型强子对撞机使用了 5000 万条线。 -不包括后端代码, Facebook(前端和各种[登录页面](https://www.ideazinc.com/top-20-amazing-landing-pages-reviewed/))的代码量就有7000万行。 +不包括后端代码, Facebook(前端和各种[登录页面](https://www.ideazinc.com/top-20-amazing-landing-pages-reviewed/))的代码量就有 7000 万行。 结构清晰的叙述性文本(与项目符号或纯文本相比)能够帮助作者或产品设计师阐释论点背后的“原因”。 @@ -95,7 +95,7 @@ 第三,你可以通过起草和完成“常见问题”(FAQ)来提高你的产品写作技能。人们对你的产品有什么不理解,为什么不理解?试着思考和回复用户的这些问题。 -写FAQ有两方面的好处,一是可以提高你的写作水平,二是你能更深入理解产品的哪些方面可能对用户来说是难以理解的。有了这种洞察,你可以预先解决这些问题。 +写 FAQ 有两方面的好处,一是可以提高你的写作水平,二是你能更深入理解产品的哪些方面可能对用户来说是难以理解的。有了这种洞察,你可以预先解决这些问题。 ## 将写作和产品设计结合起来 diff --git a/chinese/articles/html-basics-for-beginners.md b/chinese/articles/html-basics-for-beginners.md index 4f37af8f9..a67ef4a37 100644 --- a/chinese/articles/html-basics-for-beginners.md +++ b/chinese/articles/html-basics-for-beginners.md @@ -5,9 +5,9 @@ ![十五分钟零基础HTML入门](https://www.freecodecamp.org/news/content/images/size/w2000/2021/01/Ep10_html.png) -如果你想建立一个网站,你需要学的第一个编程语言就是HTML。 +如果你想建立一个网站,你需要学的第一个编程语言就是 HTML。 -这篇文章里,我们会介绍HTML语言的基础。看完文章后,我们会只使用HTML建立一个简单的网站。 +这篇文章里,我们会介绍 HTML 语言的基础。看完文章后,我们会只使用 HTML 建立一个简单的网站。 如果你想了解更多,可以看这个视频: @@ -15,7 +15,7 @@ ## 什么是 HTML? -作为超文本标记语言的代表,HTML是一个较为简单的语言。它包含了用来搭建一个网站页面的不同元素。 +作为超文本标记语言的代表,HTML 是一个较为简单的语言。它包含了用来搭建一个网站页面的不同元素。 ![](https://www.freecodecamp.org/news/content/images/2021/01/Screen-Shot-2021-01-11-at-1.16.17-PM.png) @@ -25,7 +25,7 @@ ![](https://www.freecodecamp.org/news/content/images/2021/01/Screen-Shot-2021-01-11-at-1.16.34-PM.png) -HTML元素 +HTML 元素 元素的开头通常是一个包含元素名称的开始标签。它被尖括号包围。开始标签代表着元素开始的地方。 @@ -43,7 +43,7 @@ HTML元素 在上面的例子中,`<img>` 元素只包含一个没有任何内容的标签。 此元素用于在文档中插入来自 [Unsplash](https://unsplash.com/) 的图像文件。 -## 如何嵌套HTML元素? +## 如何嵌套 HTML 元素? ```html <div class="my-list"> @@ -66,7 +66,7 @@ HTML元素 ![如何嵌套HTML元素](https://www.freecodecamp.org/news/content/images/2021/01/Screen-Shot-2021-01-12-at-10.45.05-AM.png) -## 什么是HTML属性? +## 什么是 HTML 属性? 元素也有属性,这些属性包含关于不会出现在内容中的元素的额外信息。 @@ -93,9 +93,9 @@ But not every attribute has the same pattern. Some can exist without values, and In this example, if we want to disable the button, all we have to do is pass a `disabled` attribute without any values. This means that the presence of the attribute represents the true value, otherwise, the absence represents the false value.在这个例子中,如果我们想禁用这个按钮(button),我们所要做的就是传递一个不带任何值的 `disabled` 属性。 也就是说,这个属性的存在代表真值,不存在则代表假值。 -### 常见HTML元素 +### 常见 HTML 元素 -HTML总共有超过100个元素。 但是在 90% 的情况下,最常用的大约只有20个。 我把它们分成5种: +HTML 总共有超过 100 个元素。 但是在 90% 的情况下,最常用的大约只有 20 个。 我把它们分成 5 种: #### 节元素 @@ -143,7 +143,7 @@ HTML总共有超过100个元素。 但是在 90% 的情况下,最常用的大 [developer.mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTML/Element)上可以找到所有元素。 但是对于初学者来说,只需要知道最常见的就足够了。 -## 块\级 vs 内联HTML元素 +## 块\级 vs 内联 HTML 元素 一个元素默认是块\级元素。 @@ -153,7 +153,7 @@ HTML总共有超过100个元素。 但是在 90% 的情况下,最常用的大 ![](https://www.freecodecamp.org/news/content/images/2021/01/Screen-Shot-2021-01-11-at-1.17.22-PM.png) -块级 vs. 内联HTML元素 +块级 vs. 内联 HTML 元素 `<div>` 和`<span>`分别代表块\级元素和内联元素。 在这个例子中,`<div>` 元素占用 3 行,而 `<span>` 元素只占用 1 行。 @@ -161,7 +161,7 @@ HTML总共有超过100个元素。 但是在 90% 的情况下,最常用的大 如果我们回顾最常见的 HTML 元素,内联元素有:`<span>、<input>、<button>、<label>、<textarea>、<img>、<a>、<br>`。 -## 如何在HTML中添加注释 +## 如何在 HTML 中添加注释 ```html <p>这是一个段落.</p> @@ -172,9 +172,9 @@ HTML总共有超过100个元素。 但是在 90% 的情况下,最常用的大 注释的目的是在代码中间解释逻辑或仅仅是组织代码。 -HTML注释被包裹在特殊标记`<!-- 和 -->`中,并且它们在浏览器中被忽略。 +HTML 注释被包裹在特殊标记`<!-- 和 -->`中,并且它们在浏览器中被忽略。 -## 如何使用HTML实体 +## 如何使用 HTML 实体 如果您想显示文本:`<p> 标签定义了一个段落。`,但浏览器将 `<p>` 解释为新元素的开始标签怎么办? 在这种情况下,我们可以像下面的一样使用 HTML 实体: @@ -185,9 +185,9 @@ HTML注释被包裹在特殊标记`<!-- 和 -->`中,并且它们在浏览器 ``` -## 如何在HTML中使用表情符号 +## 如何在 HTML 中使用表情符号 -在现代网络中,我们可以很容易地在HTML中显示表情符号,像这样:👻 +在现代网络中,我们可以很容易地在 HTML 中显示表情符号,像这样:👻 ```html <p>😀 咧嘴笑.</p> @@ -196,7 +196,7 @@ HTML注释被包裹在特殊标记`<!-- 和 -->`中,并且它们在浏览器 ``` -## HTML中初学者常见错误 +## HTML 中初学者常见错误 ### 1\. 标签/元素名称 @@ -226,13 +226,13 @@ HTML注释被包裹在特殊标记`<!-- 和 -->`中,并且它们在浏览器 ``` -单双引号不可以混用。你应该始终使用双引号。如果需要的话,使用HTML实体。 +单双引号不可以混用。你应该始终使用双引号。如果需要的话,使用 HTML 实体。 -## 如何用HTML搭建一个简单的网站 +## 如何用 HTML 搭建一个简单的网站 -单个HTML元素不足以创建一个网站。因此,让我们看看从头开始构建一个简单的网站还需要什么。 +单个 HTML 元素不足以创建一个网站。因此,让我们看看从头开始构建一个简单的网站还需要什么。 -### 如何创建一个HTML文件 +### 如何创建一个 HTML 文件 首先,让我们打开 [Visual Studio Code](https://code.visualstudio.com/)(或你最喜欢的代码编辑器)。 在选择的文件夹中,创建一个新文件并将其命名为 index.html。 @@ -253,13 +253,13 @@ HTML注释被包裹在特殊标记`<!-- 和 -->`中,并且它们在浏览器 ``` -这是HTML文档组成网站所需的最少代码。文件里有: +这是 HTML 文档组成网站所需的最少代码。文件里有: -1. `<!DOCTYPE html>`:首先我们有 Doctype。出于一些奇怪的历史原因,我们必须在HTML中包含文档类型。 +1. `<!DOCTYPE html>`:首先我们有 Doctype。出于一些奇怪的历史原因,我们必须在 HTML 中包含文档类型。 2. `<html lang="en"></html>`:`<html>` 元素包装了页面上的所有内容,也称为根元素。我们应该始终包含 `lang` 属性来声明页面的语言。 3. `<head></head>`:`<head>` 元素是用来包含所有内容的容器,但不是向用户显示的内容。 -4. `<meta charset="UTF-8" />`:第一个meta元素用于设置字符集为UTF\-8。UTF\-8包括大部分书面语言字符。 -5.`<meta name="viewport" content="width=device-width, initial-scale=1.0" />`:第二个meta元素指定浏览器视口。此设置适用于移动\优化站点。 +4. `<meta charset="UTF-8" />`:第一个 meta 元素用于设置字符集为 UTF-8。UTF-8 包括大部分书面语言字符。 +5.`<meta name="viewport" content="width=device-width, initial-scale=1.0" />`:第二个 meta 元素指定浏览器视口。此设置适用于移动\优化站点。 6. `<title>Document`: 这是`` 元素。它设置页面的标题。 7. `<body></body>`:`<body>` 元素包含页面上的所有内容。 @@ -269,7 +269,7 @@ HTML注释被包裹在特殊标记`<!-- 和 -->`中,并且它们在浏览器 首先,让我们给出煎饼食谱的`<title>` 元素内容。 这会改变网页标签页上的文本。在 `<body>` 元素中,我们创建 3 个元素:`<header>`、`<main>` 和 `<footer>`,分别代表 3 个部分。 -#### 1\. Build the header section构建页头部分 +#### 1. Build the header section 构建页头部分 在页头中,我们想要标志和导航。 因此,让我们为标志创建一个内容为`所有食谱`的`div`。 @@ -452,9 +452,9 @@ HTML注释被包裹在特殊标记`<!-- 和 -->`中,并且它们在浏览器 ``` ## 总结 -仅仅使用HTML就可以构建一个简单的网站。但如果要构建一个美观实用的网站,我们需要学习CSS和JavaScript。 +仅仅使用 HTML 就可以构建一个简单的网站。但如果要构建一个美观实用的网站,我们需要学习 CSS 和 JavaScript。 -你可以在社交媒体或 Youtube 上关注我,并获取有关这些主题的未来更新。 同时,你可以查看 [freeCodeCamp Curriculum](https://www.freecodecamp.org/learn) 通过解决小题目来练习HTML。 +你可以在社交媒体或 Youtube 上关注我,并获取有关这些主题的未来更新。 同时,你可以查看 [freeCodeCamp Curriculum](https://www.freecodecamp.org/learn) 通过解决小题目来练习 HTML。 除此之外,希望你可以继续享受编程,期待在未来的帖子中见到你👋. diff --git a/chinese/articles/html-button-onclick-javascript-click-event-tutorial.md b/chinese/articles/html-button-onclick-javascript-click-event-tutorial.md index f342732da..4dd99e4c5 100644 --- a/chinese/articles/html-button-onclick-javascript-click-event-tutorial.md +++ b/chinese/articles/html-button-onclick-javascript-click-event-tutorial.md @@ -7,11 +7,11 @@ 当你访问一个网站时,你可能点击一些东西,像链接或者按键。 -链接可能跳转到当前网页的某一部分,也可能该网站的另一个网页,更或者是另一个网站。按键由Javascrpt各种事件控制,触发某些函数 +链接可能跳转到当前网页的某一部分,也可能该网站的另一个网页,更或者是另一个网站。按键由 Javascrpt 各种事件控制,触发某些函数 -在本教程中,我们将探索在JavaScript中使用两种不同的方式触发点击事件。 +在本教程中,我们将探索在 JavaScript 中使用两种不同的方式触发点击事件。 -首先,我们将看传统的 `onclick` 风格 ,你可以在HTML里添加。然后我们将看到更现代的 "click" `eventListner` 是如何工作的, 它可以让HTML和JavaScript分离。 +首先,我们将看传统的 `onclick` 风格 ,你可以在 HTML 里添加。然后我们将看到更现代的 "click" `eventListner` 是如何工作的, 它可以让 HTML 和 JavaScript 分离。 ## 如何使用`onclick`事件 @@ -33,11 +33,11 @@ 请注意 `onclick`属性值是你想执行的函数名,它会调用该函数。 -在JavaScript中,它会通过函数名调用函数,当你在函数名后加上括号。 +在 JavaScript 中,它会通过函数名调用函数,当你在函数名后加上括号。 ## `onclick` 事件例子 -我已经准备好了一些基本的HTML和样式,我们练习怎样把 `onclick` 事件加入进去。 +我已经准备好了一些基本的 HTML 和样式,我们练习怎样把 `onclick` 事件加入进去。 ```html <div> @@ -46,7 +46,7 @@ </div> ``` -让它好看些的CSS,以及所有其他的示例代码: +让它好看些的 CSS,以及所有其他的示例代码: ```css body { @@ -82,9 +82,9 @@ button.orange { 这是我们看到的网页: ![changeColor](https://www.freecodecamp.org/news/content/images/2021/08/changeColor.png) -我们的目标是当我们点击这个按键时,文本的颜色变成蓝色。所以我们需要添加一个 `onclick`属性到按键上,然后白编写JavaScript函数来改变颜色。 +我们的目标是当我们点击这个按键时,文本的颜色变成蓝色。所以我们需要添加一个 `onclick`属性到按键上,然后白编写 JavaScript 函数来改变颜色。 -我们需要在HTML做一些小小的改变. +我们需要在 HTML 做一些小小的改变. ```html <div> @@ -93,15 +93,15 @@ button.orange { </div> ``` -我们要执行的函数是`changeColor()`,所以我们需要把它写到一个JavaScript文件里,或者写再HTML文件的`<script>`标签里。 +我们要执行的函数是`changeColor()`,所以我们需要把它写到一个 JavaScript 文件里,或者写再 HTML 文件的`<script>`标签里。 -如果你想把你的脚本写在一个JavaScript文件中,你需要在HTML里用下面的语法引用它。 +如果你想把你的脚本写在一个 JavaScript 文件中,你需要在 HTML 里用下面的语法引用它。 ```html <script src="path-to-javascript-file"></script> ``` -如果你想在HTML里写脚本,把它放在script标签里。 +如果你想在 HTML 里写脚本,把它放在 script 标签里。 ```html <script> @@ -111,9 +111,9 @@ button.orange { 现在让我们来写`changeColor()`函数 -首先,我们需要选择我们想操作的元素,也就是在`<p>`标签内的 `freeCodeCamp ` 文本 +首先,我们需要选择我们想操作的元素,也就是在`<p>`标签内的 `freeCodeCamp` 文本 -在JavaScript中,你可以用DOM的 getElementById(),getElementsByClassName(),或者querySelector()方法把DOM对象存储在变量中。 +在 JavaScript 中,你可以用 DOM 的 getElementById(),getElementsByClassName(),或者 querySelector()方法把 DOM 对象存储在变量中。 在本教程中,我将使用`querySelector()` ,因为它更现代,而且运行更快。我还将使用 `const`来声明我们的变量,而不用`let`和`var`。因为使用`const`,事情将变得更安全,变量是只读的。 @@ -121,7 +121,7 @@ button.orange { const name = document.querySelector(".name"); ``` -现在我们已经选定了文本,让我们来编写自己函数。在JavaScript中,函数的基本语法是这样的。 +现在我们已经选定了文本,让我们来编写自己函数。在 JavaScript 中,函数的基本语法是这样的。 ```js function funcctionName () { @@ -139,9 +139,9 @@ function changeColor() { 发生了什么事情? -HTML中的`changeColor()`是我们要执行的函数,如果的这个名字与HTML中的函数名不一样,它就不会起作用。所以我们的函数名写成 `changeColor`。 +HTML 中的`changeColor()`是我们要执行的函数,如果的这个名字与 HTML 中的函数名不一样,它就不会起作用。所以我们的函数名写成 `changeColor`。 -在DOM(文档对象模型,指所有的HTML)中,要改变任何有关`style`的东西,你需要写上`style`,然后加上一个`.`。后面是你想要改变的东西,这可以是颜色(color),背景颜色(background),字体大小(fontsize)等。 +在 DOM(文档对象模型,指所有的 HTML)中,要改变任何有关`style`的东西,你需要写上`style`,然后加上一个`.`。后面是你想要改变的东西,这可以是颜色(color),背景颜色(background),字体大小(fontsize)等。 因此,在我们的函数中,我们声明了变量来获取`freeCodeCamp`文本,然后我们把颜色改为蓝色。 @@ -180,15 +180,15 @@ function changeColor(color) { } ``` - name变量获取了`name`的DOM对象(我们在这里存储了`freeCodeCamp`文本),然后颜色会根据我们传入`changeColor()`的颜色值变化。 + name 变量获取了`name`的 DOM 对象(我们在这里存储了`freeCodeCamp`文本),然后颜色会根据我们传入`changeColor()`的颜色值变化。 ![changeColors](https://www.freecodecamp.org/news/content/images/2021/08/changeColors.gif) -## 如何使用 `eventListener`在JavaScript中 +## 如何使用 `eventListener`在 JavaScript 中 -在JacaScript中,有多种方法做同一件事。随着JavaScirpt本身发展,我们开始需要将HTML,CSS和JavaScript代码分开,以达到最佳实践。 +在 JacaScript 中,有多种方法做同一件事。随着 JavaScirpt 本身发展,我们开始需要将 HTML,CSS 和 JavaScript 代码分开,以达到最佳实践。 -事件监听器使JavaScript和HTML分离成为可能。你也可以用`onclick`做到这一点,但是在这里采取另一种方法。 +事件监听器使 JavaScript 和 HTML 分离成为可能。你也可以用`onclick`做到这一点,但是在这里采取另一种方法。 ### 基本 `eventListener` 语法 @@ -198,7 +198,7 @@ function changeColor(color) { 现在让我们使用`ckick eventListner`将`freeCodeCamp`文本变成蓝色。 -这是我们新的HTML: +这是我们新的 HTML: ```html <div> @@ -211,7 +211,7 @@ function changeColor(color) { ![colorChange](https://www.freecodecamp.org/news/content/images/2021/08/colorChange.png) -这次在我们的脚本中,我们也需要选择按键(而不仅是`freeCodeCamp`文本)。因为在我们的按键的标签里没有任何JavaScript,这很酷。 +这次在我们的脚本中,我们也需要选择按键(而不仅是`freeCodeCamp`文本)。因为在我们的按键的标签里没有任何 JavaScript,这很酷。 这是我们的脚本的样子。 @@ -235,13 +235,13 @@ btn.addEventListener("click", changeColor); ![changeColorWithEvents](https://www.freecodecamp.org/news/content/images/2021/08/changeColorWithEvents.gif) -## 如何通过JavaScript建立一个 `Show More` 和 `Show Less` 按键。 +## 如何通过 JavaScript 建立一个 `Show More` 和 `Show Less` 按键 学习的最好办法之一是去动手创建一个项目,用到我们已经学到的`onclick` 和 `click eventLister`,去做一些事情。 当你访问一个博客,你往往先看到文章的招录,然后你可以点击 `read more`按键,显示剩余内容。让我们尝试实现这个。 -这是我们要处理的HTML: +这是我们要处理的 HTML: ```html <article id="content"> @@ -273,12 +273,12 @@ btn.addEventListener("click", changeColor); <button onclick="showMore()">Show more</button> ``` -这是简单的HTML,是有关freeCodeCamp的事情。有个按键,我们已经给它加上了`onClick`。我们要执行的函数是`showMore()`,等下我们会写上。 +这是简单的 HTML,是有关 freeCodeCamp 的事情。有个按键,我们已经给它加上了`onClick`。我们要执行的函数是`showMore()`,等下我们会写上。 -没有CSS,是下面的效果: +没有 CSS,是下面的效果: ![articleunstyled](https://www.freecodecamp.org/news/content/images/2021/08/articleunstyled.png) -它不是很难看,但是我们可以让它更美观,通过下面的CSS: +它不是很难看,但是我们可以让它更美观,通过下面的 CSS: ```css <style> @@ -332,7 +332,7 @@ btn.addEventListener("click", changeColor); </style> ``` -CSS做了什么? +CSS 做了什么? 通过选择器(`*`),我们删除了分配给元素的默认 `margin`和`padding`,这样我们可以添加自己的`margin`和`padding`。 @@ -340,27 +340,27 @@ CSS做了什么? 我们用`Flexbox`将文字内容全部居中,并给它一个浅灰色的背景。 -我们的`<article>`元素,是文本的容器,宽为400px,白色背景(#fff),顶部有20px的padding,左右各20px的padding,底部为0px 的padding。 +我们的`<article>`元素,是文本的容器,宽为 400px,白色背景(#fff),顶部有 20px 的 padding,左右各 20px 的 padding,底部为 0px 的 padding。 -段落里的字体的标签大小为18px(font-size: 18px),然后我们给它们的最大高度为270px(max-height: 270px)。由于设置了最大高度,所有的文字不能包含进来,就会溢出,为了解决这个问题,我们将溢出设置为隐藏(overflow: hidden),这样一开始就不会显示这些文字。 +段落里的字体的标签大小为 18px(font-size: 18px),然后我们给它们的最大高度为 270px(max-height: 270px)。由于设置了最大高度,所有的文字不能包含进来,就会溢出,为了解决这个问题,我们将溢出设置为隐藏(overflow: hidden),这样一开始就不会显示这些文字。 -过渡属性设置为每个变化在1秒后发生(transition: max-height 1s),所有的文本都是两端对齐(text-align: justify),并且有20px 的顶部边距(margin-top: 20px) +过渡属性设置为每个变化在 1 秒后发生(transition: max-height 1s),所有的文本都是两端对齐(text-align: justify),并且有 20px 的顶部边距(margin-top: 20px) -因为我们去掉默认的`margin`,我们的段落将会挤到一起。避免这个,我们设置了`p`元素16px的底边距(margin-bottom: 16px),让段落彼此分开。 +因为我们去掉默认的`margin`,我们的段落将会挤到一起。避免这个,我们设置了`p`元素 16px 的底边距(margin-bottom: 16px),让段落彼此分开。 -我们的选择器 `article.open` 有个属性`max-height` 设置为 1000px。这意味着任何一个`aritcle`元素有个`class` open,`maximum height` 将从270px 变为1000px,去显示文章的其余部分。这是用JavaScript实现的。 +我们的选择器 `article.open` 有个属性`max-height` 设置为 1000px。这意味着任何一个`aritcle`元素有个`class` open,`maximum height` 将从 270px 变为 1000px,去显示文章的其余部分。这是用 JavaScript 实现的。 -我们设置按键,使用深色背景(background: #0e0b22),白色按键(color: #fff),无边框(border: none),半径为4px(border-radius: 4px)。 +我们设置按键,使用深色背景(background: #0e0b22),白色按键(color: #fff),无边框(border: none),半径为 4px(border-radius: 4px)。 -最后,我们使用CSS中的伪类`hover`将按键的光标改为指针(pointer),当用户将光标悬停在按键上时,背景颜色会发生轻微的变化。 +最后,我们使用 CSS 中的伪类`hover`将按键的光标改为指针(pointer),当用户将光标悬停在按键上时,背景颜色会发生轻微的变化。 -这就是CSS。 +这就是 CSS。 我们的页面更好看了: ![articlestyled](https://www.freecodecamp.org/news/content/images/2021/08/articlestyled.png) -下一件我们需要要做的事,编写我们的JavaScript,这样我们可以看到被隐藏的文章部分了。 +下一件我们需要要做的事,编写我们的 JavaScript,这样我们可以看到被隐藏的文章部分了。 我们有一个`onclick`属性在我们的按键内,去调用 `showMore()`函数, 让我们写这个函数。 @@ -389,7 +389,7 @@ function showMore() { 函数做了什么? -我们使用`if...else`语句,这是JavaScript的一个重要语句。它根据某个条件进行切换。 +我们使用`if...else`语句,这是 JavaScript 的一个重要语句。它根据某个条件进行切换。 这是它的基本语法: @@ -401,14 +401,14 @@ if (condition == "something") { } ``` -如果`article.className` 为`open`时(也就是说,我们想给它加上open的类,在CSS中被设置为最大高度为1000px),然后我们可以看到文件的其余部分。 如果不为`open`,文章回到初始状态,即它的一部分被隐藏。 +如果`article.className` 为`open`时(也就是说,我们想给它加上 open 的类,在 CSS 中被设置为最大高度为 1000px),然后我们可以看到文件的其余部分。 如果不为`open`,文章回到初始状态,即它的一部分被隐藏。 我们在`else`语句中给它设置`open`的`class`,它显示文章其余部分。在`if` 语句中把 `open`的`class`去掉,这使它恢复初始状态。 我们的代码能在`Show More` 和 `Show Less` 间进行切换: ![article](https://www.freecodecamp.org/news/content/images/2021/08/article.gif) -我们可以把HTML和JavaScript分离的前提下,接着使用`onclick` ,因为`onclick`是JavaScript。所以把它写到一个JavaScript文件里,而不是写在HTML。 +我们可以把 HTML 和 JavaScript 分离的前提下,接着使用`onclick` ,因为`onclick`是 JavaScript。所以把它写到一个 JavaScript 文件里,而不是写在 HTML。 ```js button.onclick = function () { @@ -480,6 +480,6 @@ function readMore() { ## 总结 -我希望本教程帮助你了解点击事件在JavaScript中是怎么工作的。我们探讨了两种不同的方法,所以你可以在编码中使用它们。 +我希望本教程帮助你了解点击事件在 JavaScript 中是怎么工作的。我们探讨了两种不同的方法,所以你可以在编码中使用它们。 感谢你的阅读,跟随教程进行编程。 \ No newline at end of file diff --git a/chinese/articles/html-space-how-to-add-spaces.md b/chinese/articles/html-space-how-to-add-spaces.md index aa30b5ea8..9af7dba93 100644 --- a/chinese/articles/html-space-how-to-add-spaces.md +++ b/chinese/articles/html-space-how-to-add-spaces.md @@ -21,7 +21,7 @@ | 字符 | HTML 代码 | | --------------------|:-----------:| -| 不换行空格 | `  ` | +| 不换行空格 | ` ` | | En 空格 | ` ` | | Em 空格 | ` ` | | 窄空格 | ` ` | diff --git a/chinese/articles/http-request-methods-explained.md b/chinese/articles/http-request-methods-explained.md index 85ef2a52d..ce4ceebd6 100644 --- a/chinese/articles/http-request-methods-explained.md +++ b/chinese/articles/http-request-methods-explained.md @@ -9,7 +9,7 @@ 为了深入了解 HTTP 方法的工作原理,本文还将介绍 HTTP 有关上下文和背景信息。 -### 这篇文章讲要介绍的主题: +### 这篇文章讲要介绍的主题 - HTTP 协议 - 客户端-服务器架构 diff --git a/chinese/articles/http-vs-https.md b/chinese/articles/http-vs-https.md index cd4c518b1..1dbae9d93 100644 --- a/chinese/articles/http-vs-https.md +++ b/chinese/articles/http-vs-https.md @@ -5,43 +5,43 @@ ![HTTP vs HTTPS – What's the Difference?](https://www.freecodecamp.org/news/content/images/size/w2000/2022/07/HTTP-VS-HTTPS.png) -我们每天都和HTTP还有HTTPS打交道,但是很多人都不知道这两者之间的区别是什么。 +我们每天都和 HTTP 还有 HTTPS 打交道,但是很多人都不知道这两者之间的区别是什么。 大多数计算机用户看到浏览器告知他们这个应用不安全,很有可能有黑客想要获取他们重要的信息的时候。用户可能跑得比博尔特还要快。 -但这个安全风险是可以避免的,这就是为什么HTTPS引入并取代HTTP。今天我们就来讨论这个话题。 :) +但这个安全风险是可以避免的,这就是为什么 HTTPS 引入并取代 HTTP。今天我们就来讨论这个话题。 :) -## 我们将讨论: +## 我们将讨论 -1. 什么是HTTP? -2. HTTP如何工作 -3. HTTP的功能 +1. 什么是 HTTP? +2. HTTP 如何工作 +3. HTTP 的功能 4. 如何知道一个网站不安全 -5. 所有的HTTP网站都不安全吗? -6. 什么是HTTPS? -7. HTTPS如何工作 -8. HTTPS的功能 +5. 所有的 HTTP 网站都不安全吗? +6. 什么是 HTTPS? +7. HTTPS 如何工作 +8. HTTPS 的功能 9. 加密的工作原理 10. 如何知道一个网站是安全的? -11. SSL证书是什么? -12. SSL是如何工作的? -13. 如何给我的网站申请SSL? -14. 在哪里申请SSL证书? -15. 可以免费获取SSL证书吗? -16. HTTPS和HTTP的主要区别 +11. SSL 证书是什么? +12. SSL 是如何工作的? +13. 如何给我的网站申请 SSL? +14. 在哪里申请 SSL 证书? +15. 可以免费获取 SSL 证书吗? +16. HTTPS 和 HTTP 的主要区别 17. 总结 -## 什么是HTTP? +## 什么是 HTTP? -超文本传输协议(HTTP)是浏览器和你想要访问的网站(web服务器)之间的一种沟通方法。 +超文本传输协议(HTTP)是浏览器和你想要访问的网站(web 服务器)之间的一种沟通方法。 协议允许你通过浏览器从服务器获取你想要的信息。 -通过类比来了解HTTP和HTTPS是一个很好的办法。我们都知道浏览器和服务器是通过HTTP来进行通信的。HTTP通常是纯文本。就好比世界上大多数人都说英语,如果一个黑客知道怎么说英语,并且黑进了你的电脑,这样他就可以很轻易地获取你的密码。 +通过类比来了解 HTTP 和 HTTPS 是一个很好的办法。我们都知道浏览器和服务器是通过 HTTP 来进行通信的。HTTP 通常是纯文本。就好比世界上大多数人都说英语,如果一个黑客知道怎么说英语,并且黑进了你的电脑,这样他就可以很轻易地获取你的密码。 但是在肯尼亚,我们的母语是图尔卡纳语。如果你不了解这门语言,并且在肯尼亚发现两个图尔卡纳人在对话,你就会听不懂他们在说什么。 -这就是HTTPS的精华所在。它做了加密处理,理论上黑客不知道浏览器和服务器之间发生了什么通信。 +这就是 HTTPS 的精华所在。它做了加密处理,理论上黑客不知道浏览器和服务器之间发生了什么通信。 ![Client to server](https://user-images.githubusercontent.com/33565767/178446926-d904cc04-57cd-4427-bdcc-e76c35f8b51b.png "client/browser communicating with server") @@ -51,65 +51,65 @@ 客户端(大多数情况下是浏览器)发送一个信息,在计算机术语中叫做请求(request)。然后服务器应答,通常被称为响应(response)。 -HTTP可以有效地发送HTML文档、图片和视频到web浏览器,方便用户观看。HTTP同样也使用HTML格式将数据传输到服务器。 +HTTP 可以有效地发送 HTML 文档、图片和视频到 web 浏览器,方便用户观看。HTTP 同样也使用 HTML 格式将数据传输到服务器。 ![Where HTTP Protocol sits](https://user-images.githubusercontent.com/33565767/178460366-d0568e2d-d107-4afe-a778-0ce1d224b176.png "HTTP Protocol is between the web browser(client) and the web server, which is in constant communication with the server side script and the database.") -## HTTP如何运作 +## HTTP 如何运作 -HTTP通过纯文本传输数据。举个例子,如果你在访问你的银行网页,这个网页使用的是HTTP,那么黑客就可以访问这个页面,并且读取任何你发送的信息。 +HTTP 通过纯文本传输数据。举个例子,如果你在访问你的银行网页,这个网页使用的是 HTTP,那么黑客就可以访问这个页面,并且读取任何你发送的信息。 -此时HTTPS就派上用场了。许多公司都应用了HTTPS,确保用户安全地发送信息。我们会在后文章讨论。 +此时 HTTPS 就派上用场了。许多公司都应用了 HTTPS,确保用户安全地发送信息。我们会在后文章讨论。 -## HTTP的特征 +## HTTP 的特征 -- 纯文本。在HTTP创建之初,开发者们只想解决一个问题:仅服务于文本文件。现在,HTTP已经被运用于文本以外更多地方。 +- 纯文本。在 HTTP 创建之初,开发者们只想解决一个问题:仅服务于文本文件。现在,HTTP 已经被运用于文本以外更多地方。 -- 七层协议。HTTP是网络通信OSI模型的第七层协议。第七层是应用层,也是OSI模型的最外层。 其他的层级包括物理层、数据链路层、网络层、传输层、会话层和表示层。想要了解更多OSI模型的信息,可以参考freeCodeCamp的Youtube频道里由Brain Ferrill讲解的网络工作的视频。视频中包含除了OSI以外更多精彩内容。 [Computer Networking Course - Network Engineering \[CompTIA Network+ Exam Prep\]](https://www.youtube.com/watch?v=qiQR5rTSshw&t=27s&ab_channel=freeCodeCamp.org) +- 七层协议。HTTP 是网络通信 OSI 模型的第七层协议。第七层是应用层,也是 OSI 模型的最外层。 其他的层级包括物理层、数据链路层、网络层、传输层、会话层和表示层。想要了解更多 OSI 模型的信息,可以参考 freeCodeCamp 的 Youtube 频道里由 Brain Ferrill 讲解的网络工作的视频。视频中包含除了 OSI 以外更多精彩内容。 [Computer Networking Course - Network Engineering \[CompTIA Network+ Exam Prep\]](https://www.youtube.com/watch?v=qiQR5rTSshw&t=27s&ab_channel=freeCodeCamp.org) -- 不安全。当你通过纯文本发送HTTP请求的时候,你同样也收到纯文本响应。也就是说任何可以访问这些请求和响应的人都可以读取这些信息。 +- 不安全。当你通过纯文本发送 HTTP 请求的时候,你同样也收到纯文本响应。也就是说任何可以访问这些请求和响应的人都可以读取这些信息。 ![Insecure connection](https://user-images.githubusercontent.com/33565767/179723161-3ec27c27-df79-4749-b810-974583cf1687.png "Insecure connection during a normal HTTP connection by the user") -- 轻量。HTTP的优势在与轻量。因为没有像HTTPS一样,通过加密来保护数据,所以HTTP传输速度非常快。 +- 轻量。HTTP 的优势在与轻量。因为没有像 HTTPS 一样,通过加密来保护数据,所以 HTTP 传输速度非常快。 -- HTTP通常在端口80监听。 +- HTTP 通常在端口 80 监听。 ## 如何知道一个网站不安全 -当网站不安全的时候,Chrome通常会发出一个警告: `Your connection is not private`(你的连接不是私人的)。 +当网站不安全的时候,Chrome 通常会发出一个警告: `Your connection is not private`(你的连接不是私人的)。 ![HTTP Not secure](https://user-images.githubusercontent.com/33565767/182329336-d405d5b4-f5bb-45df-b936-aa1d04b9dffd.png "Your connection is not secure when going to a site that is not secure") -在Chrome的URL输入框内通常会显示红色的`Not Secure`(不安全)。 +在 Chrome 的 URL 输入框内通常会显示红色的`Not Secure`(不安全)。 ![Not secure URL image](https://user-images.githubusercontent.com/33565767/182329466-d2db68a8-7033-4f64-bb66-0665e8fc0c62.png "URL of an insecure website") -## 所有的HTTP网站都不安全吗? +## 所有的 HTTP 网站都不安全吗? -假设你正在浏览一个meme网站,一边滚动图片一边哈哈大笑。如果网站使用的是HTTP协议,这没什么大不了。 +假设你正在浏览一个 meme 网站,一边滚动图片一边哈哈大笑。如果网站使用的是 HTTP 协议,这没什么大不了。 -这时你觉得有些无聊了,打算登陆你的银行的网站,网站没有使用HTTPS协议,此时你就向黑客双手奉上了你的账户信息。 +这时你觉得有些无聊了,打算登陆你的银行的网站,网站没有使用 HTTPS 协议,此时你就向黑客双手奉上了你的账户信息。 -所以安全的底线是你浏览的信息是否无关紧要。如果不重要,HTTP是安全的,但是如果是需要保密的信息,HTTP就不够用。 +所以安全的底线是你浏览的信息是否无关紧要。如果不重要,HTTP 是安全的,但是如果是需要保密的信息,HTTP 就不够用。 -## 什么是HTTPS? +## 什么是 HTTPS? -超文本传输安全协议,即HTTPS是浏览器和你登陆的网站(服务器)之间安全的通信。 +超文本传输安全协议,即 HTTPS 是浏览器和你登陆的网站(服务器)之间安全的通信。 -## HTTPS如何运作 +## HTTPS 如何运作 -HTTPS通过加密数据这样的安全协议来确保通信的安全。 +HTTPS 通过加密数据这样的安全协议来确保通信的安全。 -对于大多数网站来说,拥有HTTPS的方式是获取一个SSL (Secure Sockets Layer安全套接层)或者TLS (Transport Layer Security传输层安全协议)证书。 +对于大多数网站来说,拥有 HTTPS 的方式是获取一个 SSL (Secure Sockets Layer 安全套接层)或者 TLS (Transport Layer Security 传输层安全协议)证书。 -现在,SSL已经兼容TLS,所以你没有必要获取一个TLS证书。 +现在,SSL 已经兼容 TLS,所以你没有必要获取一个 TLS 证书。 -## HTTPS的特证 +## HTTPS 的特证 -- 加密数据。通过TLS/SSL协议来加密数据。 +- 加密数据。通过 TLS/SSL 协议来加密数据。 - 是第四层(传输层)协议。 -- 公钥和私钥的密钥交换发生在HTTPS中以加密和解密数据。 -- 不如HTTP轻量。当HTTPS发生加密和解密的时候,就变得笨重。 -- HTTPS在端口443监听。 +- 公钥和私钥的密钥交换发生在 HTTPS 中以加密和解密数据。 +- 不如 HTTP 轻量。当 HTTPS 发生加密和解密的时候,就变得笨重。 +- HTTPS 在端口 443 监听。 ## 加密的工作原理 @@ -121,7 +121,7 @@ HTTPS通过加密数据这样的安全协议来确保通信的安全。 ## 如何知道一个网站是安全的? -检查一个网站是否是安全的,你通常可以查看URL信息栏是否有一把锁。如果有一把锁,则服务器和客户端之间的通信就是安全的。 +检查一个网站是否是安全的,你通常可以查看 URL 信息栏是否有一把锁。如果有一把锁,则服务器和客户端之间的通信就是安全的。 ![Showing that the site is secure](https://user-images.githubusercontent.com/33565767/178449484-738fb908-901d-4a61-9f8f-fa5a39029012.png "A padlock that shows the site is secure on the URL") @@ -129,19 +129,19 @@ HTTPS通过加密数据这样的安全协议来确保通信的安全。 ![Shows site is secure](https://user-images.githubusercontent.com/33565767/180213859-21460cfa-6a8c-484e-81e5-5dba4fc31f2a.png "The URL section with more details of a secure site") -## SSL证书是什么? +## SSL 证书是什么? -SSL证书是一个小文件,告诉你的浏览器,比方说,freecodecamp.org是它所声称的网站,并且是可靠的。 +SSL 证书是一个小文件,告诉你的浏览器,比方说,freecodecamp.org 是它所声称的网站,并且是可靠的。 证书的认证即是向客户(用户)确认他们所连接的服务器是管理该域(domain)的服务器。认证是为了使用户免受如域名欺骗这样的安全问题的影响。 -证书包含一个公钥,并告诉您正在尝试连接的网站的所有者是谁。如果网站没有SSL证书,则无法使用TLS加密。 +证书包含一个公钥,并告诉您正在尝试连接的网站的所有者是谁。如果网站没有 SSL 证书,则无法使用 TLS 加密。 -如果你是网站所有者,你可以创建自己的SSL证书(也称为自签名证书)。但这种方法的问题在于Chrome等浏览器不信任这些证书。它们倾向于信任由证书颁发机构颁发的证书。 +如果你是网站所有者,你可以创建自己的 SSL 证书(也称为自签名证书)。但这种方法的问题在于 Chrome 等浏览器不信任这些证书。它们倾向于信任由证书颁发机构颁发的证书。 -## SSL是如何运作的? +## SSL 是如何运作的? -SSL加密有两种类型,非对称和对称。SSL加密结合了这两种加密方式。下文会做详细介绍: +SSL 加密有两种类型,非对称和对称。SSL 加密结合了这两种加密方式。下文会做详细介绍: ### 什么是非对称加密? @@ -156,13 +156,13 @@ SSL加密有两种类型,非对称和对称。SSL加密结合了这两种加 私钥只能在该特定服务器上找到,其他任何人都没有。这就是为什么非对称加密更强大且更难破解,因为它有两个不同的密钥,私钥和公钥。这两个密钥协同工作以确保数据更安全。 -这也是为什么这种加密的长度是1024/2048位。 +这也是为什么这种加密的长度是 1024/2048 位。 ### 什么是对称加密? 对称加密就简单很多。你只有一把密钥。客户端使用一个密钥进行加密,服务器使用相同的密钥解密数据。 -对称加密更轻量。长度为128/256位。但与非对称相比,它更容易被入侵,这并不意味着它没有用。当我们使用SSL时,我们将非对称和对称结合起来,能够使通信更安全、更私密。 +对称加密更轻量。长度为 128/256 位。但与非对称相比,它更容易被入侵,这并不意味着它没有用。当我们使用 SSL 时,我们将非对称和对称结合起来,能够使通信更安全、更私密。 ![Symmetric encryption](https://user-images.githubusercontent.com/33565767/181720497-326e0dd9-5e0b-4bfb-b01a-2effec5ab9e0.png "How symmetric encryption works by using one key on the client side to encrypt and the same key on the server side to decrypt") @@ -183,44 +183,44 @@ SSL加密有两种类型,非对称和对称。SSL加密结合了这两种加 假设你关闭了浏览器,并在第二天登录 —— 一切重新开始,会话密钥会再次被创建。 -## 如何给我的网站申请SSL? +## 如何给我的网站申请 SSL? -如果您是网站所有者,您可以从证书颁发机构获取SSL证书。 +如果您是网站所有者,您可以从证书颁发机构获取 SSL 证书。 -然后在托管网站的Web服务器上安装证书。大多数情况下,托管网站的托管公司会为您处理此过程。 +然后在托管网站的 Web 服务器上安装证书。大多数情况下,托管网站的托管公司会为您处理此过程。 -## 在哪里申请SSL证书? +## 在哪里申请 SSL 证书? -有些组织颁发安全证书,这些组织称为证书颁发机构。其中一些证书颁发机构包括:DigiCert、Comodo等。 +有些组织颁发安全证书,这些组织称为证书颁发机构。其中一些证书颁发机构包括:DigiCert、Comodo 等。 许多开发人员从这些组织获得证书。由于这些组织办法的证书是使用最广泛,浏览器通常信任来自这些组织的证书。 -## 可以免费获取SSL证书吗? +## 可以免费获取 SSL 证书吗? -Cloudflare免费提供SSL证书。它是最早采取这一行动的互联网安全公司之一。 +Cloudflare 免费提供 SSL 证书。它是最早采取这一行动的互联网安全公司之一。 如果你想申请一个,[可以点击这里](https://www.cloudflare.com/ssl/)。 -## HTTPS的用途 +## HTTPS 的用途 -HTTPS在安全性方面起到了很大的作用。没有它,传递敏感信息将成为一个巨大的挑战,尤其是当你的企业需要一种安全的通信方式时。 +HTTPS 在安全性方面起到了很大的作用。没有它,传递敏感信息将成为一个巨大的挑战,尤其是当你的企业需要一种安全的通信方式时。 -接受在线支付的网站(如电子商务网站)通常需要HTTPS。这是为了避免信用卡详细信息和登录信息等被盗 (引用: [Tony Messer](https://www.entrepreneur.com/article/281633))。 +接受在线支付的网站(如电子商务网站)通常需要 HTTPS。这是为了避免信用卡详细信息和登录信息等被盗 (引用: [Tony Messer](https://www.entrepreneur.com/article/281633))。 -## HTTPS和HTTP的主要区别 +## HTTPS 和 HTTP 的主要区别 -- HTTPS中启用了加密层,而HTTP中没有加密层。 -- 数据在HTTPS中受到保护,而在HTTP中则不受保护。 -- 当使用HTTPS时,网站在Google中的排名会提高,而使用HTTP时,不会获得任何排名提升。 -- 使用HTTPS可以防止网络钓鱼,而使用HTTP没有保护。 -- 使用HTTPS符合支付行业规定,但是HTTP不合规。 -- 在最初几秒钟内加载HTTPS可能比加载HTTP慢。 -- 获得SSL证书可能需要花钱,而HTTP没有认证成本。 -- 在chrome上使用HTTPS网站更方便,使用HTTP网站会经常收到不安全提示。 +- HTTPS 中启用了加密层,而 HTTP 中没有加密层。 +- 数据在 HTTPS 中受到保护,而在 HTTP 中则不受保护。 +- 当使用 HTTPS 时,网站在 Google 中的排名会提高,而使用 HTTP 时,不会获得任何排名提升。 +- 使用 HTTPS 可以防止网络钓鱼,而使用 HTTP 没有保护。 +- 使用 HTTPS 符合支付行业规定,但是 HTTP 不合规。 +- 在最初几秒钟内加载 HTTPS 可能比加载 HTTP 慢。 +- 获得 SSL 证书可能需要花钱,而 HTTP 没有认证成本。 +- 在 chrome 上使用 HTTPS 网站更方便,使用 HTTP 网站会经常收到不安全提示。 ## 总结 -HTTP和HTTPS在开发者的日常生活中扮演了重要的角色,浏览器和服务器之间的通信是我们工作的动力。 +HTTP 和 HTTPS 在开发者的日常生活中扮演了重要的角色,浏览器和服务器之间的通信是我们工作的动力。 尽可能地保护用户的数据,以防止他们的信息被盗,将获得用户的信任并提供更好的用户体验。 diff --git a/chinese/articles/interaction-design-evaluate-interaction-costs-improve-ux.md b/chinese/articles/interaction-design-evaluate-interaction-costs-improve-ux.md index e20d0f63e..cd22759d0 100644 --- a/chinese/articles/interaction-design-evaluate-interaction-costs-improve-ux.md +++ b/chinese/articles/interaction-design-evaluate-interaction-costs-improve-ux.md @@ -187,7 +187,7 @@ TA 和可用性指标是进阶话题,完全可以另起炉灶,所以我把 换句话说,用户会权衡每个动作的收益和成本,并选择收益与成本最平衡的路径。 -用户会倾向于具有最低预期交互成本的路径。即使有一个成本较低的路径,如果这个路径不直观或不熟悉,他们最终会选择他们更熟悉的路径,因为MIC较低。 +用户会倾向于具有最低预期交互成本的路径。即使有一个成本较低的路径,如果这个路径不直观或不熟悉,他们最终会选择他们更熟悉的路径,因为 MIC 较低。 具有高动机的用户(例如,由于您的营销或品牌推广)更有可能为完成他们的目标付出高昂的互动成本。 例如,如果苹果公司的网站有很高的交互成本,但用户可能仍然有足够的动力来完成他们的任务。 @@ -201,7 +201,7 @@ TA 和可用性指标是进阶话题,完全可以另起炉灶,所以我把 ## 总结 -了解交互成本是任何现代产品设计师的一项重要技能。我们鼓励你超越表面的交互设计考虑,深入研究众多的PIC和MIC因素。 +了解交互成本是任何现代产品设计师的一项重要技能。我们鼓励你超越表面的交互设计考虑,深入研究众多的 PIC 和 MIC 因素。 当然,我们应该努力尽可能地减少交互成本。但在必不得已的情况下,我们应该做出权衡,以确保主要用例的摩擦最小。 @@ -209,4 +209,4 @@ TA 和可用性指标是进阶话题,完全可以另起炉灶,所以我把 如果你喜欢这篇文章,[\***\*加入我的免费新闻通讯\*\***](https://theambitiousdesigner.substack.com/)"雄心勃勃的设计师",以获得更多职业和设计方面的见解。 -我还在Facebook上运营一个[私人指导小组](https://www.facebook.com/groups/richarduxmentorship)和一个设计[Instagram](https://www.instagram.com/richard.ux/)账户。 +我还在 Facebook 上运营一个[私人指导小组](https://www.facebook.com/groups/richarduxmentorship)和一个设计[Instagram](https://www.instagram.com/richard.ux/)账户。 diff --git a/chinese/articles/intoduction-to-nodejs.md b/chinese/articles/intoduction-to-nodejs.md index 5c6156f33..275bff3fa 100644 --- a/chinese/articles/intoduction-to-nodejs.md +++ b/chinese/articles/intoduction-to-nodejs.md @@ -5,23 +5,23 @@ ![How to Get Started with Node.js – Beginner's Guide to Node](https://www.freecodecamp.org/news/content/images/size/w2000/2022/07/1200px-Node.js_logo.svg.png) -Node.js是一个JavaScript运行时环境,将其能力扩展到服务器端。它是建立在Chrome的V8 JavaScript引擎上。 +Node.js 是一个 JavaScript 运行时环境,将其能力扩展到服务器端。它是建立在 Chrome 的 V8 JavaScript 引擎上。 -Node是一个事件驱动的非阻塞I/O模型。这意味着它是异步的,并且因为一个请求而阻塞(而是立即移动到下一个请求),这使得Node异常快速和高效。 +Node 是一个事件驱动的非阻塞 I/O 模型。这意味着它是异步的,并且因为一个请求而阻塞(而是立即移动到下一个请求),这使得 Node 异常快速和高效。 -所谓事件驱动,是指一旦Node启动,它就会启动所有的变量和函数,并等待事件的发生。 +所谓事件驱动,是指一旦 Node 启动,它就会启动所有的变量和函数,并等待事件的发生。 ![image-195](https://www.freecodecamp.org/news/content/images/2022/06/image-195.png) -NPM即Node包管理工具(Node Package Manager),辅助你管理Node包。 NPX即Node包执行(Node Package Execute),它可以执行任何npm包,甚至无需安装。 +NPM 即 Node 包管理工具(Node Package Manager),辅助你管理 Node 包。 NPX 即 Node 包执行(Node Package Execute),它可以执行任何 npm 包,甚至无需安装。 -可以前往[https://nodejs.org/en/download/](https://nodejs.org/en/download/)下载NPM。 +可以前往[https://nodejs.org/en/download/](https://nodejs.org/en/download/)下载 NPM。 -## 编写你的第一个Node.js项目(Hello World) +## 编写你的第一个 Node.js 项目(Hello World) -在你的项目文件中创建一个名为hello\_world.js的文件。 +在你的项目文件中创建一个名为 hello_world.js 的文件。 -然后在如VS Code这样的代码编辑器中打开文件。在编辑器中输入`console.log(“Hello World”);`。 +然后在如 VS Code 这样的代码编辑器中打开文件。在编辑器中输入`console.log(“Hello World”);`。 打开终端,并且导航到文件所在的位置。 @@ -29,11 +29,11 @@ NPM即Node包管理工具(Node Package Manager),辅助你管理Node包。 ![image-196](https://www.freecodecamp.org/news/content/images/2022/06/image-196.png) -## 如何导入Node核心模块 +## 如何导入 Node 核心模块 让我们从基础包开始,即**fs (文件系统)**。我们使用它来创建、读取和修改文件。 -导入fs模块,输入命令:`const fs = require(“fs”);`。 +导入 fs 模块,输入命令:`const fs = require(“fs”);`。 使用这个模块中的函数,可以参考[文档](https://nodejs.org/docs/latest-v17.x/api/fs.html#file-system)。 @@ -54,11 +54,11 @@ fs.appendFileSync(filename, content);. ![1*dOqUqcuJ5a5vl_BQ_E0dSg](https://miro.medium.com/max/842/1*dOqUqcuJ5a5vl_BQ_E0dSg.png) -## 如何安装NPM包 +## 如何安装 NPM 包 -我们将使用一个名为**superheroes(超级英雄)**的基础NPM包 (包含了一个随机的超级英雄清单)来帮助你理解NPM是如何运作的。 +我们将使用一个名为**superheroes(超级英雄)**的基础 NPM 包 (包含了一个随机的超级英雄清单)来帮助你理解 NPM 是如何运作的。 -我们可以在cmd中使用这条命令来安装任意npm包: +我们可以在 cmd 中使用这条命令来安装任意 npm 包: ```cmd npm install superheroes @@ -74,9 +74,9 @@ console.log(sh.random());. ![1*WfHNl2GDgyXBEwfV6oV0GQ](https://miro.medium.com/max/1400/1*WfHNl2GDgyXBEwfV6oV0GQ.png) -再来试一试另外一个包。让我们安装时下最流行的一个npm包——“chalk",这个包可以改变终端字符串的样式。 +再来试一试另外一个包。让我们安装时下最流行的一个 npm 包——“chalk",这个包可以改变终端字符串的样式。 -使用以下命令安装chalk(我们将安装版本2.4.2,在这个版本中可以使用**require**方法) +使用以下命令安装 chalk(我们将安装版本 2.4.2,在这个版本中可以使用**require**方法) ```js npm install chalk@2.4.2 @@ -90,11 +90,11 @@ chalk.color(text) ![1*AQ5TX0vxzPn5N0lzrSBbJw](https://miro.medium.com/max/1400/1*AQ5TX0vxzPn5N0lzrSBbJw.png) -更多信息参考[chalk包的文档](https://www.npmjs.com/package/chalk)。 +更多信息参考[chalk 包的文档](https://www.npmjs.com/package/chalk)。 -## 如何在程序中初始化NPM +## 如何在程序中初始化 NPM -我们可以使用以下命令来初始化NPM: +我们可以使用以下命令来初始化 NPM: ```js npm init @@ -112,15 +112,15 @@ npm init ![1*hYaMdTgcLdABQ1qqjQdpRQ](https://miro.medium.com/max/1400/1*hYaMdTgcLdABQ1qqjQdpRQ.png) -### 所以,package.json是什么? +### 所以,package.json 是什么? -package.json是Nodejs项目的一部分。它包含了所有依赖项(NPM包)的记录和每一个项目的原数据。 +package.json 是 Nodejs 项目的一部分。它包含了所有依赖项(NPM 包)的记录和每一个项目的原数据。 如果其他人下载了这个项目,他们可以通过这个文件来安装所有运行程序需要的依赖项。 -## 如何使用Moment.js — 一个NPM包 +## 如何使用 Moment.js — 一个 NPM 包 -这是使用最多的NPM包之一,可以使用这个包来解析和验证日期。 +这是使用最多的 NPM 包之一,可以使用这个包来解析和验证日期。 使用以下命令安装包: @@ -134,7 +134,7 @@ npm i moment const moment = require(“moment”); ``` -通过创建一个Date对象来获取当前日期和时间(JavaScript方法),运行以下代码: +通过创建一个 Date 对象来获取当前日期和时间(JavaScript 方法),运行以下代码: ```js const time = new Date(); @@ -154,11 +154,11 @@ console.log(parsedTime); ![1*V3hJ24cmTASx9k6Rv83gXg](https://miro.medium.com/max/1400/1*V3hJ24cmTASx9k6Rv83gXg.png) -该项目的package.json中包含的所有依赖项 — 这个例子中的依赖项就是**moment**。 +该项目的 package.json 中包含的所有依赖项 — 这个例子中的依赖项就是**moment**。 ![1*kKFpiaEOtsRbxN67do4HDw](https://miro.medium.com/max/1400/1*kKFpiaEOtsRbxN67do4HDw.png) -在项目文件夹中也有**node\_modules**文件夹。该文件夹包含了所有项目依赖的的依赖项,包含moment,以及moment依赖的依赖包。 +在项目文件夹中也有**node\_modules**文件夹。该文件夹包含了所有项目依赖的的依赖项,包含 moment,以及 moment 依赖的依赖包。 ![1*-mxxdXnGzLxG98LE2ebMDQ](https://miro.medium.com/max/454/1*-mxxdXnGzLxG98LE2ebMDQ.png) @@ -168,33 +168,33 @@ console.log(parsedTime); ![1*b1VMBTQ3HtQtnaHUWGY8iQ](https://miro.medium.com/max/1400/1*b1VMBTQ3HtQtnaHUWGY8iQ.png) -# 如何使用Express JS — 一个NodeJS框架 +# 如何使用 Express JS — 一个 NodeJS 框架 -Express是Node.js的一个web应用框架,该框架提供了全面的功能来支持web和移动应用的开发。 +Express 是 Node.js 的一个 web 应用框架,该框架提供了全面的功能来支持 web 和移动应用的开发。 -### 如何安装Express +### 如何安装 Express -使用以下命令来创建Express: +使用以下命令来创建 Express: ```js npm install express ``` -然后这样导入Express: +然后这样导入 Express: ```js const express = require("express"); ``` -### 如何创建一个Express应用 +### 如何创建一个 Express 应用 -使用以下命令来创建Express应用: +使用以下命令来创建 Express 应用: ```js const app = express() ``` -### 如何在端口3000启动服务器 +### 如何在端口 3000 启动服务器 ```js app.listen(3000, () => { @@ -212,9 +212,9 @@ app.listen(3000, () => { 可以使用 `app.get()`函数定义 “/”路由。 -**app.get (route, callback function)**函数被用于处理所有GET请求。 +**app.get (route, callback function)**函数被用于处理所有 GET 请求。 -这个回调函数有两个参数,**req**和**res**,分别指代的是HTTP请求和期望的响应。参数名(req,res)并不是固定的,所有你可以重命名为其他值。 +这个回调函数有两个参数,**req**和**res**,分别指代的是 HTTP 请求和期望的响应。参数名(req,res)并不是固定的,所有你可以重命名为其他值。 ```js app.get("/", (req,res) => { @@ -224,9 +224,9 @@ app.get("/", (req,res) => { } ``` -## 如何使用Express来创建Hello World程序 +## 如何使用 Express 来创建 Hello World 程序 -在这个部分中我们将使用Express创建基本的Hello World程序: +在这个部分中我们将使用 Express 创建基本的 Hello World 程序: ```js const express = require("express"); @@ -243,15 +243,15 @@ app.listen(3000, () => { ![1*uRqmENgESv8cdq-0oSaX8A](https://miro.medium.com/max/1060/1*uRqmENgESv8cdq-0oSaX8A.png) -## 如何在Express中渲染静态文件 +## 如何在 Express 中渲染静态文件 -这部分介绍如何使用Express来渲染静态文件的概念。 +这部分介绍如何使用 Express 来渲染静态文件的概念。 -首先,创建一个新的项目文件夹,并且使用 `npm init -y`来初始化npm。 +首先,创建一个新的项目文件夹,并且使用 `npm init -y`来初始化 npm。 -使用 `npm i express`来安装Express,并创建一个名为app.js的文件。 +使用 `npm i express`来安装 Express,并创建一个名为 app.js 的文件。 -创建一个app,并在端口3000监听: +创建一个 app,并在端口 3000 监听: ```js const express = require("express); @@ -261,9 +261,9 @@ app.listen(3000, () => { } ``` -在根目录创建一个名为public的文件夹,来渲染静态web页面,如:HTML、CSS和JS。 +在根目录创建一个名为 public 的文件夹,来渲染静态 web 页面,如:HTML、CSS 和 JS。 -由于本教程重点在后端,所有我们不会花时间在前端部分,在public文件夹中,我们仅创建HTML文件。 +由于本教程重点在后端,所有我们不会花时间在前端部分,在 public 文件夹中,我们仅创建 HTML 文件。 ![1*-OiGmKZaz7GKc3NdNVjZdg](https://miro.medium.com/max/1142/1*-OiGmKZaz7GKc3NdNVjZdg.png) @@ -295,21 +295,21 @@ app.listen(3000, () => { ![1*2U5Qi3XKOaNF0MjXSTo0tg](https://miro.medium.com/max/1034/1*2U5Qi3XKOaNF0MjXSTo0tg.png) -## 如何在Express中渲染动态文件 +## 如何在 Express 中渲染动态文件 在这个部分我们将学习如何使用一个输入对象的值来渲染动态文件。 -有一些,如:pug、handlebars、ejs等模板用于动态页面的渲染。这些模板使得我们可以在运行时注入动态数据、if条件和循环。 +有一些,如:pug、handlebars、ejs 等模板用于动态页面的渲染。这些模板使得我们可以在运行时注入动态数据、if 条件和循环。 -在这里我们将讨论handlebars。 +在这里我们将讨论 handlebars。 -安装包 (express和hbs): +安装包 (express 和 hbs): ```js npm i hbs express ``` -创建文件名为app.js的文件,并且导入包: +创建文件名为 app.js 的文件,并且导入包: ```js const express = require(“express”); @@ -317,7 +317,7 @@ const hbs = require(“hbs”); const path = require(“path”); ``` -创建Express,并在端口3000监听: +创建 Express,并在端口 3000 监听: ```js const app = express(); @@ -326,7 +326,7 @@ app.listen(3000, (req,res) => { } ``` -将视图引擎(view engine)设置为hbs,使得handlebars生效: +将视图引擎(view engine)设置为 hbs,使得 handlebars 生效: ```js app.set(“view engine”, “hbs”); @@ -340,7 +340,7 @@ app.set(“view engine”, “hbs”); app.set(“views”, path.join(__dirname,“/views”); ``` -然后在根目录中创建 **views** 文件夹。 并在文件夹中创建index.hbs文件(.hbs是handlebars的扩展名)并插入以下HTML代码: +然后在根目录中创建 **views** 文件夹。 并在文件夹中创建 index.hbs 文件(.hbs 是 handlebars 的扩展名)并插入以下 HTML 代码: ### index.hbs @@ -358,7 +358,7 @@ app.set(“views”, path.join(__dirname,“/views”); **`{{author}}`** — 是插入动态数据的语法 -我们再创建一个app.get函数来处理路由"/"上的GET请求,并且发送动态数据**author**。 +我们再创建一个 app.get 函数来处理路由"/"上的 GET 请求,并且发送动态数据**author**。 ```js app.get("/", (req, res) => { @@ -370,7 +370,7 @@ app.get("/", (req, res) => { **`res.render`**是一个渲染视图的函数,在这里我们传入了两个参数。第一个是去掉扩展名的文件名,第二个是本地变量对象,比方说 **author**。 -### app.js文件 +### app.js 文件 ```js const express = require("express"); @@ -397,31 +397,31 @@ app.listen(3000, (req, res) => { ![1*JQt1mgjLTU-LJJ0XS7UH3A](https://miro.medium.com/max/824/1*JQt1mgjLTU-LJJ0XS7UH3A.png) -# 如何使用Handlebars创建高级模板 +# 如何使用 Handlebars 创建高级模板 -在这一部分我们将学习可复用组件。在前面的章节我们给每一个页面的header和footer创建了相同的组件。 +在这一部分我们将学习可复用组件。在前面的章节我们给每一个页面的 header 和 footer 创建了相同的组件。 这里的重复性工作就可以通过高级模板来简化。也就是说我们创建一个组件,并在需要的地方反复使用。 -### Handlebars引入部分(Partials)的概念 +### Handlebars 引入部分(Partials)的概念 -Partials是可被其他模板调用的handlebar文件。 Partials是一个被广泛应用的模板类概念,所以不仅限于Handlebars。 +Partials 是可被其他模板调用的 handlebar 文件。 Partials 是一个被广泛应用的模板类概念,所以不仅限于 Handlebars。 -想要构建可以复用的模板,可以将它们单独放在同一个文件夹内(Partial),然后在不同的模板中使用。可以将Partial理解为模块化模板的一种简单技术。 +想要构建可以复用的模板,可以将它们单独放在同一个文件夹内(Partial),然后在不同的模板中使用。可以将 Partial 理解为模块化模板的一种简单技术。 -可以通过以下步骤创建partial: +可以通过以下步骤创建 partial: -- 初始化npm → `npm init -y` -- 安装必要的包、Express以及hbs → `npm i express hbs` +- 初始化 npm → `npm init -y` +- 安装必要的包、Express 以及 hbs → `npm i express hbs` - 创建文件夹模板 -- 在文件夹模板内部创建另外两个文件夹: **partials和views** +- 在文件夹模板内部创建另外两个文件夹: **partials 和 views** - 创建文件 **app.js** ![1*98jLDll1IWq-vd8H0ieNCg](https://miro.medium.com/max/472/1*98jLDll1IWq-vd8H0ieNCg.png) 文件结构类似 -让我们创建两个partial文件: header.hbs和footer.hbs。同时也创建两个视图:index.hbs和about.hbs。 +让我们创建两个 partial 文件: header.hbs 和 footer.hbs。同时也创建两个视图:index.hbs 和 about.hbs。 ![1*E32yq-EHCLFfUFzbgIbJJg](https://miro.medium.com/max/422/1*E32yq-EHCLFfUFzbgIbJJg.png) @@ -501,15 +501,15 @@ app.listen(3000, () => { }); ``` -这里基本和在Express中渲染动态数据章节类似,除了使用partial的时候我们需要 **注册partials**。 +这里基本和在 Express 中渲染动态数据章节类似,除了使用 partial 的时候我们需要 **注册 partials**。 -### 如何注册partials +### 如何注册 partials ```js hbs.registerPartials(path_to_partials) ``` -由于我们在模板文件夹中创建了partials目录,这里是 partials 的路径: +由于我们在模板文件夹中创建了 partials 目录,这里是 partials 的路径: ```js hbs.registerPartials(path.join(__dirname, "/templates/partials")); @@ -517,10 +517,10 @@ hbs.registerPartials(path.join(__dirname, "/templates/partials")); # 总结 -在这本文章中,我们从理论到实践讲解了Node.js。虽然我们不能从一篇简短的文章中习得Node.js所有内容,但是我已经尽我所能地在这篇文章中涵盖了重要的知识点,来辅助你开启Node.js之旅。 +在这本文章中,我们从理论到实践讲解了 Node.js。虽然我们不能从一篇简短的文章中习得 Node.js 所有内容,但是我已经尽我所能地在这篇文章中涵盖了重要的知识点,来辅助你开启 Node.js 之旅。 -简言之,我们讨论了什么是Node.js,即一个非阻塞、事件驱动的JavaScript运行时环境,它是异步的、可以使用单线程来执行操作。我们还讨论了使用最广泛的短小、灵活的Node.js的web应用框架——Express。 +简言之,我们讨论了什么是 Node.js,即一个非阻塞、事件驱动的 JavaScript 运行时环境,它是异步的、可以使用单线程来执行操作。我们还讨论了使用最广泛的短小、灵活的 Node.js 的 web 应用框架——Express。 -我们还讲解了Node.js的NPM、 NPX以及静态和动态渲染。 +我们还讲解了 Node.js 的 NPM、 NPX 以及静态和动态渲染。 -总而言之,Node.js是一项令人惊叹的技术,而且由于其庞大的社区,其可能性是无穷无尽的。 +总而言之,Node.js 是一项令人惊叹的技术,而且由于其庞大的社区,其可能性是无穷无尽的。 diff --git a/chinese/articles/introduction-to-algorithms-with-javascript-examples.md b/chinese/articles/introduction-to-algorithms-with-javascript-examples.md index 52382866d..ce840aa61 100644 --- a/chinese/articles/introduction-to-algorithms-with-javascript-examples.md +++ b/chinese/articles/introduction-to-algorithms-with-javascript-examples.md @@ -34,9 +34,9 @@ - [遍历算法](#traversing-algorithms) - [广度优先(BFS)](#breadth-first-search-bfs-) - [深度优先(DFS)](#depth-first-search-dfs-) - - [先序DFS](#pre-order-dfs) - - [后序DFS](#post-order-dfs) - - [中序DFS](#in-order-dfs) + - [先序 DFS](#pre-order-dfs) + - [后序 DFS](#post-order-dfs) + - [中序 DFS](#in-order-dfs) - [总结](#wrap-up) <h1 id="what-is-an-algorithm">什么是算法</h1> @@ -190,13 +190,13 @@ console.log(twoNumberSum([1,2,3,4,5], 10)) // [] 需要一个客观且不会发生变化的方式来计算算法的性能,**渐进符号**就派上用场了。 -渐进符号(又称**大O**符号)是用来 **分析和比较当输入增加时算法的性能变化**的一种系统。 +渐进符号(又称**大 O**符号)是用来 **分析和比较当输入增加时算法的性能变化**的一种系统。 -大O是分析和比较不同算法复杂度(时间和空间上)的一种标准方法,因为复杂度的计算的是**输入发生变化时,算法运行的次数的变化**,无论环境发生什么改变,两者之间的关系不变。 +大 O 是分析和比较不同算法复杂度(时间和空间上)的一种标准方法,因为复杂度的计算的是**输入发生变化时,算法运行的次数的变化**,无论环境发生什么改变,两者之间的关系不变。 有各种各样复杂度的算法,但是最常用的几个如下: -- **常数 — O(1):** 指对运行次数和空间的需求永远独立于输入,保持不变的时候。比如向一个函数输入一个数字,返回这个数字减去10的结果。不论你输入的是100还是1000000,函数始终都运行单个运算(减去10),所以复杂度是常数O(1)。 +- **常数 — O(1):** 指对运行次数和空间的需求永远独立于输入,保持不变的时候。比如向一个函数输入一个数字,返回这个数字减去 10 的结果。不论你输入的是 100 还是 1000000,函数始终都运行单个运算(减去 10),所以复杂度是常数 O(1)。 - **对数 — O(log n):** 指随着输入的增加,所需的运行次数和空间需求的增长逐渐放缓。这种类型的复杂性通常出现在[分治算法](https://zh.m.wikipedia.org/zh/%E5%88%86%E6%B2%BB%E6%B3%95)或查找算法中。经典的例子是二分查找,通过不断将数据集拆分成两部分,得到最终结果。 - **线性 — O(n):** 指所需的运行次数和空间需求与输入同速率增长时。以打印数组每一个值的循环为例,运行的次数会随着数组的长度而增长,所以复杂度是线性的 O(n)。 - **平方 — O(n²):** 指所需运行次数和空间需求以输入的平方增长时。嵌套循环是这个的经典例子。假设我们有一个遍历整个数字数组的循环,并且在该循环中有另一个循环再次遍历整个数组。这样数组中的每个值都被遍历了两次,因此复杂度是平方 O(n²)。 @@ -209,7 +209,7 @@ console.log(twoNumberSum([1,2,3,4,5], 10)) // [] 为了更好地理解,让我们回到之前的问题,并分析解决方案的复杂度。 -### 示例1: +### 示例 1 ```javascript function twoNumberSum(array, targetSum) { @@ -237,15 +237,15 @@ console.log(twoNumberSum([1,2,3,4,5], 10)) // [] 每一次遍历为一个任务: -- 如果数组中有**3**个数字,那么每一个数字都需要遍历三遍一共9遍(三乘以数组里的三个数字),这个任务总共要执行**12**次。 -- 如果数组中有4个数字,那么每一个数字都需要遍历4遍一共16遍(四乘以数组里的三个数字),这个任务总共要执行**20**次。 -- 如果数组中有5个数字,那么每一个数字都需要遍历5遍一共25遍(五乘以数组里的三个数字),这个任务总共要执行**30**次。 +- 如果数组中有**3**个数字,那么每一个数字都需要遍历三遍一共 9 遍(三乘以数组里的三个数字),这个任务总共要执行**12**次。 +- 如果数组中有 4 个数字,那么每一个数字都需要遍历 4 遍一共 16 遍(四乘以数组里的三个数字),这个任务总共要执行**20**次。 +- 如果数组中有 5 个数字,那么每一个数字都需要遍历 5 遍一共 25 遍(五乘以数组里的三个数字),这个任务总共要执行**30**次。 可以看到,与输入相比该算法中的任务数量如何呈指数增长且不成比例。该算法的复杂度是平方 – **O(n²)**。 每当我们遇到嵌套遍历,我们的反应链应该是平方复杂度 => 不妙 => 可能有更好的解决办法。 -### 示例2: +### 示例 2 ```javascript function twoNumberSum(array, targetSum) { @@ -275,7 +275,7 @@ console.log(twoNumberSum([1,2,3,4,5], 10)) // [] 因为我们仅遍历了一次数组,所以这个解法比上个好。但是我们对数组进行了排序(通常是对数复杂度)然后再遍历(线性复杂度)。这个解法的复杂度为 **O(n log(n))。** -### 示例3: +### 示例 3 ```javascript function twoNumberSum(array, targetSum) { @@ -302,7 +302,7 @@ console.log(twoNumberSum([1,2,3,4,5], 10)) // [] 在这个例子中,我们仅遍历了一次,并且在遍历之前没有做任何操作。因为了任务数量为三个里面最少的,所以这是最优解。这种情况的复杂度为 – **O(n)**. -复杂度就是 **算法背后最重要的概念**。了解如何比较不同的实现,哪一种方式更为有效十分有必要,因此如果你对这个概念尚不清晰,我鼓励你再看一遍上面的例子,或者查阅其他的资料。你还可以 [观看这个超级棒的freeCodeCamp 视频教学](https://www.youtube.com/watch?v=8hly31xKli0)。 +复杂度就是 **算法背后最重要的概念**。了解如何比较不同的实现,哪一种方式更为有效十分有必要,因此如果你对这个概念尚不清晰,我鼓励你再看一遍上面的例子,或者查阅其他的资料。你还可以 [观看这个超级棒的 freeCodeCamp 视频教学](https://www.youtube.com/watch?v=8hly31xKli0)。 <h1 id="searching-algorithms">查找算法</h1> @@ -332,7 +332,7 @@ console.log(search(11)) // -1 因为数组未被排序,所以我们并不知道每个值的大概位置,最好的办法就是一次检查一个值。这种算法的复杂度是 **线性- O(n)**,因为最坏的情况是我们遍历了整个数组才找到我们需要的值。 -线性查找被很多JavaScript内置方法采用,如 `indexOf`、 `includes`和`findIndex`。 +线性查找被很多 JavaScript 内置方法采用,如 `indexOf`、 `includes`和`findIndex`。 <h2 id="binary-search">二分查找</h2> @@ -517,7 +517,7 @@ console.log(arr) // [1,2,3,4,5,6,7,8,9,10] - 递归地将数据平分两半直至每一个“一半”仅有一个值 - 再递归地将各个“一半”排序直至合并成原数组长度的数组 -这个算法的复杂度为 **O(n log n)**, 因为拆解数组的复杂度为log n,而对比大小的复杂度为n。 +这个算法的复杂度为 **O(n log n)**, 因为拆解数组的复杂度为 log n,而对比大小的复杂度为 n。 ![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1641396131234/Oiryt3mR92.png?auto=compress,format&format=webp) @@ -628,7 +628,7 @@ console.log(quickSort(arr)) // [1,2,3,4,5,6,7,8,9,10] - 为每个位数(从 0 到 9)创建“桶”,并根据评估将每个值放入其对应的桶中。 - 将现有列表替换为在桶中排序的值,从 0 开始到 9。 -这个算法的复杂度为 **O(n\*k)**,k是最大数的位数。因为没有相互比较大小,所以这个算法的运行速度比其他的要快,但是这个方法只对数字列表奏效。 +这个算法的复杂度为 **O(n\*k)**,k 是最大数的位数。因为没有相互比较大小,所以这个算法的运行速度比其他的要快,但是这个方法只对数字列表奏效。 如果想要使用一个与数据无关的排序算法,可以使用我们之前讨论的方法。 @@ -698,11 +698,11 @@ console.log(radixSort(arr)) // [1,2,3,4,5,6,7,8,9,10] <h2 id='breadth-first-search-bfs-">广度优先(BFS)</h2 -让我们首先来分析BFS。正如我们介绍过的,BFS会首先横向遍历数组。在下面的图示中,数值将以这个顺序被遍历: `[10, 6, 15, 3, 8, 20]`. +让我们首先来分析 BFS。正如我们介绍过的,BFS 会首先横向遍历数组。在下面的图示中,数值将以这个顺序被遍历: `[10, 6, 15, 3, 8, 20]`. ![image-40](https://www.freecodecamp.org/news/content/images/2022/06/image-40.png) -一般来说,BFS分为以下几个步骤: +一般来说,BFS 分为以下几个步骤: - 创建一个队列和变量来存储”被访问过“的节点 - 将根节点放置在队列 @@ -780,9 +780,9 @@ console.log(tree.BFS()) // [ 10, 6, 15, 3, 8, 20 ] <h2 id="depth-first-search-dfs-">深度优先(DFS)</h2> -DFS会首先横向遍历数组。在上面的图示中,数值将以这个顺序被遍历:`[10, 6, 3, 8, 15, 20]`. +DFS 会首先横向遍历数组。在上面的图示中,数值将以这个顺序被遍历:`[10, 6, 3, 8, 15, 20]`. -这种DFS又称作“先序遍历”,DFS主要分为三种,这三种的区别在于节点被遍历的顺序。 +这种 DFS 又称作“先序遍历”,DFS 主要分为三种,这三种的区别在于节点被遍历的顺序。 - **先序:** 先访问当前节点,然后左边的节点,然后右边的节点 - **后序:** 先访问所有左手边的子节点,再访问所有右手边的子节点,最后访问当前节点 @@ -792,7 +792,7 @@ DFS会首先横向遍历数组。在上面的图示中,数值将以这个顺 <h3 id="pre-order-dfs">先序DFS</h3> -在先序DFS中,我们将进行以下步骤: +在先序 DFS 中,我们将进行以下步骤: - 创建一个存储所有被访问过的节点 - 在变量中存储树的根节点 @@ -868,7 +868,7 @@ console.log(tree.DFSPreOrder()) // [ 10, 6, 3, 8, 15, 20 ] <h3 id="post-order-dfs">后续DFS</h3> -后续DFS算法的执行步骤如下: +后续 DFS 算法的执行步骤如下: - 创建一个变量存储访问过的节点 - 在变量中存储树的根节点 @@ -944,7 +944,7 @@ console.log(tree.DFSPostOrder()) // [ 3, 8, 6, 20, 15, 10 ] <h3 id="in-order-dfs">中序DFS</h3> -中序DFS算法的执行步骤如下: +中序 DFS 算法的执行步骤如下: - 创建一个变量存储被访问过的节点 - 在变量中存储树结构的根节点 @@ -1020,7 +1020,7 @@ console.log(tree.DFSInOrder()) // [ 3, 6, 8, 10, 15, 20 ] 你可能已经注意到,先序、后序和中序的实现非常类似,仅改变了遍历节点的顺序。但其实这三种实现得到的结果大相径庭,有时其中一种方法可能比其他的要有用得多。 -何时使用BFS或者DFS取决于数据结构是如何组织的 +何时使用 BFS 或者 DFS 取决于数据结构是如何组织的 一般来说如果你有一个结构非常宽的树或者图(意味着在同一水平线上有很多后代节点),就应该采用深度优先。如果是处理分支很长的大型树或者图,就应该使用广度优先。 diff --git a/chinese/articles/introduction-to-evolutionary-game-theory.md b/chinese/articles/introduction-to-evolutionary-game-theory.md index 3c51552a5..ffea590d6 100644 --- a/chinese/articles/introduction-to-evolutionary-game-theory.md +++ b/chinese/articles/introduction-to-evolutionary-game-theory.md @@ -29,7 +29,7 @@ 这种设定被叫做“复制器动力学”。不难通过建模来模拟和探索各种不同的演化博弈论模型。 -演化博弈论的经典模型是约翰·梅纳德·史密斯在20世纪70年代推广的“鹰-鸽”博弈。 +演化博弈论的经典模型是约翰·梅纳德·史密斯在 20 世纪 70 年代推广的“鹰-鸽”博弈。 在这个博弈中,有一群动物争夺有限的资源(例如食物)。一个个体赢得的资源越多,他的适应度就越高。 @@ -62,7 +62,7 @@ 将鹰派在种群中的频率表示为 _p_,鸽派的频率则为 _1-p_。 -现在,定义两个函数F(H)和F(D),这两个函数分别定义了鹰派策略和鸽派策略的期望适合度。 +现在,定义两个函数 F(H)和 F(D),这两个函数分别定义了鹰派策略和鸽派策略的期望适合度。 鹰派策略意味着参加一场频率为 _p_ 的鹰派对鹰派竞赛。这样做的期望效用被理解为平均结果。一半的时间某个鹰派赢得 _V_,一半的时间它损失 _C_。 @@ -99,13 +99,13 @@ - 如果成本 _C_ 大于价值 _V_,则策略将会在平衡里共存。 -代入 _V_\=4和 _C_\=6,我们发现当2/3的种群使用鹰策略时,出现了平衡。 +代入 _V_\=4 和 _C_\=6,我们发现当 2/3 的种群使用鹰策略时,出现了平衡。 -您可以通过在Python里模拟模型来测试这一点。 +您可以通过在 Python 里模拟模型来测试这一点。 ### 代码 -在叫做bird.py的文件里: +在叫做 bird.py 的文件里: ```Python import random @@ -165,7 +165,7 @@ class Bird: return Bird(self.strategy) ``` -下一个文件叫做simulation.py. +下一个文件叫做 simulation.py. 1. 初始化鸽派的种群 2. 定义一个时间步长函数来模拟随机竞赛。 @@ -250,7 +250,7 @@ if __name__ == "__main__": main() ``` -看,这是个 _V_\=4,_C_\=6的例子: +看,这是个 _V_=4 , _C_=6 的例子: ![](https://cdn.substack.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F44100a89-d377-435e-bf11-97b046db5f32_1438x1048.png) @@ -268,6 +268,6 @@ if __name__ == "__main__": 最后,编程工具和软件让我们可以通过模拟来测试理论预测。 -如果你觉得这篇文章很有趣,你可能也会觉得[如何用R建模流行病](news/How-to-Model-an-Epidemic-With-R/)值得一看。 +如果你觉得这篇文章很有趣,你可能也会觉得[如何用 R 建模流行病](news/How-to-Model-an-Epidemic-With-R/)值得一看。 你可以在[gleeson.substack.com](https://gleeson.substack.com/)里关注我的更多文章 \ No newline at end of file diff --git a/chinese/articles/introduction-to-linked-lists-in-python.md b/chinese/articles/introduction-to-linked-lists-in-python.md index e45acf71a..8be902b1c 100644 --- a/chinese/articles/introduction-to-linked-lists-in-python.md +++ b/chinese/articles/introduction-to-linked-lists-in-python.md @@ -43,17 +43,17 @@ 这些就是你在尝试实现一个链表前需要考虑的事情, -现在所有的理论都结束了,是时候去真正地实现一个链表了。我们将使用Python语言来实现,但在这里学到的大部分都可以应用到任何你使用的语言上。最重要的是理解其中的思想。 +现在所有的理论都结束了,是时候去真正地实现一个链表了。我们将使用 Python 语言来实现,但在这里学到的大部分都可以应用到任何你使用的语言上。最重要的是理解其中的思想。 -## 如何在Python中使用链表 +## 如何在 Python 中使用链表 这里有一个创建链表的技巧——它帮助我更好地理解链表。 你只需要意识到你将要添加到链表中的每一个元素都只是一个结点(就像链条中的一环)。**头结点**(链表中的第一个结点)的特殊之处在于你先确定一个**头结点**,然后再开始向它后面添加其他结点。 请记住一个链表就像一个链条是怎么连接在一起的。 -这里是Joe和一些环,他会帮助我们去学习链表。 +这里是 Joe 和一些环,他会帮助我们去学习链表。 ![Joe-and-the-chain](https://www.freecodecamp.org/news/content/images/2022/09/Joe-and-the-chain.png) @@ -91,7 +91,7 @@ class LinkedList: `append()`函数能让你添加结点到链表上。让我们来探索它是怎么工作的。 ![append](https://www.freecodecamp.org/news/content/images/2022/09/append.png) -如果我有两个值 - 例如1和2 - 然后我想要将它们添加到链表中,第一件事就是将它们定义为单独的结点(就像链条中的环一样)。我们可以这样做: +如果我有两个值 - 例如 1 和 2 - 然后我想要将它们添加到链表中,第一件事就是将它们定义为单独的结点(就像链条中的环一样)。我们可以这样做: ```python e1 = Node(1) e2 = Node(2) @@ -139,13 +139,13 @@ class LinkedList: 所以我们在这里做的就是简单地遍历每个结点去看它的值是否是我们需要删除的。但是当我们遍历链表的时候,我们也需要一直跟踪前一个值(我们还要将链表重新链接起来)。我们通过 **`prev = current`** 来实现这个,你可以在下面看到 :)。 ![delete-1](https://www.freecodecamp.org/news/content/images/2022/09/delete-1.png) -所以当要删除的结点被找到后,**`prev`** 变量包含了它前面的结点,所以它的next值可以很简单地切换为指向另一个结点 - 在这种情况下也就是与我们想要删除的结点相连的其他结点。我希望这能解释得通 :) 。 +所以当要删除的结点被找到后,**`prev`** 变量包含了它前面的结点,所以它的 next 值可以很简单地切换为指向另一个结点 - 在这种情况下也就是与我们想要删除的结点相连的其他结点。我希望这能解释得通 :) 。 让我们来研究将一个 **结点插入** 到一个特定的地方。我们将使用我们的链条作为类比来更好地理解。 当你有一个链条,然后你想要增加链条的长度,你有三个选择。你可以: 1. 在链条的头部添加一个环 (元素)。(这很简单,不是吗?) -2. 把它添加到链条的末尾。(这和1很像) +2. 把它添加到链条的末尾。(这和 1 很像) 3. 或者你可以把它添加到链条中间的任何一个位置(有一点棘手) @@ -184,9 +184,9 @@ class LinkedList: 在上面的代码中,我们传入了一个插入结点的位置。如果这个位置是第一个,这就代表着这个结点要作为头结点。由于我们不是很确定,我们可以初始化一个循环和一个计数器来跟踪这个循环。 -如果我们传入的位置是1(也就是头结点),简单地将当前的头结点存储为变量dummy,然后创建一个新头结点,接着将之前的头结点(也就是整个链条)加入到这个新头结点后。 +如果我们传入的位置是 1(也就是头结点),简单地将当前的头结点存储为变量 dummy,然后创建一个新头结点,接着将之前的头结点(也就是整个链条)加入到这个新头结点后。 -如果这个位置不是1,那么就遍历这个链条直到你找到了那个位置。 +如果这个位置不是 1,那么就遍历这个链条直到你找到了那个位置。 在这篇文章的最后,让我们以任何你想要的形式来展示链表的值 - 例如,打印出来或者把它添加到一个列表中。我将只会把它们打印出来。 diff --git a/chinese/articles/javascript-async-await-tutorial-learn-callbacks-promises-async-await-by-making-icecream.md b/chinese/articles/javascript-async-await-tutorial-learn-callbacks-promises-async-await-by-making-icecream.md index 685c6045f..765d4671f 100644 --- a/chinese/articles/javascript-async-await-tutorial-learn-callbacks-promises-async-await-by-making-icecream.md +++ b/chinese/articles/javascript-async-await-tutorial-learn-callbacks-promises-async-await-by-making-icecream.md @@ -13,26 +13,26 @@ ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b1j935dg72g9u8zvh2oi.png) -# 以下是我们将在本文中介绍的内容: +# 以下是我们将在本文中介绍的内容 -- 什么是异步JavaScript? -- JavaScript中的同步与异步 -- Callbacks如何在JavaScript中运作 -- Promises如何在JavaScript中运作 -- Async / Await 如何在JavaScript中运作 +- 什么是异步 JavaScript? +- JavaScript 中的同步与异步 +- Callbacks 如何在 JavaScript 中运作 +- Promises 如何在 JavaScript 中运作 +- Async / Await 如何在 JavaScript 中运作 让我们开始吧! -## 如果你喜欢,也可以在YouTube上观看本教程 +## 如果你喜欢,也可以在 YouTube 上观看本教程 # What is Asynchronous JavaScript? -# 什么是异步JavaScript? +# 什么是异步 JavaScript? ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7yd96tgxvuowqmfgcx6b.png) 如果您想高效地构建项目,那么这个概念很适合您。 -异步JavaScript理论可以帮助您将大型复杂的项目分解为较小的任务。 +异步 JavaScript 理论可以帮助您将大型复杂的项目分解为较小的任务。 然后你可以使用这三种技巧 – **callbacks, promises or Async/await** – 中的任何一种来运行这些小任务来获得最好的结果。 @@ -46,7 +46,7 @@ 在同步系统中,任务一个接一个地完成。 -想象一下,如果你只有一只手去完成10项任务, 那么在同一个时间你只能做一个任务。 +想象一下,如果你只有一只手去完成 10 项任务, 那么在同一个时间你只能做一个任务。 看看这个动图👇 – 这里会发生一件事: @@ -54,7 +54,7 @@ 您将看到,直到第一个图像完全加载,第二个图像才开始加载。 -JavaScript默认是同步的 **\[单线程\]**。你可以这样想 ——— 单线意味着一次只能做一件事。 +JavaScript 默认是同步的 **\[单线程\]**。你可以这样想 ——— 单线意味着一次只能做一件事。 ## 什么是异步系统? @@ -68,11 +68,11 @@ JavaScript默认是同步的 **\[单线程\]**。你可以这样想 ——— 同样,所有的图像都以自己的速度加载。它们都不会等待其他任务的完成。 -## 总结一下同步JS和异步JS: +## 总结一下同步 JS 和异步 JS 想象三张图片在跑马拉松,在一个: -- **同步** 系统,三张图片在同一条跑道上。一个不能超过另外一个。比赛得一个接一个地完成。如果2号图像停止,后续的图片也会停止。 +- **同步** 系统,三张图片在同一条跑道上。一个不能超过另外一个。比赛得一个接一个地完成。如果 2 号图像停止,后续的图片也会停止。 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w1r9y4ghhq0t8wjb1u9h.png) @@ -89,7 +89,7 @@ JavaScript默认是同步的 **\[单线程\]**。你可以这样想 ——— ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5m6p1qy522lj3auvl5ty.png) -为了测试同步系统,用JavaScript写以下代码: +为了测试同步系统,用 JavaScript 写以下代码: ```javascript console.log(" I "); @@ -107,7 +107,7 @@ console.log(" Ice Cream "); ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y5d0o8unbe8c67qeqz0w.png) -我们假设吃冰淇淋需要两秒钟。现在,让我们测试一个异步系统。用JavaScript编写下面的代码。 +我们假设吃冰淇淋需要两秒钟。现在,让我们测试一个异步系统。用 JavaScript 编写下面的代码。 **注意:** 不用担心,我们将在本文后面讨论 `setTimeout()` 函数。 @@ -133,11 +133,11 @@ console.log("Ice Cream") ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2mzbtcnm67v2iys7cix7.png) -对于这个项目,你只需要打开[Codepen.io](https://codepen.io/)直接开始编码。或者,你可以用VS code编辑器来做。 +对于这个项目,你只需要打开[Codepen.io](https://codepen.io/)直接开始编码。或者,你可以用 VS code 编辑器来做。 -打开JavaScript部分,然后打开开发人员控制台。我们将编写代码并在控制台中查看结果。 +打开 JavaScript 部分,然后打开开发人员控制台。我们将编写代码并在控制台中查看结果。 -# 什么是JavaScript中的回调函数? +# 什么是 JavaScript 中的回调函数? ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s5iloofqsv3lcdl4flsi.png) @@ -169,7 +169,7 @@ console.log("Ice Cream") 为了更详细地解释这一点,让我们开始做冰淇淋店的生意。 -## 等等... +## 等等 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cq8exwor5aiciu2j6jwu.png) @@ -198,7 +198,7 @@ let stocks = { ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6dcwr770l0ubupv0r2gj.png) -您可以像这样将这些其他成分存储在JavaScript对象中:👇 +您可以像这样将这些其他成分存储在 JavaScript 对象中:👇 ```javascript let stocks = { @@ -279,7 +279,7 @@ order(production); ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tnr74waq6noc0djln3qx.png) -## 清除console.log日志 +## 清除 console.log 日志 保留这段代码并删除所有的东西 \[不要删除我们的 stocks 变量\]。在我们的第一个函数中,传递另一个参数,以便我们可以接收订单\[水果名\]: @@ -307,7 +307,7 @@ order("", production); **图表包含制作冰淇淋的步骤** -在这个图表中,您可以看到第一步是下订单,这需要2秒。第二步是切水果(2秒),第三步是加水和冰(1秒),步骤4启动机器(1秒),第5步是选择容器(2秒),第六步是选择配料(3秒),以及第七步,也就是最后一步,端上冰淇淋,这需要2秒。 +在这个图表中,您可以看到第一步是下订单,这需要 2 秒。第二步是切水果(2 秒),第三步是加水和冰(1 秒),步骤 4 启动机器(1 秒),第 5 步是选择容器(2 秒),第六步是选择配料(3 秒),以及第七步,也就是最后一步,端上冰淇淋,这需要 2 秒。 要建立计时,函数 `setTimeout()` 非常好,因为它也使用一个回调函数作为参数。 @@ -342,11 +342,11 @@ order(0, production); 下面是控制台中的结果:👇 -**注意** 2秒后才会显示结果。 +**注意** 2 秒后才会显示结果。 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/edwji5vauypoezj3bxdk.png) -如果您想知道我们是如何从stock变量中采摘草莓的,下面是代码 👇 +如果您想知道我们是如何从 stock 变量中采摘草莓的,下面是代码 👇 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ia38z3x6b96xpq3aid91.png) @@ -440,11 +440,11 @@ let production = () =>{ 解决方案是什么? -# 如何使用Promise来避免回调地狱 +# 如何使用 Promise 来避免回调地狱 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x3neys1hxsrifgg5qm6x.png) -Promises的发明是为了解决回调地狱的问题和更好地处理我们的任务的。 +Promises 的发明是为了解决回调地狱的问题和更好地处理我们的任务的。 ## 休息一下 @@ -456,9 +456,9 @@ Promises的发明是为了解决回调地狱的问题和更好地处理我们的 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7qo1zheuin2825osozvc.png) -**promised的格式说明** +**promised 的格式说明** -让我们一起来剖析promises。 +让我们一起来剖析 promises。 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gozy5r1nfubzeq5t5t25.png) @@ -466,7 +466,7 @@ Promises的发明是为了解决回调地狱的问题和更好地处理我们的 **promise 周期的图解** -如上图所示,一个promise有三种状态 +如上图所示,一个 promise 有三种状态 - **Pending:** 这是初始阶段。这里什么也没有发生。 你可以这样想,你的客户正在慢慢地给你下订单。但是他们还没有点任何东西。 - **Resolved:** 这意味着你的顾客已经收到了他们的食物并且很高兴。 @@ -495,7 +495,7 @@ Promises的发明是为了解决回调地狱的问题和更好地处理我们的 **图表包含制作冰淇淋的步骤** -为了实现这一点,让我们在JavaScript中创建一个变量: 👇 +为了实现这一点,让我们在 JavaScript 中创建一个变量: 👇 ```javascript let is_shop_open = true; @@ -519,7 +519,7 @@ let order = ( time, work ) =>{ } ``` -我们的promise有2部分: +我们的 promise 有 2 部分: - Resolved \[ 用户拿到了冰激凌 \] - Rejected \[ 用户没有拿到冰激凌 \] @@ -547,7 +547,7 @@ let order = ( time, work ) => { ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3wik2xel68yue93yapm6.png) -让我们在 `if` 语句中使用 `setTimeout()` 函数在promise中添加时间和工作因素。跟我来👇 +让我们在 `if` 语句中使用 `setTimeout()` 函数在 promise 中添加时间和工作因素。跟我来👇 **注意:** 在现实生活中,你也可以避免时间因素。 这完全取决于你的工作性质。 @@ -584,7 +584,7 @@ order( 2000, ()=>console.log(`${stocks.Fruits[0]} was selected`)) // pass a ☝️ function here to start working ``` -2秒后的结果是👇: +2 秒后的结果是👇: ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/erzjup8wt505j502e73n.png) @@ -598,19 +598,19 @@ order( 2000, ()=>console.log(`${stocks.Fruits[0]} was selected`)) ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l27ytifkoedl22kc97lh.png) -**使用 .then 处理函数的promise 链说明** +**使用 .then 处理函数的 promise 链说明** -当我们的promise被resolve时, .then 处理函数返回一个promise。 +当我们的 promise 被 resolve 时, .then 处理函数返回一个 promise。 -#### 例子如下: +#### 例子如下 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1qpeewo19qbhzj47goos.png) -让我说得简单点: 这类似于给某人指示。你告诉别人“先做这个,然后做那个,然后做其他的事情,然后…”,然后……,然后……”等。 +让我说得简单点: 这类似于给某人指示。你告诉别人“先做这个,然后做那个,然后做其他的事情,然后……”,然后……,然后……”等。 -- 他的首要任务是我们原始promise。 -- 一旦完成了一小部分工作,剩下的任务就返回了新的promise +- 他的首要任务是我们原始 promise。 +- 一旦完成了一小部分工作,剩下的任务就返回了新的 promise 让我们在我们的项目中实现这一点。 在代码的底部编写以下代码行。👇 @@ -676,15 +676,15 @@ order(2000,()=>console.log(`${stocks.Fruits[0]} was selected`)) ## 错误处理 -当出现错误时,我们需要一种处理错误的方法。但首先,我们需要了解promise周期: +当出现错误时,我们需要一种处理错误的方法。但首先,我们需要了解 promise 周期: ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jlm7zwonbxszeaccyohv.png) ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z2ajcu52rxzwq64g81vp.png) -**promise周期说明** +**promise 周期说明** -为了捕获错误,让我们将变量改为false。 +为了捕获错误,让我们将变量改为 false。 ```javascript let is_shop_open = false; @@ -692,7 +692,7 @@ let is_shop_open = false; 也就是说我们的店关门了。我们不再卖冰淇淋给顾客了。 -为了处理这种情况我们使用 `.catch` 函数。类似 `.then`,它也返回一个promise,但只有当我们最初的promise被rejected时才会执行。 +为了处理这种情况我们使用 `.catch` 函数。类似 `.then`,它也返回一个 promise,但只有当我们最初的 promise 被 rejected 时才会执行。 这里有一个小提示: @@ -722,9 +722,9 @@ let is_shop_open = false; ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gdq3i0agj4volq46ycue.png) -有一个叫做"finally"的函数,不管我们的promise是被resolve了还是被rejecte了它都会被执行。 +有一个叫做"finally"的函数,不管我们的 promise 是被 resolve 了还是被 rejecte 了它都会被执行。 -**例如:** 不管我们是没有顾客还是有100个顾客,我们的店都会在一天结束的时候关门 +**例如:** 不管我们是没有顾客还是有 100 个顾客,我们的店都会在一天结束的时候关门 如果您想对此进行测试,请在最下面编写以下代码: 👇 @@ -740,13 +740,13 @@ let is_shop_open = false; 请大家欢迎 Async / Await~ -# Async / Await 如何在JavaScript中工作 +# Async / Await 如何在 JavaScript 中工作 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ra7483f90b69pjl2cbae.png) -这应该是编写promise的更好方式,它可以帮助我们保持代码的简单和干净。 +这应该是编写 promise 的更好方式,它可以帮助我们保持代码的简单和干净。 -你所要做的就是在任何常规函数之前写 `async` 关键字,它就变成了一个promise。 +你所要做的就是在任何常规函数之前写 `async` 关键字,它就变成了一个 promise。 ## 先休息一下 @@ -758,7 +758,7 @@ let is_shop_open = false; ## JavaScript 中 Promises vs Async/Await -在async/await之前,为了写一个promise,我们这样写: +在 async/await 之前,为了写一个 promise,我们这样写: ```javascript function order(){ @@ -769,7 +769,7 @@ function order(){ } ``` -现在使用async/await,我们可以这么写: +现在使用 async/await,我们可以这么写: ```javascript //👇 神奇的关键字 @@ -778,24 +778,24 @@ function order(){ } ``` -## 等等... +## 等等 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t1pjzw6zl0h21tyyh9u3.png) 你必须理解-> - 如何使用 `try` 和 `catch` 关键字 -- 如何使用await 关键字 +- 如何使用 await 关键字 ## 如何使用 Try 和 Catch 关键字 -我们使用 `try` 关键字来运行代码,同时使用 `catch` 来捕获错误。这和我们看promise时看到的概念是一样的。 +我们使用 `try` 关键字来运行代码,同时使用 `catch` 来捕获错误。这和我们看 promise 时看到的概念是一样的。 -让我们来比较一下。我们来看一个小demo,然后开始编码。 +让我们来比较一下。我们来看一个小 demo,然后开始编码。 -### JS中的Promise -> resolve 和 reject +### JS 中的 Promise -> resolve 和 reject -我们在resolve中这样使用resolve和reject: +我们在 resolve 中这样使用 resolve 和 reject: ```javascript function kitchen(){ @@ -818,9 +818,9 @@ kitchen() // run the code .finally() // end of the promise [optional] ``` -### JS中的 Async / Await -> try,catch +### JS 中的 Async / Await -> try,catch -当我们使用async/await时,我们可以这么写: +当我们使用 async/await 时,我们可以这么写: ```javascript //👇 神奇的关键字 @@ -845,15 +845,15 @@ kitchen() // 调用 不要慌,我们接下来将讨论 `await` 关键字。 -现在希望你理解了promise和Async / Await之间的区别了。 +现在希望你理解了 promise 和 Async / Await 之间的区别了。 -## 如何使用JavaScript的Await关键字 +## 如何使用 JavaScript 的 Await 关键字 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fry577xha7313ead96xy.png) -关键字 `await` 使JavaScript等待,直到一个promise reslove 时才会返回它的结果。 +关键字 `await` 使 JavaScript 等待,直到一个 promise reslove 时才会返回它的结果。 -### 如何在JavaScrip中使用await关键字 +### 如何在 JavaScrip 中使用 await 关键字 我们回冰淇淋店去吧。我们不知道顾客更喜欢哪种配料,巧克力还是花生。所以我们需要停止我们的机器,然后去问我们的顾客他们想在冰淇淋上加什么。 @@ -863,11 +863,11 @@ kitchen() // 调用 - 清洁桌子 - 点单,等等。 -## 一个Await关键字代码示例 +## 一个 Await 关键字代码示例 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8r5w5aapofalnq882wat.png) -让我们创建一个小promise来询问要使用那种配料。这个过程需要3秒。 +让我们创建一个小 promise 来询问要使用那种配料。这个过程需要 3 秒。 ```javascript function toppings_choice (){ @@ -881,7 +881,7 @@ function toppings_choice (){ } ``` -现在,让我们首先使用async关键字来创建kitchen函数。 +现在,让我们首先使用 async 关键字来创建 kitchen 函数。 ```javascript async function kitchen(){ @@ -920,7 +920,7 @@ console.log("taking orders") ### 注意 -当使用Async/ Await时,你也可以使用promise的核心部分 `.then`, `.catch`, 和 `.finally`函数。 +当使用 Async/ Await 时,你也可以使用 promise 的核心部分 `.then`, `.catch`, 和 `.finally`函数。 ### 我们再开一家冰淇淋店吧 @@ -1065,7 +1065,7 @@ async function kitchen(){ 恭喜你读完了本文!在本文中,您可以了解到: - 同步和异步系统之间的区别 -- 异步JavaScript使用3种机制(callbacks, promises, and Async/ Await) +- 异步 JavaScript 使用 3 种机制(callbacks, promises, and Async/ Await) 这是你阅读到最后的奖励。❤️ diff --git a/chinese/articles/javascript-closures-explained-with-example.md b/chinese/articles/javascript-closures-explained-with-example.md index 90c13d4f4..8aabd2698 100644 --- a/chinese/articles/javascript-closures-explained-with-example.md +++ b/chinese/articles/javascript-closures-explained-with-example.md @@ -5,7 +5,7 @@ ![Closure in JavaScript – Explained with Examples](https://www.freecodecamp.org/news/content/images/size/w2000/2022/04/safar-safarov-LKsHwgzyk7c-unsplash-2.jpg) -这篇文章将讲解JavaScript中的闭包。我会说明闭包的定义,展现一个日常抓取使用闭包的例子以及闭包的优缺点。 +这篇文章将讲解 JavaScript 中的闭包。我会说明闭包的定义,展现一个日常抓取使用闭包的例子以及闭包的优缺点。 ## 目录 @@ -22,7 +22,7 @@ 在学习这篇文章之前,你必须理解以下内容: -- JavaScript的[执行上下文](https://www.freecodecamp.org/news/javascript-execution-context-and-hoisting/ +- JavaScript 的[执行上下文](https://www.freecodecamp.org/news/javascript-execution-context-and-hoisting/ - [Fetch API](https://www.freecodecamp.org/news/javascript-fetch-api-tutorial-with-js-fetch-post-and-header-examples/)的定义和使用 <h2 id='what-are-closures'> 什么是闭包?</h2> @@ -53,7 +53,7 @@ let buttonProps = (borderRadius) => { 那么问题来了,内部函数如何解析位于父作用域的变量呢? -这得益于词法作用域。JS编译器可以根据词法作用域来解析当前作用域的变量,即解析嵌套函数中的变量。 +这得益于词法作用域。JS 编译器可以根据词法作用域来解析当前作用域的变量,即解析嵌套函数中的变量。 所以根据上述例子,我们可以得出 `createVariantButtonProps` 可以获取外部函数`buttonProps`中的变量。 @@ -64,7 +64,7 @@ let buttonProps = (borderRadius) => { 让我们来深入了解这两个特性。 -### 即便外部函数不再存在,闭包也可以获取父函数中的变量。 +### 即便外部函数不再存在,闭包也可以获取父函数中的变量 这是闭包的基础功能,是闭包的生命信仰,也可以说是闭包运作的指导原则。 @@ -102,16 +102,16 @@ const primaryButtonProps = primaryButton("primary", "red"); <h2 id='use-case-of-closure-creating-a-fetch-utility-with-closures'>如何使用闭包</h2> -我们已经学习了什么是闭包,现在让我们创建一个可以被广泛使用的实用函数。这个函数将使用REST API来处理如GET和POST之类的方法。 +我们已经学习了什么是闭包,现在让我们创建一个可以被广泛使用的实用函数。这个函数将使用 REST API 来处理如 GET 和 POST 之类的方法。 在这个例子中: -- 我们将利用[JSON placeholder APIs](https://jsonplaceholder.typicode.com/)的数据。我们可以使用REST API来处理一些虚构的数据。 -- 我们将使用JavaScript的[fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) API。 +- 我们将利用[JSON placeholder APIs](https://jsonplaceholder.typicode.com/)的数据。我们可以使用 REST API 来处理一些虚构的数据。 +- 我们将使用 JavaScript 的[fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) API。 我们先来讨论一下为什么要设计这样的实用函数,有以下几个原因: -- 我们并不想在每一次fetch调用中都重新定义基础URL (或其他基础参数)。 所以我们可以创建一个机制将基础URL或者参数作为状态存储。 +- 我们并不想在每一次 fetch 调用中都重新定义基础 URL (或其他基础参数)。 所以我们可以创建一个机制将基础 URL 或者参数作为状态存储。 - 删除多余的代码。 - 模块化代码库。 @@ -132,10 +132,10 @@ const fetchUtility = (baseURL, headers) => { }; ``` -- `fetchUtility`接受两个参数 `baseURL`和`headers`。这两个参数会在后面闭包用到,来创建基础URL和头部。 +- `fetchUtility`接受两个参数 `baseURL`和`headers`。这两个参数会在后面闭包用到,来创建基础 URL 和头部。 - 还有`createFetchInstance`函数,接受`route` `requestMethod` 和 `data`参数。 -- 接着在函数内创建一个请求实例,通过代码 `${baseURL}${route}`来创建URL。同时我们也传入一个对象,包含请求方式、请求头和数据(如果获取得到数据)。 -- 然后返回一个fetch API的实例和一个请求对象。 +- 接着在函数内创建一个请求实例,通过代码 `${baseURL}${route}`来创建 URL。同时我们也传入一个对象,包含请求方式、请求头和数据(如果获取得到数据)。 +- 然后返回一个 fetch API 的实例和一个请求对象。 - 最后返回 `createFetchInstance`函数。 接下来让我们实际操作看看,调用 `fetchUtility` 函数初始化 `baseURL`: @@ -144,7 +144,7 @@ const fetchUtility = (baseURL, headers) => { const fetchInstance = fetchUtility("https://jsonplaceholder.typicode.com"); ``` -初始化基础URL  +初始化基础 URL  - 仔细观察会发现`fetchInstance`获取了`fetchUtility`函数闭包的变量。 - 然后,我们向闭包`fetchInstance`传入路由和请求的方式: @@ -155,9 +155,9 @@ const [getFunc, getReq] = fetchInstance("/todos/1", "GET"); 执行闭包 -如你所见,一个包含fetch API实例和我们配置的请求体的数组被返回。 +如你所见,一个包含 fetch API 实例和我们配置的请求体的数组被返回。 -最后,我们使用`getFunc`这个fetch API 调用 `getReq`请求,如下: +最后,我们使用`getFunc`这个 fetch API 调用 `getReq`请求,如下: ```Javascript getFunc(getReq) @@ -165,7 +165,7 @@ getFunc(getReq) .then((data) => console.log(data)); ``` -我们可以一样的方法来创建一个POST请求。这样重新调用 `fetchInstance`: +我们可以一样的方法来创建一个 POST 请求。这样重新调用 `fetchInstance`: ```Javascript const [postFunc, postReq] = fetchInstance( @@ -179,7 +179,7 @@ const [postFunc, postReq] = fetchInstance( ); ``` -在执行POST请求的时候也可以用GET请求一样的方法: +在执行 POST 请求的时候也可以用 GET 请求一样的方法: ```Javascript postFunc(postReq) diff --git a/chinese/articles/javascript-design-patterns-explained.md b/chinese/articles/javascript-design-patterns-explained.md index 7e2c68c70..b8b6224d3 100644 --- a/chinese/articles/javascript-design-patterns-explained.md +++ b/chinese/articles/javascript-design-patterns-explained.md @@ -31,15 +31,15 @@ <h1 id="what-are-design-patterns">什么是设计模式?</h1> -设计模式这个概念是由[一本名为《设计模式:可复用面向对象软件的基础》](https://en.wikipedia.org/wiki/Design_Patterns)推广而来, 这本书在1994年由四个C++工程师编写。 +设计模式这个概念是由[一本名为《设计模式:可复用面向对象软件的基础》](https://en.wikipedia.org/wiki/Design_Patterns)推广而来, 这本书在 1994 年由四个 C++工程师编写。 -这本书探讨了面向对象的编程的能力和陷阱,并介绍了23种可以用来解决编程问题的模式。 +这本书探讨了面向对象的编程的能力和陷阱,并介绍了 23 种可以用来解决编程问题的模式。 这些模式**并不是算法或者具体的实现**。它们更像是**想法、观点和抽象**,辅助你去解决一些特定问题。 根据要素的不同模式的实现也各不相同,重要的是模式背后的概念,它可以帮助我们更好地解决问题。 -话虽如此,但是请记住,这些模式建立在C++的OOP的基础之上,当使用更现代的编程语言如JavaScript时,模式可能不等效,甚至给代码添加了不必要的样本。 +话虽如此,但是请记住,这些模式建立在 C++的 OOP 的基础之上,当使用更现代的编程语言如 JavaScript 时,模式可能不等效,甚至给代码添加了不必要的样本。 不过把这些模式当作一般的编程知识来了解没有坏处。 @@ -233,7 +233,7 @@ bug2.saySmthg() // 输出: "Martiniano Buggland walks the walk and talks the tal **原型**允许把一个对象作为蓝图创建另一个对象,新对象继承原对象的属性和方法。 -如果你已经使用过一段时间的JavaScript,你应该对[原型继承](https://www.freecodecamp.org/news/prototypes-and-inheritance-in-javascript/)有一定了解。 +如果你已经使用过一段时间的 JavaScript,你应该对[原型继承](https://www.freecodecamp.org/news/prototypes-and-inheritance-in-javascript/)有一定了解。 原型链继承的结果和使用类相似,只是更为灵活,因为属性和方法可以不通过同一个类在对象之间共享。 @@ -269,7 +269,7 @@ console.log(bug1.flyAway()) // Flyyyy like an eagle! **适配器**允许两个接口不兼容的对象相互交互。 -假设你的应用程序调用一个API并会返回一个[XML](https://www.freecodecamp.org/news/what-is-an-xml-file-how-to-open-xml-files-and-the-best-xml-viewers/),然后将结果发送给另一个API来处理信息,但是处理信息的API期待的是[JSON](https://www.freecodecamp.org/news/what-is-json-a-json-file-example/)格式。因为格式不兼容,所以你不能直接发送信息,需要先 _适配_ 结果。 😉 +假设你的应用程序调用一个 API 并会返回一个[XML](https://www.freecodecamp.org/news/what-is-an-xml-file-how-to-open-xml-files-and-the-best-xml-viewers/),然后将结果发送给另一个 API 来处理信息,但是处理信息的 API 期待的是[JSON](https://www.freecodecamp.org/news/what-is-json-a-json-file-example/)格式。因为格式不兼容,所以你不能直接发送信息,需要先 _适配_ 结果。 😉 我们可以举一个更简单的例子来具象化这个概念。假设我们有一个以城市为元素的数组,以及一个可以返回拥有最多人口城市的函数。数组中的城市人口以百万为单位计数,但是有一个新城市的人口单位不是百万: @@ -306,11 +306,11 @@ console.log(MostHabitantsInMillions()) // 8.9 <h2 id="decorator-pattern">装饰</h2> -**装饰**通过增加一个修饰对象来包裹原来的对象,从而给原来的对象添加新的行为。 如果你熟悉React或者高阶组件(HOC),你内心的小铃铛可能会叮当一下。 +**装饰**通过增加一个修饰对象来包裹原来的对象,从而给原来的对象添加新的行为。 如果你熟悉 React 或者高阶组件(HOC),你内心的小铃铛可能会叮当一下。 -从技术上讲,React中的组件是函数而不是对象。但如果你仔细思索React上下文(React Context)或者[Memo](https://www.freecodecamp.org/news/memoization-in-javascript-and-react/)是怎么运作的,你会发现我们将组件作为子组件传入HOC后,子组件而可以访问某些功能。 +从技术上讲,React 中的组件是函数而不是对象。但如果你仔细思索 React 上下文(React Context)或者[Memo](https://www.freecodecamp.org/news/memoization-in-javascript-and-react/)是怎么运作的,你会发现我们将组件作为子组件传入 HOC 后,子组件而可以访问某些功能。 -在下面的例子里中ContextProvider组件接受子组件作为prop: +在下面的例子里中 ContextProvider 组件接受子组件作为 prop: ```javascript @@ -377,7 +377,7 @@ export default function App() { } ``` -接着,我们使用`useContext`钩子,使得应用内所有组件都可以获得定义在Context的状态(state): +接着,我们使用`useContext`钩子,使得应用内所有组件都可以获得定义在 Context 的状态(state): ```javascript @@ -397,13 +397,13 @@ export default AboutPage **外观**模式给库、框架以及其他复杂的类集提供简化的接口。 -嗯……我们可以举的例子非常多,不是吗?React本身以及各种各样的软件开发相关的库就是基于这个模式。特别是当你思考[声明式编程](https://www.freecodecamp.org/news/an-introduction-to-programming-paradigms/#declarative-programming),会发现这个范式就是使用抽象的方法对开发者隐藏复杂性。 +嗯……我们可以举的例子非常多,不是吗?React 本身以及各种各样的软件开发相关的库就是基于这个模式。特别是当你思考[声明式编程](https://www.freecodecamp.org/news/an-introduction-to-programming-paradigms/#declarative-programming),会发现这个范式就是使用抽象的方法对开发者隐藏复杂性。 -JavaScript中的 `map`、 `sort`、 `reduce` 和 `filter`函数都是很好的例子,这些函数的背后其实是我们的老朋友`for`循环。 +JavaScript 中的 `map`、 `sort`、 `reduce` 和 `filter`函数都是很好的例子,这些函数的背后其实是我们的老朋友`for`循环。 -另一个例子是一些UI库,如:[MUI](https://mui.com/)。正如以下示例所展现的这样,库提供了组件,组件带来了内置特性和功能,帮助我们更快、更轻松地构建代码。 +另一个例子是一些 UI 库,如:[MUI](https://mui.com/)。正如以下示例所展现的这样,库提供了组件,组件带来了内置特性和功能,帮助我们更快、更轻松地构建代码。 -这些代码最后都会编译成简单的HTML元素,这是浏览器唯一能理解的东西。组件只是采用了抽象的办法,使得我们的编码过程更容易。 +这些代码最后都会编译成简单的 HTML 元素,这是浏览器唯一能理解的东西。组件只是采用了抽象的办法,使得我们的编码过程更容易。 ![thewolfofwallstreet-fairydust](https://www.freecodecamp.org/news/content/images/2022/06/thewolfofwallstreet-fairydust.gif) @@ -476,7 +476,7 @@ export default function BasicTable() { **代理**模式为另一个对象提供替代或者占位符。这个想法是控制对原始对象的访问,当请求到达实际的原始对象之前或者之后再执行某种操作。 -如果你熟悉[ExpressJS](https://expressjs.com/)的话,这个概念就不陌生。Express是用于开发NodeJS API的框架,其中一个功能就是中间件的使用。中间件是我们可以在请求到达终点之前、之中和之后执行的一段代码。 +如果你熟悉[ExpressJS](https://expressjs.com/)的话,这个概念就不陌生。Express 是用于开发 NodeJS API 的框架,其中一个功能就是中间件的使用。中间件是我们可以在请求到达终点之前、之中和之后执行的一段代码。 让我们看一个例子。是一个验证身份令牌的函数,不用太关注验证是如何实现的,但是要注意函数接受令牌作为参数,一旦验证完毕就会调用`next()`函数。 @@ -497,7 +497,7 @@ module.exports = function authenticateToken(req, res, next) { } ``` -这个函数就是一个中间件,我们可以API中的任意终点使用这个中间件。只需要将其添加在终点地址之后,终点的函数声明之前: +这个函数就是一个中间件,我们可以 API 中的任意终点使用这个中间件。只需要将其添加在终点地址之后,终点的函数声明之前: ```javascript router.get('/:jobRecordId', authenticateToken, async (req, res) => { @@ -525,16 +525,16 @@ router.get('/:jobRecordId', authenticateToken, async (req, res) => { **责任链**将请求通过处理链传递,链条上的每一个处理程序决定要么处理请求,要么将请求传递给链条上的下一个处理程序。 -我们可以使用之前示例来演示这个模式,因为Express的中间件就是一种处理程序,要么处理请求,要么将其传递给下一个处理程序。 +我们可以使用之前示例来演示这个模式,因为 Express 的中间件就是一种处理程序,要么处理请求,要么将其传递给下一个处理程序。 如果你想要另一个示例,可以考虑任何需要通过步骤来一步一步实现信息处理的系统。在每个步骤中,不同的实体负责执行操作,并且只有在满足特定条件时,信息才会传递给另一个实体。 -需要使用API的前端应用程序就是很好的例子: +需要使用 API 的前端应用程序就是很好的例子: -- 有一个负责渲染UI的函数 -- 一旦渲染,另一个函数向API终点发出请求 +- 有一个负责渲染 UI 的函数 +- 一旦渲染,另一个函数向 API 终点发出请求 - 如果终点响应符合预期,则将信息传递给另一个函数,该函数以给定方式对数据进行排序并存储在变量中 -- 一旦变量存储了所需的信息,另一个函数负责在UI中呈现它。 +- 一旦变量存储了所需的信息,另一个函数负责在 UI 中呈现它。 可以看到这里有许多不同的实体协作执行任务。每个都负责该任务的一个“步骤”,这有助于代码模块化和关注点分离。👌👌 @@ -542,7 +542,7 @@ router.get('/:jobRecordId', authenticateToken, async (req, res) => { **迭代器**用于遍历集合的元素。这在现代编程语言中显得微不足道,但并非如此。 -JavaScript内置函数(`for`、 `forEach`、`for...of`、 `for...in`、 `map`、`reduce`、 `filter`等)就是手边可以拿来遍历数据结构的方法。 +JavaScript 内置函数(`for`、 `forEach`、`for...of`、 `for...in`、 `map`、`reduce`、 `filter`等)就是手边可以拿来遍历数据结构的方法。 [遍历算法](https://www.freecodecamp.org/news/introduction-to-algorithms-with-javascript-examples/#traversing-algorithms) 以及更为复杂的[树和图这样的数据结构](https://www.freecodecamp.org/news/data-structures-in-javascript-with-examples/)使用的代码也是迭代器的例子。 @@ -550,7 +550,7 @@ JavaScript内置函数(`for`、 `forEach`、`for...of`、 `for...in`、 `map`、 **观察者**模式允许你定义一个订阅机制来通知多个对象他们正在观察的对象发生的任何事件。基本上,这就像在给定对象上有一个事件侦听器,当该对象执行我们正在侦听的操作时,我们会采取一些行动。 -React的useEffect钩子就是一个很好的例子。 useEffect在我们声明的那一刻执行给定的函数。 +React 的 useEffect 钩子就是一个很好的例子。 useEffect 在我们声明的那一刻执行给定的函数。 钩子分为两个主要部分:可执行函数和依赖数组。如果数组为空,如下例所示,每次渲染组件时都会执行该函数。 @@ -564,7 +564,7 @@ React的useEffect钩子就是一个很好的例子。 useEffect在我们声明 useEffect(() => { console.log('var1 has changed') }, [var1]) ``` -也可以将JavaScript的事件监听器视为观察者模式。另外,响应式编程和库如[RxJS](https://rxjs.dev/),用来处理异步信息和事件的方法也是这个模式。 +也可以将 JavaScript 的事件监听器视为观察者模式。另外,响应式编程和库如[RxJS](https://rxjs.dev/),用来处理异步信息和事件的方法也是这个模式。 <h1 id="roundup">总结</h1> diff --git a/chinese/articles/javascript-es-modules-and-module-bundlers.md b/chinese/articles/javascript-es-modules-and-module-bundlers.md index ff1bedf5c..afda59101 100644 --- a/chinese/articles/javascript-es-modules-and-module-bundlers.md +++ b/chinese/articles/javascript-es-modules-and-module-bundlers.md @@ -2581,7 +2581,7 @@ And there we have it. I hope you have found this article helpful. Thanks for reading! -### **And here's a useful ReactJS resource:** +### **And here's a useful ReactJS resource** I wrote a book about React! diff --git a/chinese/articles/javascript-lexical-scope-tutorial.md b/chinese/articles/javascript-lexical-scope-tutorial.md index 95724017e..8be657a97 100644 --- a/chinese/articles/javascript-lexical-scope-tutorial.md +++ b/chinese/articles/javascript-lexical-scope-tutorial.md @@ -82,13 +82,13 @@ console.log(profile()) // 'Oluwatobi Sofela' **writeName() scope ---> sayName() scope ---> profile() scope ---> global scope** -换言之,从`fullName`变量的执行作用域到它的词法作用域(此处指全局作用域)之间有4层作用域。 +换言之,从`fullName`变量的执行作用域到它的词法作用域(此处指全局作用域)之间有 4 层作用域。 **注意:** 在[JavaScript](https://www.codesweetly.com/html-css-javascript/)作用域链中,全局作用域是整个作用域链的终点。 ## 作用域链是如何工作的? -Javascript的作用域链规定了编译器在查找 **被调用变量** 的词法作用域时所遵循的查找规则。 +Javascript 的作用域链规定了编译器在查找 **被调用变量** 的词法作用域时所遵循的查找规则。 考虑如下示例代码: @@ -167,17 +167,17 @@ console.log(profile()) [**点击查看源代码**](https://stackblitz.com/edit/web-platform-9mpvfv?file=script.js) -**特别强调3点:** +**特别强调 3 点:** - 假如编译器没有在任何作用域中找到`fullName`,那么编译器将返回一个引用错误`Uncaught ReferenceError: fullName is not defined`。 -- 全局作用域是Javascript作用域链的最后一个作用域,即全局作用域是查找的终点。 +- 全局作用域是 Javascript 作用域链的最后一个作用域,即全局作用域是查找的终点。 - 一个内部作用域(子作用域)可以访问它的外部作用域(父作用域),但是外部作用域不能访问它的子作用域。 举个例子,在上面代码中,`writeName()`可以访问它的任何父级作用域(比如`sayName()`的局部作用域,`profile()`的局部作用域,或者全局作用域)。 但是,无论是`sayName()`和`profile()`的局部作用域,还是全局作用域都不能访问`writeName()`的作用域。 ## 作用域小结 -Javascript中所有的作用域都是一个可被访问的区间。 +Javascript 中所有的作用域都是一个可被访问的区间。 因此,如果你女(男)朋友打电话让你去他的私人作用域时,切记他们是在邀请你去他们的私人空间 😜! @@ -249,7 +249,7 @@ console.log(getName()) // 'Oluwatobi' ## 词法作用域如何工作? -Javascript表达式的词法作用域决定了哪些代码可以访问它。 +Javascript 表达式的词法作用域决定了哪些代码可以访问它。 换言之,只有词法作用域内的代码才可以访问该作用域内部的代码。 @@ -311,9 +311,9 @@ console.log(displayFullName()); **注意:** -- 上述2个示例中,`displayFullName()`不能访问`showLastName()`中`lastName`变量。 +- 上述 2 个示例中,`displayFullName()`不能访问`showLastName()`中`lastName`变量。 但`displayFullName()`可以调用`showLastName()`,而`showLastName()`返回了`lastName`变量的值。 -- 词法作用域的替换方案是[动态作用域](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope_vs._dynamic_scope_2),但是除了在极少的开发语言(比如bash脚本)中使用外,很少使用它。 +- 词法作用域的替换方案是[动态作用域](https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexical_scope_vs._dynamic_scope_2),但是除了在极少的开发语言(比如 bash 脚本)中使用外,很少使用它。 ## 总结 @@ -323,4 +323,4 @@ console.log(displayFullName()); ## 写在最后 -这篇文章不仅讨论了在Javascript中什么是词法作用域,还探讨了为什么它是一个很重要的编程概念。 感谢你的阅读! +这篇文章不仅讨论了在 Javascript 中什么是词法作用域,还探讨了为什么它是一个很重要的编程概念。 感谢你的阅读! diff --git a/chinese/articles/javascript-package-manager-npm-and-yarn.md b/chinese/articles/javascript-package-manager-npm-and-yarn.md index 94ce5cc41..f2dbf1a17 100644 --- a/chinese/articles/javascript-package-manager-npm-and-yarn.md +++ b/chinese/articles/javascript-package-manager-npm-and-yarn.md @@ -7,7 +7,7 @@ 一个 **包管理器** 是开发人员用来自动寻找、下载、安装、配置、升级和删除系统包的工具。 -本文将向你展示所有你需要的东西,以开始使用NPM和Yarn等软件包管理器。 +本文将向你展示所有你需要的东西,以开始使用 NPM 和 Yarn 等软件包管理器。 但是,究竟为什么我们在开发工作流程中需要一个软件包管理器呢?让我们来了解一下。 @@ -33,7 +33,7 @@ 一个 **包管理器** 是开发人员用来自动查找、下载、安装、配置、升级和卸载计算机包的工具。 -NPM(Node Package Manager)和Yarn(Yet Another Resource Negotiator)是两个常用的软件包管理器。 +NPM(Node Package Manager)和 Yarn(Yet Another Resource Negotiator)是两个常用的软件包管理器。 一个 **package registry** 是一个数据库(存储),用于存储成千上万的包(库、插件、框架或工具)。 @@ -43,25 +43,25 @@ NPM(Node Package Manager)和Yarn(Yet Another Resource Negotiator)是两 所以,现在我们知道了什么是包管理器以及为什么需要它,我们可以讨论如何使用两个流行的包管理器 NPM 和 Yarn。 -请注意,外面有许多关于NPM和Yarn的争论--所以我们在这里会避免这些争论,因为最好的软件包管理器是最适合你的。 +请注意,外面有许多关于 NPM 和 Yarn 的争论--所以我们在这里会避免这些争论,因为最好的软件包管理器是最适合你的。 -因此,本文将向你展示NPM和Yarn是如何工作的,而不是告诉你哪个软件包管理器是最好的。然后由你来决定你喜欢哪一个。 +因此,本文将向你展示 NPM 和 Yarn 是如何工作的,而不是告诉你哪个软件包管理器是最好的。然后由你来决定你喜欢哪一个。 -另外,你可以选择在一个特定的项目中使用NPM,在另一个项目中使用Yarn,这取决于你认为哪个管理器最适合这项工作。 +另外,你可以选择在一个特定的项目中使用 NPM,在另一个项目中使用 Yarn,这取决于你认为哪个管理器最适合这项工作。 因此,不再多说,让我们开始学习如何安装这两个管理器。 -## 如何安装Node Package Manager (NPM) +## 如何安装 Node Package Manager (NPM) -NPM会在安装Node时自动安装。 +NPM 会在安装 Node 时自动安装。 -因此,要在你的系统上安装NPM,请到 [NodeJS](https://nodejs.org/en/) 网站上获取Node的 [最新LTS或当前版本](https://tamalweb.com/which-nodejs-version)。 +因此,要在你的系统上安装 NPM,请到 [NodeJS](https://nodejs.org/en/) 网站上获取 Node 的 [最新 LTS 或当前版本](https://tamalweb.com/which-nodejs-version)。 -## 如何安装Yarn +## 如何安装 Yarn -最好是通过NPM来安装Yarn。因此,首先,从 [Node.js](https://nodejs.org/en/)网站上安装NPM。 +最好是通过 NPM 来安装 Yarn。因此,首先,从 [Node.js](https://nodejs.org/en/)网站上安装 NPM。 -一旦你安装了NPM,就像这样继续安装Yarn: +一旦你安装了 NPM,就像这样继续安装 Yarn: ```bash npm install -g yarn @@ -69,7 +69,7 @@ npm install -g yarn ## 如何检查已安装的 Node 版本 -要检查你系统上安装的Node.js版本,请运行: +要检查你系统上安装的 Node.js 版本,请运行: ```bash node -v @@ -77,51 +77,51 @@ node -v 上面片段中的`-v`标志是 `--version` 的缩写. -## 如何检查已安装的NPM版本 +## 如何检查已安装的 NPM 版本 -要检查你系统上安装的NPM版本,请运行: +要检查你系统上安装的 NPM 版本,请运行: ```bash npm -v ``` -## 如何检查已安装的Yarn版本 +## 如何检查已安装的 Yarn 版本 -要检查你系统上安装的Yarn版本,请运行: +要检查你系统上安装的 Yarn 版本,请运行: ```bash yarn -v ``` -## 如何升级Node Package Manager +## 如何升级 Node Package Manager -通过运行以下程序更新到最新的NPM版本: +通过运行以下程序更新到最新的 NPM 版本: ```bash npm install npm@latest -g ``` -## 如何升级NodeJS +## 如何升级 NodeJS -假设你希望升级你的Node.js安装。在这种情况下,你有两个选择: +假设你希望升级你的 Node.js 安装。在这种情况下,你有两个选择: -### Option 1: 通过NodeJS网站升级 +### Option 1: 通过 NodeJS 网站升级 -升级你的NodeJS安装的一种方法是手动从 [Node.js 官网](https://nodejs.org/en/)下载并安装最新版本。 +升级你的 NodeJS 安装的一种方法是手动从 [Node.js 官网](https://nodejs.org/en/)下载并安装最新版本。 ### Option 2: 通过版本管理工具进行升级 -另一种升级NodeJS安装的方法是使用 [版本管理器](https://nodejs.org/en/download/package-manager/),如 [NVM](https://github.com/nvm-sh/nvm)、[n](https://github.com/tj/n),或 [nvs](https://github.com/jasongin/nvs)。 +另一种升级 NodeJS 安装的方法是使用 [版本管理器](https://nodejs.org/en/download/package-manager/),如 [NVM](https://github.com/nvm-sh/nvm)、[n](https://github.com/tj/n),或 [nvs](https://github.com/jasongin/nvs)。 ## 如何升级 Yarn -通过运行以下程序更新到最新的Yarn版本: +通过运行以下程序更新到最新的 Yarn 版本: ```bash yarn set version latest ``` -所以,现在我们的电脑上有了NPM(或Yarn),我们可以开始使用安装管理器来寻找、安装、配置和删除我们项目的包。 +所以,现在我们的电脑上有了 NPM(或 Yarn),我们可以开始使用安装管理器来寻找、安装、配置和删除我们项目的包。 但究竟什么是包?让我们来了解一下。 @@ -145,7 +145,7 @@ yarn set version latest 2. 使用下面的 NPM 或 Yarn 安装命令安装你的包(取决于你选择的项目使用的包管理器)。 -**注意:** 你的系统必须安装Node和NPM,下面的NPM(和Yarn)安装命令才能工作。你可以通过安装最新的LTS或Node.js网站的当前版本来获得这两者。 +**注意:** 你的系统必须安装 Node 和 NPM,下面的 NPM(和 Yarn)安装命令才能工作。你可以通过安装最新的 LTS 或 Node.js 网站的当前版本来获得这两者。 #### NPM installation 命令 @@ -153,7 +153,7 @@ yarn set version latest npm install package-name --save ``` -注意,上面的`--save` 命令指示NPM将 `package.json` 文件中的 `package-name` 保存为项目所依赖的包之一。 +注意,上面的`--save` 命令指示 NPM 将 `package.json` 文件中的 `package-name` 保存为项目所依赖的包之一。 假设你希望安装一个精确版本的软件包。在这种情况下,在软件包的名称后面添加 `@[版本号]`,像这样: @@ -167,7 +167,7 @@ npm install package-name@4.14.1 --save npm install package-name --save-dev ``` -上面的命令将使NPM下载三个项目到你的项目根目录:一个 `node_modules` 文件夹,一个 `package.json` 文件,和一个 `package-lock.json` 文件。我们将在本文后面详细讨论这些项目。 +上面的命令将使 NPM 下载三个项目到你的项目根目录:一个 `node_modules` 文件夹,一个 `package.json` 文件,和一个 `package-lock.json` 文件。我们将在本文后面详细讨论这些项目。 #### Yarn installation 命令 @@ -187,7 +187,7 @@ yarn add package-name@4.14.1 yarn add package-name --dev ``` -上面的命令将使Yarn下载三个项目到你的项目根目录:一个 `node_modules` 文件夹,一个`package.json` 文件,和一个 `yarn.lock` 文件。我们将在本文后面详细讨论这些项目。 +上面的命令将使 Yarn 下载三个项目到你的项目根目录:一个 `node_modules` 文件夹,一个`package.json` 文件,和一个 `yarn.lock` 文件。我们将在本文后面详细讨论这些项目。 所以,现在我们知道了如何在本地安装一个包,我们可以讨论全局包的安装。 @@ -201,7 +201,7 @@ yarn add package-name --dev npm install package-name -g ``` -或者,你可以像这样使用Yarn: +或者,你可以像这样使用 Yarn: ```bash yarn global add package-name @@ -213,7 +213,7 @@ yarn global add package-name 一般来说,最好是在本地安装一个软件包。下面是本地安装和全局安装之间的一些区别。 -#### 差别1:安装位置 +#### 差别 1:安装位置 一个本地安装的包会被安装在你执行`npm install package-name`(或`yarn add package-name`)命令的目录下。 @@ -221,25 +221,25 @@ yarn global add package-name 相比之下,全局安装的包会被安装在你系统中的一个位置。具体位置取决于你系统的配置。 -#### 差异2:软件包版本 +#### 差异 2:软件包版本 假设你在本地安装了你的包。那么,你可以使用同一个包的不同版本来开发多个应用程序(译者注:你可以在不同的目录安装)。 然而,当你在全球范围内安装时,你被迫为你的所有应用程序使用相同的软件包版本。 -#### 差别3:更新 +#### 差别 3:更新 本地安装允许你选择你想升级到最新版本的项目包。这使你更容易管理那些破坏与其他软件包兼容性的升级。 然而,升级一个全局安装的包会更新所有项目的包--如果升级破坏了与其他包的兼容性,这可能会导致维护的噩梦。 -#### 差异4:使用建议 +#### 差异 4:使用建议 全局安装最适合你打算只在你的命令行上使用的软件包--特别是当它们提供了可在不同项目中重复使用的可执行命令。 然而,本地安装最适合于你打算在程序中使用的软件包--通过`import`语句或`require()`函数。 -#### 差异5:例子 +#### 差异 5:例子 [NPM](https://www.npmjs.com/), [React Native CLI](https://reactnative.dev/docs/environment-setup), [Gatsby CLI](https://www.gatsbyjs.com/docs/reference/gatsby-cli/), [Grunt CLI](https://gruntjs.com/getting-started), 和 [Vue CLI](https://cli.vuejs.org/) 是著名的需要全局软件包的例子。 @@ -248,18 +248,18 @@ yarn global add package-name **注意:** - 你可以对你打算在命令行和项目中使用的软件包[进行本地和全局安装](https://nodejs.org/en/blog/npm/npm-1-0-global-vs-local-installation/#when-you-can-t-choose)。这种包的典型例子是[ExpressJS](https://expressjs.com/)和[CoffeeScript](https://coffeescript.org/)。 -- 你的包管理器不会执行已安装的包。NPM(和Yarn)只将包安装到`node_modules`目录。而如果你指定了`--save`命令,你的管理器会把软件包的细节添加到`package.json`文件中。 +- 你的包管理器不会执行已安装的包。NPM(和 Yarn)只将包安装到`node_modules`目录。而如果你指定了`--save`命令,你的管理器会把软件包的细节添加到`package.json`文件中。 - 要执行(运行)任何[可执行](https://helpdeskgeek.com/how-to/what-is-an-executable-file-how-to-create-one/)包,你必须自己明确地这样做。我们将在本文后面的章节中讨论如何做。 但究竟什么是 `node_modules` 文件夹、`package.json` 文件、`package-lock.json` 文件和 `yarn.lock` 文件?让我们来了解一下。 ## 什么是 "node_modules "文件夹? -**node/modules**目录是NPM放置所有为你的项目下载的本地软件包的文件夹。 +**node/modules**目录是 NPM 放置所有为你的项目下载的本地软件包的文件夹。 ## 什么是`package.json`文件? -**package.json**文件是一个JSON文件,包管理器(如NPM和Yarn)使用它来存储特定项目的信息。 +**package.json**文件是一个 JSON 文件,包管理器(如 NPM 和 Yarn)使用它来存储特定项目的信息。 换句话说,`package.json`文件是一个项目的元数据文件。 @@ -267,9 +267,9 @@ yarn global add package-name `package.json` 文件: -- 使得将你的项目发布到NPM registry 成为可能 +- 使得将你的项目发布到 NPM registry 成为可能 - 使得其他人能够轻松地管理和安装你的软件包 -- 帮助NPM轻松管理 [模块](https://www.codesweetly.com/javascript-modules-tutorial/) 的依赖关系 +- 帮助 NPM 轻松管理 [模块](https://www.codesweetly.com/javascript-modules-tutorial/) 的依赖关系 - 使得你的软件包可以重现并与其他开发者共享 ### 如何创建一个`package.json`文件 @@ -280,7 +280,7 @@ yarn global add package-name npm init ``` -或者,如果你的软件包管理器是Yarn,运行: +或者,如果你的软件包管理器是 Yarn,运行: ```bash yarn init @@ -298,7 +298,7 @@ yarn init npm init -y ``` -或者,如果你的软件包管理器是Yarn,运行: +或者,如果你的软件包管理器是 Yarn,运行: ```bash yarn init -y @@ -326,7 +326,7 @@ yarn init -y `package.json`的属性使你的项目可以被包管理器和终端用户使用。 -假设你想把你的包发布到NPM registry。在这种情况下,你的 `package.json` 文件必须有 `"name"` 和`"version"` 字段。 +假设你想把你的包发布到 NPM registry。在这种情况下,你的 `package.json` 文件必须有 `"name"` 和`"version"` 字段。 然而,如果你不打算发布你的包,在这种情况下,所有字段--包括 `"name"` 和 `"version"` 属性--都是可选的。 @@ -340,7 +340,7 @@ yarn init -y - 一个单字 - 小写字母 -- 并且小于或等于214个字符 +- 并且小于或等于 214 个字符 请注意,你可以用连字符和下划线将单词连接起来。 @@ -370,7 +370,7 @@ yarn init -y `"description"` 字段是一个包含项目目的简要描述的属性。 -NPM建议有一个`"description"`属性,使你的包在NPM网站上更容易找到。 +NPM 建议有一个`"description"`属性,使你的包在 NPM 网站上更容易找到。 当人们运行`npm search`命令时,你的描述将被显示出来。 @@ -386,7 +386,7 @@ NPM建议有一个`"description"`属性,使你的包在NPM网站上更容易 `"main"` 字段表示一个项目的 [入口点](https://www.codesweetly.com/web-tech-glossary/#entry-point). -换句话说,当有人运行 `require()` 函数时,Node将把调用解析为 `require(<package.json:main>)`。 +换句话说,当有人运行 `require()` 函数时,Node 将把调用解析为 `require(<package.json:main>)`。 **这是一个例子:** @@ -408,7 +408,7 @@ NPM建议有一个`"description"`属性,使你的包在NPM网站上更容易 } ``` -如果你把package.json的 `"private"` 属性设置为 `true`,包管理器将不会发布你的项目。 +如果你把 package.json 的 `"private"` 属性设置为 `true`,包管理器将不会发布你的项目。 因此,设置该属性是防止意外发布你的包的一个好办法。 @@ -495,12 +495,12 @@ NPM建议有一个`"description"`属性,使你的包在NPM网站上更容易 } ``` -因此,每当用户从NPM registry 安装你的项目时,依赖项属性就会确保包管理器能够自动找到并安装列出的包。 +因此,每当用户从 NPM registry 安装你的项目时,依赖项属性就会确保包管理器能够自动找到并安装列出的包。 请注意,你可以通过以下两种方式将一个包添加到 `"dependencies"`字段中: - 手动添加你的项目在生产中依赖的每个软件包的名称和 [语义版本](https://docs.npmjs.com/about-semantic-versioning)。 -- 在你的终端上运行 `npm install package-name --save-prod` 命令。如果Yarn是你的软件包管理器,则可以使用 `yarn add package-name`。 +- 在你的终端上运行 `npm install package-name --save-prod` 命令。如果 Yarn 是你的软件包管理器,则可以使用 `yarn add package-name`。 #### devDependencies @@ -524,11 +524,11 @@ NPM建议有一个`"description"`属性,使你的包在NPM网站上更容易 请记住,你可以通过以下两种方式将包添加到 `"devDependencies"` 字段中: - 手动添加你的项目在开发和测试中所依赖的每个软件包的名称和语义版本。 -- 在你的终端上运行`npm install package-name --save-dev`命令。如果Yarn是你的软件包管理器,则可以使用`yarn add package-name --dev`。 +- 在你的终端上运行`npm install package-name --save-dev`命令。如果 Yarn 是你的软件包管理器,则可以使用`yarn add package-name --dev`。 #### homepage -`"homepage"` 字段指定了你的项目主页的URL。 +`"homepage"` 字段指定了你的项目主页的 URL。 **这是一个例子:** @@ -544,27 +544,27 @@ NPM建议有一个`"description"`属性,使你的包在NPM网站上更容易 **package-lock.json** 文件是 NPM 用来记录你在本地安装到项目 `node_modules` 目录下的所有软件包的准确版本的 [文件](https://www.codesweetly.com/document-vs-data-vs-code/#h-what-is-a-document)。 -`package-lock.json` 文件使应用程序以您发布到 NPM registry 后以确切方式100% 复现。 +`package-lock.json` 文件使应用程序以您发布到 NPM registry 后以确切方式 100% 复现。 因此,假设一个用户克隆了你的应用程序并运行了`npm install`命令。在这种情况下,`package-lock.json`确保用户下载的是你用来开发应用程序的确切版本的软件包。 例如,假设一个用户克隆了你的应用程序,其中没有`package-lock.json`文件,而该应用程序中使用的一个依赖项有一个较新的版本。 -假设`package.json`文件中的依赖关系的版本号有一个圆点符号(例如,`^2.6.2`)。在这种情况下,NPM将安装该依赖的最新次要版本--这可能导致应用程序产生错误的结果。 +假设`package.json`文件中的依赖关系的版本号有一个圆点符号(例如,`^2.6.2`)。在这种情况下,NPM 将安装该依赖的最新次要版本--这可能导致应用程序产生错误的结果。 -然而,假设用户克隆了你的应用程序,其中包含一个`package-lock.json`文件。在这种情况下,NPM将安装`package-lock.json`文件中记录的依赖关系的确切版本--不管是否存在更新的版本。 +然而,假设用户克隆了你的应用程序,其中包含一个`package-lock.json`文件。在这种情况下,NPM 将安装`package-lock.json`文件中记录的依赖关系的确切版本--不管是否存在更新的版本。 -因此,用户将始终以你发布到NPM注册表的精确方式获得你的应用程序。 +因此,用户将始终以你发布到 NPM 注册表的精确方式获得你的应用程序。 -换句话说,NPM使用`package-lock.json`文件将你的软件包的依赖关系锁定在你用于项目开发的特定版本号上。 +换句话说,NPM 使用`package-lock.json`文件将你的软件包的依赖关系锁定在你用于项目开发的特定版本号上。 -**注意:** 只要你运行 `npm update` 命令,NPM就会更新 `package-lock.json` 文件中记录的软件包。 +**注意:** 只要你运行 `npm update` 命令,NPM 就会更新 `package-lock.json` 文件中记录的软件包。 ## 什么是 `yarn.lock` 文件? -`yarn.lock`文件是Yarn用来记录你在项目的`node_modules`目录下安装的所有软件包的确切版本。 +`yarn.lock`文件是 Yarn 用来记录你在项目的`node_modules`目录下安装的所有软件包的确切版本。 -`yarn.lock`文件与NPM的 [package-lock.json](#what-is-a-package-lock-json-file) 锁文件相当。 +`yarn.lock`文件与 NPM 的 [package-lock.json](#what-is-a-package-lock-json-file) 锁文件相当。 我们之前提到,你的软件包管理器不会执行已安装的软件包--除非你自己明确地这样做。让我们来讨论一下如何做。 @@ -580,9 +580,9 @@ NPM建议有一个`"description"`属性,使你的包在NPM网站上更容易 ./node_modules/.bin/package-name ``` -### 在package.json的 `scripts` 字段中添加该软件包 +### 在 package.json 的 `scripts` 字段中添加该软件包 -另一种执行软件包的方法是,首先将其添加到你的项目package.json文件的`"scripts"`字段中,像这样: +另一种执行软件包的方法是,首先将其添加到你的项目 package.json 文件的`"scripts"`字段中,像这样: ```json { @@ -602,7 +602,7 @@ npm run desired-name 注意,上面的命令是 `npm run-script desired-name` 的简写。 -另外,你也可以像这样用Yarn来执行这个包: +另外,你也可以像这样用 Yarn 来执行这个包: ```bash yarn run desired-name @@ -626,7 +626,7 @@ yarn run desired-name npm run build ``` -或者,如果你的软件包管理器是Yarn,你可以像这样运行webpack: +或者,如果你的软件包管理器是 Yarn,你可以像这样运行 webpack: ```bash yarn run build @@ -634,13 +634,13 @@ yarn run build ### 使用 NPX -运行可执行包的一个更快的方法是像这样使用NPX: +运行可执行包的一个更快的方法是像这样使用 NPX: ```bash npx package-name ``` -有了NPX,你不再需要把你的包添加到项目的`package.json`文件的`"scripts"`字段中。 +有了 NPX,你不再需要把你的包添加到项目的`package.json`文件的`"scripts"`字段中。 NPX(Node Package Execute)是一个 [Node package runner](https://nodejs.dev/learn/the-npx-nodejs-package-runner),可以自动找到并执行指定的包。 @@ -652,13 +652,13 @@ npx webpack 上面的命令会自动找到并执行 [webpack](https://www.codesweetly.com/javascript-module-bundler/)。因此,我们不需要在 `package.json` 文件的 `"build": "webpack"` 属性添加到我们的 `package.json` 文件的 `"scripts"` 字段中。 -**注意:**当你安装Node 8.2/NPM 5.2.0或更高版本时,NPX自动被安装。 +**注意:**当你安装 Node 8.2/NPM 5.2.0 或更高版本时,NPX 自动被安装。 你也可以使用你喜欢的 Node.js 版本运行一些代码。让我们来看看如何。 -## 如何使用你喜欢的Node.js版本来运行代码 +## 如何使用你喜欢的 Node.js 版本来运行代码 -你可以使用 `@` 字符和 [node npm package](https://www.npmjs.com/package/node) 来指定你希望用来执行代码的Node.js版本。 +你可以使用 `@` 字符和 [node npm package](https://www.npmjs.com/package/node) 来指定你希望用来执行代码的 Node.js 版本。 **这是一个例子:** @@ -666,19 +666,19 @@ npx webpack npx node@7 index.js ``` -上面的片段告诉NPX用最新的Node 7 major版本来运行`index.js`。 +上面的片段告诉 NPX 用最新的 Node 7 major 版本来运行`index.js`。 -使用`node@`命令是一个有用的方法,可以避免使用Node.js版本管理工具,如[nvm](https://github.com/nvm-sh/nvm)在Node版本之间切换。 +使用`node@`命令是一个有用的方法,可以避免使用 Node.js 版本管理工具,如[nvm](https://github.com/nvm-sh/nvm)在 Node 版本之间切换。 -假设你想确认NPX将使用哪个Node版本来运行你的代码。在这种情况下,运行: +假设你想确认 NPX 将使用哪个 Node 版本来运行你的代码。在这种情况下,运行: ```bash npx node@7 -v ``` -上面的片段将显示NPX将用于运行你的代码的最新Node版本,即7大版本--例如,`v7.10.1`。 +上面的片段将显示 NPX 将用于运行你的代码的最新 Node 版本,即 7 大版本--例如,`v7.10.1`。 -## 如何检查是否有过时的本地Packages(软件包) +## 如何检查是否有过时的本地 Packages(软件包) 要确定你的项目的任何包是否已经过时,请运行: @@ -690,7 +690,7 @@ npm outdated 否则,请看这篇 [npm-outdated article](https://docs.npmjs.com/cli/v6/commands/npm-outdated)对该命令输出的详细解释。 -另外,你也可以像这样使用Yarn: +另外,你也可以像这样使用 Yarn: ```bash yarn outdated @@ -716,7 +716,7 @@ npm outdated -g --depth=0 npm list ``` -或者像这样使用Yarn: +或者像这样使用 Yarn: ```bash yarn list @@ -750,7 +750,7 @@ npm list package-name npm list -g ``` -或者像这样使用Yarn: +或者像这样使用 Yarn: ```bash yarn list -g @@ -776,7 +776,7 @@ npm list -g package-name ## 如何更新软件包 -下面是如何用NPM和Yarn更新软件包: +下面是如何用 NPM 和 Yarn 更新软件包: ### 如何将一个特定的软件包更新到其最新版本 @@ -784,7 +784,7 @@ npm list -g package-name npm update package-name ``` -或者,对于用Yarn管理的项目,运行: +或者,对于用 Yarn 管理的项目,运行: ```bash yarn upgrade package-name @@ -818,7 +818,7 @@ npm update -g ## 如何卸载软件包 -下面是如何用NPM和Yarn卸载软件包的方法: +下面是如何用 NPM 和 Yarn 卸载软件包的方法: ### 如何从一个特定的项目中卸载一个软件包 @@ -833,7 +833,7 @@ npm uninstall package-name - `-S` (或 `--save`) 标志,将对项目`package.json`文件中`dependencies`字段中软件包的引用删除。 - `-D` (或 `--save-dev`) 标志,将对项目`package.json`文件中`devDependencies`字段中对包的引用删除。 -对于用Yarn管理的项目,运行: +对于用 Yarn 管理的项目,运行: ```bash yarn remove package-name @@ -849,11 +849,11 @@ npm uninstall package-name -g 注意,最好的做法是不要从`node_modules`文件夹中手动删除包,因为这种行为会影响到其他依赖它的 _modules_ 。 -但NodeJS中的 _modules_ 到底是什么?让我们在下面找出答案。 +但 NodeJS 中的 _modules_ 到底是什么?让我们在下面找出答案。 -## 到底什么是NodeJS的模块(module)? +## 到底什么是 NodeJS 的模块(module)? -NodeJS中的 **module** 是`node_modules`文件夹中的任何文件,计算机可以通过Node的 `require()` 函数加载。 +NodeJS 中的 **module** 是`node_modules`文件夹中的任何文件,计算机可以通过 Node 的 `require()` 函数加载。 **这是一个例子:** @@ -870,7 +870,7 @@ const myModule = require("./codesweetly.js"); 另外,请注意,如果一个模块可以被 `require()` 函数加载,该模块必须是下列之一: - 一个包,其`package.json`文件包含一个 `"main"` 字段。 -- 一个JavaScript文件。 +- 一个 JavaScript 文件。 ## 如何将你的项目发布到 NPM Registry @@ -888,7 +888,7 @@ NPM 对于 [公共包开发者](https://www.npmjs.com/products) 是一个免费 ### Step 2: 登录 -从命令行登录到你的NPM账户,像这样: +从命令行登录到你的 NPM 账户,像这样: ```bash npm login @@ -904,11 +904,11 @@ npm login npm publish ``` -确保你的包的名字目前在NPM上不存在。否则,你在发布时将得到一个错误。 +确保你的包的名字目前在 NPM 上不存在。否则,你在发布时将得到一个错误。 -你可以使用 `npm search` 命令(或 [NPM website](https://www.npmjs.com/)的搜索栏)来搜索你想使用的名字是否已经存在于NPM上。 +你可以使用 `npm search` 命令(或 [NPM website](https://www.npmjs.com/)的搜索栏)来搜索你想使用的名字是否已经存在于 NPM 上。 -假设所有适合你的包的名字都已经被占用了。在这种情况下,NPM允许你将你的项目作为一个范围发布。 +假设所有适合你的包的名字都已经被占用了。在这种情况下,NPM 允许你将你的项目作为一个范围发布。 换句话说,你可以把你的包作为你的用户名的一个子部分发布。让我们看看下面的方法。 @@ -927,7 +927,7 @@ npm publish } ``` -NPM的默认设置是假定一个范围内的名称包是一个私人项目。因此,如果你使用 `npm publish` 命令来共享一个范围内的名称包,你会得到一个错误。 +NPM 的默认设置是假定一个范围内的名称包是一个私人项目。因此,如果你使用 `npm publish` 命令来共享一个范围内的名称包,你会得到一个错误。 因此,要把你的包发布为你的用户名的范围,请在 `npm publish` 命令中添加 `--access=public` 标志: @@ -939,19 +939,19 @@ npm publish --access=public ## 概述 -这篇文章讨论了什么是软件包管理器。我们还看了两个流行的包管理器(NPM和Yarn)是如何工作的。 +这篇文章讨论了什么是软件包管理器。我们还看了两个流行的包管理器(NPM 和 Yarn)是如何工作的。 谢谢你的阅读! -### **这里有一个有用的ReactJS资源:** +### **这里有一个有用的 ReactJS 资源** -我写了一本关于React! +我写了一本关于 React! - 对初学者友好 ✔ - 它有可用的代码片段 ✔ - 它包含可扩展的项目 ✔ - 它有大量易于掌握的例子 ✔ -[React Explained Clearly](https://amzn.to/30iVPIG) 这本书包含了你了解ReactJS所需要的一切。 +[React Explained Clearly](https://amzn.to/30iVPIG) 这本书包含了你了解 ReactJS 所需要的一切。 [![React Explained Clearly Book Now Available at Amazon](https://www.freecodecamp.org/news/content/images/2022/01/Twitter-React_Explained_Clearly-CodeSweetly-Oluwatobi_Sofela.jpg)](https://amzn.to/30iVPIG) diff --git a/chinese/articles/javascript-performance-async-defer.md b/chinese/articles/javascript-performance-async-defer.md index 8c75fa1e9..2394ebd77 100644 --- a/chinese/articles/javascript-performance-async-defer.md +++ b/chinese/articles/javascript-performance-async-defer.md @@ -278,7 +278,7 @@ Async 属性的介绍 - 对于不执行 DOM 操作的外部脚本,使用 `async`。`async` 不能保证脚本执行时页面渲染的中断。 - 对所有执行 DOM 操作的脚本使用 `defer`。带有 `defer` 属性的脚本会在页面加载结束时依次执行。 -## 在我们结束之前... +## 在我们结束之前 现在就说到这里。我希望你能发现这篇文章的信息量和洞察力。本文使用的所有源代码都可以在 [这个 GitHub 仓库](https://github.com/atapas/youtube/tree/main/javascript/load-async-defer) 上找到。 diff --git a/chinese/articles/javascript-string-comparison-how-to-compare-strings-in-js.md b/chinese/articles/javascript-string-comparison-how-to-compare-strings-in-js.md index 358cd2cfe..55762de64 100644 --- a/chinese/articles/javascript-string-comparison-how-to-compare-strings-in-js.md +++ b/chinese/articles/javascript-string-comparison-how-to-compare-strings-in-js.md @@ -45,7 +45,7 @@ const compareValue = string1.localeCompare(string2) // 1 ``` -上面的比较给出了 `1`,因为在英语环境中,banana的 ba**n** 在 back 的 ba**c** 之后。 +上面的比较给出了 `1`,因为在英语环境中,banana 的 ba**n** 在 back 的 ba**c** 之后。 再来一个例子: diff --git a/chinese/articles/landing-my-first-development-job-what-a-crazy-journey.md b/chinese/articles/landing-my-first-development-job-what-a-crazy-journey.md index 9b650b82c..cadda1f3e 100644 --- a/chinese/articles/landing-my-first-development-job-what-a-crazy-journey.md +++ b/chinese/articles/landing-my-first-development-job-what-a-crazy-journey.md @@ -26,15 +26,15 @@ 现在我对怎么合理安排休息时间有了很大的改变,不像以前一样把时间花费在一些“无脑消费”上(如果你喜欢社交媒体或看一些有趣的动物视频,我并不是说你是“无脑”,我只是说,这类行为通常不需要你的投入,你只需要打开手机,看着屏幕就可以了。其实,我也时不时这样)。 -在这段时间里,我浏览了各种学习资源,一直在纠结哪种适合我,就像一个在糖果店里的孩子到处盯一盯。我曾经有一个文件夹,里面保存了大约300个链接,全是我计划要学习的东西。现在我知道了,找到适合自己的并坚持下去,远比选来选去,这看看、那看看要好得多。 +在这段时间里,我浏览了各种学习资源,一直在纠结哪种适合我,就像一个在糖果店里的孩子到处盯一盯。我曾经有一个文件夹,里面保存了大约 300 个链接,全是我计划要学习的东西。现在我知道了,找到适合自己的并坚持下去,远比选来选去,这看看、那看看要好得多。 -我之前总是学习到很晚,妻子和女儿都睡着了。我经常熬夜到凌晨12点半,有时甚至是2点,然后不得不在早上6点起床去做我的日常工作。起初,当学习的东西很简单的时候,对我来说还可以适应。但是,当我开始学习 JavaScript,学习网络、服务器、无障碍等基础知识时,我发现越是熬夜我的大脑就越难以集中精力。 +我之前总是学习到很晚,妻子和女儿都睡着了。我经常熬夜到凌晨 12 点半,有时甚至是 2 点,然后不得不在早上 6 点起床去做我的日常工作。起初,当学习的东西很简单的时候,对我来说还可以适应。但是,当我开始学习 JavaScript,学习网络、服务器、无障碍等基础知识时,我发现越是熬夜我的大脑就越难以集中精力。 在坚持了几个月之后,我不得不开始改变我的学习计划。 当然,我并没有想停止我的学习和项目,只是将我的日程安排做了下调整。 -每天早上,我4点起床,不再像以前一样熬夜。然后学习和做项目,再去健身房,一天就这样开始了。不得不说,在我的学习过程中,这是非常关键的,因为我记住了更多东西,做了更多项目,最终一步步在往前发展。 +每天早上,我 4 点起床,不再像以前一样熬夜。然后学习和做项目,再去健身房,一天就这样开始了。不得不说,在我的学习过程中,这是非常关键的,因为我记住了更多东西,做了更多项目,最终一步步在往前发展。 同时,在这期间我开始购买各种和 web 开发相关的学习资源(主要是 Udemy 的课程和书籍)。但这并不是一个明智的选择。如果你找到了一个你喜欢并适合你学习的资源,那么坚持下去,直到将它学完。不要像我这样从一个跳到另一个。 @@ -52,7 +52,7 @@ 在学习这些课程的时候,我开发了很多很棒的项目,比如[卡片匹配游戏](https://github.com/JS-goose/card-matching-game),[街机游戏](https://github.com/JS-goose/frontend-nanodegree-arcade-game),[餐厅点评 App](https://github.com/JS-goose/mws-restaurant-stage-1),[邻里定位 App](https://github.com/JS-goose/neighborhood-map-react)。但除了这些项目,还认识了很多朋友,与其他开发者合作也是一份宝贵的经验。 -可以这么说,没有这门课程,就不会有今天的我。在这9个月的课程中,我收获了很多宝贵的东西。 +可以这么说,没有这门课程,就不会有今天的我。在这 9 个月的课程中,我收获了很多宝贵的东西。 ## 求职 @@ -80,11 +80,11 @@ 以下是我找工作的一些数据统计: -- 通过申请平台提交求职申请:271次 -- 提交之后,有公司以某种形式回应我:28次 -- 电话面试:7次 -- 面谈:8次 -- 拿到 offer:3家公司(一家是可以签订全职合同,但可能性很小;一家是兼职形式;最终我得到了一份全职工作) +- 通过申请平台提交求职申请:271 次 +- 提交之后,有公司以某种形式回应我:28 次 +- 电话面试:7 次 +- 面谈:8 次 +- 拿到 offer:3 家公司(一家是可以签订全职合同,但可能性很小;一家是兼职形式;最终我得到了一份全职工作) - 没有消息:**多得数不过来** 如果你正在找工作,不要让这些数字打击你!的确,这是很令人沮丧,让人想退缩。填了一份申请表,投了简历却从来没有得到任何回复,确实很糟糕,但请不要放弃!你一定会成功!就像我一样。 @@ -100,11 +100,11 @@ - 不要因为某些公司拒绝你而觉得自己是个失败者 - 向公司展示你可以不需要教程构建一个项目 - 当需要帮助时,就寻求帮助(对我来说很难) -- 去各种平台上申请工作,别忘了 [ GitHub jobs](https://jobs.github.com/) 也可以 +- 去各种平台上申请工作,别忘了 [GitHub jobs](https://jobs.github.com/) 也可以 - 保证充足的睡眠、锻炼,以及注意饮食健康(健康是根本) - 时不时休息一下 - 不要和别人比较,你的工作。技术等等 -- 不要在意花了多长时间,3个月或3年(像我一样),这不是一场比赛 +- 不要在意花了多长时间,3 个月或 3 年(像我一样),这不是一场比赛 - 努力学习,永不言弃 最后,希望你一切顺利!如果你需要建议或帮助,可以在 [Twitter](https://twitter.com/jj_goose) 或 [LinkedIn](https://www.linkedin.com/in/jj-goose/) 上和我联系。我喜欢帮助别人,结识新朋友。 diff --git a/chinese/articles/learn-css-background-properties.md b/chinese/articles/learn-css-background-properties.md index 05713499f..08a3c88b1 100644 --- a/chinese/articles/learn-css-background-properties.md +++ b/chinese/articles/learn-css-background-properties.md @@ -21,7 +21,7 @@ - [简写](#short-hand) - [总结](#conclusion) -如果你喜欢这篇文章也可以在YouTube上观看本教程: +如果你喜欢这篇文章也可以在 YouTube 上观看本教程: # 所有属性 @@ -29,11 +29,11 @@ ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l25y304vndphll4795hr.png) -## 什么是CSS背景(background)属性? +## 什么是 CSS 背景(background)属性? ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iic3rs5ewx8c9xp6vryq.png) -CSS背景(background)属性让我们可以控制图像的大小和属性,这样我们就可以为不同大小的屏幕制作 **响应性强的** 图像。它能帮助我们创建响应式网页。 +CSS 背景(background)属性让我们可以控制图像的大小和属性,这样我们就可以为不同大小的屏幕制作 **响应性强的** 图像。它能帮助我们创建响应式网页。 例如, @@ -46,18 +46,18 @@ CSS背景(background)属性让我们可以控制图像的大小和属性, ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u72rvfe5181640ikqa32.png) -在编码之前,你需要知道一点HTML, CSS的相关知识,以及如何使用 VS code。 +在编码之前,你需要知道一点 HTML, CSS 的相关知识,以及如何使用 VS code。 要测试属性及其值,请遵循以下步骤 👇 -1. 创建一个名为 'BACKGROUND-PROJECT' 的文件夹并在VS code中打开。 +1. 创建一个名为 'BACKGROUND-PROJECT' 的文件夹并在 VS code 中打开。 2. 创建 `index.html` 和 `style.css` 文件。 3. 在 VS code 中安装 'live server' 4. 开启 live server ## HTML -在HTML文件的 **body标签** 中创建一个类名为 'container' 的div。 +在 HTML 文件的 **body 标签** 中创建一个类名为 'container' 的 div。 ```html <div class="container"></div> @@ -65,7 +65,7 @@ CSS背景(background)属性让我们可以控制图像的大小和属性, ## CSS -在CSS中,你 **必须** 为容器提供一个高度,否则将不会看图像。 在我们的例子中,我们将它设置为100vh,像这样: +在 CSS 中,你 **必须** 为容器提供一个高度,否则将不会看图像。 在我们的例子中,我们将它设置为 100vh,像这样: ```css .container{ @@ -73,7 +73,7 @@ CSS背景(background)属性让我们可以控制图像的大小和属性, } ``` -## 下载项目的图像。 +## 下载项目的图像 图像在我的 **[GitHub repository](https://github.com/JoyShaheb/Project-image-repo/tree/main/Background-property-images)** 中。 以下是如何获得它们: @@ -108,7 +108,7 @@ CSS背景(background)属性让我们可以控制图像的大小和属性, 我们可以通过 **两种方式** 使用 background-image 属性 - 通过图像的路径 -- 通过具体的 **图像URL** +- 通过具体的 **图像 URL** ### 如何通过图像路径使用 `background-image` 属性 @@ -123,7 +123,7 @@ CSS背景(background)属性让我们可以控制图像的大小和属性, ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1jfuda4p0ki1hish775o.png) -当你想在CSS中指定一个图像路径时,有三种情况: +当你想在 CSS 中指定一个图像路径时,有三种情况: 1. 当 `image` 和 `style.css` 在同一文件夹下 2. 当 `image` 在下一级文件夹中 @@ -149,11 +149,11 @@ CSS背景(background)属性让我们可以控制图像的大小和属性, } ``` -当图像在 **下一级文件夹**,`style.css` 在上一级文件夹时。注意,下图中的`kitty.png` 在Assets文件夹中,而 `style.css` 在上一级文件夹中。 +当图像在 **下一级文件夹**,`style.css` 在上一级文件夹时。注意,下图中的`kitty.png` 在 Assets 文件夹中,而 `style.css` 在上一级文件夹中。 ![Alt Text](https://www.freecodecamp.org/news/content/images/2021/04/Frame-26.png) -要设置 `kitty.png` 的文件路径,我们需要在 `style.css` 中的引号后面写一个点和斜杠,就像这样(./)。然后我们写入文件夹的名称,然后斜杠(/),最后我们写入image的名称,像这样: 👇 +要设置 `kitty.png` 的文件路径,我们需要在 `style.css` 中的引号后面写一个点和斜杠,就像这样(./)。然后我们写入文件夹的名称,然后斜杠(/),最后我们写入 image 的名称,像这样: 👇 ```css .container{ @@ -167,7 +167,7 @@ CSS背景(background)属性让我们可以控制图像的大小和属性, ``` -如果图像是在 **上一级文件夹** 中,我们需要返回。注意,在下图 👇 中,`style.css` 在 **src** 文件夹中,`kitty.png` 在**src文件夹外**。 +如果图像是在 **上一级文件夹** 中,我们需要返回。注意,在下图 👇 中,`style.css` 在 **src** 文件夹中,`kitty.png` 在**src 文件夹外**。 ![Alt Text](https://www.freecodecamp.org/news/content/images/2021/04/Frame-27.png) @@ -224,7 +224,7 @@ CSS背景(background)属性让我们可以控制图像的大小和属性, } ``` -你可以通过 **3种方式** 使用 background-size 属性 +你可以通过 **3 种方式** 使用 background-size 属性 - 使用 Cover / Contain 值 - 设置图像宽度和高度 @@ -238,7 +238,7 @@ Bear 大小 : \[718px X 614px\] ### Cover 值 -为此,我们必须包含一个图像,为其设置高度,并禁止图像重复。我们在CSS中这样做: 👇 +为此,我们必须包含一个图像,为其设置高度,并禁止图像重复。我们在 CSS 中这样做: 👇 ```css .container{ @@ -280,7 +280,7 @@ Bear 大小 : \[718px X 614px\] ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/36p9azoztkvawbvy6244.png) -以下是CSS中的语法: 👇 +以下是 CSS 中的语法: 👇 ```css .container{ @@ -316,7 +316,7 @@ Bear 大小 : \[718px X 614px\] ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/629rnxirqrdr8p5fapcd.png) -我们在写完类名之后编写如下CSS语法 👇 +我们在写完类名之后编写如下 CSS 语法 👇 ```CSS .container{ @@ -325,7 +325,7 @@ Bear 大小 : \[718px X 614px\] } ``` -这个属性有6个值: +这个属性有 6 个值: - repeat - repeat-x @@ -356,7 +356,7 @@ Bear 大小 : \[718px X 614px\] ### repeat 值 -通过使用这个值,我们可以沿着 **X轴和Y轴** 重复对应图像多次,直到容器被填充满。在这,kitty 的大小是 200px x 200px。 +通过使用这个值,我们可以沿着 **X 轴和 Y 轴** 重复对应图像多次,直到容器被填充满。在这,kitty 的大小是 200px x 200px。 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/26zsa1dn161pawjqxuqp.png) @@ -375,7 +375,7 @@ To duplicate this result, we write -> ### repeat-x 值 -顾名思义,我们可以使用这个值在容器X轴上重复对应图像,直到容器 X 轴被填充满。 Kitty的大小: 200px X 200px。 +顾名思义,我们可以使用这个值在容器 X 轴上重复对应图像,直到容器 X 轴被填充满。 Kitty 的大小: 200px X 200px。 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pl4znzrwcevpr5w1a4i5.png) @@ -393,7 +393,7 @@ To duplicate this result, we write -> ### repeat-y 值 -与 "repeat-x" 同理,但是是作用在 **Y轴上** 的。Kitty 的大小: 200px X 200px. +与 "repeat-x" 同理,但是是作用在 **Y 轴上** 的。Kitty 的大小: 200px X 200px. ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7yo3i3bp8cw2r6zqhtvm.png) @@ -429,7 +429,7 @@ To duplicate this result, we write -> ### space 值 -这个值在 X 轴和 Y 轴都会起作用。当我们调整窗口大小时,我们可以看到值 **space和round** 之间的主要区别。 注意,当我们调整窗口大小时,我们有 **空得空间**: +这个值在 X 轴和 Y 轴都会起作用。当我们调整窗口大小时,我们可以看到值 **space 和 round** 之间的主要区别。 注意,当我们调整窗口大小时,我们有 **空得空间**: ![Space](https://media.giphy.com/media/cO0jNSpVi0I3FWD62G/giphy.gif) @@ -564,7 +564,7 @@ We write the syntax after writing the selector name, like this  👇 - content-box - inherit -在标准 CSS 盒子模型中,最外侧是border。然后是padding,最后是在中间的内容(content)。 +在标准 CSS 盒子模型中,最外侧是 border。然后是 padding,最后是在中间的内容(content)。 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p3mdn6hpd1u892akrkj5.png) @@ -791,7 +791,7 @@ background-attachment: scroll; ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/newvcc4rvegnbkblwzyb.png) -对于这个案例,我们将图像 `kitty.png` 放在长和宽都为200px,蓝色背景的容器中。代码如下所示: +对于这个案例,我们将图像 `kitty.png` 放在长和宽都为 200px,蓝色背景的容器中。代码如下所示: ```css .container{ diff --git a/chinese/articles/learn-css-media-queries-by-building-projects.md b/chinese/articles/learn-css-media-queries-by-building-projects.md index cb81c6ddc..3c67182c6 100644 --- a/chinese/articles/learn-css-media-queries-by-building-projects.md +++ b/chinese/articles/learn-css-media-queries-by-building-projects.md @@ -19,7 +19,7 @@ Topics to discuss at a glance: ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7vrrjohwnmdvbtexg1r2.png) -## You can watch this tutorial on YouTube as well if you like: +## You can watch this tutorial on YouTube as well if you like # What are CSS Media Queries? @@ -65,7 +65,7 @@ So, write this line inside the html file: ## What is SCSS? -We'll Use SCSS, not CSS. But..... what is SCSS? +We'll Use SCSS, not CSS. But…… what is SCSS? ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q5a3hp7070ls26dovja2.png) @@ -148,7 +148,7 @@ Responsive website also means **Responsive Images**. So we're also going to make ![Down Git Steps to follow](https://cloud.githubusercontent.com/assets/5456665/17822364/940bded8-6678-11e6-9603-b84d75bccec1.gif) -And.... we're all set! Let's start coding. 😊 +And…… we're all set! Let's start coding. 😊 ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u5qj78v41btdq7sewdzs.png) @@ -175,7 +175,7 @@ Let's divide the syntax into four sections: 3. min-width & max-width Functions 4. The Code itself -### To understand all 4 section of the syntax, let's start our **First Project**: +### To understand all 4 section of the syntax, let's start our **First Project** ![Project-1 Video](https://media.giphy.com/media/t4QOOfmnupAqnHcI9H/giphy.gif) @@ -317,7 +317,7 @@ To be honest, there's no such thing as a standard screen break-point guide becau Here's a list of every device screen resolution on [CSS-Tricks](https://css-tricks.com/snippets/css/media-queries-for-standard-devices/). -### The max-width function: +### The max-width function Using this function, we are creating a boundary. This will work as long as we are **inside the boundary**. Here's a sample 👇 @@ -348,7 +348,7 @@ At the bottom, insert the media query like this 👇 } ``` -### The min-width function: +### The min-width function We are also creating a boundary here. But this will work if we go **outside the boundary**. Here's a sample: 👇 @@ -428,7 +428,7 @@ And our `.container` class should look like this: We're all 50% done! Now let's setup the four media queries. -## But Wait... +## But Wait ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/net2cuugxdaz9idwbwhl.png) @@ -514,11 +514,11 @@ Congratulations on completing project 1! Now **take a break. You deserve it.** We'll build a small responsive Website for our second project. -### Here's what the desktop view will look like: +### Here's what the desktop view will look like ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ay01oqdseulalsw3gpdh.png) -### And here's the mobile view: +### And here's the mobile view ![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hob05vxov52hrm5quz0a.png) diff --git a/chinese/articles/learn-flexbox-build-5-layouts.md b/chinese/articles/learn-flexbox-build-5-layouts.md index cb22a1f25..ac021e8f2 100644 --- a/chinese/articles/learn-flexbox-build-5-layouts.md +++ b/chinese/articles/learn-flexbox-build-5-layouts.md @@ -20,7 +20,7 @@ Here's a practical guide to help you learn CSS Flexbox in 2021 by building 5 res ### You can [**check out the Figma design here**](https://www.figma.com/file/d1bG4msAzxixv9jWP8e4nA/Master-CSS-Flexbox-Layout?node-id=0%3A1) -### You can also watch this tutorial on my YouTube channel here: +### You can also watch this tutorial on my YouTube channel here ## What is Flexbox? @@ -42,7 +42,7 @@ This chart contains **every possible property and value** you can use when using ![Flex Box Chart](https://dev-to-uploads.s3.amazonaws.com/i/gv3jyh4xt4fbwtq1qejn.png) -## Before We Get Started... +## Before We Get Started ![](https://www.freecodecamp.org/news/content/images/2021/03/Desktop---2--1-.png) diff --git a/chinese/articles/learn-jenkins-by-building-a-ci-cd-pipeline.md b/chinese/articles/learn-jenkins-by-building-a-ci-cd-pipeline.md index 2b283b8a8..9d4ded772 100644 --- a/chinese/articles/learn-jenkins-by-building-a-ci-cd-pipeline.md +++ b/chinese/articles/learn-jenkins-by-building-a-ci-cd-pipeline.md @@ -38,7 +38,7 @@ DevOps 管道是一套流程和工具,可以用它们来实现软件应用程 - 安装插件 - Blue Ocean - 创建一个管道 -- 安装Git +- 安装 Git - Jenkinsfile - 更新管道 - 配合 npm 使用 Jenkins diff --git a/chinese/articles/learn-react-usestate-hook-in-10-minutes.md b/chinese/articles/learn-react-usestate-hook-in-10-minutes.md index 20a3d27e5..49dd1d48f 100644 --- a/chinese/articles/learn-react-usestate-hook-in-10-minutes.md +++ b/chinese/articles/learn-react-usestate-hook-in-10-minutes.md @@ -5,29 +5,29 @@ ![React Hooks for Beginners – Learn to Use the useState Hook in 10 Minutes](https://www.freecodecamp.org/news/content/images/size/w2000/2021/09/philipp-katzenberger-jVx8JaO2Ddc-unsplash.jpg) -大家好🌈我很久没有写关于在React中处理状态的文章了。上一次是在[这篇文章](https://www.freecodecamp.org/news/get-pro-with-react-setstate-in-10-minutes-d38251d1c781/)中,四年前,它似乎帮助了很多人。 +大家好🌈我很久没有写关于在 React 中处理状态的文章了。上一次是在[这篇文章](https://www.freecodecamp.org/news/get-pro-with-react-setstate-in-10-minutes-d38251d1c781/)中,四年前,它似乎帮助了很多人。 我收到了大量的观看和惊人的反馈,所以非常感谢——你们真的很棒!🎸 -从那以后已经过去很久了。hook从v16.8版本(2019年)开始在React中出现,在React中使用state时有很多需要跟上的地方。 +从那以后已经过去很久了。hook 从 v16.8 版本(2019 年)开始在 React 中出现,在 React 中使用 state 时有很多需要跟上的地方。 -您正在学习state并想通过**useState** hook成为专业人员吗? +您正在学习 state 并想通过**useState** hook 成为专业人员吗? 太好了,你来对地方了!拿上一杯咖啡(或茶),系好安全带,我们出发吧! -顺便说一下——如果您正在寻找如何使用setState(在类组件中),那么我建议您查看我以前的文章 - [“如何在10分钟内成为使用React setState()的专家”](https://www.freecodecamp.org/news/get-pro-with-react-setstate-in-10-minutes-d38251d1c781/)。 +顺便说一下——如果您正在寻找如何使用 setState(在类组件中),那么我建议您查看我以前的文章 + [“如何在 10 分钟内成为使用 React setState()的专家”](https://www.freecodecamp.org/news/get-pro-with-react-setstate-in-10-minutes-d38251d1c781/)。 -## 什么是React Hook? +## 什么是 React Hook? -hook(钩子)是一种特殊的函数,它允许您“钩入”各种React特性。假设一个函数返回一个有两个值的数组: +hook(钩子)是一种特殊的函数,它允许您“钩入”各种 React 特性。假设一个函数返回一个有两个值的数组: -- **第一个值:** 一个带有状态state的变量。 -- **第二个值:** 一个带有处理程序handle(改变当前状态的函数)的变量。 +- **第一个值:** 一个带有状态 state 的变量。 +- **第二个值:** 一个带有处理程序 handle(改变当前状态的函数)的变量。 就是这样,很简单。 🥞 -记住,在JavaScript中 **"值是函数,函数是值"**。 我是在2017年从 [**MPJ**](https://www.youtube.com/c/funfunfunction)学到这一点的,MPJ是我最喜欢的开发者和youtuber之一。谢谢MPJ所做的一切!! +记住,在 JavaScript 中 **"值是函数,函数是值"**。 我是在 2017 年从 [**MPJ**](https://www.youtube.com/c/funfunfunction)学到这一点的,MPJ 是我最喜欢的开发者和 youtuber 之一。谢谢 MPJ 所做的一切!! 如果这让你有点困惑,这里有一个例子: @@ -45,34 +45,34 @@ Let's see what's happening here: 有道理吗?你明白我的意思了吗?是的,**函数就是值,值就是函数**!这就是你现在需要知道的。 -**useState**,特别地,它允许您向函数组件(声明为函数而不是类的组件)添加React状态。 +**useState**,特别地,它允许您向函数组件(声明为函数而不是类的组件)添加 React 状态。 实际上,状态保存在钩子内部,但是可以从“调用”钩子的组件访问。 -## React Hooks的规则 +## React Hooks 的规则 -除了hook是JavaScript函数这一事实之外,在使用它们时还需要遵循一些规则: +除了 hook 是 JavaScript 函数这一事实之外,在使用它们时还需要遵循一些规则: ### 只在顶层调用钩子 -不要在循环、条件或嵌套函数中调用钩子。总是在React函数(组件)的顶层使用钩子,在任何早期返回之前。 +不要在循环、条件或嵌套函数中调用钩子。总是在 React 函数(组件)的顶层使用钩子,在任何早期返回之前。 -这背后的原因是,每次组件呈现时,必须以相同的顺序调用钩子。这使得React能够正确地保存多个useState和useEffect调用之间的钩子状态。 +这背后的原因是,每次组件呈现时,必须以相同的顺序调用钩子。这使得 React 能够正确地保存多个 useState 和 useEffect 调用之间的钩子状态。 -#### 只有React函数的调用钩子 +#### 只有 React 函数的调用钩子 -这意味着您可以从React函数(组件)或自定义钩子调用钩子,但不能从常规JavaScript函数调用。 +这意味着您可以从 React 函数(组件)或自定义钩子调用钩子,但不能从常规 JavaScript 函数调用。 这里有一个有用的插件[here](https://www.npmjs.com/package/eslint-plugin-react-hooks),它强制执行钩子的规则。这是一个非常有用的方法,所以一定要尝试一下。 -## useState hook的结构 +## useState hook 的结构 -要使用useState钩子,您需要知道一些事情。 +要使用 useState 钩子,您需要知道一些事情。 💡你可以查看下面的图来更好地理解我将在这里解释的内容。 -1. 您必须从React库导入它。 -2.您必须在React组件中调用它 +1. 您必须从 React 库导入它。 +2.您必须在 React 组件中调用它 ```javascript const [state, setState] = useState(initialValue) @@ -86,7 +86,7 @@ const [state, setState] = useState(initialValue) const array = useState(initialValue) ``` -然后我可以使用状态,在位置0内,作为数组[0],用处理函数setState,在位置1内,作为数组\[1\]。 +然后我可以使用状态,在位置 0 内,作为数组[0],用处理函数 setState,在位置 1 内,作为数组[1]。 解构数组的声明性更强,因为我们知道它的第一个和第二个位置值,我们知道它们对应于状态值和用于更改它的处理程序。 @@ -94,7 +94,7 @@ const array = useState(initialValue) const [first, second] = useState(initialValue) ``` -是的,我们做到了。但我们可以把任何东西称为first和second。唯一的规则是这些变量对应于**useState**函数(hook)返回的数组的第一个和第二个位置。 +是的,我们做到了。但我们可以把任何东西称为 first 和 second。唯一的规则是这些变量对应于**useState**函数(hook)返回的数组的第一个和第二个位置。 ```javascript const [state, setState] = useState(initialValue) @@ -106,37 +106,37 @@ const [something, setSomething] = useState(initialSomething) 去吧——我会等的! (_Edo 喝了一小口_ ☕) -3\.  然后可以自由呈现状态,或者调用setState来更新状态值。 +3.  然后可以自由呈现状态,或者调用 setState 来更新状态值。 这是一个最简单的全功能示例: ![carbon](https://www.freecodecamp.org/news/content/images/2022/06/carbon.png) -useState hook的结构 +useState hook 的结构 ## 何时使用 useState Hook -为了理解何时使用这个钩子hook,我们需要从学习何时需要状态state开始。 +为了理解何时使用这个钩子 hook,我们需要从学习何时需要状态 state 开始。 -乍一看,我们认为当我们需要一个随时间变化的变量时,我们需要保持它in state。但在大多数情况下,事实并非如此。我的意思是,如果你的变量可以从其他数据推导出来,那么你就不需要状态了。 +乍一看,我们认为当我们需要一个随时间变化的变量时,我们需要保持它 in state。但在大多数情况下,事实并非如此。我的意思是,如果你的变量可以从其他数据推导出来,那么你就不需要状态了。 -### State 案例 1: +### State 案例 1 主题颜色, 根据时间变暗或变亮的,可以从系统数据中推导出来。、 -我们可以简单地从JS的date函数中获得时间(日期)。所以我们在这里不需要状态,对吧?这是一个可以用表达式或函数声明的常量const。 +我们可以简单地从 JS 的 date 函数中获得时间(日期)。所以我们在这里不需要状态,对吧?这是一个可以用表达式或函数声明的常量 const。 -### State 案例 2: +### State 案例 2 一个模式开关 (显示/隐藏模式). -模式切换可以为true或false,当用户单击按钮时触发。因此,在这种情况下,我们真的需要状态,因为我们不能推导这类信息-它只取决于“何时和是否”用户触发事件。 +模式切换可以为 true 或 false,当用户单击按钮时触发。因此,在这种情况下,我们真的需要状态,因为我们不能推导这类信息-它只取决于“何时和是否”用户触发事件。 要注意这个区别——什么可以派生推导,什么取决于用户。 当需要存储来自用户的输入时,您将需要使用 **useState** 钩子。 -💡作为经验法则,您应该只使用状态state来保存这类信息——需要用户输入数据或触发事件。 +💡作为经验法则,您应该只使用状态 state 来保存这类信息——需要用户输入数据或触发事件。 另一个常用的例子是表单数据。几乎每个应用程序或网站都需要从用户那里收集信息。要做到这一点,通常(或强制)需要有一个表格。 @@ -144,7 +144,7 @@ useState hook的结构 很好,我们继续。 -## 如何在React中使用多个状态变量 +## 如何在 React 中使用多个状态变量 如果我们需要处理多个状态,最好和推荐的第一种方法是分别处理它们,像这样: @@ -152,17 +152,17 @@ useState hook的结构 猫狗计数器(处理多个状态变量) -这样做并没有什么错,尽管它看起来很原始。 当我们继续使用JavaScript原语(在本例中是数字)时,这是一种很好的线性方法。 +这样做并没有什么错,尽管它看起来很原始。 当我们继续使用 JavaScript 原语(在本例中是数字)时,这是一种很好的线性方法。 你也可以在一个对象中混合状态: ![carbon--2-](https://www.freecodecamp.org/news/content/images/2022/06/carbon--2-.png) -这个例子变得有点复杂。我们已经初始化了一个对象,而不是一个原语值。当我们调用setPets时,我们必须意识到我们需要展开现有的pets对象,然后添加更改,否则我们将丢失它。 +这个例子变得有点复杂。我们已经初始化了一个对象,而不是一个原语值。当我们调用 setPets 时,我们必须意识到我们需要展开现有的 pets 对象,然后添加更改,否则我们将丢失它。 -对于旧的setState API,这不是强制性的 – 它会理解您想要更新state object的key。但现在它不是了,我喜欢它。现在它更具声明性,是JavaScript中的基本概念。 +对于旧的 setState API,这不是强制性的 – 它会理解您想要更新 state object 的 key。但现在它不是了,我喜欢它。现在它更具声明性,是 JavaScript 中的基本概念。 -如果您不熟悉spread语法,请查看或阅读这个有用的[教程](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) or [read this helpful tutorial](https://www.freecodecamp.org/news/javascript-object-destructuring-spread-operator-rest-parameter/)。 +如果您不熟悉 spread 语法,请查看或阅读这个有用的[教程](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) or [read this helpful tutorial](https://www.freecodecamp.org/news/javascript-object-destructuring-spread-operator-rest-parameter/)。 ## 异步状态 @@ -176,20 +176,20 @@ useState hook的结构 所以,我更新了我们最初的狗的例子。这次我创建了一个**handleDogsCount**函数来向您显示它。 -在handleDogsCount内部,我用新值调用 **setDogs**。 +在 handleDogsCount 内部,我用新值调用 **setDogs**。 如果我需要立即为另一个操作使用状态值,会发生什么? -对,状态还没更新。实现即时操作的最佳方法是使用传递给handleDogsCount函数的值,并且——暂时忘记狗的状态值——事先知道该值没有及时更新(这很棘手,但事实就是如此)。 +对,状态还没更新。实现即时操作的最佳方法是使用传递给 handleDogsCount 函数的值,并且——暂时忘记狗的状态值——事先知道该值没有及时更新(这很棘手,但事实就是如此)。 ## 如何以函数的方式突变状态 -好了,现在我们知道状态不会立即改变。还有一个相关的问题。如果你可以每秒点击More按钮1M次,会发生什么? +好了,现在我们知道状态不会立即改变。还有一个相关的问题。如果你可以每秒点击 More 按钮 1M 次,会发生什么? -可能,在点击1M的最后,计数器将是999\_998(或更少),而不是预期的 1\_000\_000。 +可能,在点击 1M 的最后,计数器将是 999_998(或更少),而不是预期的 1_000_000。 -为了避免这种情况发生,我们可以用函数的方式设置状态。我们将获取前一个状态的值,以便React能够正确地批处理所有请求并线性更新状态。这样我们就不会在中间丢失信息。 +为了避免这种情况发生,我们可以用函数的方式设置状态。我们将获取前一个状态的值,以便 React 能够正确地批处理所有请求并线性更新状态。这样我们就不会在中间丢失信息。 要做到这一点,你可以简单地做以下几点: @@ -197,19 +197,19 @@ useState hook的结构 以函数的方式改变状态 -好的,很酷。现在我们确信React在处理我们的1M请求以改变状态时不会错过任何东西。 +好的,很酷。现在我们确信 React 在处理我们的 1M 请求以改变状态时不会错过任何东西。 -我们不是抓取dogs变量来添加或减去1,而是依赖于暴露在useState setState handle内部的previousState(在本例中是setDogs函数)。 +我们不是抓取 dogs 变量来添加或减去 1,而是依赖于暴露在 useState setState handle 内部的 previousState(在本例中是 setDogs 函数)。 注意,对象和数组是通过引用进行比较的,因此复杂的状态应该在其他钩子(如**useEffect**)的依赖项数组中得到适当的控制。我们将在后面的另一篇文章中讨论它! -如果你是JavaScript新手,让我给你一个关于我所说的内容的剧透: +如果你是 JavaScript 新手,让我给你一个关于我所说的内容的剧透: ![carbon--5-](https://www.freecodecamp.org/news/content/images/2022/06/carbon--5-.png) 引用类型比较 -正如你所看到的,**c**并不严格等于**d**。是的,去试试吧!JavaScript比较复杂对象(所有非[基本类型](https://developer.mozilla.org/en-US/docs/Glossary/Primitive))的方法是引用,而不是值。 +正如你所看到的,**c**并不严格等于**d**。是的,去试试吧!JavaScript 比较复杂对象(所有非[基本类型](https://developer.mozilla.org/en-US/docs/Glossary/Primitive))的方法是引用,而不是值。 如果我将它字符串化,就意味着我在比较字符串。因为它们都是基本类型,所以它们严格相等(按值比较)。 @@ -223,13 +223,13 @@ const [ dogs, setDogs] = useState(() => expensiveComputation()) 这意味着我们在延迟初始化变量。初始值只会在初始渲染时赋值(如果是函数的话)。 -In subsequent renders (due to change of state in the component or a parent component), the argument of the useState hook will be ignored and the current value will be retrieved.在随后的渲染中(由于组件或父组件的状态更改),useState钩子的参数将被忽略,并将检索当前值。 +In subsequent renders (due to change of state in the component or a parent component), the argument of the useState hook will be ignored and the current value will be retrieved.在随后的渲染中(由于组件或父组件的状态更改),useState 钩子的参数将被忽略,并将检索当前值。 ## 总结 看来我们的旅程已经走到了尽头。 -您已经了解了什么是钩子、钩子的规则、useState如何工作、它的结构以及如何处理多个状态。 +您已经了解了什么是钩子、钩子的规则、useState 如何工作、它的结构以及如何处理多个状态。 您还了解了一些陷阱(比如处理状态对象,或者该状态是异步的),以及一些改进性能的技巧,比如将状态初始化为函数,以避免不断地计算该计算。 @@ -237,11 +237,11 @@ In subsequent renders (due to change of state in the component or a parent compo ## 最后但同样重要的 -我是 [Edo](https://eduardovedes.com/)。我是freeCodeCamp的倡导者,喜欢帮助人们转行从事软件工程。 +我是 [Edo](https://eduardovedes.com/)。我是 freeCodeCamp 的倡导者,喜欢帮助人们转行从事软件工程。 -如果你正在转行,或者正在考虑转行,读一点在freeCodeCamp出版物上发表的我的[故事](https://www.freecodecamp.org/news/from-civil-engineer-to-web-developer-with-freecodecamp/)可能会激励你。 +如果你正在转行,或者正在考虑转行,读一点在 freeCodeCamp 出版物上发表的我的[故事](https://www.freecodecamp.org/news/from-civil-engineer-to-web-developer-with-freecodecamp/)可能会激励你。 -你可能也对 ["如何在6个月内成为一名初级软件工程师"](https://www.freecodecamp.org/news/how-to-become-a-junior-software-engineer-in-6-months/)感兴趣。 +你可能也对 ["如何在 6 个月内成为一名初级软件工程师"](https://www.freecodecamp.org/news/how-to-become-a-junior-software-engineer-in-6-months/)感兴趣。 如果你喜欢这篇文章,请在[Twitter](https://twitter.com/eduardovedes) 上关注我,联系我,这样我们就可以聊天了! @@ -249,6 +249,6 @@ In subsequent renders (due to change of state in the component or a parent compo Edo -### 更多关于 React Hooks... +### 更多关于 React Hooks 1. [React Documentation](https://reactjs.org/docs/hooks-state.html) diff --git a/chinese/articles/learn-redux-by-making-a-counter-application.md b/chinese/articles/learn-redux-by-making-a-counter-application.md index 2f6c76a41..9ba3d7107 100644 --- a/chinese/articles/learn-redux-by-making-a-counter-application.md +++ b/chinese/articles/learn-redux-by-making-a-counter-application.md @@ -9,7 +9,7 @@ Redux 是前端应用的状态管理库。开发者通常会通过 react-redux 刚接触 Redux 可能会被吓到,你可能需要一段时间才能适应它,而且可能最终要翻看以前的代码才能找到解决问题的办法。 -常遭人诟病的是,Redux 需要使用很多样板代码来管理应用状态。确实如此——在小型应用中使用 Redux 犹如拿大炮打蚂蚁。在 React 项目中,还有其他可用方案,如属性(prop)钻取和上下文(context);Vue项目中可以使用 VueX;Angular 项目中可以使用 NGRX。 +常遭人诟病的是,Redux 需要使用很多样板代码来管理应用状态。确实如此——在小型应用中使用 Redux 犹如拿大炮打蚂蚁。在 React 项目中,还有其他可用方案,如属性(prop)钻取和上下文(context);Vue 项目中可以使用 VueX;Angular 项目中可以使用 NGRX。 在 React 应用中,Redux 解决的主要问题是,将应用的所有状态保存在一个全局的 store 中,而不是通过(单向的)属性传递来管理状态。这样就可以在任意需要的地方访问到全局状态了,非常方便! diff --git a/chinese/articles/learn-solidity-handbook.md b/chinese/articles/learn-solidity-handbook.md index d9bf197c3..3b4d9f4d4 100644 --- a/chinese/articles/learn-solidity-handbook.md +++ b/chinese/articles/learn-solidity-handbook.md @@ -149,7 +149,7 @@ constant 和 immutable 变量的值可以并且仅可以被赋予一次。在赋 因此,如果我们将 `qtyCups` 状态变量设为 constant 或 immutable,我们将无法再对其调用 `increment()` 或 `decrement()` 函数(事实上,调用的话代码将无法编译!)。 -constant 的值必须在代码本身中硬编码(hardcode),而immutable 变量可以将它们的值设置一次,通常是通过构造函数中的赋值(我们很快就会讨论构造函数)。你可以在[此处的文档](https://docs.soliditylang.org/en/v0.8.16/contracts.html#constant-and-immutable-state-variables)中阅读更多内容。 +constant 的值必须在代码本身中硬编码(hardcode),而 immutable 变量可以将它们的值设置一次,通常是通过构造函数中的赋值(我们很快就会讨论构造函数)。你可以在[此处的文档](https://docs.soliditylang.org/en/v0.8.16/contracts.html#constant-and-immutable-state-variables)中阅读更多内容。 ## 智能合约中的变量作用域 智能合约中的变量有 3 个作用域: @@ -516,7 +516,7 @@ Solidity 中值类型(Value Types)和引用类型(Reference Types) 地址的默认值(如果你声明一个类型地址的变量但不分配任何值,则将具有的值)为`0x00000000000000000000000000000000000000000000000000`,这也是此表达式所代表的值:`address(0)`。 -布尔值表示真值还是假值。最后,我们有[固定大小的字节数组](https://docs.soliditylang.org/en/v0.8.17/types.html#fixed-size-byte-arrays),如 `bytes1`、`bytes2` … `bytes32`。这些是包含字节的固定长度数组。所有这些类型的值在代码中传递时都会被复制。 +布尔值表示真值还是假值。最后,我们有[固定大小的字节数组](https://docs.soliditylang.org/en/v0.8.17/types.html#fixed-size-byte-arrays),如 `bytes1`、`bytes2` …… `bytes32`。这些是包含字节的固定长度数组。所有这些类型的值在代码中传递时都会被复制。 对于“引用类型”,我们有数组,它们可以在声明时指定固定大小,或者动态大小的数组。虽然它们声明时大小是固定的,但其大小可以“调整”,因为数组中元素的数量会增加。 @@ -710,7 +710,7 @@ Solidity 允许开发人员更改父合约中的函数在派生合约中的实 编译器会给出两个错误: -1. 在合约A中,会提示你“trying to override non-virtual function(试图覆盖非虚函数)”,提示你是否忘记添加 `virtual` 关键字。 +1. 在合约 A 中,会提示你“trying to override non-virtual function(试图覆盖非虚函数)”,提示你是否忘记添加 `virtual` 关键字。 2. 在合约 B 中,它会抱怨 `getName()` 函数缺少标识符 `override`。 这意味着你在合约 B 中的新 `getName` 试图重写父合约中同名的函数,但父合约的函数未标记为 `virtual` - 这意味着它无法被重写。 @@ -805,7 +805,7 @@ Solidity 允许开发人员更改父合约中的函数在派生合约中的实 为什么整数 2022 会转换为 230?这显然不是我们预想的结果。明显是一个错误,对吧? -原因是大小为 256 位的无符号整数将包含 256 个二进制数字(0 或 1)。所以 `a` 保存整数值 '2022' 并且该值(以位为单位)将有 256 位数字,其中大部分将为 0,除了最后 11 位数字将是......(通过将 2022 从十进制系统到二进制 [此处](https://decimaltobinary.com/256-in-binary))。 +原因是大小为 256 位的无符号整数将包含 256 个二进制数字(0 或 1)。所以 `a` 保存整数值 '2022' 并且该值(以位为单位)将有 256 位数字,其中大部分将为 0,除了最后 11 位数字将是……(通过将 2022 从十进制系统到二进制 [此处](https://decimaltobinary.com/256-in-binary))。 另一方面,`b` 的值只有 8 位或数字,即 11100110。这个二进制数转换为十进制时(你可以使用相同的转换器 - 只需再另一个框中填写!)是 230。不是 2022 . @@ -855,7 +855,7 @@ Solidity 不处理小数点。这在未来可能会改变,但目前你无法 在科学计数法中,10^18 也表示为 1e18,其中 1e 表示 10,后面的数字表示 1e 的指数。 -所以下面的代码会产生一个编译器错误:“_Return argument type rational\_const 3 / 2 is not implicitly convertible to expected type…int256_”: +所以下面的代码会产生一个编译器错误:“_Return argument type rational\_const 3 / 2 is not implicitly convertible to expected type...int256_”: <table style="border:none;border-collapse:collapse;"><colgroup></colgroup><tbody><tr style="height:0pt"><td style="vertical-align:top;background-color:#002451;padding:5pt 5pt 5pt 5pt;overflow:hidden;overflow-wrap:break-word;"><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ebbbff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">function</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffffff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#bbdaff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">divideBy1e18</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffffff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">()</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#bbdaff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">public</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffffff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#bbdaff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">pure</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffffff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#bbdaff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">returns</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffffff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> (</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffc58f;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">int</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffffff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">) {</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffffff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"><br></span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffffff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">    </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ebbbff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">return</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffffff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> </span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffc58f;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1500000000000000000</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffffff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">/(</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffc58f;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">1e18</span><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffffff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">);</span><span style="font-size:9pt;font-family:Consolas,sans-serif;color:#7285b7;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;"> // 1.5 → Solidity can’t handle this.</span></p><p dir="ltr" style="line-height:1.38;margin-top:0pt;margin-bottom:0pt;"><span style="font-size:11pt;font-family:Consolas,sans-serif;color:#ffffff;background-color:#002451;font-weight:400;font-style:normal;font-variant:normal;text-decoration:none;vertical-align:baseline;white-space:pre;white-space:pre-wrap;">}</span></p></td></tr></tbody></table> @@ -1109,7 +1109,7 @@ Solidity 事件包含三类数据: 1. 发出事件的合约地址。 2. 主题(用于过滤日志查询的索引事件参数)。 -3. 非索引参数,简称“数据”,采用ABI编码,以十六进制表示。此数据需要按照 ABI 编码和解码部分中描述的方式进行 ABI 解码。 +3. 非索引参数,简称“数据”,采用 ABI 编码,以十六进制表示。此数据需要按照 ABI 编码和解码部分中描述的方式进行 ABI 解码。 在 Remix 中工作时,你还可以在控制台中检查事件,如下所示: diff --git a/chinese/articles/learn-to-code-book.md b/chinese/articles/learn-to-code-book.md index a162fa4fd..5a7bc9da2 100644 --- a/chinese/articles/learn-to-code-book.md +++ b/chinese/articles/learn-to-code-book.md @@ -193,7 +193,7 @@ 然后在 LinkedIn 上添加他们为好友,在 Twitter 上关注他们。如果你参加的技术会议有会后聚餐的安排,不要错过。 -### 提升自己的圈内名声。 +### 提升自己的圈内名声 可以考虑为自己做的项目录制一段 demo,然后发到网上。 @@ -696,7 +696,7 @@ if __name__ == "__main__": 我们正在做的事情被称为 "声明式编程" (declarative programming)。我们在宣布 "计算机,你应该这样做"。而计算机足够聪明(编程足够好),能够理解我们的指令并执行它们。 -在2023的今天,我们大多数正在使用的代码风格就是“命令性编程”。我们一步步地告诉计算机,我们要做什么。 +在 2023 的今天,我们大多数正在使用的代码风格就是“命令性编程”。我们一步步地告诉计算机,我们要做什么。 计算机很呆,所以我们必须帮它们把一只脚放在另一只脚前面,以让它知道怎么走路。 @@ -724,11 +724,11 @@ if __name__ == "__main__": 因此,你在过程中遇到的问题大概率是前所未有的,而且可能没有人可以帮你。 -人们每天在谷歌上输入的查询中,有15%是以前从未被搜索过的。如果你正在搜索的内容是这15里的东西,那可能有点不妙。 +人们每天在谷歌上输入的查询中,有 15%是以前从未被搜索过的。如果你正在搜索的内容是这 15 里的东西,那可能有点不妙。 我的理论是:大多数开发人员会想出如何解决一个问题,然后简单地继续前进,而不会把它记录在任何地方,因此,你可能是少数不得不为相同的问题找出自己的解决方案的开发者之一。 -然后,当然我们还有很多古老的论坛主题和Stackflow页面。 +然后,当然我们还有很多古老的论坛主题和 Stackflow 页面。 <figure class="kg-card kg-card-image kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/size/w1000/2023/01/wisdom_of_the_ancients_png__485-270_.png" alt="来自XKCD的绘画" class="kg-image"><figcaption>来自XKCD的绘画</figcaption></figure> @@ -736,7 +736,7 @@ if __name__ == "__main__": 好消息是:当你练习多了以后, **能力** 和 **自信** 都会随之而来。 -很快你就会知道在Google上搜什么了。对于文档通常是如何组织的,以及在哪里寻找目标信息,你很快就会有第二直觉,而且你也会知道该在哪里问哪些问题。 +很快你就会知道在 Google 上搜什么了。对于文档通常是如何组织的,以及在哪里寻找目标信息,你很快就会有第二直觉,而且你也会知道该在哪里问哪些问题。 我希望这些含糊不清的问题都能有一个简单的答案,但是你不得不承认,学习编程就是一个含糊不清的过程。而且即使是有经验的开发者也会为模棱两可的问题而烦恼。 @@ -796,15 +796,15 @@ if __name__ == "__main__": 所以这里有一些小建议: -#### Tip #1:明确自己在这方面并不是独一无二的糟糕。 +#### Tip #1:明确自己在这方面并不是独一无二的糟糕 -每个学习代码的人都会为试图与计算机进行Vulcan心灵融合并让它理解你而感到沮丧。(这是另一个来自《星际迷航》的参考资料)。 +每个学习代码的人都会为试图与计算机进行 Vulcan 心灵融合并让它理解你而感到沮丧。(这是另一个来自《星际迷航》的参考资料)。 当然,很多人在他们是孩子的时候就开始编程了。他们会表现得像他们一直很擅长编程,但他们很可能像我们成年人一样挣扎,随着时间的推移,他们只是忘记了那几个小时的挫折。 把电脑看作你的朋友,而不是对手,电脑有什么错,它只是想让你把自己的指令明确一些罢了。 -#### Tip #2:呼吸。 +#### Tip #2:呼吸 很多人在面对错误信息的时候,本能反应都是咬牙切齿。然后重新在代码编辑器中盲目的修改代码,希望以某种幸运的方式通过这个问题。 @@ -818,7 +818,7 @@ if __name__ == "__main__": 如果你有一个新闻编辑室,里面都是黑猩猩在做这件事,要多久才会有一只黑猩猩随机打出"to be or not to be"这句话? -假设每只黑猩猩每秒钟打一个随机字符。他们中的一个可能需要1万亿年的时间来输入"to be or not to be",那是10的18次方,十亿亿。 +假设每只黑猩猩每秒钟打一个随机字符。他们中的一个可能需要 1 万亿年的时间来输入"to be or not to be",那是 10 的 18 次方,十亿亿。 即使假设黑猩猩保持健康,打字机定期维修,当它们中的一个设法打出"to be or not to be"时,银河系将是一个寒冷、黑暗的虚空。 @@ -826,11 +826,11 @@ if __name__ == "__main__": 在这段时间里,你几乎肯定可以想出办法来教那些黑猩猩如何打英文单词。它们可能会设法打出《哈姆雷特》的全部内容,而不仅仅是其中最著名的一句。 -即使你真的很幸运,通过了这个bug,你会学到什么? +即使你真的很幸运,通过了这个 bug,你会学到什么? 因此,与其惊慌失措,不如花些时间,理解代码,理解正在发生的事情,然后修复错误。 -务必花时间去理解错误的代码,不要成为一个quintillionarian(五亿年)的黑猩猩。(我认为quintillionarian意味着一个五亿岁的人,虽然根据谷歌,以前没有人用过这个词)。 +务必花时间去理解错误的代码,不要成为一个 quintillionarian(五亿年)的黑猩猩。(我认为 quintillionarian 意味着一个五亿岁的人,虽然根据谷歌,以前没有人用过这个词)。 与其盲目地尝试,寄希望于简单地跳过错误信息,不如放慢速度。 @@ -848,7 +848,7 @@ if __name__ == "__main__": “橡皮鸭调试”对于让自己慢下来、尝试专注于眼前的工作是很有帮助的。 -当然,你不一定要用橡皮鸭子。你可以向你的宠物仙人掌解释你的Python应用程序,解释你的SQL查询给那只一直跳到你键盘上的猫。 +当然,你不一定要用橡皮鸭子。你可以向你的宠物仙人掌解释你的 Python 应用程序,解释你的 SQL 查询给那只一直跳到你键盘上的猫。 大声解释你的想法似乎能帮助你更好地处理这一情况。 @@ -876,7 +876,7 @@ if __name__ == "__main__": > “计算机科学教育不能使任何人成为专家级的程序员,就像研究画笔和颜料不会让人成为专家级的画家一样。” - Eric Raymond,开发人员、计算机科学家和作家。 -计算机科学系在传统上是数学系的一部分,20世纪60年代和70年代的大学不知道该把整个计算机的事情放在哪里。 +计算机科学系在传统上是数学系的一部分,20 世纪 60 年代和 70 年代的大学不知道该把整个计算机的事情放在哪里。 在其他大学,计算机科学被认为是电气工程的延伸。直到最近,即使是加州大学伯克利分校--世界上最伟大的公立大学之一--也只提供计算机科学学位,作为电气工程的一种双主修课程。 @@ -884,9 +884,9 @@ if __name__ == "__main__": 截至本文写作时,计算机科学是你能得到的报酬最高的学位,甚至高于专注于金钱的领域,如金融和经济学。 -[根据Glassdoor](https://www.glassdoor.com/blog/50-highest-paying-college-majors/), 美国的计算机科学专业平均在第一份工作中比其他专业赚得更多。70000美刀对一个刚从大学毕业的人来说是一笔不小的收入。 +[根据 Glassdoor](https://www.glassdoor.com/blog/50-highest-paying-college-majors/), 美国的计算机科学专业平均在第一份工作中比其他专业赚得更多。70000 美刀对一个刚从大学毕业的人来说是一笔不小的收入。 -高于护理专业(59000美元)、金融专业(55000美元)和建筑专业(50000美元)。 +高于护理专业(59000 美元)、金融专业(55000 美元)和建筑专业(50000 美元)。 OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门工作。这可能对任何人都不是新闻,但这是为什么呢? @@ -898,9 +898,9 @@ OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门 而且我相信他们,他们不再需要本科学位。 -我们已经有很多freeCodeCamp的校友在这些公司找到了工作,其中有些人没有相应的学士学位。 +我们已经有很多 freeCodeCamp 的校友在这些公司找到了工作,其中有些人没有相应的学士学位。 -但那些找到这些工作的freeCodeCamp校友可能必须是特别强的候选人,以克服他们没有学士学位的事实。 +但那些找到这些工作的 freeCodeCamp 校友可能必须是特别强的候选人,以克服他们没有学士学位的事实。 你可以看一下这些职位空缺,他们判断候选人的标准有很多: @@ -936,9 +936,9 @@ OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门 对于大部分雇主来说,就是这样的道理。 -多数情况下,HR只是通过招聘过滤软件的勾选框来筛选简历,过滤掉没有学位的应聘人员。他们可能完全没看过这些被筛选掉的简历。 +多数情况下,HR 只是通过招聘过滤软件的勾选框来筛选简历,过滤掉没有学位的应聘人员。他们可能完全没看过这些被筛选掉的简历。 -必须强调,并不是所有HR都这样。但大多数HR都这样,在美国如此,其他国家可能更为严重。 +必须强调,并不是所有 HR 都这样。但大多数 HR 都这样,在美国如此,其他国家可能更为严重。 这很糟糕,但这就是劳动力市场目前的状况,在几十年后可能会好转也可能没有变化。 @@ -950,7 +950,7 @@ OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门 - 如宿舍生活、交友、自我探索等“大学经验”。(美国学生基本不住在学校,也就没有类似的经验) - 通识课程帮助你成为“全面人才”(听说过[“Freshman 15”](https://www.healthline.com/nutrition/freshman-15)(大一胖三斤半)这个笑话吗?但确实有很多大一新生过劳肥) -再次申明,学士学位真正的意义(美国人花十万美元甚至更多来支付大学4年的学费)是因为雇主要求学历。 +再次申明,学士学位真正的意义(美国人花十万美元甚至更多来支付大学 4 年的学费)是因为雇主要求学历。 当然,获得学士学位还有其他的好处,如:更多从军的选择、更容易获得工作签证等。 @@ -968,9 +968,9 @@ OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门 理论上是的。在科技行业的某些领域的确需要副学士文凭,这个文凭也会帮助你更快获得面试机会。 -虽然如此,我不鼓励你为了副学士文凭而去读大学。我更鼓励你待够4年,拿到大学文凭,这个的含金量更高。 +虽然如此,我不鼓励你为了副学士文凭而去读大学。我更鼓励你待够 4 年,拿到大学文凭,这个的含金量更高。 -美国教育部的数据显示,在整个职业生涯中,大学学历的薪资比副学士学历高31%。 +美国教育部的数据显示,在整个职业生涯中,大学学历的薪资比副学士学历高 31%。 这两种学历的区别可比普通本科以及计算机科学本科的区别要大得多。 @@ -984,7 +984,7 @@ OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门 但我并不认为你应该辞掉工作脱产学习。 -脱产学习是为“传统”学生设计的。他们通常在18到22岁之间(如果服过兵役可能年龄稍长一些),除了高中校外打工和暑期实习,并未有任何工作经验。 +脱产学习是为“传统”学生设计的。他们通常在 18 到 22 岁之间(如果服过兵役可能年龄稍长一些),除了高中校外打工和暑期实习,并未有任何工作经验。 传统的大学的学费高昂,一般学生会结合奖学金、家庭基金和学生贷款来支付。 @@ -996,7 +996,7 @@ OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门 一些本地社区大学和州立大学的衍生项目也提供学位。许多项目都是线上的,其中一部分还可以自己管理学习进度,你可以根据自己的工作安排完成学业的时间。 -做好研究功课。如果一个大学看上去不错,推荐你使用LinkedIn找到该学校校友,向他们寻求帮助。询问他们的经验,他们觉得这些课程是否物有所值。 +做好研究功课。如果一个大学看上去不错,推荐你使用 LinkedIn 找到该学校校友,向他们寻求帮助。询问他们的经验,他们觉得这些课程是否物有所值。 我不推荐通过借贷的方式来获得学位。去一个学费没那么贵的学校更好。学位只是一个学位而已,只要是来自一个授权的机构,基本上就可以服务于你需要用学位达成的大部分目的。 @@ -1022,9 +1022,9 @@ OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门 是的,我认为学士学位会在未来几十年,甚至是几百年依旧保持重要的地位。 -学士学位已经存在超过1000年了。 +学士学位已经存在超过 1000 年了。 -许多美国顶尖学校比美国历史还悠久。(哈佛有400年历史) +许多美国顶尖学校比美国历史还悠久。(哈佛有 400 年历史) 学士学位的消亡这一说法实属夸大。 @@ -1038,13 +1038,13 @@ OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门 但你也可以作为一个开发人员,在恒温的办公环境,通过维护服务器、修补代码库赚钱同样丰厚的收入。 -前者工作环境危险,对背部有损伤;而后者工作环境舒适,即便是40岁,你也可以继续工作下去。 +前者工作环境危险,对背部有损伤;而后者工作环境舒适,即便是 40 岁,你也可以继续工作下去。 许多抨击大学的“意见领袖”本身从自己的大学学历获益。 我认为倡导“大学无用论”的其中一个理由是:很难将学习与获得地位提升联系起来。 -大学只是阶级信号的一种形式吗 – 即富裕阶级将财富传承到下一代的一种方式?总体来说,在哈佛找到一位富家子弟比穷苦学子的概率要大20倍。 +大学只是阶级信号的一种形式吗 – 即富裕阶级将财富传承到下一代的一种方式?总体来说,在哈佛找到一位富家子弟比穷苦学子的概率要大 20 倍。 事实是:人生的本质就是不公平。但这并不改变劳动力市场的运行规律。 @@ -1084,7 +1084,7 @@ OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门 ### 如何自学代码 -大部分开发者都是自学的,即便是有相关学历的程序员也在Stack Overflow的年度调查中宣称自己是“自学”。 +大部分开发者都是自学的,即便是有相关学历的程序员也在 Stack Overflow 的年度调查中宣称自己是“自学”。 ![stack-overflow](https://www.freecodecamp.org/news/content/images/2023/01/stack-overflow.jpeg)大部分在职程序员认为自己的“自学的” (图片来源: Stack Overflow 2016 调查) @@ -1104,7 +1104,7 @@ OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门 如果有人对你自称是自学成才提出异议,只需说:“按照你的标准,只要不是被狼养大的,就不能声称自己是自学成才的。” -将本书的这一部分指给他们看,并告诉他们:“Quincy预料到了你的傲慢。”然后继续你的生活。 +将本书的这一部分指给他们看,并告诉他们:“Quincy 预料到了你的傲慢。”然后继续你的生活。 人生苦短,别浪费时间。 @@ -1120,7 +1120,7 @@ OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门 世界上有各种学习者的社区,相互帮助提高技能。 -社区是一个很难定义的词。Twitter Tech话题是一个社区吗? freeCodeCamp 论坛呢?还是许多专门针对特定代码技能的 Discord 组和 reddit 话题? +社区是一个很难定义的词。Twitter Tech 话题是一个社区吗? freeCodeCamp 论坛呢?还是许多专门针对特定代码技能的 Discord 组和 reddit 话题? 我认为这些都是社区。如果经常有人活跃,互相帮助,我就认为这是一个社区。 @@ -1134,7 +1134,7 @@ OK--所以获得计算机科学学位可以帮助你找到一份高薪的入门 简短的回答是:这并不重要。学好一门编程语言后,学习第二语言就容易多了。 -有不同类型的编程语言,但今天大多数开发是使用 "高级脚本语言",如JavaScript和Python。这些语言放弃了C语言等 "低级编程语言 "的效率,得到的回报是:更容易使用。 +有不同类型的编程语言,但今天大多数开发是使用 "高级脚本语言",如 JavaScript 和 Python。这些语言放弃了 C 语言等 "低级编程语言 "的效率,得到的回报是:更容易使用。 今天的计算机比 1970 年代和 80 年代快数十亿倍。那时人们用 C 等语言编写大部分程序。现在计算机速度足以弥补脚本语言的相对低效。 @@ -1146,12 +1146,12 @@ Python 是用于科学计算(数据科学和机器学习)的强大语言。 > "任何可以用 JavaScript 编写的应用程序,最终都会用 JavaScript 编写。" – 阿特伍德定律 (Jeff Atwood, Stack Overflow 和 Discourse 发起人) -你的整个职业生涯都可以使用JavaScript,并不需要学习第二门语言。 -(话是这么说,但是你之后可能会学习Python,或者其他语言) +你的整个职业生涯都可以使用 JavaScript,并不需要学习第二门语言。 +(话是这么说,但是你之后可能会学习 Python,或者其他语言) 所以我建议从 JavaScript 开始。它不仅比 Java 和 C++ 等语言更易于使用,而且更易于学习。对于懂 JavaScript 的人来说,工作机会要多得多。 -![Find_Javascript_Jobs_with_great_pay_and_benefits_in_United_States___Indeed_com_--](https://www.freecodecamp.org/news/content/images/2023/01/Find_Javascript_Jobs_with_great_pay_and_benefits_in_United_States___Indeed_com_--.png)通过Indeed获得的职位搜索截图。 我输入了"javascript",地区是美国,搜索结果为68838个工作。 +![Find_Javascript_Jobs_with_great_pay_and_benefits_in_United_States___Indeed_com_--](https://www.freecodecamp.org/news/content/images/2023/01/Find_Javascript_Jobs_with_great_pay_and_benefits_in_United_States___Indeed_com_--.png)通过 Indeed 获得的职位搜索截图。 我输入了"javascript",地区是美国,搜索结果为 68838 个工作。 其他需要你专注的技能是 **HTML** 和 **CSS**。如果网页是躯体,那么 HTML 就是骨架,而 CSS 则是皮肤。( Javascript 将会是肌肉——使得网页的运转及交互成为可能。) @@ -1231,7 +1231,7 @@ Python 是用于科学计算(数据科学和机器学习)的强大语言。 现在,让我们换换思路,关注下如何打造另一驱动:你的人际关系。 -## 第2章:如何打造你的人际关系网络 +## 第 2 章:如何打造你的人际关系网络 > "独行者速,众行者远。" – 非洲谚语 @@ -1251,7 +1251,7 @@ Python 是用于科学计算(数据科学和机器学习)的强大语言。 我会向你展示如何建造一个强大的、可以在未来数十年内为你提供支持的人际关系网络。 -### 故事时间:一位老师如何在30岁时,在科技圈建立人际关系网络 +### 故事时间:一位老师如何在 30 岁时,在科技圈建立人际关系网络 *前情回顾: Quincy 通过阅读、观看免费在线课程和与开发者在当地的黑客空间共度时光,学习了一些编程知识。他刚刚完成了他的第一个项目,并作了首次技术演讲。* @@ -1817,7 +1817,7 @@ Mike 完全不是一个技术型的人。他的工作是产品经理。他虽然 我的队友笑了。我不确定他们是否觉得我是认真的。他们说,“那你不要迟到啊。” -但我是认真的。我在笔记本电脑上运行了一个应用程序的范例,在GPS中输入斯坦福,然后开始了我的任务。 +但我是认真的。我在笔记本电脑上运行了一个应用程序的范例,在 GPS 中输入斯坦福,然后开始了我的任务。 我曾经在俄克拉荷马州一所十分普通的州立大学学习。因此当我来到这所世界上最好的大学时,我真的感到力不从心。 @@ -1843,13 +1843,13 @@ Mike 完全不是一个技术型的人。他的工作是产品经理。他虽然 “我知道论文参考文献是什么。” -“好吧。来看看这个。”我将我的夹克放到一边,然后掏出我那200美元的笔记本电脑。当我发表了那尴尬的推销词时,他迁就了我一会儿。 +“好吧。来看看这个。”我将我的夹克放到一边,然后掏出我那 200 美元的笔记本电脑。当我发表了那尴尬的推销词时,他迁就了我一会儿。 我说:“看这里。我有一个浏览器插件。我去任何网站,仅需点击这个按钮,它就会为我创建一个引用。” -这个孩子挑起眉毛。“它能做MLA吗?” +这个孩子挑起眉毛。“它能做 MLA 吗?” -我强忍着兴奋说:“MLA, APA,甚至Chicago都可以。请看。”我点击了按钮,并且出现了三个引用-每个都有自己的复制按钮。 +我强忍着兴奋说:“MLA, APA,甚至 Chicago 都可以。请看。”我点击了按钮,并且出现了三个引用-每个都有自己的复制按钮。 那个孩子点了点头,似乎很感兴趣。因此我试图尽快完成交易。 @@ -1861,11 +1861,11 @@ Mike 完全不是一个技术型的人。他的工作是产品经理。他虽然 最后他说:“很酷,我加入了。” -“太棒了。这应该需要20美元。” +“太棒了。这应该需要 20 美元。” 那孩子退缩了。“什么?这么贵。” -这是风险资本补贴初创企业的时代,Uber和Lyft在争夺市场份额的过程中每次乘车都在亏损。因此,他的反应并不完全令人惊讶。 +这是风险资本补贴初创企业的时代,Uber 和 Lyft 在争夺市场份额的过程中每次乘车都在亏损。因此,他的反应并不完全令人惊讶。 我快速思考了一会儿。“那么,你身上带了多少现金?” @@ -1883,9 +1883,9 @@ Mike 完全不是一个技术型的人。他的工作是产品经理。他虽然 演讲结束时,我展示了我与那位斯坦福学生的合影,他们现在是我们的付费客户。当我举起那张现金时,观众爆发出热烈的掌声。 -总的来说,这是我一生中最令人振奋的经历之一。我们获得了第二名,并从赞助该活动的一家公司那里获得了一些API积分。 +总的来说,这是我一生中最令人振奋的经历之一。我们获得了第二名,并从赞助该活动的一家公司那里获得了一些 API 积分。 -在会后派对上,我仅仅吃了些披萨,这样我就有更多时间和所有人交流了。我在LinkedIn上联系了他们。并且在Twitter上关注了他。我和人们一起自拍,并贴上了这个活动的标签。 +在会后派对上,我仅仅吃了些披萨,这样我就有更多时间和所有人交流了。我在 LinkedIn 上联系了他们。并且在 Twitter 上关注了他。我和人们一起自拍,并贴上了这个活动的标签。 这是我编程旅程中的一个分水岭。我向那个房间里的人证明了我可以帮助设计、编码,甚至销售一款应用。更重要的是,我也向自己证明了这一点。 @@ -1916,11 +1916,11 @@ Mike 完全不是一个技术型的人。他的工作是产品经理。他虽然 我不知道我的祖父母对软件开发或黑客松了解多少。但他们说他们为我感到骄傲。 -现在他们去世了,但我经常回想起这段对话。我珍惜他们的鼓励。他们对一位30多岁的老师的信任,并且这位老师正疯狂地想成为一名开发人员。 +现在他们去世了,但我经常回想起这段对话。我珍惜他们的鼓励。他们对一位 30 多岁的老师的信任,并且这位老师正疯狂地想成为一名开发人员。 -从那以后,我一直在参加黑客松。在此过程中,我不断组建新的团队,学习新的工具。你永远不会忘记第一次让API工作的场景或者终于明白了Git命令是如何工作的。你永远不会忘记那些在你身边忙碌的人,他们试图让应用程序在演示过程中保持一致。 +从那以后,我一直在参加黑客松。在此过程中,我不断组建新的团队,学习新的工具。你永远不会忘记第一次让 API 工作的场景或者终于明白了 Git 命令是如何工作的。你永远不会忘记那些在你身边忙碌的人,他们试图让应用程序在演示过程中保持一致。 -TechCrunch Disrupt黑客松。DeveloperWeek黑客松。ProgrammableWeb黑客松。The $1 Million Prize Salesforce黑客松。这么多大型的黑客马拉松,学到了这么多东西。这是我成为开发者的摇床。 +TechCrunch Disrupt 黑客松。DeveloperWeek 黑客松。ProgrammableWeb 黑客松。The $1 Million Prize Salesforce 黑客松。这么多大型的黑客马拉松,学到了这么多东西。这是我成为开发者的摇床。 在这个过程中,我不仅成功地提升了自己的技能和人脉,而且我现在也赢得黑客松的名声。 @@ -1952,7 +1952,7 @@ TechCrunch Disrupt黑客松。DeveloperWeek黑客松。ProgrammableWeb黑客松 [image__2000-1338_](https://www.freecodecamp.org/news/content/images/2023/01/image__2000-1338_.png)谋杀绿脚趾(左)。他没有工作,没有技能,也没有精力。但他很冷静,冷静得不得了。 -开发人员很容易说:“哦,是的。我对JavaScript了如指掌。我可以为您构建任何类型的JavaScript应用程序,在您能想到的任何设备上运行。” +开发人员很容易说:“哦,是的。我对 JavaScript 了如指掌。我可以为您构建任何类型的 JavaScript 应用程序,在您能想到的任何设备上运行。” 或者可以说:“我总是在预期内提前发布代码。” @@ -1964,7 +1964,7 @@ TechCrunch Disrupt黑客松。DeveloperWeek黑客松。ProgrammableWeb黑客松 > > 那么你什么都擅长了。” -(这句话的真实出处不详。但我愿意想象这句话是20世纪20年代一个戴着大礼帽和单片眼镜的骗子说的。) +(这句话的真实出处不详。但我愿意想象这句话是 20 世纪 20 年代一个戴着大礼帽和单片眼镜的骗子说的。) 任何人都会撒谎。有些人确实如此。 @@ -1980,7 +1980,7 @@ TechCrunch Disrupt黑客松。DeveloperWeek黑客松。ProgrammableWeb黑客松 说谎的战利品总是在那里,闪闪发光。有些人愿意向这种诱惑屈服。 -雇主们知道这一点。他们知道你不能相信任何声称知道全栈JavaScript开发的人。对于那些能获得公司徽章、公司电子邮件地址和生产数据库的密钥的人,你必须保持谨慎。 +雇主们知道这一点。他们知道你不能相信任何声称知道全栈 JavaScript 开发的人。对于那些能获得公司徽章、公司电子邮件地址和生产数据库的密钥的人,你必须保持谨慎。 这就是为什么雇主使用行为面试问题——试图抓住那些更有可能不诚实的人。 @@ -2035,7 +2035,7 @@ If you're playing to win, I recommend doing your research ahead of time. Who are the event sponsors? Usually it will be Business-to-Developer type companies, with APIs, database tools, or various Software-as-a-Service offerings. -通常情况下,黑客马拉松会提供针对赞助商的奖品。"对[赞助商]API的最佳使用"。将你的时间集中在将特定的赞助商工具纳入你的项目中,而不是试图赢得大奖,这可能更容易。你仍然可以在你的LinkedIn或你的履历表上把这些作为胜利。胜利就是胜利。 +通常情况下,黑客马拉松会提供针对赞助商的奖品。"对[赞助商]API 的最佳使用"。将你的时间集中在将特定的赞助商工具纳入你的项目中,而不是试图赢得大奖,这可能更容易。你仍然可以在你的 LinkedIn 或你的履历表上把这些作为胜利。胜利就是胜利。 有时,黑客马拉松是如此高调--或者奖金如此可观--以至于尝试直接赢得比赛是有意义的。 @@ -2051,13 +2051,13 @@ Who are the event sponsors? Usually it will be Business-to-Developer type compan ### 如何为开源项目做贡献 -为开放源代码做贡献是建立自己声誉的最直接方式之一。大多数雇主都会看你的GitHub档案,其中会突出显示你的Git提交历史。 +为开放源代码做贡献是建立自己声誉的最直接方式之一。大多数雇主都会看你的 GitHub 档案,其中会突出显示你的 Git 提交历史。 ![raisedadead__Mrugesh_Mohapatra__--](https://www.freecodecamp.org/news/content/images/2023/01/raisedadead__Mrugesh_Mohapatra__--.png) 以上是 Mrugesh Mohapatra 的 GitHub 简介,他为 freeCodeCamp.org 做了大量的平台开发和 DevOps 工作。注意他的活动栏是多么的绿色。仅在过去一年就有 2208 个贡献。 -许多开源项目的维护者,如Linux基金会、Mozilla(火狐),当然还有 freeCodeCamp 自己,对代码质量有很高的标准。 +许多开源项目的维护者,如 Linux 基金会、Mozilla(火狐),当然还有 freeCodeCamp 自己,对代码质量有很高的标准。 你可以通过阅读开放的 GitHub 问题来寻找已知的错误或功能请求。然后你可以进行代码修改,并打开一个拉动请求。如果维护者合并了你的拉动请求,这将是你帽子上的一个重要标志。 @@ -2077,7 +2077,7 @@ Who are the event sponsors? Usually it will be Business-to-Developer type compan 你可以广泛地将这些称为 "内容"。我不太喜欢这个词,因为它使这些作品感觉是一次性的。但人们就是这样称呼它。 -软件开发是一个令人难以置信的广泛领域,有许多不同的主题,你可以接近。有开发者生活方式的博客,编码面试准备教程,Twitch上的编码直播流,以及开发者访谈播客。 +软件开发是一个令人难以置信的广泛领域,有许多不同的主题,你可以接近。有开发者生活方式的博客,编码面试准备教程,Twitch 上的编码直播流,以及开发者访谈播客。 可能有整个类别的开发者内容,我们甚至还没有想到,这将在未来十年内打破。 @@ -2087,9 +2087,9 @@ Who are the event sponsors? Usually it will be Business-to-Developer type compan 例如,有一些开发者专门从事特定技术栈的教程。 -我的朋友安德鲁-布朗是一位来自多伦多的前CTO,他已经通过了所有主要的DevOps考试。他创建了[为所有AWS、Azure和谷歌云认证做准备的免费课程](https://www.freecodecamp.org/news/azure-developer-certification-az-204-pass-the-exam-with-this-free-13-5-hour-course/),还经营着一个考试准备服务。 +我的朋友安德鲁-布朗是一位来自多伦多的前 CTO,他已经通过了所有主要的 DevOps 考试。他创建了[为所有 AWS、Azure 和谷歌云认证做准备的免费课程](https://www.freecodecamp.org/news/azure-developer-certification-az-204-pass-the-exam-with-this-free-13-5-hour-course/),还经营着一个考试准备服务。 -世界上有超过2000万的软件开发人员。这是有可能消费你的内容的很多人,他们会知道你是谁。 +世界上有超过 2000 万的软件开发人员。这是有可能消费你的内容的很多人,他们会知道你是谁。 ### 如何在大公司工作中提升地位 @@ -2099,7 +2099,7 @@ Who are the event sponsors? Usually it will be Business-to-Developer type compan 雇主看候选人以前在哪里工作有一些实际原因。它减少了招聘失败的风险。 -你可以通过努力提高自己的声望等级来建立自己的声誉。你可以从一个本地雇主到财富500强公司,并最终到大型科技巨头之一。 +你可以通过努力提高自己的声望等级来建立自己的声誉。你可以从一个本地雇主到财富 500 强公司,并最终到大型科技巨头之一。 当然,在巨头公司工作并不适合所有人。我将在第四章中更多地谈论这个问题。但要知道,这是你建立自己声誉的一个选择。 @@ -2121,7 +2121,7 @@ Who are the event sponsors? Usually it will be Business-to-Developer type compan 如果你把人们送到一个网站,他们可能不会完全掌握他们正在看的东西,以及为什么它是如此特别。 -这就是为什么我建议你使用屏幕捕捉工具来录制2分钟的视频演示。 +这就是为什么我建议你使用屏幕捕捉工具来录制 2 分钟的视频演示。 两分钟的时间应该足够展示项目如何运作。一旦你完成了这些,你就可以解释一些实施细节,以及你所做的设计决定。 @@ -2129,17 +2129,17 @@ Who are the event sponsors? Usually it will be Business-to-Developer type compan 一旦你用你的应用程序运行的引人注目的演示吸引了人们,你就可以解释你想要的所有细节。你的听众现在会有更多的背景,也会更感兴趣。 -两分钟也是一个神奇的长度,因为你可以把视频上传到推特上,当人们滚动经过它时,它就会在推特上自动播放。自动播放的视频在Twitter上被观看的可能性要高得多。它们消除了必须点击播放按钮的摩擦,或导航到另一个网站。 +两分钟也是一个神奇的长度,因为你可以把视频上传到推特上,当人们滚动经过它时,它就会在推特上自动播放。自动播放的视频在 Twitter 上被观看的可能性要高得多。它们消除了必须点击播放按钮的摩擦,或导航到另一个网站。 <iframe id="twitter-widget-0" scrolling="no" frameborder="0" allowtransparency="true" allowfullscreen="true" class="" title="Twitter Tweet" src="https://platform.twitter.com/embed/Tweet.html?creatorScreenName=ossia&dnt=false&embedId=twitter-widget-0&features=eyJ0ZndfdGltZWxpbmVfbGlzdCI6eyJidWNrZXQiOltdLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X2ZvbGxvd2VyX2NvdW50X3N1bnNldCI6eyJidWNrZXQiOnRydWUsInZlcnNpb24iOm51bGx9LCJ0ZndfaG9yaXpvbl90aW1lbGluZV8xMjAzNCI6eyJidWNrZXQiOiJ0cmVhdG1lbnQiLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X3R3ZWV0X2VkaXRfYmFja2VuZCI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfcmVmc3JjX3Nlc3Npb24iOnsiYnVja2V0Ijoib24iLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X3Nob3dfYnVzaW5lc3NfdmVyaWZpZWRfYmFkZ2UiOnsiYnVja2V0Ijoib24iLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X2NoaW5fcGlsbHNfMTQ3NDEiOnsiYnVja2V0IjoiY29sb3JfaWNvbnMiLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X3R3ZWV0X3Jlc3VsdF9taWdyYXRpb25fMTM5NzkiOnsiYnVja2V0IjoidHdlZXRfcmVzdWx0IiwidmVyc2lvbiI6bnVsbH0sInRmd19taXhlZF9tZWRpYV8xNTg5NyI6eyJidWNrZXQiOiJ0cmVhdG1lbnQiLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X3NlbnNpdGl2ZV9tZWRpYV9pbnRlcnN0aXRpYWxfMTM5NjMiOnsiYnVja2V0IjoiaW50ZXJzdGl0aWFsIiwidmVyc2lvbiI6bnVsbH0sInRmd19leHBlcmltZW50c19jb29raWVfZXhwaXJhdGlvbiI6eyJidWNrZXQiOjEyMDk2MDAsInZlcnNpb24iOm51bGx9LCJ0ZndfZHVwbGljYXRlX3NjcmliZXNfdG9fc2V0dGluZ3MiOnsiYnVja2V0Ijoib24iLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X3ZpZGVvX2hsc19keW5hbWljX21hbmlmZXN0c18xNTA4MiI6eyJidWNrZXQiOiJ0cnVlX2JpdHJhdGUiLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X3Nob3dfYmx1ZV92ZXJpZmllZF9iYWRnZSI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfbGVnYWN5X3RpbWVsaW5lX3N1bnNldCI6eyJidWNrZXQiOnRydWUsInZlcnNpb24iOm51bGx9LCJ0Zndfc2hvd19nb3ZfdmVyaWZpZWRfYmFkZ2UiOnsiYnVja2V0Ijoib24iLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X3Nob3dfYnVzaW5lc3NfYWZmaWxpYXRlX2JhZGdlIjp7ImJ1Y2tldCI6Im9uIiwidmVyc2lvbiI6bnVsbH0sInRmd190d2VldF9lZGl0X2Zyb250ZW5kIjp7ImJ1Y2tldCI6Im9uIiwidmVyc2lvbiI6bnVsbH19&frame=false&hideCard=false&hideThread=false&id=1603405016525688834&lang=en&origin=https%3A%2F%2Fwww.freecodecamp.org%2Fnews%2Flearn-to-code-book%2F&sessionId=5fb6a17a050a6f4d155b53178392f91afe5602a7&siteScreenName=freecodecamp&theme=light&widgetsVersion=2b959255e8896%3A1673658205745&width=550px" data-tweet-id="1603405016525688834" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 22px; vertical-align: middle; position: static; visibility: visible; width: 550px; height: 708px; display: block; flex-grow: 1;"></iframe> -一个twitter克隆项目的视频演示,由freeCodeCamp的校友建立。这个视频没有画外音解释,只有2分钟的UI演示。但它仍然非常好。 +一个 twitter 克隆项目的视频演示,由 freeCodeCamp 的校友建立。这个视频没有画外音解释,只有 2 分钟的 UI 演示。但它仍然非常好。 -你可以把这些项目演示视频放到YouTube、Twitter、你的GitHub档案,当然还有你自己的作品集网站上。 +你可以把这些项目演示视频放到 YouTube、Twitter、你的 GitHub 档案,当然还有你自己的作品集网站上。 -为了捕捉这个视频,我建议使用QuickTime,它是MacOS内置的。如果你是在Windows上,你可以使用Game Recorder,它在Windows 10中是免费的。 +为了捕捉这个视频,我建议使用 QuickTime,它是 MacOS 内置的。如果你是在 Windows 上,你可以使用 Game Recorder,它在 Windows 10 中是免费的。 -如果你想要一个更强大的工具,OBS是免费和开源的。它比较难学,但可以无限地定制。 +如果你想要一个更强大的工具,OBS 是免费和开源的。它比较难学,但可以无限地定制。 至于录音技巧:保持你的字体大小尽可能大,并使用外部麦克风。任何你能找到的麦克风--即使是廉价的耳机--都会比对着你的笔记本电脑的内置麦克风说话好。 @@ -2191,7 +2191,7 @@ Who are the event sponsors? Usually it will be Business-to-Developer type compan 你应该假设,就所有的意图和目的而言,猫已经离开了袋子。没有办法让猫回到袋子里。无论你刚才说了什么:那都是在你的永久记录上。 -你可以删除这句话。你可以删除你的账户。你甚至可以尝试把它从谷歌搜索结果中删除。但是有人可能已经在Wayback Machine上备份了它。而当这些数据库中的某一个在几年后不可避免地被黑客攻击时,这些数据可能仍然在那里的某个地方,准备重新出现。 +你可以删除这句话。你可以删除你的账户。你甚至可以尝试把它从谷歌搜索结果中删除。但是有人可能已经在 Wayback Machine 上备份了它。而当这些数据库中的某一个在几年后不可避免地被黑客攻击时,这些数据可能仍然在那里的某个地方,准备重新出现。 现在是一个可怕的时代,要做一个大嘴巴。所以不要这样做。在你说话之前先想想。 @@ -2233,7 +2233,7 @@ Who are the event sponsors? Usually it will be Business-to-Developer type compan ![1690px-Pieter_Bruegel_de_Oude_-_De_val_van_Icarus](https://www.freecodecamp.org/news/content/images/2023/01/1690px-Pieter_Bruegel_de_Oude_-_De_val_van_Icarus.jpg) -Pieter Bruegel the Elder的《伊卡洛斯坠落的风景》,约1560年。伊卡洛斯本可以是一个竞争者。他本可以成为一个人物。但相反,他只是双腿消失在海里。农民和牧羊人不屑于从他们的工作中抬起头来看一看他的渺小。 +Pieter Bruegel the Elder 的《伊卡洛斯坠落的风景》,约 1560 年。伊卡洛斯本可以是一个竞争者。他本可以成为一个人物。但相反,他只是双腿消失在海里。农民和牧羊人不屑于从他们的工作中抬起头来看一看他的渺小。 ### 在声誉受损前,控制住瘾 @@ -2243,11 +2243,11 @@ Pieter Bruegel the Elder的《伊卡洛斯坠落的风景》,约1560年。伊 我不是医生。我也不打算给你讲 "毒品是坏事"。但我要说的是:你可能听说过硅谷的风潮,在那里,开发人员滥用药物,认为他们可以以某种方式提高他们的编码或解决问题的能力。 -有一段时间,有一个 "微剂量LSD "的趋势。有一个药物安非他命的趋势。 +有一段时间,有一个 "微剂量 LSD "的趋势。有一个药物安非他命的趋势。 我的直觉反应是:这些东西可能给你带来的任何优势都可能是不可持续的,而且在一个较长的时期内是净负值。 -不要感受到服用精神活性药物的同伴压力。不要感受到在欢乐时光喝酒的同伴压力。(自从7年前我的女儿出生以来,我没有喝过那么多的啤酒,我不觉得我错过了什么)。 +不要感受到服用精神活性药物的同伴压力。不要感受到在欢乐时光喝酒的同伴压力。(自从 7 年前我的女儿出生以来,我没有喝过那么多的啤酒,我不觉得我错过了什么)。 如果你正在从毒瘾中恢复,请注意学习代码和获得开发人员的工作将是一个紧张的过程。给自己定个节奏,这样你就不会有复发的风险。 @@ -2307,7 +2307,7 @@ Never lose sight of how much you have. And how much you have to lose. 我确信我有很多其他的思维方式和行为方式都不是最理想的。 在讲述者技巧的帮助下,我希望将来能够发现它们并加以完善,以免给人留下错误的印象。 -### 你的声誉将成为你的传奇。 +### 你的声誉将成为你的传奇 想想看在你的故事的结尾,你想成为谁。 你希望人们如何看待你在地球上的时间。 然后从那里向后推进。 @@ -2489,7 +2489,7 @@ Quincy 老头还是想把事情做好。 他为自己所取得的成就感到自 - 筛选简历——其中许多都是盲目申请尽可能多的工作的求职者的低成本努力 - 开始面试过程,这可能涉及让候选人飞到当前城市并住在酒店 - 涉及许多其他团队成员的多轮面试。 对于一些雇主来说,这是一个为期数天的事情 -- 选择最终候选人,并谈判报价...... +- 选择最终候选人,并谈判报价…… - 许多候选人无论如何都不会接受 - 签订合同和帮员工入职 - 给与让他们访问敏感的内部系统的权限 @@ -2544,7 +2544,7 @@ Quincy 老头还是想把事情做好。 他为自己所取得的成就感到自 这就是我写这本书的原因之一。我不希望你们都在雇主的“前门”排队。 -### 如果你建立了自己的技能、人脉和声誉,你就可以绕过很多工作申请流程。 +### 如果你建立了自己的技能、人脉和声誉,你就可以绕过很多工作申请流程 在本书中,我一直在教你一些增加“走运”获得工作机会可能性的技巧。 @@ -2830,7 +2830,7 @@ Quincy 老头还是想把事情做好。 他为自己所取得的成就感到自 我浏览代码库的速度越来越快,代码出错时,我读取堆栈轨迹的速度也越来越快,也能更快地发布新功能了。 -每次老板叫我去办公室时,我都在想:“完了,这次没跑了,我今天就要被炒鱿鱼了。”但他只是给我更多bug来修,更多要开发的功能。谢天谢地。 +每次老板叫我去办公室时,我都在想:“完了,这次没跑了,我今天就要被炒鱿鱼了。”但他只是给我更多 bug 来修,更多要开发的功能。谢天谢地。 最离谱的就在这了:我怕被解雇怕得要死,他却完全不知道怎么回事。 @@ -2838,19 +2838,19 @@ Quincy 老头还是想把事情做好。 他为自己所取得的成就感到自 有一天我坐在 Nick 旁边,看他好像很累,我去厨房给他拿了瓶汽水。 -我回来后,他打开拉环喝了一口,靠在椅子上,盯着满是代码的显示器。“这个bug啊,哥们我花了三周时间来修,做梦都在调试。” +我回来后,他打开拉环喝了一口,靠在椅子上,盯着满是代码的显示器。“这个 bug 啊,哥们我花了三周时间来修,做梦都在调试。” “三周时间修一个 bug ?”我问道。我从未听说过这样的事。 -“有些bug更难修,这个就坏得很。” +“有些 bug 更难修,这个就坏得很。” 这句话给了我当头一棒。我总是把我的任务按份处理,比如修一个 bug 要半天时间,超过了半天就肯定是我做的不对。 -但是,有着加利福尼亚大学的计算机科学学位、多年同一代码库工作经验的尼克却在一个bug上困住了三个星期。 +但是,有着加利福尼亚大学的计算机科学学位、多年同一代码库工作经验的尼克却在一个 bug 上困住了三个星期。 -也许是我对自己太苛刻了,可能我修的一些bug不是“半天的bug”,而是“两三天的 bug ”。是,我缺乏经验,工作也慢。但就算这样,我是不是给自己设立了不切实际的标准呢? +也许是我对自己太苛刻了,可能我修的一些 bug 不是“半天的 bug”,而是“两三天的 bug ”。是,我缺乏经验,工作也慢。但就算这样,我是不是给自己设立了不切实际的标准呢? -在开发新功能时,有的功能开发要5天,甚至是两周时间而修bug的时候没有这样的时间规划,但其实bug也会遇到同样的问题。 +在开发新功能时,有的功能开发要 5 天,甚至是两周时间而修 bug 的时候没有这样的时间规划,但其实 bug 也会遇到同样的问题。 回家后,我深入了解了下“冒名顶替综合症”,也缓解了不少焦虑。 @@ -2892,21 +2892,21 @@ Quincy 老头还是想把事情做好。 他为自己所取得的成就感到自 我感到气愤,什么也没说,但心里想:“这是我的工作,脚本抢了我的活。” -当然,现在每每回想起自己的反应我都感到好笑。我想象着自己已经40多岁了,每周还要放下几次别的事情,来做代码保姆。 +当然,现在每每回想起自己的反应我都感到好笑。我想象着自己已经 40 多岁了,每周还要放下几次别的事情,来做代码保姆。 工作中,如果一项任务可以自动化,可以分解成计算机可以可靠完成的小步骤,那么就应该自动化。 你的时间应该留给更有趣的工作。 -![is_it_worth_the_time_2x-1](https://www.freecodecamp.org/news/content/images/2023/01/is_it_worth_the_time_2x-1.png)这张XKCD的图表,帮你弄清一项任务是否值得投入时间来实现自动化。 +![is_it_worth_the_time_2x-1](https://www.freecodecamp.org/news/content/images/2023/01/is_it_worth_the_time_2x-1.png)这张 XKCD 的图表,帮你弄清一项任务是否值得投入时间来实现自动化。 ### 多听老人言 我向团队里每个人都学到了很多。我从 Mike 那里学到了产品设计概念。他带我在沙滩上跑步,教我如何用前脚掌跑步,前脚掌先于脚跟着地,这样对关节更为友好。 -从Nick那里我学到了快速软件工程(agile software engineering)的概念,他帮我从图书馆挑选了软件开发书籍,甚至还邀请我去他家参加乔迁派对,那次我见到了他的几个孩子。 +从 Nick 那里我学到了快速软件工程(agile software engineering)的概念,他帮我从图书馆挑选了软件开发书籍,甚至还邀请我去他家参加乔迁派对,那次我见到了他的几个孩子。 -在这家公司工作了大概一年时间,我觉得到了创业的时候了,我想要做与在线学习相关的内容。我找到CTO坐下谈话,把要离开的消息告诉了他。 +在这家公司工作了大概一年时间,我觉得到了创业的时候了,我想要做与在线学习相关的内容。我找到 CTO 坐下谈话,把要离开的消息告诉了他。 我说:“很感激大家雇用了我,显然我是全公司最弱的开发者。” @@ -2916,7 +2916,7 @@ Quincy 老头还是想把事情做好。 他为自己所取得的成就感到自 然后他说,“但这很聪明,你是个聪明人。你**总是想成为乐队里最差的乐手**,希望身边都是比你优秀的人,这样你才能成长。” -两周后,我检查了当天的代码更改,上交了我的开启支持问题,把Mac电脑重置为出厂设置,交给了主管。 +两周后,我检查了当天的代码更改,上交了我的开启支持问题,把 Mac 电脑重置为出厂设置,交给了主管。 我和队友们握了握手,走出门,走入加州的傍晚。 @@ -2930,7 +2930,7 @@ Quincy 老头还是想把事情做好。 他为自己所取得的成就感到自 ### 开发者第一年的经验教训 -作为专业开发者的第一年,我做了很多正确的选择,我给自己打个B-。 +作为专业开发者的第一年,我做了很多正确的选择,我给自己打个 B-。 但如果有机会重新来过,有些选择会不同。 @@ -2942,7 +2942,7 @@ Quincy 老头还是想把事情做好。 他为自己所取得的成就感到自 人到中年,头衔里还有个“初级”总觉得有点尴尬。但只要耐心、努力,就能克服这一点。 -每天我都面临的问题是:我有十年的专业经验了。我不是个入门员工。是,我是个开发新手,但我在教学与管理方面都经验丰富。(上一份教学工作中,我管理了30名员工。) +每天我都面临的问题是:我有十年的专业经验了。我不是个入门员工。是,我是个开发新手,但我在教学与管理方面都经验丰富。(上一份教学工作中,我管理了 30 名员工。) 但尽管过去的工作经验丰富,我仍然是个入门级的开发者,我还是个新手。 @@ -3002,19 +3002,19 @@ Quincy 老头还是想把事情做好。 他为自己所取得的成就感到自 前文中我提到了产品经理 Mike,他也负责创业活动。他“不懂技术”,在编码方面的知识有限,但我敢说我从他身上学到的东西和其他人一样多。 -你可能会和其他部门的人一起工作——设计师、产品经理、项目经理、IT人员、QA人员、营销人员,甚至是财务和会计人员,从这些人身上也可以学到很多东西。 +你可能会和其他部门的人一起工作——设计师、产品经理、项目经理、IT 人员、QA 人员、营销人员,甚至是财务和会计人员,从这些人身上也可以学到很多东西。 确实,你应该与团队其他开发者之间建立牢固的联系。但请保持好奇,在餐厅或公司活动中与不同的人一起活动,你永远不知道谁会是下一个帮你学习技能、建立人脉或声誉的人。 ### 不要太舒适,也不要太早专攻 -我经常给第一次学习代码的人这样建议:“从一般的编码技能(JavaScript、SQL、Linux等等)开始学起,然后专门从事这项工作。” +我经常给第一次学习代码的人这样建议:“从一般的编码技能(JavaScript、SQL、Linux 等等)开始学起,然后专门从事这项工作。” 我的想法是一旦学会了最常用的工具,就可以学习那些不太常用的工具。 比如说,学会了 PostgreSQL,很容易就能学会 MySQL。学会了 Node.js,很容易就能学会 uby on Rails 或 Java Spring Boot。 -但有些人在工作中太早专精了,老板可能会要求他们“专管”某个API或特性。如果在这方面做得好,老板可能就会继续给类似的项目。 +但有些人在工作中太早专精了,老板可能会要求他们“专管”某个 API 或特性。如果在这方面做得好,老板可能就会继续给类似的项目。 你只负责管理自己,而老板在管理很多人,无法细致地了解你能力和兴趣,可能会把你看成“某专业人”,只给你安排相关的任务。 diff --git a/chinese/articles/learn-web-development-free-full-stack-developer-courses-for-beginners.md b/chinese/articles/learn-web-development-free-full-stack-developer-courses-for-beginners.md index 08a1f2f4a..73a10df29 100644 --- a/chinese/articles/learn-web-development-free-full-stack-developer-courses-for-beginners.md +++ b/chinese/articles/learn-web-development-free-full-stack-developer-courses-for-beginners.md @@ -5,9 +5,9 @@ ![Learn Web Development – Free Full Stack Developer Courses for Beginners](https://www.freecodecamp.org/news/content/images/size/w2000/2022/01/ferenc-almasi-L8KQIPCODV8-unsplash.jpg) -术语[全栈](https://www.freecodecamp.org/news/what-is-a-full-stack-developer-back-end-front-end-full-stack-engineer/)开发者指的是同时处理Web应用的前端和后端组件。 +术语[全栈](https://www.freecodecamp.org/news/what-is-a-full-stack-developer-back-end-front-end-full-stack-engineer/)开发者指的是同时处理 Web 应用的前端和后端组件。 -前台是用户与之交互的东西,而后台是Web应用的逻辑。 +前台是用户与之交互的东西,而后台是 Web 应用的逻辑。 在这篇文章中,我将介绍一些可以帮助你成为全栈开发者的资源: @@ -15,7 +15,7 @@ - [CS50's Web Programming with Python and JavaScript](https://www.edx.org/course/cs50s-web-programming-with-python-and-javascript) - [The Odin Project](https://www.theodinproject.com/) -我还将提供更多的YouTube课程链接,你可以通过建立更多的项目继续练习你的技能。 +我还将提供更多的 YouTube 课程链接,你可以通过建立更多的项目继续练习你的技能。 - [freeCodeCamp](#freecodecamp) - [The Odin Project](#the-odin-project) @@ -26,31 +26,31 @@ ![Screen-Shot-2022-01-31-at-1.02.41-AM](https://www.freecodecamp.org/news/content/images/2022/01/Screen-Shot-2022-01-31-at-1.02.41-AM.png) -[freeCodeCamp](https://www.freecodecamp.org/learn/)是一个免费的在线互动学习平台,你可以在这里学习网络开发,并顺便获得认证。每个课程都有一系列的挑战,你将学习材料,然后完成5个认证项目。 +[freeCodeCamp](https://www.freecodecamp.org/learn/)是一个免费的在线互动学习平台,你可以在这里学习网络开发,并顺便获得认证。每个课程都有一系列的挑战,你将学习材料,然后完成 5 个认证项目。 -前四门课程涵盖前端技术,包括HTML、CSS、Vanilla JavaScript、React和D3。后端开发、关系数据库课程和质量保证认证涵盖Node、Express、SQL、用Chai测试、MongoDB等。 +前四门课程涵盖前端技术,包括 HTML、CSS、Vanilla JavaScript、React 和 D3。后端开发、关系数据库课程和质量保证认证涵盖 Node、Express、SQL、用 Chai 测试、MongoDB 等。 以下是你将建立的一些项目的列表。 - 产品登陆页 - 随机报价机 -- 25+5时钟 +- 25+5 时钟 - 世界杯数据库 - 数独解题器 -完成这些认证后,你将知道如何构建全栈Web应用程序。从那里你可以为你的portfolio建立自己的项目,并开始申请初级工作。 +完成这些认证后,你将知道如何构建全栈 Web 应用程序。从那里你可以为你的 portfolio 建立自己的项目,并开始申请初级工作。 -其余的认证包括Python和机器学习。这些都是比较中高级的认证,并假定你已经完成了之前的JavaScript认证。 +其余的认证包括 Python 和机器学习。这些都是比较中高级的认证,并假定你已经完成了之前的 JavaScript 认证。 -如果你在课程上需要帮助,请联系[freeCodeCamp论坛](https://forum.freecodecamp.org/),世界各地的开发者可以在代码上帮助你。 +如果你在课程上需要帮助,请联系[freeCodeCamp 论坛](https://forum.freecodecamp.org/),世界各地的开发者可以在代码上帮助你。 ## The Odin Project ![Screen-Shot-2022-01-31-at-1.22.36-AM](https://www.freecodecamp.org/news/content/images/2022/01/Screen-Shot-2022-01-31-at-1.22.36-AM.png) -奥丁项目是一个免费的基于项目的在线平台,在这里你可以学习[全栈式JavaScript](https://www.theodinproject.com/paths/full-stack-javascript?)或[全栈式Ruby on Rails](https://www.theodinproject.com/paths/full-stack-ruby-on-rails?)。 +奥丁项目是一个免费的基于项目的在线平台,在这里你可以学习[全栈式 JavaScript](https://www.theodinproject.com/paths/full-stack-javascript?)或[全栈式 Ruby on Rails](https://www.theodinproject.com/paths/full-stack-ruby-on-rails?)。 -你将首先通过[基础课程](https://www.theodinproject.com/paths/foundations/courses/foundations),教你HTML、CSS、JavaScript基础知识、Git、命令行以及如何使用文本编辑器。这些课程有建议的读物、作业和项目,以便在学习过程中完成。 +你将首先通过[基础课程](https://www.theodinproject.com/paths/foundations/courses/foundations),教你 HTML、CSS、JavaScript 基础知识、Git、命令行以及如何使用文本编辑器。这些课程有建议的读物、作业和项目,以便在学习过程中完成。 以下是你将在[基础课程](https://www.theodinproject.com/paths/foundations/courses/foundations)中建立的一些项目的清单。 @@ -58,19 +58,19 @@ - Etch-a-Sketch - Landing page -从那里你可以选择JavaScript或Ruby on Rails课程。  如果你需要帮助决定选择哪一个,请阅读[The Odin Project的这一指南](https://www.theodinproject.com/paths/foundations/courses/foundations/lessons/choose-your-path-forward)。 +从那里你可以选择 JavaScript 或 Ruby on Rails 课程。  如果你需要帮助决定选择哪一个,请阅读[The Odin Project 的这一指南](https://www.theodinproject.com/paths/foundations/courses/foundations/lessons/choose-your-path-forward)。 -[Ruby on Rails课程](https://www.theodinproject.com/paths/full-stack-ruby-on-rails?)包括Ruby编程、中高级HTML和CSS、Ruby on Rails框架等。 +[Ruby on Rails 课程](https://www.theodinproject.com/paths/full-stack-ruby-on-rails?)包括 Ruby 编程、中高级 HTML 和 CSS、Ruby on Rails 框架等。 -以下是你将在[Ruby on Rails课程](https://www.theodinproject.com/paths/full-stack-ruby-on-rails?)中建立的一些项目。 +以下是你将在[Ruby on Rails 课程](https://www.theodinproject.com/paths/full-stack-ruby-on-rails?)中建立的一些项目。 - Tic Tac Toe - SQL Zoo - Personal Portfolio -[JavaScript课程](https://www.theodinproject.com/paths/full-stack-javascript?)涵盖了中高级HTML和CSS、JavaScript、MongoDB、Node、Express等内容。他们还包括一份关于[如何被雇用](https://www.theodinproject.com/paths/full-stack-ruby-on-rails/courses/getting-hired)的有用指南,以便找到你的第一份工作。 +[JavaScript 课程](https://www.theodinproject.com/paths/full-stack-javascript?)涵盖了中高级 HTML 和 CSS、JavaScript、MongoDB、Node、Express 等内容。他们还包括一份关于[如何被雇用](https://www.theodinproject.com/paths/full-stack-ruby-on-rails/courses/getting-hired)的有用指南,以便找到你的第一份工作。 -以下是你将在[JavaScript课程](https://www.theodinproject.com/paths/full-stack-javascript?)中建立的一些项目: +以下是你将在[JavaScript 课程](https://www.theodinproject.com/paths/full-stack-javascript?)中建立的一些项目: - restaurant page - weather app @@ -78,26 +78,26 @@ 如果你在课程上需要帮助,请联系[The Odin Project discord channel](https://discord.com/invite/fbFCkYabZB). -## CS50 Python和JavaScript Web编程 +## CS50 Python 和 JavaScript Web 编程 -这个[CS50 Web编程课程](https://www.edx.org/course/cs50s-web-programming-with-python-and-javascript)将教你HTML、CSS、JavaScript、Git、Python、Django、SQL等。你首先需要学习[CS50的计算机科学入门](https://www.edx.org/course/introduction-computer-science-harvardx-cs50x)。 +这个[CS50 Web 编程课程](https://www.edx.org/course/cs50s-web-programming-with-python-and-javascript)将教你 HTML、CSS、JavaScript、Git、Python、Django、SQL 等。你首先需要学习[CS50 的计算机科学入门](https://www.edx.org/course/introduction-computer-science-harvardx-cs50x)。 -计算机科学导论将通过一系列问题集和一个最终项目教给你编程的基础知识。然后你可以进入Web编程课程,进一步发展你的技能。 +计算机科学导论将通过一系列问题集和一个最终项目教给你编程的基础知识。然后你可以进入 Web 编程课程,进一步发展你的技能。 -该课程由David Malan和Brian Yu教授,他们是哈佛大学的顶级讲师。在你完成这些课程后,你将在全栈Web开发方面有一个坚实的起步基础。 +该课程由 David Malan 和 Brian Yu 教授,他们是哈佛大学的顶级讲师。在你完成这些课程后,你将在全栈 Web 开发方面有一个坚实的起步基础。 这两门课程都可以在[edX](https://www.edx.org/)上找到,可以免费审核。 -如果你在CS50课程中需要帮助,请通过他们的任何一个[社交媒体平台](https://cs50.harvard.edu/x/2022/communities/)联系。 +如果你在 CS50 课程中需要帮助,请通过他们的任何一个[社交媒体平台](https://cs50.harvard.edu/x/2022/communities/)联系。 -## YouTube上的全栈项目教程 +## YouTube 上的全栈项目教程 -在你建立了全栈Web开发的基本基础后,你可以研究这些额外的资源,以建立更多的项目来加强你的技能。 +在你建立了全栈 Web 开发的基本基础后,你可以研究这些额外的资源,以建立更多的项目来加强你的技能。 -- [Flutter & Firebase课程--建立一个全栈式的仿Instagram](https://www.youtube.com/watch?v=mEPm9w5QlJM) +- [Flutter & Firebase 课程--建立一个全栈式的仿 Instagram](https://www.youtube.com/watch?v=mEPm9w5QlJM) - [云计算中的全栈网络开发课程 - Svelte, Postgres, Vercel, Gitpod](https://www.youtube.com/watch?v=OUzaUJ3gEug) -- [全栈MERN项目 - 构建和部署一个应用程序 | React + Redux, Node, Express, MongoDB\[Part 1/2\]](https://www.youtube.com/watch?v=ngc9gnGgUdA) -- [全栈MERN项目 - 构建和部署一个应用程序 | React + Redux, Node, Express, MongoDB\[Part 2/2\]](https://www.youtube.com/watch?v=aibtHnbeuio) -- [使用Django和Vue的电子商务网站教程(Django Rest 框架)](https://www.youtube.com/watch?v=Yg5zkd9nm6w) -- [Python微服务网络应用(使用React、Django、Flask)--完整课程](https://www.youtube.com/watch?v=0iB5IPoTDts) +- [全栈 MERN 项目 - 构建和部署一个应用程序 | React + Redux, Node, Express, MongoDB[Part 1/2]](https://www.youtube.com/watch?v=ngc9gnGgUdA) +- [全栈 MERN 项目 - 构建和部署一个应用程序 | React + Redux, Node, Express, MongoDB[Part 2/2]](https://www.youtube.com/watch?v=aibtHnbeuio) +- [使用 Django 和 Vue 的电子商务网站教程(Django Rest 框架)](https://www.youtube.com/watch?v=Yg5zkd9nm6w) +- [Python 微服务网络应用(使用 React、Django、Flask)--完整课程](https://www.youtube.com/watch?v=0iB5IPoTDts) - [PERN Stack 课程 - 仿 Yelp(Postgres, Express, React, Node.js)](https://www.youtube.com/watch?v=J01rYl9T3BU) \ No newline at end of file diff --git a/chinese/articles/learning-go-from-zero-to-hero-d2a3223b3d86.md b/chinese/articles/learning-go-from-zero-to-hero-d2a3223b3d86.md index 8a5779574..db099088b 100644 --- a/chinese/articles/learning-go-from-zero-to-hero-d2a3223b3d86.md +++ b/chinese/articles/learning-go-from-zero-to-hero-d2a3223b3d86.md @@ -1,4 +1,4 @@ -> - 原文地址:[Learning Go — from zero to hero](https://www.freecodecamp.org/news/learning-go-from-zero-to-hero-d2a3223b3d86/) +> - 原文地址:[Learning Go—from zero to hero](https://www.freecodecamp.org/news/learning-go-from-zero-to-hero-d2a3223b3d86/) > - 原文作者:[Milap Neupane](https://www.freecodecamp.org/news/author/milapneupane/) > - 译者: > - 校对者: @@ -121,7 +121,7 @@ Like any other programming language, Go supports various different data structur #### **Number, String, and Boolean** Some of the supported number store types are int, int8, int16, int32, int64, -uint, uint8, uint16, uint32, uint64, uintptr… +uint, uint8, uint16, uint32, uint64, uintptr…… The string type stores a sequence of bytes. It is represented and declared with keyword `string`. @@ -905,7 +905,7 @@ As we see in above no more than 2 messages are accepted by a channel. #### Why is Golang Successful? -> Simplicity… — Rob-pike +> Simplicity…… — Rob-pike ### Great! diff --git a/chinese/articles/learning-python-from-zero-to-hero-updated-issue.md b/chinese/articles/learning-python-from-zero-to-hero-updated-issue.md index b545d0883..b19615f47 100644 --- a/chinese/articles/learning-python-from-zero-to-hero-updated-issue.md +++ b/chinese/articles/learning-python-from-zero-to-hero-updated-issue.md @@ -4,32 +4,32 @@ > * 校对者: ![Learning Python: From Zero to Hero](https://cdn-media-1.freecodecamp.org/images/1*ueWmI48uuShON-hX7LwI0w.png) -Python学习:从小白到大神(https://cdn-media-1.freecodecamp.org/images/1*ueWmI48uuShON-hX7LwI0w.png) +Python 学习:从小白到大神(https://cdn-media-1.freecodecamp.org/images/1*ueWmI48uuShON-hX7LwI0w.png) 作者:TK -第一个问题:Python是什么?创始人吉多*范苏罗姆认为,Python是一种 ->“高级编程语言,其核心设计哲学强调代码的可读性和句法,因此几行代码就能够表达程序员的想法。” -为什么我要学Python呢?一个原因在于,Python非常美妙,Python语言可以相当自然流畅地表达我的想法。 -另一个原因就是,Python编程在很多方面都大有可为,包括但不限于数据科学、网站开发、机器学习。Quora, Pinterest和Spotify的后端网站开发都在使用Python。因此,让我们一同来学习Python吧。 +第一个问题:Python 是什么?创始人吉多*范苏罗姆认为,Python 是一种 +> “高级编程语言,其核心设计哲学强调代码的可读性和句法,因此几行代码就能够表达程序员的想法。” +为什么我要学 Python 呢?一个原因在于,Python 非常美妙,Python 语言可以相当自然流畅地表达我的想法。 +另一个原因就是,Python 编程在很多方面都大有可为,包括但不限于数据科学、网站开发、机器学习。Quora, Pinterest 和 Spotify 的后端网站开发都在使用 Python。因此,让我们一同来学习 Python 吧。 ### 基础知识 #### 1\. 变量 很简单,你可以把变量理解成储存值的代码。 -在Python中,定义一个变量、给变量赋值十分简单。例如:给变量"one"赋值1,代码如下: +在 Python 中,定义一个变量、给变量赋值十分简单。例如:给变量"one"赋值 1,代码如下: ```py one = 1 ``` -这样就可以把1赋值给变量"one"了,很简单吧。 +这样就可以把 1 赋值给变量"one"了,很简单吧。 ```py two = 2 some_number = 10000 ``` -你可以把任意数值赋值给你想要的任意变量。如上述代码,整数2赋值给了变量"two",整数10000赋值给了变量"some\_number"。 +你可以把任意数值赋值给你想要的任意变量。如上述代码,整数 2 赋值给了变量"two",整数 10000 赋值给了变量"some\_number"。 除了整数,布尔值(True / False)、字符串、浮点数和其他数据类型也可以赋值给变量。 @@ -45,7 +45,7 @@ my_name = "Leandro Tk" #### 2\. 控制语句:条件判断 -"If"条件语句检验条件真假。如果条件为真,执行if后面的条件。举个例子: +"If"条件语句检验条件真假。如果条件为真,执行 if 后面的条件。举个例子: ```py if True: @@ -53,9 +53,9 @@ if True: ``` -2比1大,条件为真,所以执行print后面的程序。 +2 比 1 大,条件为真,所以执行 print 后面的程序。 -如果if后面的条件语句为假,则执行else后面的语句。 +如果 if 后面的条件语句为假,则执行 else 后面的语句。 ```py if 1 > 2: @@ -64,9 +64,9 @@ else: print("1 is not greater than 2") ``` -1小于2,条件为假,所以执行else后面的语句。 +1 小于 2,条件为假,所以执行 else 后面的语句。 -也可以使用elif语句: +也可以使用 elif 语句: ```py if 1 > 2: @@ -79,16 +79,16 @@ else: #### 3\. 循环/迭代 -Python有很多不同的循环/迭代模式。今天说两个:while循环和for循环。 +Python 有很多不同的循环/迭代模式。今天说两个:while 循环和 for 循环。 -while循环:当条件为真,while模块语句执行。我们可以用while循环来输出1-10这几个数字。 +while 循环:当条件为真,while 模块语句执行。我们可以用 while 循环来输出 1-10 这几个数字。 ```py num = 1 ``` -while循环需要循环条件。如果循环条件为真,循环继续。上述代码表明,num值为11时,循环条件为假,循环停止。 +while 循环需要循环条件。如果循环条件为真,循环继续。上述代码表明,num 值为 11 时,循环条件为假,循环停止。 另外一种代码形式可以更好的理解循环条件: @@ -99,18 +99,18 @@ loop_condition = True 循环条件为真,循环继续,直到循环条件为假。 -for循环:模块语句运用"num"变量时,for循环迭代变量。for循环代码可以跟while循环一样输出数字1-10。 +for 循环:模块语句运用"num"变量时,for 循环迭代变量。for 循环代码可以跟 while 循环一样输出数字 1-10。 ```py for i in range(1, 11): print(i) ``` -看见了吧,就是这么简单。整数数列range()循环从1开始,直到第十一个元素停止循环(输出元素截止到第十个)。 +看见了吧,就是这么简单。整数数列 range()循环从 1 开始,直到第十一个元素停止循环(输出元素截止到第十个)。 ### 列表(List):集合(Collection)|数组(Array)|数据结构(Data Structure) -设想一下你需要把整数1储存在一个变量中。但现在可能你想储存2,甚至储存3,4,5…… +设想一下你需要把整数 1 储存在一个变量中。但现在可能你想储存 2,甚至储存 3,4,5…… 是否有一种方式可以避免将我所想要的所有的整数存在不计其数的变量当中?——确实有。 @@ -124,13 +124,13 @@ my_integers = [1, 2, 3, 4, 5] 现在你可能要问了:怎样才可以从数组中取一个数值呢? -问得好。列表中有个概念,叫index。列表中的第一个元素为index 0,第二个是index 1,依次类推。 +问得好。列表中有个概念,叫 index。列表中的第一个元素为 index 0,第二个是 index 1,依次类推。 -更简单来说,我们可以用index来指代数组中的每个元素。 +更简单来说,我们可以用 index 来指代数组中的每个元素。 ![](https://cdn-media-1.freecodecamp.org/images/1*ReMk6NgghLII20vPD6uNEA.jpeg) -Python句法比较容易理解: +Python 句法比较容易理解: ```py my_integers = [5, 7, 1, 3, 4] @@ -156,7 +156,7 @@ relatives_names = [ 我们刚学习了列表中的各个元素如何工作。但我还需要告诉你们如何向列表中添加元素(向列表添加一个事项)。 -最常见的方法是运用append函数。我们一起来看看如何运用。 +最常见的方法是运用 append 函数。我们一起来看看如何运用。 ```py bookshelf = [] @@ -166,7 +166,7 @@ print(bookshelf[0]) # The Effective Engineer print(bookshelf[1]) # The 4 Hour Work Week ``` -append函数特别简单,只需要将元素添加为append的参数(正如"The Effective Engineer")。 +append 函数特别简单,只需要将元素添加为 append 的参数(正如"The Effective Engineer")。 有关列表的知识就说到这里。我们一起来看看另外一个数据结构。 @@ -184,7 +184,7 @@ dictionary_example = { } ``` -元素key指向value。那运用key,就可以得到字典中的value。让我们试一下: +元素 key 指向 value。那运用 key,就可以得到字典中的 value。让我们试一下: ```py dictionary_tk = { @@ -195,13 +195,13 @@ dictionary_tk = { ``` -我创建了一个关于我自己的一个字典,包括有我的名字、绰号和国籍。这些属性就是字典中的key。 +我创建了一个关于我自己的一个字典,包括有我的名字、绰号和国籍。这些属性就是字典中的 key。 -我们刚已经学过了如何运用索引得到列表中的值,那我们也可以运用索引(即字典中的key)来得到字典中value的存放位置。 +我们刚已经学过了如何运用索引得到列表中的值,那我们也可以运用索引(即字典中的 key)来得到字典中 value 的存放位置。 -例子当中,我用存储在字典中的value输出了一个关于我的短语。很简单吧~ +例子当中,我用存储在字典中的 value 输出了一个关于我的短语。很简单吧~ -字典还有一个很酷的属性,就是value的值可变。在我创建的字典中,我想要增加我的年龄"age"作为一个key,并且存入我年龄的整数值。 +字典还有一个很酷的属性,就是 value 的值可变。在我创建的字典中,我想要增加我的年龄"age"作为一个 key,并且存入我年龄的整数值。 ```py dictionary_tk = { @@ -213,9 +213,9 @@ dictionary_tk = { ``` -现在key-value当中,key是字符串,value是整数。 +现在 key-value 当中,key 是字符串,value 是整数。 -跟列表一样,现在我们要学习如何向字典中添加元素。key指向value是字典的一大组成。向字典中添加元素时也是如此。 +跟列表一样,现在我们要学习如何向字典中添加元素。key 指向 value 是字典的一大组成。向字典中添加元素时也是如此。 ```py dictionary_tk = { @@ -227,11 +227,11 @@ dictionary_tk['age'] = 24 ``` -一点都不复杂:我们只需要在字典中设定一个value,并且对应一个key就可以了。 +一点都不复杂:我们只需要在字典中设定一个 value,并且对应一个 key 就可以了。 ### 迭代:数据结构中的循环 -正如我们在Python基础[1]中学到的内容,列表迭代非常简单。我们这些用Python开发的程序员通常使用for循环。一起来做: +正如我们在 Python 基础[1]中学到的内容,列表迭代非常简单。我们这些用 Python 开发的程序员通常使用 for 循环。一起来做: ```py bookshelf = [ @@ -244,9 +244,9 @@ bookshelf = [ ``` -所以我们输出了书架上的每一本书(但我们能做的远不止于此)。这就是Python——简单且直观。 +所以我们输出了书架上的每一本书(但我们能做的远不止于此)。这就是 Python——简单且直观。 -对于哈希数据结构而言,我们也可以使用for循环,但同时我们应该用到key: +对于哈希数据结构而言,我们也可以使用 for 循环,但同时我们应该用到 key: ```py dictionary = { "some_key": "some_value" } @@ -255,9 +255,9 @@ for key in dictionary: ``` -这个例子就解释了如何在for循环中使用key。我们可以输出字典中的每个key以及其对应的value。 +这个例子就解释了如何在 for 循环中使用 key。我们可以输出字典中的每个 key 以及其对应的 value。 -还可以使用iteritems方法。 +还可以使用 iteritems 方法。 ```py dictionary = { "some_key": "some_value" } @@ -266,7 +266,7 @@ for key, value in dictionary.items(): ``` -我们将这两个参数命名为key和value,但这不是必须。我们也可以有其他命名。比如说: +我们将这两个参数命名为 key 和 value,但这不是必须。我们也可以有其他命名。比如说: ```py dictionary_tk = { @@ -280,11 +280,11 @@ for attribute, value in dictionary_tk.items(): ``` -可以看到,例子中我们用attribute替代了key,代码同样运行流畅。真棒! +可以看到,例子中我们用 attribute 替代了 key,代码同样运行流畅。真棒! ### 类和对象 -#### 理论先导: +#### 理论先导 对象(object)代表了真实世界当中的实体,例如汽车、狗狗、自行车等。这些对象有两个主要特点:数据(data)和行为(behavior)。 @@ -296,7 +296,7 @@ for attribute, value in dictionary_tk.items(): 类从对象中抽象而来。真实世界当中,一种类型下有很多物体,比如汽车。所有汽车的制作过程基本一致,设计模型也相差无几,都有引擎、轮胎等等。每辆汽车都是同一批设计图纸的产物,有着相同的零部件。 -#### Python面向对象的程序设计模式:ON +#### Python 面向对象的程序设计模式:ON 面向对象的程序设计语言——Python,有类和对象这两个概念。 @@ -304,7 +304,7 @@ for attribute, value in dictionary_tk.items(): 所以,类就是一种模型,一种定义属性和行为的方式(正如理论部分所讲)。举个例子,车辆这种类就有它自己的属性,属性能够定义什么对象是车辆。轮胎数量、油箱类型、座位数量、最高时速等等,都是车辆的属性。 -有了这些前提,我们一起来看看Python中类的表达句法: +有了这些前提,我们一起来看看 Python 中类的表达句法: ```py class Vehicle: @@ -320,9 +320,9 @@ car = Vehicle() print(car) # <main.Vehicle instance at 0x7fb1de6c2638> ``` -这里的car就是Vehicle类中的一个对象。 +这里的 car 就是 Vehicle 类中的一个对象。 -记住,我们的Vehicle类中有四个属性:车轮数量、邮箱类型、座位数量和最高时速。创建一种车辆对象时我们便设置好这些属性。所以这里,一开始我们就定义类来接收数据。 +记住,我们的 Vehicle 类中有四个属性:车轮数量、邮箱类型、座位数量和最高时速。创建一种车辆对象时我们便设置好这些属性。所以这里,一开始我们就定义类来接收数据。 ```py class Vehicle: @@ -333,13 +333,13 @@ class Vehicle: self.maximum_velocity = maximum_velocity ``` -**我们运用了'init'这种构造方法。所以创建车辆对象时,我们可以便定义这些属性。设想,我们喜欢**Tesla Model S,**,自然而然我们便想创建这样一种对象。这个对象有四个车轮,由电力驱动,有四个车座,最高时速可达250km,合每小时155英里。那我们就来创建这个对象: +**我们运用了'init'这种构造方法。所以创建车辆对象时,我们可以便定义这些属性。设想,我们喜欢**Tesla Model S,**,自然而然我们便想创建这样一种对象。这个对象有四个车轮,由电力驱动,有四个车座,最高时速可达 250km,合每小时 155 英里。那我们就来创建这个对象: ```py tesla_model_s = Vehicle(4, 'electric', 5, 250) ``` -属性包括四个车轮、用电驱动、五个车座、250km的最高时速。 +属性包括四个车轮、用电驱动、五个车座、250km 的最高时速。 所有属性都已经设置完毕。但是我们怎么获得这些属性的值呢?这就需要“向对象发送一条信息询问”,我们把这一过程称为“方法”(method),也就是“对象的行为”。我们来做一下: @@ -370,7 +370,7 @@ class Vehicle: self<span class="token punctuation" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 153, 153);">.</span>__number_of_wheels <span class="token operator" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(154, 110, 58);">=</span> number</code></pre><p style="box-sizing: inherit; margin: 0px 0px 1.5em; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 22px; vertical-align: baseline; min-width: 100%;">And we can use these methods as attributes:</p><pre class=" language-py" style="box-sizing: inherit; margin: 1.5em 0px 3em; padding: 20px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: 1.5em; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 1.4rem; vertical-align: baseline; color: rgb(27, 27, 50); background: rgb(238, 238, 240); text-shadow: rgb(255, 255, 255) 0px 1px; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; tab-size: 4; hyphens: none; overflow: auto; min-width: 100%; max-width: 100%;"><code class=" language-py" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: inherit; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: inherit; vertical-align: baseline; color: rgb(0, 0, 0); background: transparent; text-shadow: rgb(255, 255, 255) 0px 1px; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; tab-size: 4; hyphens: none;">tesla_model_s <span class="token operator" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(154, 110, 58);">=</span> Vehicle<span class="token punctuation" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 153, 153);">(</span><span class="token number" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 0, 85);">4</span><span class="token punctuation" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 153, 153);">,</span> <span class="token string" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(102, 153, 0);">'electric'</span><span class="token punctuation" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 153, 153);">,</span> <span class="token number" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 0, 85);">5</span><span class="token punctuation" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 153, 153);">,</span> <span class="token number" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 0, 85);">250</span><span class="token punctuation" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 153, 153);">)</span> ``` -这个跟定义“方法”不太一样。这种情况下,“方法”作为参数运转。举个例子,设定车轮数量时,我们不是把“2”作为参数,而是把“2”赋值给“车轮数量”这一属性。而这也是写pythonic,getter和setter代码的一种方法。 +这个跟定义“方法”不太一样。这种情况下,“方法”作为参数运转。举个例子,设定车轮数量时,我们不是把“2”作为参数,而是把“2”赋值给“车轮数量”这一属性。而这也是写 pythonic,getter 和 setter 代码的一种方法。 当然“方法”也有其他用途,以"**make\_noise**"方法为例: ```py @@ -396,7 +396,7 @@ class Vehicle: #### 公共实例变量 -对于Python当中的类而言,我们在构造方法中预置一个公共实例变量,如下: +对于 Python 当中的类而言,我们在构造方法中预置一个公共实例变量,如下: 在构造方法内部: @@ -413,7 +413,7 @@ tk = Person('TK') print(tk.first_name) # => TK ``` -在类当中,如果我们直接给first_name赋值: +在类当中,如果我们直接给 first_name 赋值: ```py class Person: @@ -441,11 +441,11 @@ print(tk.first_name) # => Kaio #### 非公共实例变量 -> We don’t use the term “private” here, since no attribute is really private in Python (without a generally unnecessary amount of work). — [PEP 8][3]因为Python(没有一大堆不必要的工作时)当中没有一个变量完全私有不公开,所以我们这里使用了非公共(non-public)这个词,而没用使用私有(private)这个词。[PEP 8][3] +> We don’t use the term “private” here, since no attribute is really private in Python (without a generally unnecessary amount of work). — [PEP 8][3]因为 Python(没有一大堆不必要的工作时)当中没有一个变量完全私有不公开,所以我们这里使用了非公共(non-public)这个词,而没用使用私有(private)这个词。[PEP 8][3] 跟公共实例变量无异,我们也可以在构造方法和类当中定义非公共实例变量。句法差异在于:对于非公共实例变量而言,需要在变量名字前使用"_". -> 根据Python软体基金会[4],“Python中不存在“私有”(private)实例变量,除了在对象内部,否则不能得到私有实例变量。然而,Python中大多数代码都遵循一条惯例:无论是函数、方法或者数据成员,只有其名字前有一条下划线(例如"_spam_"_),它就可以被视为API(应用编程接口)的非公共部分。” +> 根据 Python 软体基金会[4],“Python 中不存在“私有”(private)实例变量,除了在对象内部,否则不能得到私有实例变量。然而,Python 中大多数代码都遵循一条惯例:无论是函数、方法或者数据成员,只有其名字前有一条下划线(例如"_spam_"_),它就可以被视为 API(应用编程接口)的非公共部分。” 例子如下: @@ -460,7 +460,7 @@ _`class Person: _`tk = Person('TK', 'tk@mail.com') print(tk._email) # [tk@mail.com][5]_`_ ->我们可以获得并且更新非公共变量。非公共变量只是一种约定俗成,不应被当做API的非公共部分。 +> 我们可以获得并且更新非公共变量。非公共变量只是一种约定俗成,不应被当做 API 的非公共部分。 所以我们采用一种方法,可以让我们在类的定义中将非公共变量当做非公共部分。两个方法("email" & "update_email")可帮助理解: @@ -487,7 +487,7 @@ print(tk.email()) # => [new_tk@mail.com][8]_` 1. 我们引入了一个新对象,包括"first_name"和"email"[tk@mail.com][9]。 2. 我们用一种方法得到了非公共变量,输出了"email"。 3. 我们试图在类之外设置一个新的"email"。 -4. 我们需要将非公共变量视作API的非公共部分。 +4. 我们需要将非公共变量视作 API 的非公共部分。 5. 运用实例方法更新了非公共变量。 6. 大功告成!可运用助手方法(the helper method)在类中更新。 @@ -524,7 +524,7 @@ class Person: <span class="token keyword" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(0, 119, 170);">return</span> self<span class="token punctuation" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 153, 153);">.</span>_age</code></pre><p style="box-sizing: inherit; margin: 0px 0px 1.5em; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 22px; vertical-align: baseline; min-width: 100%;">And now, we’ll try to call this<span> </span><code style="box-sizing: inherit; margin: 0px; padding: 0px 5px 2px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: 1em; font-family: "Roboto Mono", monospace; font-size: 0.8em; vertical-align: baseline; background: rgb(208, 208, 213); word-break: break-all;">non-public method</code><span> </span>with our object:</p><pre class=" language-py" style="box-sizing: inherit; margin: 1.5em 0px 3em; padding: 20px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: 1.5em; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: 1.4rem; vertical-align: baseline; color: rgb(27, 27, 50); background: rgb(238, 238, 240); text-shadow: rgb(255, 255, 255) 0px 1px; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; tab-size: 4; hyphens: none; overflow: auto; min-width: 100%; max-width: 100%;"><code class=" language-py" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: 400 !important; font-stretch: inherit; line-height: inherit; font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; font-size: inherit; vertical-align: baseline; color: rgb(0, 0, 0); background: transparent; text-shadow: rgb(255, 255, 255) 0px 1px; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; tab-size: 4; hyphens: none;">tk <span class="token operator" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(154, 110, 58);">=</span> Person<span class="token punctuation" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 153, 153);">(</span><span class="token string" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(102, 153, 0);">'TK'</span><span class="token punctuation" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 153, 153);">,</span> <span class="token number" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 0, 85);">25</span><span class="token punctuation" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 15px; vertical-align: baseline; color: rgb(153, 153, 153);">)</span> ``` -> 我们可以得到并且更新非公共方法。这一方法是一个惯例,应该被视为API中的非公共部分。 +> 我们可以得到并且更新非公共方法。这一方法是一个惯例,应该被视为 API 中的非公共部分。 下面的例子显示了非公共方法的使用过程: @@ -560,7 +560,7 @@ print(tk.show_age()) # => 25` 在面向对象的程序设计中,类可以从其他的类中继承共同的特点(即数据)和行为(即方法)。 -下面这个例子可以用在Python中。 +下面这个例子可以用在 Python 中。 想象有一辆汽车。汽车的属性包括车轮数量、车座数量和最高时速。那“电动汽车”类就可以从常规“汽车”类继承一些共有属性。 @@ -579,7 +579,7 @@ print(my_car.maximum_velocity)`_ 开始后,我们就可以使用所有创建好的“实例变量”。 -Python中,我们将父类(parent class)作为参数应用到子类(child class)中,好比“电动汽车”类可以从“车类”继承行为和特点。 +Python 中,我们将父类(parent class)作为参数应用到子类(child class)中,好比“电动汽车”类可以从“车类”继承行为和特点。 _`class ElectricCar(Car): def **init**(self, number_of_wheels, seating_capacity, maximum_velocity): @@ -594,32 +594,32 @@ print(my_electric_car.maximum_velocity) # => 250`_ 一个字:爽! -### 大概就这么多。 +### 大概就这么多 -总结一下我们学习的Python基础知识: +总结一下我们学习的 Python 基础知识: - 变量如何工作 - 条件语句 -- while循环 & for循环 +- while 循环 & for 循环 - 列表:集合|数组 - 字典键-值集合 - 数据结构如何迭代 - 对象和类 - 属性作为对象数据 - 方法作为对象行为 -- 如何运用Python中的getters setters和property decorator +- 如何运用 Python 中的 getters setters 和 property decorator - 封装:隐藏信息 - 继承:行为和特点 -祝贺你!你已经完成这一部分的Python学习! +祝贺你!你已经完成这一部分的 Python 学习! -如果你想学习完整的Python课程,学习更多的编程技巧,学习如何搭建项目,试着点击[**One Month Python Bootcamp_**]。期待看见你的身影☺~ +如果你想学习完整的 Python 课程,学习更多的编程技巧,学习如何搭建项目,试着点击[**One Month Python Bootcamp_**]。期待看见你的身影☺~ 想具体了解我的编程学习历程,可以关注我的网站[**The Renaissance Developer**][11]。 希望你笑口常开,终身学习,坚持编程。 -_<a href="[https://twitter.com/LeandroTk][12]_" rel="noopener" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 22px; vertical-align: baseline; background-color: transparent; color: rgb(10, 10, 35); text-decoration: underline; cursor: pointer; word-break: break-word;">[13]. ☺欢迎关注我的Twitter[https://twitter.com/LeandroTk]和GitHub[https://github.com/LeandroTk]。 +_<a href="[https://twitter.com/LeandroTk][12]_" rel="noopener" style="box-sizing: inherit; margin: 0px; padding: 0px; border: 0px; font-style: inherit; font-variant: inherit; font-weight: inherit; font-stretch: inherit; line-height: inherit; font-family: inherit; font-size: 22px; vertical-align: baseline; background-color: transparent; color: rgb(10, 10, 35); text-decoration: underline; cursor: pointer; word-break: break-word;">[13]. ☺欢迎关注我的 Twitter[https://twitter.com/LeandroTk]和 GitHub[https://github.com/LeandroTk]。 [1]: https://medium.com/the-renaissance-developer/python-101-the-basics-441136fb7cc3 [2]: https://en.wikipedia.org/wiki/Object_(computer_science) diff --git a/chinese/articles/lexical-scope-in-javascript.md b/chinese/articles/lexical-scope-in-javascript.md index 5985df5f9..16fc8af12 100644 --- a/chinese/articles/lexical-scope-in-javascript.md +++ b/chinese/articles/lexical-scope-in-javascript.md @@ -7,7 +7,7 @@ 在这篇文章中,我们将借助一些实用的例子来理解什么是词法作用域。 -我们也将简单探讨JavaScript是怎么编译和执行程序的。 +我们也将简单探讨 JavaScript 是怎么编译和执行程序的。 最后,我们将看一看如何使用词法作用域来解释未声明的变量错误和引用错误。 @@ -15,8 +15,8 @@ ## 目录 -- [JavaScript是如何执行程序的?](#how-does-javascript-execute-programs) -- [JavaScript如何解析/编译以及执行代码的?](#how-javascript-parses-compiles-and-executes-code) +- [JavaScript 是如何执行程序的?](#how-does-javascript-execute-programs) +- [JavaScript 如何解析/编译以及执行代码的?](#how-javascript-parses-compiles-and-executes-code) - [理解语法错误](#syntax-error) - [理解变量/函数提升](#variable-function-hoisting) - [什么是词法作用域](#what-is-lexical-scope)? @@ -25,7 +25,7 @@ <h2 id="how-does-javascript-execute-programs">JavaScript是如何执行程序的?</h2> -在了解JavaScript执行代码或程序之前,让我们先从理论角度探索一下,编译器是如何分步骤编译的。 +在了解 JavaScript 执行代码或程序之前,让我们先从理论角度探索一下,编译器是如何分步骤编译的。 不论使用何种编程语言,编译器都会执行以下步骤: @@ -43,18 +43,18 @@ ### 代码生成 -在这一步骤中会把AST转换成机器可以解读的代码。 +在这一步骤中会把 AST 转换成机器可以解读的代码。 以上内容简单介绍了是编译器如何运行并生成机器可读代码。 当然编译过程要比以上提到的步骤要复杂得多,但是介绍其他步骤超出了本文的讨论范围。 -我们从中能够得出JS执行代码最重要的两个步骤是: +我们从中能够得出 JS 执行代码最重要的两个步骤是: 1. 解析 2. 执行 -在理解词法作用域之前,必须首先知道JavaScript是如何执行程序的。在下一章节,我们会深入了解这两个步骤。 +在理解词法作用域之前,必须首先知道 JavaScript 是如何执行程序的。在下一章节,我们会深入了解这两个步骤。 <h2 id="how-javascript-parses-compiles-and-executes-code">JavaScript如何解析/编译以及执行代码的?</h2> @@ -62,7 +62,7 @@ 解析过程 -让我们先来探讨解析过程。在这个过程中,JavaScript引擎查看整个程序,分配不同的变量到各自的作用域,然后检查是否有错误,一旦发现错误,执行就终止。 +让我们先来探讨解析过程。在这个过程中,JavaScript 引擎查看整个程序,分配不同的变量到各自的作用域,然后检查是否有错误,一旦发现错误,执行就终止。 解析之后,执行才真正开始。 @@ -73,7 +73,7 @@ <h3 id="syntax-error">语法错误</h3> -最简单高效展示JS先解析后执行代码的例子是语法错误行为: +最简单高效展示 JS 先解析后执行代码的例子是语法错误行为: 考虑以下有问题的代码: @@ -91,12 +91,12 @@ const newToken = %((token); Uncaught SyntaxError: Unexpected token '%' ``` -仔细看这个报错,JavaScript引擎并没有执行 `console.log`声明。取而代之,引擎经历了以下步骤: +仔细看这个报错,JavaScript 引擎并没有执行 `console.log`声明。取而代之,引擎经历了以下步骤: - 在第一行:发现一个声明了的变量和定义。将这个变量保存在当前作用域中的`token`的变量中,当前作用域即全局作用域。 -- 在第二行:JavaScript引擎发现`token`变量被引用,引擎首先在当前作用域查找`token`的变量是否存在,如果存在,则为引用的`token`变量的声明。 -- 在第三行:引擎发现`newToken`变量被声明以及定义。引擎检查在当前作用域中是否有命名为`newToken`的变量,如果有,则抛出reference error;如果没有,则在当前作用域存储这个变量。 -- 在同一行,引擎也发现了需要引用变量`%((token)`,但是因为这个变量是以`%`开头的,变量命名不可以使用保留字,所以抛出syntax error(语法错误)。 +- 在第二行:JavaScript 引擎发现`token`变量被引用,引擎首先在当前作用域查找`token`的变量是否存在,如果存在,则为引用的`token`变量的声明。 +- 在第三行:引擎发现`newToken`变量被声明以及定义。引擎检查在当前作用域中是否有命名为`newToken`的变量,如果有,则抛出 reference error;如果没有,则在当前作用域存储这个变量。 +- 在同一行,引擎也发现了需要引用变量`%((token)`,但是因为这个变量是以`%`开头的,变量命名不可以使用保留字,所以抛出 syntax error(语法错误)。 <h3 id="variable-function-hoisting">理解变量/函数提升</h3> @@ -114,15 +114,15 @@ function doSomething(){ 在上述代码中,引擎这样执行程序: -- 在第一行:JavaScript引擎遇到一个叫做`doSomething`的函数。引擎在当前作用域查找,看是否有`doSomething`,如果有,则引用这个函数,没有则抛出一个reference error。 +- 在第一行:JavaScript 引擎遇到一个叫做`doSomething`的函数。引擎在当前作用域查找,看是否有`doSomething`,如果有,则引用这个函数,没有则抛出一个 reference error。 - 在解析的过程中引擎发现`function doSomething`位于当前作用域,这样,就在当前作用域添加对这个变量的引用,然后使整个程序都可以访问到这个变量。 - 最终,`doSomething`函数打印出字符串`How you doing?`. 在上述解释中,我们发现代码首先被解析成中介代码以确保变量或者函数(这里既是`doSomething`)可以在当前作用域被访问。 -这样在下一个阶段,JavaScript就知道了这个函数,并且开始执行。 +这样在下一个阶段,JavaScript 就知道了这个函数,并且开始执行。 -从上述例子,我们可以总结出JavaScript引擎在执行代码前进行了如下操作: +从上述例子,我们可以总结出 JavaScript 引擎在执行代码前进行了如下操作: 1. 解析代码 2. 生成中介代码来描述可以访问的变量或者函数 @@ -130,11 +130,11 @@ function doSomething(){ <h2 id="#what-is-lexical-scope">什么是词法作用域</h2> -决定变量或者函数在执行的时候位于什么作用域的过程被称为词法作用域。*词法*这个词源于JS编译的*分词/词法分析阶段*。 +决定变量或者函数在执行的时候位于什么作用域的过程被称为词法作用域。*词法*这个词源于 JS 编译的*分词/词法分析阶段*。 -运行代码时,JavaScript做了两件事:`解析`和`执行`。如上文所述,在解析阶段,变量或者函数的作用域被定义。这就是为什么理解代码执行的解析阶段非常重要,因为这是理解词法作用域的基础。 +运行代码时,JavaScript 做了两件事:`解析`和`执行`。如上文所述,在解析阶段,变量或者函数的作用域被定义。这就是为什么理解代码执行的解析阶段非常重要,因为这是理解词法作用域的基础。 -通俗地讲,JavaScript引擎的解析阶段就是词法作用域发生的地方。 +通俗地讲,JavaScript 引擎的解析阶段就是词法作用域发生的地方。 在了解了基础之后,让我们来看看词法作用域的主要特征: @@ -146,21 +146,21 @@ function doSomething(){ 作用域分配示意图 -然后,给变量分配作用域之后,JavaScript引起会检查该变量的父级作用域以查看其可访问性。 +然后,给变量分配作用域之后,JavaScript 引起会检查该变量的父级作用域以查看其可访问性。 -如果存在这个变量,则变量的作用域为父级作用域。如果没有的话,则抛出reference error。 +如果存在这个变量,则变量的作用域为父级作用域。如果没有的话,则抛出 reference error。 下面的示意图,展现了变量作用域的搜索。 ![ezgif.com-gif-maker--3---1-](https://www.freecodecamp.org/news/content/images/2022/05/ezgif.com-gif-maker--3---1-.gif) -JS引擎成功通过搜索每一个作用找到变量 +JS 引擎成功通过搜索每一个作用找到变量 -下面是展示JS引擎在作用域中搜索一个不存在变量的例子: +下面是展示 JS 引擎在作用域中搜索一个不存在变量的例子: ![ezgif.com-gif-maker--6---1-](https://www.freecodecamp.org/news/content/images/2022/05/ezgif.com-gif-maker--6---1-.gif) -JS引擎抛出引用错误 +JS 引擎抛出引用错误 <h2 id="understanding-lexical-scope">理解词法作用域</h2> @@ -191,7 +191,7 @@ JS引擎抛出引用错误 - 在程序执行阶段,引擎评估赋值操作符的右侧。 - 引擎使用同样的方法,在编译阶段将`allPositions`变量赋值为`undefined`。 - 在赋值操作符右侧,我们也引用`empData`变量,在这个阶段,引擎检查变量是否可以在当前作用域访问,因为可以访问,引用和赋值操作符左侧是一致的(即变量在全局作用域)。 -- 引擎仍然在右侧,并发现在map函数内有一个箭头函数。因为引擎遇到了函数定义,所以会创造一个新的作用域,即gif动画中的数字2。 +- 引擎仍然在右侧,并发现在 map 函数内有一个箭头函数。因为引擎遇到了函数定义,所以会创造一个新的作用域,即 gif 动画中的数字 2。 - 因为这是一个新的作用域,所以我们将它变成黑色。 - 箭头函数有一个叫做`data`的参数,并返回`data.position`,在解析阶段,引擎提升当前作用域和父级作用域所有需要被引用的变量。 - 在函数内部,`data`变量被引用,所以引擎检索在当前作用域中是否存在这个变量。因为这个变量在当前作用域,所以左右引用一致。 diff --git a/chinese/articles/machine-learning-pipeline.md b/chinese/articles/machine-learning-pipeline.md index 303629409..35a65f681 100644 --- a/chinese/articles/machine-learning-pipeline.md +++ b/chinese/articles/machine-learning-pipeline.md @@ -89,7 +89,7 @@ Our goal is to predict whether a candidate will change jobs based on their infor Note that I skipped categorical feature encoding for the simplicity of this article. -### Here are the steps we'll follow: +### Here are the steps we'll follow 1. Import data and encoding 2. Define sets of columns to be transformed in different ways @@ -331,7 +331,7 @@ same_pipe = joblib.load("pipe.joblib") A pipeline does not only make your code tidier, it can also help you optimize hyperparameters and data preparation methods. -### Here's what we'll cover in this section: +### Here's what we'll cover in this section - How to find the changeable pipeline parameters - How to find the best hyperparameter sets: Add a pipeline to Grid Search @@ -356,7 +356,7 @@ Below the first part you'll find what we are interested in: a list of parameters ![1*NCkmLiyit676K3M-HfEbnw](https://miro.medium.com/max/926/1*NCkmLiyit676K3M-HfEbnw.png) -The format is ****step1\_step2\_…\_parameter****. +The format is ****step1\_step2……_…\_parameter****. For example ****col\_trans****\_****cat\_pipeline****\_****one-hot****\_****sparse**** means parameter sparse of the one-hot step. @@ -372,7 +372,7 @@ clf_pipeline.set_params(model_C = 10) Grid Search is a method you can use to perform hyperparameter tuning. It helps you find the optimum parameter sets that yield the highest model accuracy. -#### Set the tuning parameters and their range. +#### Set the tuning parameters and their range Create a dictionary of tuning parameters (hyperparameters) @@ -540,7 +540,7 @@ There are 80 cases for this example. There's running time and accuracy of each c Searching for the best machine learning model can be a time-consuming task. The pipeline can make this task much more convenient so that you can shorten the model training and evaluation loop. -### Here's what we'll cover in this part: +### Here's what we'll cover in this part - Add a custom transformation - Find the best machine learning model @@ -618,7 +618,7 @@ Ah ha – you can’t have two classification models in a pipeline! The solution to this problem is to create a custom transformation that receives a model as an input and performs grid search to find the best model. -### Here are the steps we'll follow: +### Here are the steps we'll follow 1. Create a class that receives a model as an input 2. Add the class in step 1 to a pipeline diff --git a/chinese/articles/markdown-cheatsheet.md b/chinese/articles/markdown-cheatsheet.md index 1b66d9969..e78b41b7c 100644 --- a/chinese/articles/markdown-cheatsheet.md +++ b/chinese/articles/markdown-cheatsheet.md @@ -92,7 +92,7 @@ _如何显示斜体文本:_ 假设你想写一个指数或上标。你可以像这样做:`X^2^`,你将得到 X^2^。 -# 如何用Markdown制作列表 +# 如何用 Markdown 制作列表 Markdown 中有多种类型的列表,比如有序列表和无序列表。 @@ -106,7 +106,7 @@ Markdown 中有多种类型的列表,比如有序列表和无序列表。 - 米饭 - 大葱 -Markdown 代码中,在行首用 `- ` 来创建一个无需列表。 +Markdown 代码中,在行首用 `-` 来创建一个无需列表。 ``` - 辣椒油 @@ -121,7 +121,7 @@ Markdown 代码中,在行首用 `- ` 来创建一个无需列表。 1. 第一项 2. 第二项 -Markdown 代码中,在行首用 `1. ` 来创建一个有需列表(数字编号可以是自己维护的,也可以始终为一个数字,比如1)。 +Markdown 代码中,在行首用 `1.` 来创建一个有需列表(数字编号可以是自己维护的,也可以始终为一个数字,比如 1)。 ``` 1. 第一项 diff --git a/chinese/articles/memoization-in-javascript-and-react.md b/chinese/articles/memoization-in-javascript-and-react.md index 7c9b6b008..73d3ec185 100644 --- a/chinese/articles/memoization-in-javascript-and-react.md +++ b/chinese/articles/memoization-in-javascript-and-react.md @@ -7,19 +7,19 @@ 大家好!本文将讨论记忆化(memoization),这是一种优化手段,旨在减轻繁重的计算过程。 -首先我会介绍什么是记忆化以及在什么情况下要使用记忆化,然后我会通过JavaScript和React的实际例子来进一步讲解。 +首先我会介绍什么是记忆化以及在什么情况下要使用记忆化,然后我会通过 JavaScript 和 React 的实际例子来进一步讲解。 ## 文章目录 - [什么是记忆化](#what-is-memoization) - [记忆化如何运行](#how-does-memoization-work) -- [JavaScript记忆化例子](#javascript-memoization-example) -- [React记忆化例子](#react-memoization-example) +- [JavaScript 记忆化例子](#javascript-memoization-example) +- [React 记忆化例子](#react-memoization-example) - [纯组件](#pure-components) - [纯类组件](#purecomponent-class) - - [Memo高阶组件](#memo-higher-order-component) - - [什么时候使用useCallback钩子](#when-to-use-the-usecallback-hook) - - [什么时候使用useMemo钩子](#when-to-use-the-usememo-hook) + - [Memo 高阶组件](#memo-higher-order-component) + - [什么时候使用 useCallback 钩子](#when-to-use-the-usecallback-hook) + - [什么时候使用 useMemo 钩子](#when-to-use-the-usememo-hook) - [什么时候使用记忆化](#when-to-memoize) - [总结](#round-up) @@ -35,7 +35,7 @@ <h2 id="how-does-memoization-work">记忆化如何运行</h2> -JavaScript中的记忆化以两个概念为基础: +JavaScript 中的记忆化以两个概念为基础: - **闭包**: 结合了函数及其声明的词法作用域。 想要进一步了解可以阅读[这篇文章](https://www.freecodecamp.org/news/closures-in-javascript/)和[这篇文章](https://www.freecodecamp.org/news/scope-and-closures-in-javascript/)。 - **高阶函数**: 指在其他函数中运行的函数,要么是作为函数的参数,要么是被返回。想要进一步了解可以阅读[这篇文章](https://www.freecodecamp.org/news/higher-order-functions-in-javascript-examples/)。 @@ -44,7 +44,7 @@ JavaScript中的记忆化以两个概念为基础: 我将使用经典的斐波那契数列来解释这个晦涩难懂的概念。 -**斐波那契数列** 是一组数列,以1或者0打头,紧接着是1,之后的数字都是前两个数字之后,这些数字也被称作斐波那契数。 +**斐波那契数列** 是一组数列,以 1 或者 0 打头,紧接着是 1,之后的数字都是前两个数字之后,这些数字也被称作斐波那契数。 数列如下: @@ -52,7 +52,7 @@ JavaScript中的记忆化以两个概念为基础: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, … ``` -假设我们需要编写一个函数返回斐波那契数列的第n个数。由于任意斐波那契数是前两个数之和,所以可以使用递归: +假设我们需要编写一个函数返回斐波那契数列的第 n 个数。由于任意斐波那契数是前两个数之和,所以可以使用递归: ```javascript const fib = n => { @@ -90,13 +90,13 @@ const fib = (n, memo) => { memo = memo || {} ``` -接着检查memo对象的键是否包含了函数接收的参数,如果包含,直接返回对应的值。这就是魔法诞生的地方,当值被存储到memo中,我们就不需要多余的递归了。 +接着检查 memo 对象的键是否包含了函数接收的参数,如果包含,直接返回对应的值。这就是魔法诞生的地方,当值被存储到 memo 中,我们就不需要多余的递归了。 ```javascript if (memo[n]) return memo[n] ``` -如果当前值并不在memo中,我们再次调用**fib**,但将**memo**也作为参数传入,我们调用的函数就共享之前函数调用中记忆的值。需要注意的是,在返回结果之前需要先在缓存中添加结果。 +如果当前值并不在 memo 中,我们再次调用**fib**,但将**memo**也作为参数传入,我们调用的函数就共享之前函数调用中记忆的值。需要注意的是,在返回结果之前需要先在缓存中添加结果。 ```javascript return memo[n] = fib(n-1, memo) + fib(n-2, memo) @@ -106,22 +106,22 @@ return memo[n] = fib(n-1, memo) + fib(n-2, memo) <h2 id="react-memoization-example">React记忆化例子</h2> -在React中,使用记忆化可以避免没必要的重复渲染,从而优化应用。 +在 React 中,使用记忆化可以避免没必要的重复渲染,从而优化应用。 -如我在 [这篇关于管理React state的文章中](https://www.freecodecamp.org/news/how-to-manage-state-in-a-react-app/)介绍的这样,组件的再次渲染取决于两样东西:**state的改变**或者 **props的改变**。 这正是我们可以“缓存”的内容,从而避免不必要的重新渲染。 +如我在 [这篇关于管理 React state 的文章中](https://www.freecodecamp.org/news/how-to-manage-state-in-a-react-app/)介绍的这样,组件的再次渲染取决于两样东西:**state 的改变**或者 **props 的改变**。 这正是我们可以“缓存”的内容,从而避免不必要的重新渲染。 在展示代码示例之前,我们先了解一些重要的概念。 <h3 id="pure-components">纯组件</h3> -React支持类组件和函数组件。函数组件是一个返回JSX的JavaScript简单函数,类组件是一个继承React.Component的JavaScript类,并使用render方法返回JSX。 +React 支持类组件和函数组件。函数组件是一个返回 JSX 的 JavaScript 简单函数,类组件是一个继承 React.Component 的 JavaScript 类,并使用 render 方法返回 JSX。 那什么是纯组件呢?根据函数式编程范式的纯函数概念,纯函数指的是: - 返回值仅由输入值决定 - 相同输入值的返回值相同 -同样,一个React纯组件即传入同样的state和props,渲染结果相同。 +同样,一个 React 纯组件即传入同样的 state 和 props,渲染结果相同。 一个纯组件的例子如下: @@ -134,9 +134,9 @@ export default function PureComponent({name, lastName}) { } ``` -我们传入了两个props,组件渲染了两个props。如果props不变,渲染结果也不变。 +我们传入了两个 props,组件渲染了两个 props。如果 props 不变,渲染结果也不变。 -但假设我们在渲染前给每个prop添加一个随机数字,这是即便props保持不变,输出也会发生变化,这就是一个非纯组件。 +但假设我们在渲染前给每个 prop 添加一个随机数字,这是即便 props 保持不变,输出也会发生变化,这就是一个非纯组件。 ```javascript // Impure component @@ -177,11 +177,11 @@ export default ImpurePureComponent <h3 id="purecomponent-class">纯类组件</h3> -针对**类形式的纯组件**,React提供了`PureComponent`来应用记忆化。 +针对**类形式的纯组件**,React 提供了`PureComponent`来应用记忆化。 -继承`React.PureComponent`的组件进行性能和渲染优化。因为React使用`shouldComponentUpdate()` 方法来**浅比较props和state**。 +继承`React.PureComponent`的组件进行性能和渲染优化。因为 React 使用`shouldComponentUpdate()` 方法来**浅比较 props 和 state**。 -让我们来看一个例子。有一个类组件是一个计数器,在这个组件中有一个按钮控制计数器增加或者减少数字大小,还有一个子组件,传入了一个name prop,值为字符串。 +让我们来看一个例子。有一个类组件是一个计数器,在这个组件中有一个按钮控制计数器增加或者减少数字大小,还有一个子组件,传入了一个 name prop,值为字符串。 ```javascript import React from "react" @@ -223,7 +223,7 @@ class Counter extends React.Component { export default Counter ``` -子组件是一个 **纯组件** ,仅渲染接受到的prop。 +子组件是一个 **纯组件** ,仅渲染接受到的 prop。 ```javascript import React from "react" @@ -240,11 +240,11 @@ class Child extends React.Component { export default Child ``` -注意我们在两个组件都添加了console.log,以便每次渲染的时候我们可以在控制台看到信息。那么猜猜看每次我们点击增加和减少按钮的时候,控制台会出现什么消息呢? +注意我们在两个组件都添加了 console.log,以便每次渲染的时候我们可以在控制台看到信息。那么猜猜看每次我们点击增加和减少按钮的时候,控制台会出现什么消息呢? ![2022-04-24_21-59](https://www.freecodecamp.org/news/content/images/2022/04/2022-04-24_21-59.png) -即便接收到的是同样的prop,子组件也会重复渲染。 +即便接收到的是同样的 prop,子组件也会重复渲染。 应用记忆化优化项目,我们需要子组件继承`React.PureComponent`,如下: ```javascript @@ -268,7 +268,7 @@ export default Child 只有初次渲染,没有不必要的重复渲染。小菜一碟! -这样我们就讲解完毕类组件的记忆化,但是函数组件无法继承`React.PureComponent`类,所以React提供HOC和两个钩子来处理记忆化。 +这样我们就讲解完毕类组件的记忆化,但是函数组件无法继承`React.PureComponent`类,所以 React 提供 HOC 和两个钩子来处理记忆化。 <h3 id="memo-higher-order-component">Memo高阶组件</h3> @@ -323,13 +323,13 @@ console.log("Skinny Jack") }) ``` -**高阶组件(HOC)** 类似于JavaScript中的高阶函数。高阶函数指将函数作为参数或者返回其他的函数的函数。React高阶组件将组件作为prop,并且在不改变组件的前提下对这个组件进行操作。你可以把HOC想象成一个打包组件。 +**高阶组件(HOC)** 类似于 JavaScript 中的高阶函数。高阶函数指将函数作为参数或者返回其他的函数的函数。React 高阶组件将组件作为 prop,并且在不改变组件的前提下对这个组件进行操作。你可以把 HOC 想象成一个打包组件。 那么在这个例子中,`memo`执行了`PureComponent`同样的任务,避免了被打包的组件不必要的重复渲染。 <h3 id="when-to-use-the-usecallback-hook">什么时候使用useCallback钩子</h3> -值得注意的是当传入的prop是一个函数的时候,不可以使用`memo`,让我们对上面的例子稍做修改: +值得注意的是当传入的 prop 是一个函数的时候,不可以使用`memo`,让我们对上面的例子稍做修改: ```javascript import { useState } from 'react' @@ -370,13 +370,13 @@ console.log("Skinny Jack") }) ``` -这样我们的prop就是一个始终打印同样字符串的函数,我们的控制台会再次变成这个样子: +这样我们的 prop 就是一个始终打印同样字符串的函数,我们的控制台会再次变成这个样子: ![2022-04-24_22-04](https://www.freecodecamp.org/news/content/images/2022/04/2022-04-24_22-04.png) -出现这种情况是因为实际上每次父组件重新渲染就会创建一个新的函数。创建一个新的函数就意味着传入了新的prop,子组件需要重新渲染。 +出现这种情况是因为实际上每次父组件重新渲染就会创建一个新的函数。创建一个新的函数就意味着传入了新的 prop,子组件需要重新渲染。 -为了解决这个问题,React提供了**useCallback**钩子,应用如下: +为了解决这个问题,React 提供了**useCallback**钩子,应用如下: ```javascript import { useState, useCallback } from 'react' @@ -405,9 +405,9 @@ export default function Counter() { 这样就解决了子组件没有必要的重复渲染。 -useCallback在这里起到的作用是即便父组件重新渲染,函数的值不变。只要函数值不变,子组件的prop就保持不变。 +useCallback 在这里起到的作用是即便父组件重新渲染,函数的值不变。只要函数值不变,子组件的 prop 就保持不变。 -只需用useCallback钩子打包声明的函数。useCallback钩子包含一个依赖数组,可以在这个数组中声明触发函数值变化的变量(和useEffect的工作原理一样)。 +只需用 useCallback 钩子打包声明的函数。useCallback 钩子包含一个依赖数组,可以在这个数组中声明触发函数值变化的变量(和 useEffect 的工作原理一样)。 ```javascript const testingTheTest = useCallback(() => { @@ -417,7 +417,7 @@ const testingTheTest = useCallback(() => { <h3 id="when-to-use-the-usememo-hook">什么时候使用useMemo钩子</h3> -**useMemo**是类似于useCallback的一个钩子,useMemo不缓存函数,而是缓存**函数的返回值**。 +**useMemo**是类似于 useCallback 的一个钩子,useMemo 不缓存函数,而是缓存**函数的返回值**。 在这个例子中`useMemo`缓存数字`2`。 @@ -432,13 +432,13 @@ const num = 1 const answer = useMemo(() => num + 1, [num]) ``` -你可以像使用memo高阶组件一样使用useMemo。两者的区别在于,useMemo是一个带有依赖数组的钩子,而memo是一个接收函数作为参数的高阶组件,并且根据prop有条件地更新组件。 +你可以像使用 memo 高阶组件一样使用 useMemo。两者的区别在于,useMemo 是一个带有依赖数组的钩子,而 memo 是一个接收函数作为参数的高阶组件,并且根据 prop 有条件地更新组件。 -除此之外,useMemo在两次渲染之间缓存返回值,而memo在两次渲染间缓存整个react组件。 +除此之外,useMemo 在两次渲染之间缓存返回值,而 memo 在两次渲染间缓存整个 react 组件。 <h3 id="when-to-memoize">什么时候使用记忆化</h3> -记忆化是React工具包里面非常好用的工具,但你并不需要时刻都使用它。这个工具仅在遇到需要进行大量运算的功能和任务时使用。 +记忆化是 React 工具包里面非常好用的工具,但你并不需要时刻都使用它。这个工具仅在遇到需要进行大量运算的功能和任务时使用。 必须注意在上面的三个例子为了方便展示我们都监听了代码。但当任务的计算量并不繁重的时候,或许采用别的解决方面,或者放任不管是更好的选择。 diff --git a/chinese/articles/modules-in-javascript.md b/chinese/articles/modules-in-javascript.md index e21455269..e17e7c16d 100644 --- a/chinese/articles/modules-in-javascript.md +++ b/chinese/articles/modules-in-javascript.md @@ -4,7 +4,7 @@ > - 校对者: ![Modules in JavaScript – CommonJS and ESmodules Explained](https://www.freecodecamp.org/news/content/images/size/w2000/2022/04/carson-arias-7Z03R1wOdmI-unsplash.jpg) -大家好!这篇文章将讲解JavaScript模块。 +大家好!这篇文章将讲解 JavaScript 模块。 当代软件设计和工程中大量使用模块技术。 @@ -17,7 +17,7 @@ - [什么是模块,模块为什么重要](#whataremodulesandwhyaretheyuseful) - [模块的种类](#typesofmodules) - [CommonJS](#commonjsmodules) - - [ES模块](#esmodules) + - [ES 模块](#esmodules) - [使用模块](#usingmodules) - [打包模块](#bundlingmodules) - [总结](#roundup) @@ -29,7 +29,7 @@ 在开发大型项目的时候,把代码分成不同的模块奏效的原因是: - 把顾虑和功能拆分到不同的文件可以帮助视觉化代码,理顺代码的框架。 -- 组织后的代码更容易维护,更不易出现错误和bug。 +- 组织后的代码更容易维护,更不易出现错误和 bug。 - 可以在不同的文件或者项目的不同部分复用模块,这样就避免了重复书写同样的代码。 与其把程序的所有组件都放在一个文件中,我们不如把它分成不同的部分和模块,每一个部分和模块代表一个功能或者顾虑。 @@ -38,28 +38,28 @@ # 模块的种类 -在JavaScript中有很多方法来实施模块。 +在 JavaScript 中有很多方法来实施模块。 -JavaScript被创造出来的时候只是用于处理网站的小型脚本语言,所以最开始JavaScript并不支持模块。 +JavaScript 被创造出来的时候只是用于处理网站的小型脚本语言,所以最开始 JavaScript 并不支持模块。 -但随着JavaScript这门语言以及相应生态圈的成长,开发者们开始需要模块功能,所以当时涌现出来了不同的方式和库来实现模块功能。 +但随着 JavaScript 这门语言以及相应生态圈的成长,开发者们开始需要模块功能,所以当时涌现出来了不同的方式和库来实现模块功能。 -我们将讨论最近并且是运用最广泛的两种模块——CommonJS和ES模块。 +我们将讨论最近并且是运用最广泛的两种模块——CommonJS 和 ES 模块。 -顺便提一句:你知道[Javascript是只花十天就创建出来的吗](https://thenewstack.io/brendan-eich-on-creating-javascript-in-10-days-and-what-hed-do-differently-today/)? +顺便提一句:你知道[Javascript 是只花十天就创建出来的吗](https://thenewstack.io/brendan-eich-on-creating-javascript-in-10-days-and-what-hed-do-differently-today/)? -我认为在分析JavaScript的复杂性以及这门语言是如何演化的时候,必须记住这门语言诞生之初的设计并不是为了实现现在的功能的。是JavaScript生态圈推动了这门语言的改变。 +我认为在分析 JavaScript 的复杂性以及这门语言是如何演化的时候,必须记住这门语言诞生之初的设计并不是为了实现现在的功能的。是 JavaScript 生态圈推动了这门语言的改变。 -## CommonJS模块 +## CommonJS 模块 -[CommonJS](https://en.wikipedia.org/wiki/CommonJS)是在JavaScript中使用模块的一组标准,是在2009年由Mozilla的工程师Kevin Dangoor提出的。 -CommonJS主要被用做使用Node的服务端JS,浏览器不支持CommonJS。 +[CommonJS](https://en.wikipedia.org/wiki/CommonJS)是在 JavaScript 中使用模块的一组标准,是在 2009 年由 Mozilla 的工程师 Kevin Dangoor 提出的。 +CommonJS 主要被用做使用 Node 的服务端 JS,浏览器不支持 CommonJS。 -顺便提一句,Node之前支持用CommonJS来使用模块,但现在ES模块这个更新的手段也被采用。 +顺便提一句,Node 之前支持用 CommonJS 来使用模块,但现在 ES 模块这个更新的手段也被采用。 -让我们现在在实际代码中看看CommonJS。 +让我们现在在实际代码中看看 CommonJS。 -使用模块之前我们需要先在电脑上安装node,可以使用命令行 `npm init -y`. +使用模块之前我们需要先在电脑上安装 node,可以使用命令行 `npm init -y`. 首先,我们创建一个 `main.js` 文件,并在里面写入一个简单的函数。 @@ -120,13 +120,13 @@ testFunction() 很容易,不是吗!虽然容易,但确实非常有用的工具。 -## ES模块 +## ES 模块 -现在回顾一下ES模块。ES模块是ES6(2015年)年引入的标准。创建的目的是为了标准化JS模块运作,和在浏览器中使用模块的方法(在此之前并不支持模块)。 +现在回顾一下 ES 模块。ES 模块是 ES6(2015 年)年引入的标准。创建的目的是为了标准化 JS 模块运作,和在浏览器中使用模块的方法(在此之前并不支持模块)。 -相较而言,ES模块更新,刚支持浏览器和采用Node的服务端模块。 +相较而言,ES 模块更新,刚支持浏览器和采用 Node 的服务端模块。 -我们来看下面的代码片段,同样我们必须先安装Node应用 `npm init -y`. +我们来看下面的代码片段,同样我们必须先安装 Node 应用 `npm init -y`. 然后点击`package.json` 并且加上 `"type": "module"` ,如下: @@ -145,7 +145,7 @@ testFunction() "type": "module" } ``` -如果不按照上述步骤,并且想在Node中使用ES模块,我们会得到报错: +如果不按照上述步骤,并且想在 Node 中使用 ES 模块,我们会得到报错: ``` (node:29568) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. @@ -200,7 +200,7 @@ const mod1Function2 = () => console.log('Mod1 is rolling, baby!') export { mod1Function, mod1Function2 } ``` -在ES模块中另一个重要的功能是重命名,如下面的例子: +在 ES 模块中另一个重要的功能是重命名,如下面的例子: ``` // main.js @@ -259,7 +259,7 @@ const testFunction = () => { testFunction() ``` -甚至可以省去 `as` 关键字实现重命名, 因为JavaScript“知道”当我们不解构的时候,我们指的是默认导入。 +甚至可以省去 `as` 关键字实现重命名, 因为 JavaScript“知道”当我们不解构的时候,我们指的是默认导入。 ``` // main.js @@ -274,13 +274,13 @@ const testFunction = () => { testFunction() ``` -这差不多就是ES模块的所有内容,希望对你来说是简单明了的。 =) +这差不多就是 ES 模块的所有内容,希望对你来说是简单明了的。 =) # 使用模块 -现在我们已经清楚了不同种类的模块以及他们是如何运行的,现在我们来看看如何在使用HTML和原生JavaScript的网站运用模块。 +现在我们已经清楚了不同种类的模块以及他们是如何运行的,现在我们来看看如何在使用 HTML 和原生 JavaScript 的网站运用模块。 -我们来创建一个简单的HTML文件,包含一个头部,两个按钮,和一个script标签链接到 `main.js`文件。 +我们来创建一个简单的 HTML 文件,包含一个头部,两个按钮,和一个 script 标签链接到 `main.js`文件。 ``` <!-- index.html --> @@ -301,13 +301,13 @@ testFunction() </html> ``` -注意到我在script标签中声明了`type="module"`。要使用JS模块功能必须得这么做,不然会报错: +注意到我在 script 标签中声明了`type="module"`。要使用 JS 模块功能必须得这么做,不然会报错: ``` Uncaught SyntaxError: Cannot use import statement outside a module ``` -打开HTML文件,我们会看到以下界面 +打开 HTML 文件,我们会看到以下界面 ![screenshot-2](https://www.freecodecamp.org/news/content/images/2022/04/screenshot-2.png) `main.js`文件中的代码包括: @@ -326,44 +326,44 @@ testFunction() 我们分别在两个按钮上注册了时间监听器,这样来自 `mod1.js`中的函数才能被执行。 -现在可以预览和测试HTML文件了,我们必须得先配置好文件,不然直接在浏览器中打开HTML,会得到CORS(跨域)报错: +现在可以预览和测试 HTML 文件了,我们必须得先配置好文件,不然直接在浏览器中打开 HTML,会得到 CORS(跨域)报错: ``` Access to script at ... from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, brave, chrome-untrusted, https. ``` -我们可以使用VS的拓展**Live server**,或者通过 `npm init -y`创建一个Node程序,并执行`npx serve`。 +我们可以使用 VS 的拓展**Live server**,或者通过 `npm init -y`创建一个 Node 程序,并执行`npx serve`。 设置到服务后,控制台会显示如下: ![screenshot_1-1](https://www.freecodecamp.org/news/content/images/2022/04/screenshot_1-1.png) -另一件需要注意的是,如果我们点击浏览器开发者工具的网络栏,并且筛选出JS文件,会看到我们的网站在执行两个文件: `main.js` 和 `mod1.js`: +另一件需要注意的是,如果我们点击浏览器开发者工具的网络栏,并且筛选出 JS 文件,会看到我们的网站在执行两个文件: `main.js` 和 `mod1.js`: ![screenshot_3](https://www.freecodecamp.org/news/content/images/2022/04/screenshot_3.png) -当然如果要使用两个文件中的代码,我们必须加载两个文件,但这并不是最好的办法。因为如果这样做,浏览器需要执行两次请求来加载所有必须的JS。 +当然如果要使用两个文件中的代码,我们必须加载两个文件,但这并不是最好的办法。因为如果这样做,浏览器需要执行两次请求来加载所有必须的 JS。 为了提高项目表现,我们应该尽最大可能减少请求。这个时候,模块打包就派上用场。 -稍微提一句,如果你喜欢使用视频来学,[Kent C Dodds的视频很棒](https://egghead.io/lessons/javascript-use-javascript-modules-in-the-browser)。我特别推荐你去关注他,他是最好的JS老师之一,[这里还推荐另一个视频](https://www.youtube.com/watch?v=qgRUr-YUk1Q) by Fireship. ;) +稍微提一句,如果你喜欢使用视频来学,[Kent C Dodds 的视频很棒](https://egghead.io/lessons/javascript-use-javascript-modules-in-the-browser)。我特别推荐你去关注他,他是最好的 JS 老师之一,[这里还推荐另一个视频](https://www.youtube.com/watch?v=qgRUr-YUk1Q) by Fireship. ;) # 模块打包 如上文所述,把代码拆分成模块更利于代码库的整洁和复用。 -但这些优点仅针对开发阶段,在生产阶段,模块并不是最佳操作,因为这使得浏览器为每一个JS文件添加请求,从而损害了网站表现。 +但这些优点仅针对开发阶段,在生产阶段,模块并不是最佳操作,因为这使得浏览器为每一个 JS 文件添加请求,从而损害了网站表现。 -模块打包可以很好地解决这个问题。简言之,模块打包是一个可以将JS模块组合成单个文件的程序(这只是核心功能,许多模块打包器有更多其他的功能)。 +模块打包可以很好地解决这个问题。简言之,模块打包是一个可以将 JS 模块组合成单个文件的程序(这只是核心功能,许多模块打包器有更多其他的功能)。 模块打包器使得开发者们在开发阶段可以讲代码拆分,然后在生产阶段再把代码合并。 将“开发代码”转化成“生产代码”的步骤通常被称作“build”。 -这样的构建工具有很多(如[Browserify](https://browserify.org/), [Parcel](https://parceljs.org/), [Rollup.js](https://rollupjs.org/guide/en/), [Snowpack](https://www.snowpack.dev/)...) 但使用最广泛的是[Webpack](https://webpack.js.org/),然我们来看看一个Webpack的例子。 +这样的构建工具有很多(如[Browserify](https://browserify.org/), [Parcel](https://parceljs.org/), [Rollup.js](https://rollupjs.org/guide/en/), [Snowpack](https://www.snowpack.dev/)...) 但使用最广泛的是[Webpack](https://webpack.js.org/),然我们来看看一个 Webpack 的例子。 -- 注1: 如果你想更加深入地了解模块打包,[Fireship制作的视频](https://www.youtube.com/watch?v=5IG4UmULyoA&t=382s)或许是个不错的选择。 -- 注 2:Webpack是一个非常健壮且复杂的工具,除了打包JS文件之外,它还可以做其他的工作,想要了解更多,可以查阅[官方文档](https://webpack.js.org/)。 +- 注 1: 如果你想更加深入地了解模块打包,[Fireship 制作的视频](https://www.youtube.com/watch?v=5IG4UmULyoA&t=382s)或许是个不错的选择。 +- 注 2:Webpack是一个非常健壮且复杂的工具,除了打包 JS 文件之外,它还可以做其他的工作,想要了解更多,可以查阅[官方文档](https://webpack.js.org/)。 -如果还没安装Node,我们可以先通过 `npm init -y`安装,然后执行 `npm i --save-dev webpack webpack-cli`来安装webpack和webpack-cli。 +如果还没安装 Node,我们可以先通过 `npm init -y`安装,然后执行 `npm i --save-dev webpack webpack-cli`来安装 webpack 和 webpack-cli。 接着创建`webpack.config.js`文件并写入代码: @@ -380,9 +380,9 @@ module.exports = { }; ``` -这个文件将负责Webpack的设置,以及我们的应用如何工作。 +这个文件将负责 Webpack 的设置,以及我们的应用如何工作。 -在上面的代码中,我们设定了入门文件 (`entry: './main.js'`)。Webpack会从这个文件开始读取,然后分析所有依赖项(文件中所有模块), 换句话说,入门文件——main JS引入了所有其他模块。 +在上面的代码中,我们设定了入门文件 (`entry: './main.js'`)。Webpack 会从这个文件开始读取,然后分析所有依赖项(文件中所有模块), 换句话说,入门文件——main JS 引入了所有其他模块。 然后我们声明输出 —— 首先声明存储的路径,然后声明打包文件的名字。 @@ -425,7 +425,7 @@ output: { 这行代码正式我们之前分散在各个文件中的代码,但是被打包成单个文件,并且简化了。 -最后就是在 `index.html` 中更改script标签,这样我们就可以消费bundle JS了,如下: +最后就是在 `index.html` 中更改 script 标签,这样我们就可以消费 bundle JS 了,如下: ``` <!-- index.html --> @@ -446,16 +446,16 @@ output: { </html> ``` -我们可以重新浏览测试代码了,JS完美运行。如果你打开网络栏,会看到只有一个文件被加载! =D +我们可以重新浏览测试代码了,JS 完美运行。如果你打开网络栏,会看到只有一个文件被加载! =D ![screenshot_2-1](https://www.freecodecamp.org/news/content/images/2022/04/screenshot_2-1.png) 希望这个简单的例子能够帮助你理解模块打包是如何将模块结构的良好的开发体验和网站表现结合的。 # 总结 -今天的介绍就到告一段落了。在这篇文章中我们学习了什么是模块,为什么他们好用,在JavaScript中使用模块的不同方法,以及使用Webpack打包代码的一个实际例子。 +今天的介绍就到告一段落了。在这篇文章中我们学习了什么是模块,为什么他们好用,在 JavaScript 中使用模块的不同方法,以及使用 Webpack 打包代码的一个实际例子。 -JS模块的完整手册可以查阅[这篇文章](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). +JS 模块的完整手册可以查阅[这篇文章](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). 希望你喜欢这篇文章,并且从中收益,你可以在[linkedin](https://www.linkedin.com/in/germancocca/)或[twitter](https://twitter.com/CoccaGerman)上关注我。 diff --git a/chinese/articles/my-six-years-in-open-community.md b/chinese/articles/my-six-years-in-open-community.md index 2cc4e1a19..3eed84e75 100644 --- a/chinese/articles/my-six-years-in-open-community.md +++ b/chinese/articles/my-six-years-in-open-community.md @@ -518,7 +518,7 @@ I wrote in the moments at the time: The story is over. So, I don't think about the profound question of "what was I born for." -I used to discuss with a good friend that life is meaningless, go to bed early, get up early, go to the sun when the weather is good, look at the flowers on the way to work, and spend every day vigorously...... +I used to discuss with a good friend that life is meaningless, go to bed early, get up early, go to the sun when the weather is good, look at the flowers on the way to work, and spend every day vigorously…… Feel it, explore it. That is the meaning. Oh, yes, I saw a chubby puppy jumping up happily on the road two days ago, so cute. I met it again the next day, and I couldn't help but laugh. diff --git a/chinese/articles/nodejs-api-best-practices-for-scaling.md b/chinese/articles/nodejs-api-best-practices-for-scaling.md index aab121452..06d0c1434 100644 --- a/chinese/articles/nodejs-api-best-practices-for-scaling.md +++ b/chinese/articles/nodejs-api-best-practices-for-scaling.md @@ -5,15 +5,15 @@ ![Best Practices for Scaling Your Node.js REST APIs](https://www.freecodecamp.org/news/content/images/size/w2000/2022/09/Node.js-Best-Practices-1.png) -除了使用集群模式,还有各种各样扩展API的方式。在这篇教程中,我们将学习10种扩展Node.js API的方式。 +除了使用集群模式,还有各种各样扩展 API 的方式。在这篇教程中,我们将学习 10 种扩展 Node.js API 的方式。 我们经常会在处理项目的时候获取一些零散的知识以提高技能,必须通过不断地复习,才能将习得的技能应用到下次项目中。 这个方法一直奏效吗?我甚至不记得我昨天做了些什么。所以我写下了这篇教程,也是对自己知识的复盘。 -我尝试记录下这些不常被提起扩展Node.js的方法。 +我尝试记录下这些不常被提起扩展 Node.js 的方法。 -本文提及的方法不一定是你最后的救命稻草,你可以在Node.js项目的任何阶段应用这些方法。 +本文提及的方法不一定是你最后的救命稻草,你可以在 Node.js 项目的任何阶段应用这些方法。 让我们来看看本文的内容: @@ -21,16 +21,16 @@ 2. 🐢 优化数据库查询 3. ䷪ 使用断路器快速故障 4. 🔍 记录检查点 -5. 🌠 使用Kafka而非HTTP请求 +5. 🌠 使用 Kafka 而非 HTTP 请求 6. 🪝 小心内存泄露 7. 🐇 使用缓存 8. 🎏 使用连接池 9. 🕋 无缝扩展 -10. 💎 OpenAPI兼容文档 +10. 💎 OpenAPI 兼容文档 ## 使用节流 -节流可以限制对服务器的访问,以防止请求过量。使用节流的好处非常明显:可以保护应用免受大量用户爆发的困扰,也可以防止[拒绝服务攻击(Dos攻击)](https://en.wikipedia.org/wiki/Denial-of-service_attack)。 +节流可以限制对服务器的访问,以防止请求过量。使用节流的好处非常明显:可以保护应用免受大量用户爆发的困扰,也可以防止[拒绝服务攻击(Dos 攻击)](https://en.wikipedia.org/wiki/Denial-of-service_attack)。 输入和输出的速率不匹配的时候,通常是应用节流机制的时候。特别是当入站流量远超过服务器可以(或者希望)处理的流量。 @@ -43,48 +43,48 @@ 在应用程序和新闻推送服务器之间的第一个节点应用了节流: 1. _新闻推送服务(NFS)_ 订阅了你的应用以发送通知。 -2. 每秒向你的应用发送1000个请求。 -3. 根据NFS的订阅计费计划,你的应用仅处理500个请求/秒。 -4. 为前500个请求发送通知。 +2. 每秒向你的应用发送 1000 个请求。 +3. 根据 NFS 的订阅计费计划,你的应用仅处理 500 个请求/秒。 +4. 为前 500 个请求发送通知。 -必须要注意的是,所有超过500个请求/秒以外的请求都会失败,需要NFS再次尝试发送请求。 +必须要注意的是,所有超过 500 个请求/秒以外的请求都会失败,需要 NFS 再次尝试发送请求。 **当可以排队的时候,为什么要拒绝额外的请求?** 有以下理由: -1. 接受所有请求会使应用开始累积请求,这可能导致所有订阅你的应用的客户端出现单点故障(通过RAM/磁盘耗尽),包括NFS。 -2. 你不应该接受超出客户订阅计划范围的请求(在我们的例子中是NFS)。 +1. 接受所有请求会使应用开始累积请求,这可能导致所有订阅你的应用的客户端出现单点故障(通过 RAM/磁盘耗尽),包括 NFS。 +2. 你不应该接受超出客户订阅计划范围的请求(在我们的例子中是 NFS)。 -对于应用程序级别的速率限制,你可以使用Express.js API的中间件——[express-rate-limit](https://www.npmjs.com/package/express-rate-limit)。对于网络级别的节流,你可以使用类似[WAF](https://aws.amazon.com/waf/)的解决方案。 +对于应用程序级别的速率限制,你可以使用 Express.js API 的中间件——[express-rate-limit](https://www.npmjs.com/package/express-rate-limit)。对于网络级别的节流,你可以使用类似[WAF](https://aws.amazon.com/waf/)的解决方案。 -如果你使用的是发布-订阅机制,也可以限制消费者和订阅者。例如,你可以通过设置[maxBytes选项](https://kafka.js.org/docs/consuming#a-name-options-a-options)来限制消费Kafka标签(topic)的字节数据。 +如果你使用的是发布-订阅机制,也可以限制消费者和订阅者。例如,你可以通过设置[maxBytes 选项](https://kafka.js.org/docs/consuming#a-name-options-a-options)来限制消费 Kafka 标签(topic)的字节数据。 ## 优化数据库查询 有时你可能没有缓存数据,或者数据已经过期,查询数据成了唯一的选择。 -发生这种情况时,请确保你的数据库做好了准备:第一步是拥有足够的RAM和磁盘IOPS(每秒输入输出量)。 +发生这种情况时,请确保你的数据库做好了准备:第一步是拥有足够的 RAM 和磁盘 IOPS(每秒输入输出量)。 其次,尽可能优化您的查询。对于初学者来说,做对这几件事很关键: 1. 查询时尽量使用索引字段,但不要过度索引。[索引也有开销](https://www.mongodb.com/blog/post/performance-best-practices-indexing#:~:text=Eliminate%20Unnecessary%20Indexes)。 2. 对于删除,坚持软删除。如果需要永久删除,请推迟。 ([一个有趣的故事](https://httpie.io/blog/stardust)) -3. 在读取数据的时候,仅使用投影(projection)获取需要的字段。如果可以的话,去掉没有必要的元数据和方法。(例如,Mongoose提供[lean](https://mongoosejs.com/docs/tutorials/lean.html))。 -4. 尝试将数据库性能和用户体验分离。如果数据库上的CRUD可以在后台发生(即非阻塞),请执行此操作。不要让用户等待。 +3. 在读取数据的时候,仅使用投影(projection)获取需要的字段。如果可以的话,去掉没有必要的元数据和方法。(例如,Mongoose 提供[lean](https://mongoosejs.com/docs/tutorials/lean.html))。 +4. 尝试将数据库性能和用户体验分离。如果数据库上的 CRUD 可以在后台发生(即非阻塞),请执行此操作。不要让用户等待。 5. 使用更新查询直接更新所需字段。不要获取文档,更新字段,然后将整个文档保存回数据库。这会造成网络和数据库开销。 ## 使用断路器快速故障 -想象一下,你的Node.js应用程序出现突发流量,并且满足请求所需的外部服务器之一已关闭。你是否想在此后的每个请求中一直走死胡同?当然不!我们不想在注定要失败的请求上浪费时间和资源。 +想象一下,你的 Node.js 应用程序出现突发流量,并且满足请求所需的外部服务器之一已关闭。你是否想在此后的每个请求中一直走死胡同?当然不!我们不想在注定要失败的请求上浪费时间和资源。 这就是使用断路器的核心思想: **尽早失败**、 **快速失败**。 -例如,如果100个请求中有50个失败,则在接下来的X秒内不允许对该外部服务器发出任何请求。它可以防止触发必然会失败的请求。 +例如,如果 100 个请求中有 50 个失败,则在接下来的 X 秒内不允许对该外部服务器发出任何请求。它可以防止触发必然会失败的请求。 一旦线路复位,就允许请求通过。如果它们再次失败,则线路断开并重复循环。 ![Node.js Opposum circuit breaker states](https://www.freecodecamp.org/news/content/images/2022/09/circuit-breaker-nodejs-best-practices-for-scale.drawio--1-.png) -Node.js Opposum断路器状态 +Node.js Opposum 断路器状态 可以查看[Opposum](https://github.com/nodeshift/opossum)以了解更多添加断路器的信息。你也可以在[这里](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern)阅读更多关断路器的信息。 @@ -94,43 +94,43 @@ Node.js Opposum断路器状态 你可以使用[ELK stack](https://www.elastic.co/what-is/elk-stack)来设置良好的日志记录和警报管道。 -虽然日志记录是必不可少的工具,但很容易过度使用。如果记录所有内容,最终可能会耗尽磁盘IOPS,从而导致您的应用程序受到影响。 +虽然日志记录是必不可少的工具,但很容易过度使用。如果记录所有内容,最终可能会耗尽磁盘 IOPS,从而导致您的应用程序受到影响。 **一个值的借鉴的经验是只记录检查点。** 检查点可以是: 1. 当进入应用程序中的主控制流时以及在它们经过验证和清理之后的请求 -2. 与外部服务/SDK/API交互时的请求和响应。 +2. 与外部服务/SDK/API 交互时的请求和响应。 3. 对该请求的最终响应。 -4. 为catch处理程序提供有用的错误消息(错误消息具有合理的默认值)。 +4. 为 catch 处理程序提供有用的错误消息(错误消息具有合理的默认值)。 -**另外:** 如果一个请求在生命周期中经过多个服务器,您可以在日志中传递一个唯一ID,以跨服务器捕获特定请求。 +**另外:** 如果一个请求在生命周期中经过多个服务器,您可以在日志中传递一个唯一 ID,以跨服务器捕获特定请求。 -## 使用Kafka而非HTTP请求 +## 使用 Kafka 而非 HTTP 请求 -虽然存在HTTP请求的用例,但容易使用过度,请在不必要的时候避免使用HTTP请求。 +虽然存在 HTTP 请求的用例,但容易使用过度,请在不必要的时候避免使用 HTTP 请求。 让我们通过这个例子来理解: ![Overview of Kafka pub-sub using topics](https://www.freecodecamp.org/news/content/images/2022/09/kafka-over-http-nodejs-best-practices-for-scale.drawio.png) -Kafka主从结构使用标签(topic)模式 +Kafka 主从结构使用标签(topic)模式 -假设我们要创建一个如Amazon一样的产品,这个产品包含两大服务: +假设我们要创建一个如 Amazon 一样的产品,这个产品包含两大服务: 1. 供应商服务 2. 库存服务 每当你收到来自供应商服务的新库存,就推送一个库存详情到[Kafka](https://kafka.apache.org/intro)标签。库存服务监听到这个标签,并且更新数据库以确认有新的补货。 -请注意,你将库存数据推送到管道(pipeline)然后程序流,就可以继续其他的操作。程序会自动按照一定节奏消费库存服务。 **Kafka使服务解耦**。 +请注意,你将库存数据推送到管道(pipeline)然后程序流,就可以继续其他的操作。程序会自动按照一定节奏消费库存服务。 **Kafka 使服务解耦**。 -现在,如果你的库存服务出现了故障怎么办?如果是用HTTP请求就不容易处理。但如果使用Kafka,就可以重播预期的消息(例如使用[kcat](https://github.com/edenhill/kcat)). **使用Kafka的话,数据被消费后不会丢失**。 +现在,如果你的库存服务出现了故障怎么办?如果是用 HTTP 请求就不容易处理。但如果使用 Kafka,就可以重播预期的消息(例如使用[kcat](https://github.com/edenhill/kcat)). **使用 Kafka 的话,数据被消费后不会丢失**。 -当某个商品重新回到库存,你可能希望向愿望清单包含这个商品的顾客推送消息。要实现这个功能,可以让通知服务和库存服务监听同样的标签。通过这种方式, **就可以实现在不同的地方使用单个消息总线,并没有HTTP开销**。 +当某个商品重新回到库存,你可能希望向愿望清单包含这个商品的顾客推送消息。要实现这个功能,可以让通知服务和库存服务监听同样的标签。通过这种方式, **就可以实现在不同的地方使用单个消息总线,并没有 HTTP 开销**。 -KafkaJS的[开始页面](https://kafka.js.org/docs/getting-started) 分享了从Node.js应用的基础设置到上述功能的代码片段,我强烈推荐你查看,有很多内容值得研究。 +KafkaJS 的[开始页面](https://kafka.js.org/docs/getting-started) 分享了从 Node.js 应用的基础设置到上述功能的代码片段,我强烈推荐你查看,有很多内容值得研究。 ## 小心内存泄露 @@ -140,14 +140,14 @@ KafkaJS的[开始页面](https://kafka.js.org/docs/getting-started) 分享了从 ![setTimeout retaining 98% memory after execution is over](https://www.freecodecamp.org/news/content/images/2022/09/Image-Pasted-at-2022-9-6-14-58.png) -执行结束后setTimeout还保留98%的内存 +执行结束后 setTimeout 还保留 98%的内存 对于初学者,我建议: 1. 使用`--inspect`标志来执行 Node.js API -2. 在Chrome浏览器打开`chrome://inspect/#devices` +2. 在 Chrome 浏览器打开`chrome://inspect/#devices` 3. 点击 inspect > `Memory` tab > `Allocation instrumentation on timeline`. -4. 在执行一些应用功能。可以使用macOS的apache bench来同时发出多个请求,在终端执行 `curl cheat.sh/ab`查看怎么使用apache bench。 +4. 在执行一些应用功能。可以使用 macOS 的 apache bench 来同时发出多个请求,在终端执行 `curl cheat.sh/ab`查看怎么使用 apache bench。 5. 停止记录并分析内存保持器。 如果发现任何大块的保留内存,请尝试将其最小化。这个话题相关的资源很多,你可以从谷歌搜索“如何防止 Node.js 中的内存泄漏”开始探索。 @@ -164,26 +164,26 @@ KafkaJS的[开始页面](https://kafka.js.org/docs/getting-started) 分享了从 而在**延迟加载**中,数据仅在第一次读取时才写入缓存。第一次请求提供来自数据库的数据,但随后的请求使用缓存。它具有较小的成本,但增加了第一次请求的响应时间。 -要决定缓存数据的TTL(或生存时间),请问自己: +要决定缓存数据的 TTL(或生存时间),请问自己: 1. 基础数据要多久更改一次? 2. 将过期数据返回给最终用户的风险是什么? -在允许的情况下, **更长的TTL意味着更好的应用表现**。 +在允许的情况下, **更长的 TTL 意味着更好的应用表现**。 -重要的是,为你的TTL **添加一点增量**。如果应用程序一时间收到大量流量,并且您的所有缓存数据立即过期,则可能导致数据库无法承受负载,从而影响用户体验。 +重要的是,为你的 TTL **添加一点增量**。如果应用程序一时间收到大量流量,并且您的所有缓存数据立即过期,则可能导致数据库无法承受负载,从而影响用户体验。 ``` 最终TTL = TTL预估值 + 一点随机增量 ``` -TTL的计算 +TTL 的计算 有许多[缓存逐出](https://redis.io/docs/manual/eviction/)的策略,保留默认值是一种有效且可接受的方法。 ## 使用连接池 -打开一个与数据库的独立连接开销很高。它涉及TCP握手、SSL、身份验证和授权检查等。 +打开一个与数据库的独立连接开销很高。它涉及 TCP 握手、SSL、身份验证和授权检查等。 你可以利用连接池来取代独立连接 @@ -195,12 +195,12 @@ TTL的计算 那么,为什么不最大化池中的连接数呢?因为它高度依赖于硬件资源。如果忽略这一点,可能会造成巨大的性能损失。 -连接越多,每个连接的RAM越少,利用RAM的查询越慢(例如排序)。同样的原则也适用于磁盘和CPU。每个新连接都将被分配到资源。 +连接越多,每个连接的 RAM 越少,利用 RAM 的查询越慢(例如排序)。同样的原则也适用于磁盘和 CPU。每个新连接都将被分配到资源。 你可以根据自己的需求调节连接数,对于初学者来说你可以在[这里](https://www.cybertec-postgresql.com/en/tuning-max_connections-in-postgresql/)评估连接池的大小。 你可以在[这里](https://www.mongodb.com/docs/manual/administration/connection-pool-overview/)阅 -连接池相关的内容。若你使用的是PostgreSQL,你可以使用`node-postgres`包。这是一个[连接池](https://node-postgres.com/features/pooling)的内置支持。 +连接池相关的内容。若你使用的是 PostgreSQL,你可以使用`node-postgres`包。这是一个[连接池](https://node-postgres.com/features/pooling)的内置支持。 ## 无缝扩展 @@ -208,11 +208,11 @@ TTL的计算 > 垂直扩展意味着增加节点 (CPU、内存等)资源,而水平扩展意味着增加更多的节点以平衡每个节点的负载。 -如果您使用的是AWS,则可以利用自动扩展组 (ASG),它根据预定义的规则(例如,当CPU利用率超过50% 时水平扩展服务器数量。 +如果您使用的是 AWS,则可以利用自动扩展组 (ASG),它根据预定义的规则(例如,当 CPU 利用率超过 50% 时水平扩展服务器数量。 你可以通过[提前规划行为](https://docs.aws.amazon.com/autoscaling/application/userguide/examples-scheduled-actions.html)来提前规划扩展和缩小的计划,以此来应对可以遇见的流量模式(如在世界杯期间的流媒体服务)。 -准备好ASG后,在最前面添加负载均衡器将确保流量根据所选策略路由到所有实例(如[round robin](https://en.wikipedia.org/wiki/Round-robin_scheduling)。 +准备好 ASG 后,在最前面添加负载均衡器将确保流量根据所选策略路由到所有实例(如[round robin](https://en.wikipedia.org/wiki/Round-robin_scheduling)。 ![Load balancing multiple targets based on predefined rules](https://www.freecodecamp.org/news/content/images/2022/09/alb-nodejs-best-practices-for-scale.drawio--2-.png) @@ -220,25 +220,25 @@ TTL的计算 **PS:** 预估单个服务器可以处理的请求(CPU、内存、磁盘等)并分配至少 30%以上的容量总不会错。 -## OpenAPI兼容文档 +## OpenAPI 兼容文档 -它可能不会直接影响扩展Node.js应用程序的能力,但我必须将其包含在列表中。如果你曾经做过API集成,你就知道为什么了。 +它可能不会直接影响扩展 Node.js 应用程序的能力,但我必须将其包含在列表中。如果你曾经做过 API 集成,你就知道为什么了。 在向前迈出一步之前,了解有关 API 的所有信息至关重要。它使设计的集成、迭代和说理变得容易,更不用说对开发速度的帮助。 确保**为你的 Node.js API 创建 OpenAPI 规范 (OAS)**。 -这使得你以行业标准的方式创建 API 文档。OAS充当单一的事实来源。如果定义得当,它会使与 API 的交互更加高效。 +这使得你以行业标准的方式创建 API 文档。OAS 充当单一的事实来源。如果定义得当,它会使与 API 的交互更加高效。 -我在[这里](https://app.swaggerhub.com/apis/Rishabh570/test-API/0.1)创建了一个API文档样本,你可以使用[swagger inspector](https://swagger.io/tools/swagger-inspector/)来监测任意API。 +我在[这里](https://app.swaggerhub.com/apis/Rishabh570/test-API/0.1)创建了一个 API 文档样本,你可以使用[swagger inspector](https://swagger.io/tools/swagger-inspector/)来监测任意 API。 -你可以在[Swagger Hub dashboard](https://app.swaggerhub.com/home)找到所有API文档,以及创建一个新的文档。 +你可以在[Swagger Hub dashboard](https://app.swaggerhub.com/home)找到所有 API 文档,以及创建一个新的文档。 ## 行动起来吧,船长! -我们研究了十个鲜为人知的扩展Node.js的最佳实践,以及如何开启每一个最佳时间。 +我们研究了十个鲜为人知的扩展 Node.js 的最佳实践,以及如何开启每一个最佳时间。 -现在轮到你对照这个清单并探索发现你的Node.js应用程序缺少了什么。 +现在轮到你对照这个清单并探索发现你的 Node.js 应用程序缺少了什么。 获取你的检查清单 ✨ diff --git a/chinese/articles/nodejs-eventloop-tutorial.md b/chinese/articles/nodejs-eventloop-tutorial.md index 4db293ab2..611afaf3b 100644 --- a/chinese/articles/nodejs-eventloop-tutorial.md +++ b/chinese/articles/nodejs-eventloop-tutorial.md @@ -6,17 +6,17 @@ ![Event Loops in NodeJS – Beginner's Guide to Synchronous and Asynchronous Code](https://www.freecodecamp.org/news/content/images/size/w2000/2021/08/oliver-hale-2cYueJxEDz8-unsplash.jpg) -NodeJS是一个异步事件驱动的JavaScript运行环境,旨在建立可扩展的网络应用。 +NodeJS 是一个异步事件驱动的 JavaScript 运行环境,旨在建立可扩展的网络应用。 -这里的异步指的是JavaScript中所有那些在后台处理的功能,而不会阻塞任何其他请求。 +这里的异步指的是 JavaScript 中所有那些在后台处理的功能,而不会阻塞任何其他请求。 -在这篇文章中,你将学习并理解NodeJS是如何工作的,如何处理发送到服务器的所有功能或请求,无论是 _同步(synchronously)_ 还是 _异步(asynchronously)_。 +在这篇文章中,你将学习并理解 NodeJS 是如何工作的,如何处理发送到服务器的所有功能或请求,无论是 _同步(synchronously)_ 还是 _异步(asynchronously)_。 ## 什么是事件循环(Event Loop)? -你可能已经猜对了--Node在NodeJS环境中使用**事件循环(Event loop)**处理请求。但首先,让我们了解一些基本术语,这将有助于我们理解整个机制。 +你可能已经猜对了--Node 在 NodeJS 环境中使用**事件循环(Event loop)**处理请求。但首先,让我们了解一些基本术语,这将有助于我们理解整个机制。 -事件循环是一个**事件监听器(event-listener)**,它在NodeJS环境中发挥作用,并随时准备监听、处理和输出一个 _事件(event)_。 +事件循环是一个**事件监听器(event-listener)**,它在 NodeJS 环境中发挥作用,并随时准备监听、处理和输出一个 _事件(event)_。 一个事件可以是任何东西,从鼠标点击到按键或超时。 @@ -60,28 +60,28 @@ setTimeOut(function(){ }, 3000) ``` -这些函数并不逐行运行,而只是在需要运行的时候才运行,与函数的声明无关。在这种情况下,当所有的同步函数都被执行后,该函数会在3秒后自动运行。 +这些函数并不逐行运行,而只是在需要运行的时候才运行,与函数的声明无关。在这种情况下,当所有的同步函数都被执行后,该函数会在 3 秒后自动运行。 _注意:异步函数只有在所有同步函数被执行后才会运行和执行。在此之前,它们将在后台被处理。_ -如果你想了解更多关于NodeJS和异步编程的信息,你可以参考这篇[文章](https://www.freecodecamp.org/news/node-js-what-when-where-why-how-ab8424886e2/) +如果你想了解更多关于 NodeJS 和异步编程的信息,你可以参考这篇[文章](https://www.freecodecamp.org/news/node-js-what-when-where-why-how-ab8424886e2/) -但是,NodeJS如何在后台处理异步函数并先运行所有同步函数?所有这些机制都可以用NodeJS的事件循环轻松解释。 +但是,NodeJS 如何在后台处理异步函数并先运行所有同步函数?所有这些机制都可以用 NodeJS 的事件循环轻松解释。 ## 事件循环(Event Loop)是如何工作的? -现在让我们看看NodeJS事件循环如何使用Nodejs事件循环图来执行一个简单的同步程序。然后我们将检查Node是如何逐行执行程序的 +现在让我们看看 NodeJS 事件循环如何使用 Nodejs 事件循环图来执行一个简单的同步程序。然后我们将检查 Node 是如何逐行执行程序的 当我们通过本节时,你将开始理解你在这里看到的东西: ![1](https://www.freecodecamp.org/news/content/images/2021/08/1.PNG) -在左上角,你有一个将要被执行的Node文件。在左下方,你有一个程序的输出终端。然后,你有 _调用堆栈(Call stack)、Node APIs和回调队列(Callback queue)。_ 所有这些共同构成了NodeJS环境。 +在左上角,你有一个将要被执行的 Node 文件。在左下方,你有一个程序的输出终端。然后,你有 _调用堆栈(Call stack)、Node APIs 和回调队列(Callback queue)。_ 所有这些共同构成了 NodeJS 环境。 -对于同步编程,你只需要关注调用栈(call stack)。这是NodeJS环境中唯一的一部分,将在这种情况下工作。 +对于同步编程,你只需要关注调用栈(call stack)。这是 NodeJS 环境中唯一的一部分,将在这种情况下工作。 回调栈(callback stack)是一个数据结构(data structure),你用它来跟踪将在程序内部运行的所有函数的执行情况。这个数据结构只有一个开放端(open),用于添加或删除顶部项目(top items)。 -当程序开始执行时,它首先被包裹在一个匿名(anonymous )的`main()`函数中。这是由NodeJS自动定义的。所以`main()`首先被推送到回调栈中。 +当程序开始执行时,它首先被包裹在一个匿名(anonymous )的`main()`函数中。这是由 NodeJS 自动定义的。所以`main()`首先被推送到回调栈中。 ![2](https://www.freecodecamp.org/news/content/images/2021/08/2.PNG) @@ -96,7 +96,7 @@ _注意:异步函数只有在所有同步函数被执行后才会运行和执 ![4](https://www.freecodecamp.org/news/content/images/2021/08/4.PNG) ![5](https://www.freecodecamp.org/news/content/images/2021/08/5.PNG) -现在,让我们看看异步函数或程序如何在NodeJS内部执行。我们需要回调栈(callback stack)、Node API和回调队列(callback queue)一起处理一个异步函数。 +现在,让我们看看异步函数或程序如何在 NodeJS 内部执行。我们需要回调栈(callback stack)、Node API 和回调队列(callback queue)一起处理一个异步函数。 让我们先看一下这个例子: @@ -109,12 +109,12 @@ _注意:异步函数只有在所有同步函数被执行后才会运行和执 现在,接下来是`setTimeOut(...Zero...)`函数,它被添加到回调栈(callback stack)中。 -由于这是一个异步函数,它**不会**在回调栈(callback stack)中得到处理。然后,它被从回调栈(callback stack)中添加到Node APIs中,在那里,一个事件被注册,一个回调函数被设置为在后台得到处理。 +由于这是一个异步函数,它**不会**在回调栈(callback stack)中得到处理。然后,它被从回调栈(callback stack)中添加到 Node APIs 中,在那里,一个事件被注册,一个回调函数被设置为在后台得到处理。 ![4-1](https://www.freecodecamp.org/news/content/images/2021/08/4-1.PNG) ![5-1](https://www.freecodecamp.org/news/content/images/2021/08/5-1.PNG) -接下来是`setTimeOut(...Two...)`,它也从回调栈(callback stack)中被添加到Node API,因为它是一个异步函数。然后另一个回调函数被设置为在后台超时2秒后被处理。直到这一点,其他的功能可以被执行。 +接下来是`setTimeOut(...Two...)`,它也从回调栈(callback stack)中被添加到 Node API,因为它是一个异步函数。然后另一个回调函数被设置为在后台超时 2 秒后被处理。直到这一点,其他的功能可以被执行。 这就是所谓的**非阻塞**行为,所有的同步函数首先被处理和执行,异步函数在后台处理,同时等待轮到自己被执行。 @@ -139,11 +139,11 @@ _注意:异步函数不能在回调栈(callback stack)内运行,直到 ![12](https://www.freecodecamp.org/news/content/images/2021/08/12.PNG) -这就是NodeJS将如何在环境中执行同步和异步函数,以及事件循环如何管理调用异步函数。 +这就是 NodeJS 将如何在环境中执行同步和异步函数,以及事件循环如何管理调用异步函数。 ## 总结 -在这篇文章中,你了解了NodeJS的内部工作,看到了异步程序是如何被执行的。 +在这篇文章中,你了解了 NodeJS 的内部工作,看到了异步程序是如何被执行的。 现在你应该明白为什么两秒的延时函数不会阻止程序的其他部分执行。你也知道为什么零秒延时函数在 "End "打印后最后打印出数值。 diff --git a/chinese/articles/object-oriented-javascript-for-beginners.md b/chinese/articles/object-oriented-javascript-for-beginners.md index 33ad69b32..a6d11360f 100644 --- a/chinese/articles/object-oriented-javascript-for-beginners.md +++ b/chinese/articles/object-oriented-javascript-for-beginners.md @@ -5,9 +5,9 @@ ![Object-Oriented Programming in JavaScript for Beginners](https://www.freecodecamp.org/news/content/images/size/w2000/2022/04/pexels-lukas-317377.jpg) -大家好,这篇文章将使用JavaScript示例来讲解面向对象的编程(OOP)的主要特征。 +大家好,这篇文章将使用 JavaScript 示例来讲解面向对象的编程(OOP)的主要特征。 -我将讲解OOP的主要概念,OOP为什么有用以及在什么时候有用,全文展示大量JS示例。 +我将讲解 OOP 的主要概念,OOP 为什么有用以及在什么时候有用,全文展示大量 JS 示例。 如果你尚不熟悉编程范式,在深入这篇文章之前,推荐你阅读[我之前写过的范式简介](https://chinese.freecodecamp.org/news/an-introduction-to-programming-paradigms/)。 @@ -20,7 +20,7 @@ - [面向对象的编程简介](#intro-to-object-oriented-programming) - [如何创建对象——类](#how-to-create-objects-classes) - [类相关注意事项](#some-things-to-keep-in-mind-about-classes-) -- [OOP的四大原则](#the-four-principles-of-oop) +- [OOP 的四大原则](#the-four-principles-of-oop) - [继承](#inheritance) - [继承相关注意事项](#some-things-to-keep-in-mind-about-inheritance-) - [封装](#encapsulation) @@ -31,21 +31,21 @@ <h1 id="intro-to-object-oriented-programming">面向对象的编程简介</h1> -正如我之前关于编程范式的[文章](https://chinese.freecodecamp.org/news/an-introduction-to-programming-paradigms/)所述,OOP的核心是 **将关注点和责任**分离到不同**实体**。 +正如我之前关于编程范式的[文章](https://chinese.freecodecamp.org/news/an-introduction-to-programming-paradigms/)所述,OOP 的核心是 **将关注点和责任**分离到不同**实体**。 实体被编码成 **对象**,每一个实体由一组信息 (**属性**) 和行为 (**方法**)组成,并且为实体所用。 -OOP在大规模项目中非常有用,因为它方便代码的模块化和组织。 +OOP 在大规模项目中非常有用,因为它方便代码的模块化和组织。 把实体的抽象化后,我们就可以把程序看作现实世界,不同的演员出演不同的演出并且相互互动。 -为了更好的理解OOP的应用,我们来编写一个小游戏作为示例。我们将专注于游戏中角色的创建,以此来观察OOP在这个过程中是怎么起作用的。👽 👾 🤖 +为了更好的理解 OOP 的应用,我们来编写一个小游戏作为示例。我们将专注于游戏中角色的创建,以此来观察 OOP 在这个过程中是怎么起作用的。👽 👾 🤖 <h1 id="how-to-create-objects-classes">如何创建对象——类</h1> 所有电子游戏都有游戏角色,对不对?而所有的角色都具备特定的**特征** (属性) 如:肤色、身高、名字等,所有的角色还具备**能力** (方法)如:跳跃、跑步、出拳等。 对象便是一个绝佳的数据结构,来储存这些信息。👌 -假设我们有3种不同的角色“种类”,我们想要创造6个不同的角色,每一个种类两个角色。 +假设我们有 3 种不同的角色“种类”,我们想要创造 6 个不同的角色,每一个种类两个角色。 一种创建角色的方式是手动创建对象,像这样[对象初始化](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer): @@ -88,7 +88,7 @@ const Robot2 = { } ``` -所有的角色都拥有 `name`和`species`属性以及 `sayPhrase`方法。此外,每一个种类(species)都有一个专属的方法(如alien的`fly`方法)。 +所有的角色都拥有 `name`和`species`属性以及 `sayPhrase`方法。此外,每一个种类(species)都有一个专属的方法(如 alien 的`fly`方法)。 可以观察到,一些数据被所有角色共享,另一些数据被同一种类共享,还有一些数据是每个角色专属的。 @@ -177,16 +177,16 @@ Robot2.transform() // 输出: "Optimus prime!" > _“程序中类被定义为一种自定义数据结构“类型”,包含了数据运行所需的数据和行为。类定义了数据结构如何运行,但是类本身不是具体的值。若要在程序中使用具体的值,必须一次或者多次实例化(使用"new"关键字)类。“_ - 请记住类并不是具体的实体或者对象。类是我们用来创建具体对象的蓝图或者模具。 -- 通常类的命名首字母大写并使用驼峰式,class关键字创建常量,所以之后不能更改命名。 -- 类必须拥有一个constructor方法,之后被用来实例化类。JavaScript中的constructor只是一个普通的返回对象的函数。唯一特殊的地方在于,使用“new”关键字调用这个函数,会讲其原型分配为被返回的原型。 -- “this”关键字指向类本身,并在constructor方法内定义类的属性。 +- 通常类的命名首字母大写并使用驼峰式,class 关键字创建常量,所以之后不能更改命名。 +- 类必须拥有一个 constructor 方法,之后被用来实例化类。JavaScript 中的 constructor 只是一个普通的返回对象的函数。唯一特殊的地方在于,使用“new”关键字调用这个函数,会讲其原型分配为被返回的原型。 +- “this”关键字指向类本身,并在 constructor 方法内定义类的属性。 - 添加方法只需要定义函数名和函数内部需要执行的代码。 -- JavaScript是一门基于原型的语言,JavaScript中的类只是一种语法糖。虽然了解这个概念不会对你的使用造成巨大的影响,但是还是有必要知道这一点,相关话题你可以阅读[这篇文章](https://chinese.freecodecamp.org/news/prototypes-and-inheritance-in-javascript/). (ppy:换成了咱自己的链接) +- JavaScript 是一门基于原型的语言,JavaScript 中的类只是一种语法糖。虽然了解这个概念不会对你的使用造成巨大的影响,但是还是有必要知道这一点,相关话题你可以阅读[这篇文章](https://chinese.freecodecamp.org/news/prototypes-and-inheritance-in-javascript/). (ppy:换成了咱自己的链接) <h1 id="the-four-principles-of-oop">OOP的四大原则</h1> -通常OOP有四个关键原则,这四个关键原则决定了OOP程序如何运作。他们是 **继承、封装、抽象和多态**。让我们分别看看这四个特征。 +通常 OOP 有四个关键原则,这四个关键原则决定了 OOP 程序如何运作。他们是 **继承、封装、抽象和多态**。让我们分别看看这四个特征。 <h2 id="inheritance">继承</h2> @@ -256,11 +256,11 @@ class Alien extends Enemy { ... ``` -在上面的例子中,enemy类和其他所有类一样,我们使用constructor方法来接受参数,并且将它们分配给属性,方法用普通函数声明。 +在上面的例子中,enemy 类和其他所有类一样,我们使用 constructor 方法来接受参数,并且将它们分配给属性,方法用普通函数声明。 -在子类中,我们使用 `extends` 关键字来声明我们需要继承父类。在constructor方法中,我们必须声明“power”参数并且使用`super`函数,来表示属性是在父元素中声明的。 +在子类中,我们使用 `extends` 关键字来声明我们需要继承父类。在 constructor 方法中,我们必须声明“power”参数并且使用`super`函数,来表示属性是在父元素中声明的。 -当我们实例化新的对象的时候,其实我们传入了声明在constructor函数里的参数。 _哒哒!_ 我们就可以在实例中访问在父类中声明的属性和方法了。😎 +当我们实例化新的对象的时候,其实我们传入了声明在 constructor 函数里的参数。 _哒哒!_ 我们就可以在实例中访问在父类中声明的属性和方法了。😎 ```javascript const alien1 = new Alien("Ali", "I'm Ali the alien!", 10) @@ -303,7 +303,7 @@ class Alien extends Enemy { } ``` -我们首先声明新的“Character”父类,然后让Enemy类继承它。最后我们在Alien类中使用 `constructor` 和 `super` 函数来传入新的"speed"参数。 +我们首先声明新的“Character”父类,然后让 Enemy 类继承它。最后我们在 Alien 类中使用 `constructor` 和 `super` 函数来传入新的"speed"参数。 我们同样在实例化的同时传入参数, _哒哒!_ 我们又可以在实例中访问"祖父“类的属性和方法了。👴 @@ -403,7 +403,7 @@ class Alien extends Enemy { - 在继承的时候,所有父类的方法和属性都会被子类继承,我们并不能决定继承哪些,不继承哪些。(就像我们不能决定从我们的父母那里继承哪些美德和缺点一样。😅 在讲组合的时候我们会重新提到这个点)。 - 子类可以覆盖掉父类的属性和方法。 -举一个例子,在之前的代码中,Alien类继承了Enemy类的`attack` 方法,并打印 `I'm attacking with a power of ${this.power}!`: +举一个例子,在之前的代码中,Alien 类继承了 Enemy 类的`attack` 方法,并打印 `I'm attacking with a power of ${this.power}!`: ```javascript class Enemy extends Character { @@ -430,7 +430,7 @@ const alien1 = new Alien("Ali", "I'm Ali the alien!", 10, 50) alien1.attack() // 输出: I'm attacking with a power of 10! ``` -假设我们希望Alien的 `attack` 方法表现不同,我们可以覆盖这个方法: +假设我们希望 Alien 的 `attack` 方法表现不同,我们可以覆盖这个方法: ```javascript class Enemy extends Character { @@ -460,9 +460,9 @@ alien1.attack() // 输出: "Now I'm doing a different thing, HA!" <h2 id="encapsulation">封装</h2> -封装是OOP另一个关键概念。封装代表对象有“决定”将什么信息暴露在“外部”的能力。封装通过**公共和私有属性/方法**来实现。 +封装是 OOP 另一个关键概念。封装代表对象有“决定”将什么信息暴露在“外部”的能力。封装通过**公共和私有属性/方法**来实现。 -在JavaScript中,所有对象的属性和方法默认为公共的。“公共”意味着我们可以在函数体外部获取对象的属性和方法。 +在 JavaScript 中,所有对象的属性和方法默认为公共的。“公共”意味着我们可以在函数体外部获取对象的属性和方法。 ```javascript // 类 @@ -484,7 +484,7 @@ alien1.sayPhrase() // output: "I'm Ali the alien!" 为了让你更清晰地理解,让我们来看看私有属性和方法是什么样的: -假设我们希望我们的Alien类有一个`birthYear`属性, 这个属性可以执行`howOld`方法,但我们不希望这个属性被除了对象以外的任何地方访问到, 我们可以这样做: +假设我们希望我们的 Alien 类有一个`birthYear`属性, 这个属性可以执行`howOld`方法,但我们不希望这个属性被除了对象以外的任何地方访问到, 我们可以这样做: ```javascript class Alien extends Enemy { @@ -539,9 +539,9 @@ console.log(alien1) <h2 id="polymorphism">多态</h2> -最后就是多态这个概念(听上去挺复杂的,不是吗?OOP的命名赛高! 🙃)。 多态意味着“多种形态”,实际上这是一个简单的概念,表示的在不同的特定条件下使用一种方法返回不同的值。 +最后就是多态这个概念(听上去挺复杂的,不是吗?OOP 的命名赛高! 🙃)。 多态意味着“多种形态”,实际上这是一个简单的概念,表示的在不同的特定条件下使用一种方法返回不同的值。 -举个例子,我们发现Enemy类拥有 `sayPhrase`方法。 那么所有继承Enemy类的子种类都拥有 `sayPhrase`方法。 +举个例子,我们发现 Enemy 类拥有 `sayPhrase`方法。 那么所有继承 Enemy 类的子种类都拥有 `sayPhrase`方法。 但是我们在不同种类(species)调用这个方法的时候,得到不同的结果: @@ -583,7 +583,7 @@ const alien1 = new Alien("Ali", "I'm Ali the alien!", 10, 50) alien1.attack() // 输出: "Now I'm doing a different thing, HA!" ``` -这里也是多态,是因为如果我们取消ALien类中的 `attack` 方法,我们仍可以在实例中调用这个方法: +这里也是多态,是因为如果我们取消 ALien 类中的 `attack` 方法,我们仍可以在实例中调用这个方法: ```javascript alien1.attack() // 输出: "I'm attacking with a power of 10!" ``` @@ -598,7 +598,7 @@ alien1.attack() // 输出: "I'm attacking with a power of 10!" 应用的方法很简单,只需使用接受对象作为参数的函数,并且分配其需要的属性/方法。请看下面的例子: -假设我们想要给bug角色添加飞行的能力,在我们的代码中,只有外星人有 `fly` 方法。一种方式是让`Bug`类继承: +假设我们想要给 bug 角色添加飞行的能力,在我们的代码中,只有外星人有 `fly` 方法。一种方式是让`Bug`类继承: ```javascript class Alien extends Enemy { @@ -683,9 +683,9 @@ bug1.fly() // 输出: "Now Buggy can fly!" <h1 id="roundup">总结</h1> -OOP是一个强大的编程范式,可以帮助我们通过创建实体抽象来执行庞大的项目。每一个实体负责特定的信息和行为,实体之间也可以相互作用,就像现实生活这样。 +OOP 是一个强大的编程范式,可以帮助我们通过创建实体抽象来执行庞大的项目。每一个实体负责特定的信息和行为,实体之间也可以相互作用,就像现实生活这样。 -在这篇文章中我们学习了类、继承、封装、抽象、多态和组合。这些都是OOP世界中的关键概念。我们同样浏览了各种通过JavaScript实现OOP的例子。 +在这篇文章中我们学习了类、继承、封装、抽象、多态和组合。这些都是 OOP 世界中的关键概念。我们同样浏览了各种通过 JavaScript 实现 OOP 的例子。 希望你喜欢这篇文章,并从中受益。你可以在[LinkedIn](https://www.linkedin.com/in/germancocca/)或[Twitter](https://twitter.com/CoccaGerman)上关注我。 diff --git a/chinese/articles/online-coding-classes-for-beginners-2022-guide.md b/chinese/articles/online-coding-classes-for-beginners-2022-guide.md index 3c16546e4..d04f0b785 100644 --- a/chinese/articles/online-coding-classes-for-beginners-2022-guide.md +++ b/chinese/articles/online-coding-classes-for-beginners-2022-guide.md @@ -122,7 +122,7 @@ Here is the complete list of free courses, arranged by topic. Once you find a co 13. [Data Visualization with D3, JavaScript, React - Full Course \[2021\]](#data-visualization-with-d3-javascript-react-full-course-2021-) (freeCodeCamp) 14. [Next.js for Beginners - Full Course](#next-js-for-beginners-full-course) (freeCodeCamp) -## **************Python Courses (****2****2********)************** +## **************Python Courses (****2****2********)****** 1. [Programming for Everybody (Getting Started with Python)](#programming-for-everybody-getting-started-with-python-) (University of Michigan) 2. [Python Data Structures](#python-data-structures) (University of Michigan) diff --git a/chinese/articles/parking-lot-challenge-solved-in-javascript.md b/chinese/articles/parking-lot-challenge-solved-in-javascript.md index cf94f6792..acde29ad2 100644 --- a/chinese/articles/parking-lot-challenge-solved-in-javascript.md +++ b/chinese/articles/parking-lot-challenge-solved-in-javascript.md @@ -231,7 +231,7 @@ The app provides very basic functionality for managing an imaginary parking lot. When a car is parked, via the PARK! button, the relevant spot will be visualised as busy and will show the registration number of the car being parked there. The operator can un-park cars by clicking on a busy slot, that is on the car they want to “remove” from the parking lot. -## 💡The simple animation of the moving red car is just for visual effect and doesn’t have any real influence on the way the parking lot works. +## 💡The simple animation of the moving red car is just for visual effect and doesn’t have any real influence on the way the parking lot works I used [CSS modules](https://github.com/css-modules/css-modules) for styling the app. I also tried to make the app a bit mobile friendly, in case you decide to try it on your mobile device. diff --git a/chinese/articles/practical-git-and-git-workflows.md b/chinese/articles/practical-git-and-git-workflows.md index 8a6495105..d5c387beb 100644 --- a/chinese/articles/practical-git-and-git-workflows.md +++ b/chinese/articles/practical-git-and-git-workflows.md @@ -7,7 +7,7 @@ 每个人都说你应该学习 Git——你确实应该——但是说实话:Git 有点难。 -甚至在我近10年的软件开发生涯后,我还在学习 Git 的基本原理和如何更有效地使用 Git。 +甚至在我近 10 年的软件开发生涯后,我还在学习 Git 的基本原理和如何更有效地使用 Git。 就在不久前我意识到我对一个已经使用了无数次的关键命令[存在最基本的误解](https://twitter.com/johnmosesman/status/1306255666718310401)。 @@ -56,7 +56,7 @@ https://www.atlassian.com/git/tutorials/using-branches/git-checkout 首先,在开始前我们需要处理一些无聊的事情。 -如果你已经安装了 Git,注册了 GitHub 账户(或者使用任何其他的服务商,像是 GitLab 或者 Bitbucket),并且已经设置了SSH密匙的话,可以跳过这个章节。 +如果你已经安装了 Git,注册了 GitHub 账户(或者使用任何其他的服务商,像是 GitLab 或者 Bitbucket),并且已经设置了 SSH 密匙的话,可以跳过这个章节。 否则,你将首先需要 [安装 Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)。 @@ -850,7 +850,7 @@ To github.com:johnmosesman/practical-git-tutorial.git GitHub 甚至在上面的输出中为我们提供了要访问的 URL: `https://github.com/johnmosesman/practical-git-tutorial/pull/new/chapter-3` -> **几个注意事项:** 接下来的部分展示了 GitHub 的用户界面和拉取请求的流程,但这个流程应该与其他服务非常相似(如 GitLab,Bitbucket等)。 +> **几个注意事项:** 接下来的部分展示了 GitHub 的用户界面和拉取请求的流程,但这个流程应该与其他服务非常相似(如 GitLab,Bitbucket 等)。 > > 另外请记住,我使用的是我自己的仓库,所以你在这里看到的一些 URL 会与你的不同。 @@ -859,7 +859,7 @@ GitHub 甚至在上面的输出中为我们提供了要访问的 URL: `https:/ 我们可以看到几个东西: - 一个指定拉取请求名称的地方(一个主题句,以便于理解这个拉取请求是关于什么的) -- 一个描述框,以解释我们所做的修改和我们想提供的任何其他背景信息(你也可以在这里添加图片、gif或视频)。 +- 一个描述框,以解释我们所做的修改和我们想提供的任何其他背景信息(你也可以在这里添加图片、gif 或视频)。 - 而在所有这些下面是我们修改的文件的列表和其中的变化(差异)。 ![](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2021-03-24-at-10.22.13-AM.png) diff --git a/chinese/articles/prepare-for-react-technical-interviews.md b/chinese/articles/prepare-for-react-technical-interviews.md index be2c1a88e..137f848fa 100644 --- a/chinese/articles/prepare-for-react-technical-interviews.md +++ b/chinese/articles/prepare-for-react-technical-interviews.md @@ -5,19 +5,19 @@ ![How to Prepare for React Interviews – Front-End Technical Interview Guide](https://www.freecodecamp.org/news/content/images/size/w2000/2022/08/Build-a-React-Code-Editor-That-Compiles-and-Executes-in-10--Languages--2-.png) -前端技术面试往往是潜在雇主用来评估你在web开发方面技术的机会。 +前端技术面试往往是潜在雇主用来评估你在 web 开发方面技术的机会。 -面试官通常会提问你在HTML、CSS和JavaScript方面的经验和技术,他们也会针对如:React、Angular、Vue等框架进行提问。 +面试官通常会提问你在 HTML、CSS 和 JavaScript 方面的经验和技术,他们也会针对如:React、Angular、Vue 等框架进行提问。 也会有就特定领域给你出写代码的测试。 -我们一起来看看前端技术面试在React和JavaScript方面最常被问到的问题。 +我们一起来看看前端技术面试在 React 和 JavaScript 方面最常被问到的问题。 ## 面试官考察什么? -当参与前端web开发职位的面试,请准备好你会被问到各种编程语言、工具和框架方面的技巧和经验。 +当参与前端 web 开发职位的面试,请准备好你会被问到各种编程语言、工具和框架方面的技巧和经验。 -面试官希望你对web开发最新的趋势和技术有比较深刻的认识。 +面试官希望你对 web 开发最新的趋势和技术有比较深刻的认识。 请准备好描述你过去的项目,以及你是如何解决各种挑战的。 @@ -34,7 +34,7 @@ 让我们一起来看看常被问到的问题。 -## Map、ForEach、Filter和Reduce +## Map、ForEach、Filter 和 Reduce 有关`数组方法`是最常被问到的问题(通常是面试一开始)。面试官希望了解你在控制数组方面的熟悉程度。 @@ -94,7 +94,7 @@ console.log(tooYoung); 简单来说,`.reduce()`方法需要考虑`previous value(先前值)`、当前值(currentValue)和 `accumulator(累加器)`. -`.reduce()`方法的返回类型始终是一个值。当你想要处理数组所有值并且最终累积一个结果的时候,reduce可以派上用场。 +`.reduce()`方法的返回类型始终是一个值。当你想要处理数组所有值并且最终累积一个结果的时候,reduce 可以派上用场。 ```javascript // 计算三个人的年龄总和 @@ -110,18 +110,18 @@ console.log(totalAge) // 输出: 57 ``` -在上述代码中,`currentObj`是被迭代的对象。同时,`acc` 存储结果,并且输出totalAge数组最终的结果。 +在上述代码中,`currentObj`是被迭代的对象。同时,`acc` 存储结果,并且输出 totalAge 数组最终的结果。 -## 如何实现Polyfills +## 如何实现 Polyfills -[如何通过polyfills](https://www.algochurn.com/frontend/polyfills) 来实现map和filter数组,也是一个重要的面试问题。 +[如何通过 polyfills](https://www.algochurn.com/frontend/polyfills) 来实现 map 和 filter 数组,也是一个重要的面试问题。 -polyfill是一个代码块 (在JavaScript的web架构中)。通常用于在旧的浏览器中原生地实现现代的功能。 +polyfill 是一个代码块 (在 JavaScript 的 web 架构中)。通常用于在旧的浏览器中原生地实现现代的功能。 -简单来说,polyfill就是使用自定义的原生JavaScript函数来实现功能。比方说创建你自己的 `.map()` 或者 `.filter()`方法。 +简单来说,polyfill 就是使用自定义的原生 JavaScript 函数来实现功能。比方说创建你自己的 `.map()` 或者 `.filter()`方法。 -#### 如何实现`.map()`的polyfill +#### 如何实现`.map()`的 polyfill ```javascript let data = [1, 2, 3, 4, 5]; @@ -169,7 +169,7 @@ console.log(filterLog); `防抖` 是防止函数被过于频繁调用的一种方法,取而代之的是在上次调用之后等待一段时间之后再调用。 -Amazon就是一个很好的例子,不论你在查找框中输入什么,都会等待至少0.5秒之后,结果才会输出,这就是防抖。 +Amazon 就是一个很好的例子,不论你在查找框中输入什么,都会等待至少 0.5 秒之后,结果才会输出,这就是防抖。 我们来看一个实现防抖的例子:基于用户的输入来生成用户名。 @@ -195,19 +195,19 @@ let debounce = function (cb, delay) { inputEle.addEventListener("keyup", debounce(generateUsername, 300)); ``` -在上述代码中,我们尝试基于用户的输入来生成自定义的用户名。用户开始打字之后,我们并不希望马上生成用户名,而是等待300毫秒之后再生成。 这里实际上是在模仿一个API调用,假设用户输入任意内容,然后必须调用后端API来抓取一个响应。 +在上述代码中,我们尝试基于用户的输入来生成自定义的用户名。用户开始打字之后,我们并不希望马上生成用户名,而是等待 300 毫秒之后再生成。 这里实际上是在模仿一个 API 调用,假设用户输入任意内容,然后必须调用后端 API 来抓取一个响应。 `debounce()`函数接受两个值,`cb`和`delay`。`cb`是当定时器(timer)到时间之后执行的回调函数。 -我们使用`setTimeout()`来创建定时器,也就是说在setTimeout函数体内部的函数会在一定时间之后执行。 +我们使用`setTimeout()`来创建定时器,也就是说在 setTimeout 函数体内部的函数会在一定时间之后执行。 `apply`方法使用最初调用它的`对象`来调用回调函数,并在调用中应用参数(arguments)和上下文(context)。 ## 什么是闭包? -[闭包的MDN文档](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)的定义: +[闭包的 MDN 文档](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures)的定义: -> 闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript中,闭包会随着函数的创建而被同时创建。 +> 闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。 让我们来通过一个例子了解闭包是怎么运作的: @@ -227,11 +227,11 @@ start(); // 弹出内容为"Manu"的警告框 简言之,内部函数了解周围环境(词法作用域)。 -我写过一整篇博文讲解[如何战胜JavaScript面试](https://manuarora.in/blog/ace-the-javascript-interview#closures),如果你想深入了解JavaScript的面试过程,欢迎阅读。 +我写过一整篇博文讲解[如何战胜 JavaScript 面试](https://manuarora.in/blog/ace-the-javascript-interview#closures),如果你想深入了解 JavaScript 的面试过程,欢迎阅读。 ## React Hooks -当提到React Hooks的时候,前端编程面试中最常见的几个问题包括: +当提到 React Hooks 的时候,前端编程面试中最常见的几个问题包括: 1. `useState()` 2. `useReducer()` @@ -263,11 +263,11 @@ export default function App() { } ``` -`useState()`方法包含两个值,`state`变量以及`一个函数`用于修改state变量。 +`useState()`方法包含两个值,`state`变量以及`一个函数`用于修改 state 变量。 在上述代码块中,我们创建了一个 `title`状态(state)来存储页面的标题。初始状态(state)是 `freeCodeCamp`。 -点击按钮之后,`setTitle()`方法将state变量修改为`FCC`。 +点击按钮之后,`setTitle()`方法将 state 变量修改为`FCC`。 `useState()`是你在函数组件中管理状态的首选资源。 @@ -275,7 +275,7 @@ export default function App() { 简单来说,`useReducer()`是管理应用状态一个比较炫酷的方式。它的结构性更强,能够帮助你管理应用中复杂的状态。 -让我们就一个例子来理解useReducer: +让我们就一个例子来理解 useReducer: ```javascript import "./styles.css"; @@ -315,7 +315,7 @@ export default function App() { `useReducer()`接受两个参数,`reducer`函数和一个`initialState(初始状态)`值。 -reducer函数是基于 `switch-case`的实现,并返回最终状态值。 `useReducer()` 在内部使用这个值然后返回给组件。 +reducer 函数是基于 `switch-case`的实现,并返回最终状态值。 `useReducer()` 在内部使用这个值然后返回给组件。 由 `useReducer()`函数返回的值是`state`和`dispatch`。 `state`是组件内部使用的`state`值。在我们的例子中,指的是`title`和`count`,它们可以被`dispatch()`方法操控,然后由`useReducer()`方法返回。 @@ -327,9 +327,9 @@ reducer函数是基于 `switch-case`的实现,并返回最终状态值。 `use ### `useEffect()`钩子是如何运作的? -可以这样思考:如果你希望state变量改变的时候有`副作用`,可以使用`useEffect()`来触发。 +可以这样思考:如果你希望 state 变量改变的时候有`副作用`,可以使用`useEffect()`来触发。 -例如,输入栏的`input value(输入值)`发生改变之后,就要调用API。你可以在`useEffect()`中编写 `API handle(处理API)`的逻辑。 +例如,输入栏的`input value(输入值)`发生改变之后,就要调用 API。你可以在`useEffect()`中编写 `API handle(处理API)`的逻辑。 ```javascript import React, {useState, useEffect} from 'react'; @@ -349,22 +349,22 @@ export const App = () => { 一旦这个值更新并渲染之后,`useEffect()`代码块就会被调用,`console`声明被触发,然后输出最新的状态值。 - `useEffect()`的常见用例是`调用API`。假设你需要通过输入框中的内容来调用API,useEffect函数将会是最好的办法。 + `useEffect()`的常见用例是`调用API`。假设你需要通过输入框中的内容来调用 API,useEffect 函数将会是最好的办法。 这个函数的另一部分是`依赖数组`,也就是`useEffect()`的第二个参数。在我们的例子中,这个参数是`[value]`。 -也就是说每当 `value`发生改变,useEffect就会被触发。如果你不在`依赖数组`内传入任何值(即依赖数组为空数组),函数只会被调用一次。 +也就是说每当 `value`发生改变,useEffect 就会被触发。如果你不在`依赖数组`内传入任何值(即依赖数组为空数组),函数只会被调用一次。 ### `useRef()`钩子是如何运作的? -useRef使得你可以改变DOM(但不单单只使用useRef)。 +useRef 使得你可以改变 DOM(但不单单只使用 useRef)。 据文档描述: -> useRef返回一个可以修改的ref对象,其中 .current属性用于传参(initialValue)。 返回对象会在组件的整个生命周期保留。 +> useRef 返回一个可以修改的 ref 对象,其中 .current 属性用于传参(initialValue)。 返回对象会在组件的整个生命周期保留。 -简言之,如果想要在组件的整个生命周期保留某个值,就可以使用useRef。useRef的基本实现是基于DOM元素的,请看示例: +简言之,如果想要在组件的整个生命周期保留某个值,就可以使用 useRef。useRef 的基本实现是基于 DOM 元素的,请看示例: ```javascript function TextInputWithFocusButton() { @@ -423,9 +423,9 @@ function useAvailable(resource) { 让我们创建一个`存储本地存储值(local storage values)`的自定义钩子。 -### 如何创建一个自定义钩子 – useLocalStorage示例 +### 如何创建一个自定义钩子 – useLocalStorage 示例 -useLocalStorage自定义钩子是将数据保存在本地存储的一种方法。在本地存储中采用`key`和`value`对来获取和存储值,这样不论用户何时返回到web应用,都会得到同样的结果。 +useLocalStorage 自定义钩子是将数据保存在本地存储的一种方法。在本地存储中采用`key`和`value`对来获取和存储值,这样不论用户何时返回到 web 应用,都会得到同样的结果。 在下面的实现中,一旦`select`标签值发生改变,就会被存储到本地存储中。 @@ -499,9 +499,9 @@ export default function App() { `useLocalStorage`接受两个参数,`本地存储key名称`用于存储,以及必要的`默认值`。 -钩子返回两个值:你使用的key的 `local storage value(本地存储值)`以及通过`setter方法`来`改变key的值`。在例子中就是`setStoredValue`方法。 +钩子返回两个值:你使用的 key 的 `local storage value(本地存储值)`以及通过`setter方法`来`改变key的值`。在例子中就是`setStoredValue`方法。 -在`useLocalStorage.js`中,我们首先使用`localStorage.getItem()`方法中的`GET`来获取本地存储值,如果存在,我们就获取这个值,然后用`JSON.parse()`转化这个值并返回,否则initialValue就被设置为默认值。 +在`useLocalStorage.js`中,我们首先使用`localStorage.getItem()`方法中的`GET`来获取本地存储值,如果存在,我们就获取这个值,然后用`JSON.parse()`转化这个值并返回,否则 initialValue 就被设置为默认值。 `setLocalStorage()`函数需确认我们传入的是函数还是一个简单值,并且使用 `localStorage.setItem()`函数来设置本地存储的值。 @@ -510,9 +510,9 @@ export default function App() { 我确实有通过副业项目脱颖而出的经历。 -我认为创建10来个大同小异的副项目大可不必,需要做的是创建一到两个你运用了你所学的所有React/HTML/CSS/JavaScript知识好项目。 +我认为创建 10 来个大同小异的副项目大可不必,需要做的是创建一到两个你运用了你所学的所有 React/HTML/CSS/JavaScript 知识好项目。 -假设面试官一周安排了14场面试,并且需要查看14个候选人的简历,由于你创建了`每1000次访问后就收取1美元的链接缩短网站`而不是Amazon或者Netflix克隆,你就会脱颖而出。 +假设面试官一周安排了 14 场面试,并且需要查看 14 个候选人的简历,由于你创建了`每1000次访问后就收取1美元的链接缩短网站`而不是 Amazon 或者 Netflix 克隆,你就会脱颖而出。 当然,通过克隆项目锻炼技能没有什么问题。但是你至少有一个独一无二的项目才能够突出自己。 @@ -530,20 +530,20 @@ export default function App() { 言之有物。 -在获得第一份前端工作之前,我失败了超过100次,在收获成功之前,失败是获取反馈和迭代自我的常态。 +在获得第一份前端工作之前,我失败了超过 100 次,在收获成功之前,失败是获取反馈和迭代自我的常态。 如果你满足以下条件,会更容易获得前端工作: -- 你深入理解你的技术– 比方说React(甚至是HTML、 CSS和JS)。 +- 你深入理解你的技术– 比方说 React(甚至是 HTML、 CSS 和 JS)。 - 你拥有一些项目展示你的能力,突出自己。 - 你愿意花更多时间和精力去学习并且挑战自己。 -- 你经常读freeCodeCamp的文章,并且利用它准备面积问题。 (😉) +- 你经常读 freeCodeCamp 的文章,并且利用它准备面积问题。 (😉) ## **总结** 机器代码测试会有很多需要练习的问题。面试官也会有不同的问题来考察你的技能。 -你可以通过[**Algochurn**](https://algochurn.com)来练习最常见的[JavaScript面试问题](https://www.algochurn.com/blog/top-5-react-front-end-questions-to-practice-before-a-technical-interview-round)、 [React 面试问题](https://algochurn.com/frontend)和[算法题](https://algochurn.com/problems),这里不仅包含了问题还有解法。 +你可以通过[**Algochurn**](https://algochurn.com)来练习最常见的[JavaScript 面试问题](https://www.algochurn.com/blog/top-5-react-front-end-questions-to-practice-before-a-technical-interview-round)、 [React 面试问题](https://algochurn.com/frontend)和[算法题](https://algochurn.com/problems),这里不仅包含了问题还有解法。 如果你有任何问题,欢迎通过 [Twitter(@mannupaaji)](https://twitter.com/mannupaaji) 以及我的 [个人网站(manuarora.in)](https://manuarora.in)联系我。 diff --git a/chinese/articles/principle-of-lease-privilege-meaning-cybersecurity.md b/chinese/articles/principle-of-lease-privilege-meaning-cybersecurity.md index fd41f76f4..5d5869346 100644 --- a/chinese/articles/principle-of-lease-privilege-meaning-cybersecurity.md +++ b/chinese/articles/principle-of-lease-privilege-meaning-cybersecurity.md @@ -109,9 +109,9 @@ In most countries, CEOs are liable in the event of a security breach. In the unf ## Real-World Incidents -- [The 2013 Target security breach](https://www.usatoday.com/story/money/2017/05/23/target-pay-185m-2013-data-breach-affected-consumers/102063932/) — Target had to pay $18.5 million in damages after a security breach. Hackers gained access to target systems via a third-party vendor who had access to Target’s network. -- [Solarwinds breach](https://www.techtarget.com/whatis/feature/SolarWinds-hack-explained-Everything-you-need-to-know) — Attackers gained access to one of many global (and fully privileged) accounts of Solarwinds. This led to one of the biggest breaches of the 21st century affecting even the US government. -- [NSA Snowden breach](https://www.venafi.com/blog/deciphering-how-edward-snowden-breached-the-nsa) — Arguably the most famous security breach of all time, Edward Snowden had admin-level access to resources that helped him copy 1.7 million NSA files. NSA eliminated a number of sysadmins to improve the PoLP posture. +- [The 2013 Target security breach](https://www.usatoday.com/story/money/2017/05/23/target-pay-185m-2013-data-breach-affected-consumers/102063932/)—Target had to pay $18.5 million in damages after a security breach. Hackers gained access to target systems via a third-party vendor who had access to Target’s network. +- [Solarwinds breach](https://www.techtarget.com/whatis/feature/SolarWinds-hack-explained-Everything-you-need-to-know)—Attackers gained access to one of many global (and fully privileged) accounts of Solarwinds. This led to one of the biggest breaches of the 21st century affecting even the US government. +- [NSA Snowden breach](https://www.venafi.com/blog/deciphering-how-edward-snowden-breached-the-nsa)—Arguably the most famous security breach of all time, Edward Snowden had admin-level access to resources that helped him copy 1.7 million NSA files. NSA eliminated a number of sysadmins to improve the PoLP posture. ## Summary diff --git a/chinese/articles/prototypes-and-inheritance-in-javascript.md b/chinese/articles/prototypes-and-inheritance-in-javascript.md index 183b72163..168da8997 100644 --- a/chinese/articles/prototypes-and-inheritance-in-javascript.md +++ b/chinese/articles/prototypes-and-inheritance-in-javascript.md @@ -26,7 +26,7 @@ In JavaScript, an object can inherit properties of another object. The object fr You’re probably wondering: why the need for inheritance in the first place? Well, inheritance solves the problem of data and logic duplication. By inheriting, objects can share properties and methods without the need of manually setting those properties and methods on each object. -## ****How to** A**ccess** a P**rototype’s** P**roperties and** M**ethods** in JavaScript** +## ****How to** A**ccess** a P**rototype’s** P**roperties and** M**ethods** in JavaScript When we try to access a property of an object, the property is not only searched in the object itself. It's also searched in the prototype of the object, in the prototype of the prototype, and so on – until a property is found that matches the name or the end of the **prototype chain** is reached. @@ -60,7 +60,7 @@ The output will consist of several built-in properties and methods: Keep in mind that prototypes can also be changed and modified through different methods. -## ****The** P**rototype** C**hain**** +## ****The** P**rototype** C**hain At the end of the prototype chain is `Object.prototype`. All objects inherit the properties and methods of `Object`. Any attempt to search beyond the end of the chain results in `null`. diff --git a/chinese/articles/python-code-examples-sample-script-coding-tutorial-for-beginners.md b/chinese/articles/python-code-examples-sample-script-coding-tutorial-for-beginners.md index 78212ee23..15faa91ae 100644 --- a/chinese/articles/python-code-examples-sample-script-coding-tutorial-for-beginners.md +++ b/chinese/articles/python-code-examples-sample-script-coding-tutorial-for-beginners.md @@ -7,7 +7,7 @@ Hi! 欢迎。您是否正在学习 Python,如果是的,那这篇文章就是为您而准备的,在文中你将会找到 Python 语法的详尽描述以及大量的 Python 代码示例,它将会指导你的 Python 编程之旅。 -### 涵盖的内容: +### 涵盖的内容 - [Python 中的变量定义](#-variable-definitions-in-python) - [Python 中的 Hello, World!](#-hello-world-program-in-python) @@ -1263,7 +1263,7 @@ dict_values([1, 2, 25, 3, 4, 5]) -14.0 ``` -#### 乘法运算: \* +#### 乘法运算 ```python >>> 5 * 6 @@ -1299,7 +1299,7 @@ dict_values([1, 2, 25, 3, 4, 5]) **💡 提示:** 你可以用一个数字与一个字符串"相乘",结果会让字符串重复与相乘的这个数字相同的次数。 -#### 指数运算: \*\* +#### 指数运算 ```python >>> 6 ** 8 @@ -4274,7 +4274,7 @@ else: 下面是各种形式的导入语句: -### 第一种选择: +### 第一种选择 ``` import <module_name> @@ -4298,7 +4298,7 @@ import math 我们在代码中明确的指明该元素所属的模块。 -### 第二中选择: +### 第二中选择 ``` import <module> as <new_name> @@ -4318,7 +4318,7 @@ import math as m 5.0 ``` -### 第三种选择: +### 第三种选择 ``` from <module_name> import <element> @@ -4338,7 +4338,7 @@ from math import sqrt 5.0 ``` -### 第四种选择: +### 第四种选择 ``` from <module_name> import <element> as <new_name> @@ -4358,7 +4358,7 @@ from math import sqrt as square_root 5.0 ``` -### 第五种选择: +### 第五种选择 ``` from <module_name> import * diff --git a/chinese/articles/python-read-json-file-how-to-load-json-from-a-file-and-parse-dumps.md b/chinese/articles/python-read-json-file-how-to-load-json-from-a-file-and-parse-dumps.md index 79aa511ef..7368a823f 100644 --- a/chinese/articles/python-read-json-file-how-to-load-json-from-a-file-and-parse-dumps.md +++ b/chinese/articles/python-read-json-file-how-to-load-json-from-a-file-and-parse-dumps.md @@ -5,46 +5,46 @@ ![Python Read JSON File – How to Load JSON from a File and Parse Dumps](https://www.freecodecamp.org/news/content/images/size/w2000/2020/08/Read-JSON-image.png) -欢迎!如果您想学习如何在Python中和JSON文件交互,那么本文适合你。 +欢迎!如果您想学习如何在 Python 中和 JSON 文件交互,那么本文适合你。 **你将学习:** -- 为什么JSON格式如此重要。 -- json的基本结构和数据类型。 -- JSON和字典如何在Python中协同工作。 -- 如何使用Python内置的`json`模块。 -- 如何将JSON字符串转换为Python对象,反之亦然。 +- 为什么 JSON 格式如此重要。 +- json 的基本结构和数据类型。 +- JSON 和字典如何在 Python 中协同工作。 +- 如何使用 Python 内置的`json`模块。 +- 如何将 JSON 字符串转换为 Python 对象,反之亦然。 - 如何使用`loads()`和`dumps()`。 -- 如何让JSON字符串自动缩进。 -- 如何在Python中使用`load()`读取JSON文件。 -- 如何在Python中使用`dump()`将JSON字符串写入文件。 +- 如何让 JSON 字符串自动缩进。 +- 如何在 Python 中使用`load()`读取 JSON 文件。 +- 如何在 Python 中使用`dump()`将 JSON 字符串写入文件。 - 以及更多! 准备好了吗?让我们开始! ✨ -## 🔹 介绍:什么是JSON? +## 🔹 介绍:什么是 JSON? ![image-98](https://www.freecodecamp.org/news/content/images/2020/10/image-98.png) -JSON格式的产生最初是受到JavaScript(一种用于web开发的编程语言)语法的启发,但在那之后,它成为一种**独立于语言的数据格式**,并且今天我们所使用的大多数编程语言都可以生成和读取JSON。 +JSON 格式的产生最初是受到 JavaScript(一种用于 web 开发的编程语言)语法的启发,但在那之后,它成为一种**独立于语言的数据格式**,并且今天我们所使用的大多数编程语言都可以生成和读取 JSON。 -### JSON的重要性和使用例子 +### JSON 的重要性和使用例子 -基本上,JSON是一种用于存储或表示数据的格式。它的常见用例包括web开发和配置文件。 +基本上,JSON 是一种用于存储或表示数据的格式。它的常见用例包括 web 开发和配置文件。 让我们看看原因: -- **Web开发:**在Web应用程序中,通常用JSON从服务端向客户端发送数据,反之亦然。 +- **Web 开发:**在 Web 应用程序中,通常用 JSON 从服务端向客户端发送数据,反之亦然。 ![image-65](https://www.freecodecamp.org/news/content/images/2020/10/image-65.png) -- **配置文件:**JSON还用于存储配置和设置信息。例如,要创建[Google Chrome App](https://developer.chrome.com/apps/first_app#one),则需要一个名为`manifest.json`的JSON文件,用于指定应用程序的名称、描述、当前版本以及其它属性和设置。 +- **配置文件:**JSON 还用于存储配置和设置信息。例如,要创建[Google Chrome App](https://developer.chrome.com/apps/first_app#one),则需要一个名为`manifest.json`的 JSON 文件,用于指定应用程序的名称、描述、当前版本以及其它属性和设置。 ![image-99](https://www.freecodecamp.org/news/content/images/2020/10/image-99.png) -## 🔸 JSON的结构和格式 +## 🔸 JSON 的结构和格式 -既然已经知道了JSON的用途,那么让我们通过一个比萨饼订单数据的示例来看看它的基本结构: +既然已经知道了 JSON 的用途,那么让我们通过一个比萨饼订单数据的示例来看看它的基本结构: ```JSON { @@ -61,9 +61,9 @@ JSON格式的产生最初是受到JavaScript(一种用于web开发的编程语 } ``` -上面是示例的sample.json文件 +上面是示例的 sample.json 文件 -以下是JSON格式的主要特征: +以下是 JSON 格式的主要特征: - 有一系列用花括号`{}`括起来的键-值对。 - 每个键都使用以下格式映射到特定值: @@ -83,18 +83,18 @@ JSON格式的产生最初是受到JavaScript(一种用于web开发的编程语 } ``` -💡 **提示:**我们通常使用不同级别的缩进来格式化JSON,以使数据更易于阅读。在本文中,你将学习如何使用Python自动添加缩进。 +💡 **提示:**我们通常使用不同级别的缩进来格式化 JSON,以使数据更易于阅读。在本文中,你将学习如何使用 Python 自动添加缩进。 -### JSON数据类型:键和值 +### JSON 数据类型:键和值 -JSON文件具有特定规则,其用于确定哪些数据类型是有效的键和值。 +JSON 文件具有特定规则,其用于确定哪些数据类型是有效的键和值。 - **键**必须是字符串。 -- **值**可以是字符串、数字、数组、布尔值(`true`/`false`)、`null`或JSON对象。 +- **值**可以是字符串、数字、数组、布尔值(`true`/`false`)、`null`或 JSON 对象。 -根据[Python文档](https://docs.python.org/3/library/json.html#json.dumps): +根据[Python 文档](https://docs.python.org/3/library/json.html#json.dumps): -> JSON的键/值对中的键始终是[`str`类型](https://docs.python.org/3/library/stdtypes.html#str)。当字典转换为JSON时,字典中的所有键都被强制转换为字符串。 +> JSON 的键/值对中的键始终是[`str`类型](https://docs.python.org/3/library/stdtypes.html#str)。当字典转换为 JSON 时,字典中的所有键都被强制转换为字符串。 ### 风格指南 @@ -102,35 +102,35 @@ JSON文件具有特定规则,其用于确定哪些数据类型是有效的键 - 始终使用有意义的名称。 - 数组类型的值对应的键名应使用复数形式,所有其它键名应为单数形式。例如:如果对应的值是数组,请使用`orders`而不是`order`。 -- JSON数据中不应该有注释。 +- JSON 数据中不应该有注释。 ## 🔹 JSON vs. Python Dictionaries(字典数据类型) -JSON和字典从显示形式来看可能非常相似,但它们有很大的不同。让我们看看它们是如何“连接(译者:这里意思应该是它们之间的关系)”,以及如何相互补充,来使得Python成为处理JSON文件的强大工具。 +JSON 和字典从显示形式来看可能非常相似,但它们有很大的不同。让我们看看它们是如何“连接(译者:这里意思应该是它们之间的关系)”,以及如何相互补充,来使得 Python 成为处理 JSON 文件的强大工具。 -JSON是用于表示和存储数据的文件格式,而Python字典是Pythons程序运行时保存在内存中的实际数据结构(对象)。 +JSON 是用于表示和存储数据的文件格式,而 Python 字典是 Pythons 程序运行时保存在内存中的实际数据结构(对象)。 -### JSON和Python字典如何协同工作 +### JSON 和 Python 字典如何协同工作 ![image-100](https://www.freecodecamp.org/news/content/images/2020/10/image-100.png) -当我们在Python中处理JSON文件时,我们不能直接读取数据并在程序中使用它,这是因为整个文件被表示为单个字符串,我们无法单独访问键值对。 +当我们在 Python 中处理 JSON 文件时,我们不能直接读取数据并在程序中使用它,这是因为整个文件被表示为单个字符串,我们无法单独访问键值对。 除非······ -我们使用JSON文件的键-值对创建一个Python字典,这样我们就可以在程序中使用它来读取、使用和修改(如果需要的话)数据。 +我们使用 JSON 文件的键-值对创建一个 Python 字典,这样我们就可以在程序中使用它来读取、使用和修改(如果需要的话)数据。 -这是JSON和Python字典之间的“联系”:JSON是数据的字符串表示,字典是程序运行时在内存中创建的实际数据结构。(译者:字典转换为JSON叫序列化,反之为反序列化) +这是 JSON 和 Python 字典之间的“联系”:JSON 是数据的字符串表示,字典是程序运行时在内存中创建的实际数据结构。(译者:字典转换为 JSON 叫序列化,反之为反序列化) -很好,既然你已经对JSON有足够多的了解,那么让我们开始深入了解实际情况中如何在Python里使用JSON的。 +很好,既然你已经对 JSON 有足够多的了解,那么让我们开始深入了解实际情况中如何在 Python 里使用 JSON 的。 -## 🔸 JSON模块 +## 🔸 JSON 模块 -幸运的是,Python自带一个名为`json`的内置模块,安装Python时会自动安装该模块,这个模块包含一些帮助处理JSON文件和字符串的功能。 +幸运的是,Python 自带一个名为`json`的内置模块,安装 Python 时会自动安装该模块,这个模块包含一些帮助处理 JSON 文件和字符串的功能。 我们将在接下来的示例中使用此模块。 -### 如何导入JSON模块 +### 如何导入 JSON 模块 要在程序中使用`json`,只需在文件顶部写一个导入语句。(译者注:实际上只要在使用它之前导入就行) @@ -144,13 +144,13 @@ JSON是用于表示和存储数据的文件格式,而Python字典是Pythons程 ![image-76](https://www.freecodecamp.org/news/content/images/2020/10/image-76.png) -## 🔹 Python和JSON字符串 +## 🔹 Python 和 JSON 字符串 -为了说明`json`模块中最重要的一些函数是如何工作的,我们将使用JSON格式的多行字符串。 +为了说明`json`模块中最重要的一些函数是如何工作的,我们将使用 JSON 格式的多行字符串。 -### JSON字符串 +### JSON 字符串 -特别地,这个字符串只是一个普通的遵循JSON格式的多行Python字符串,我们将在示例中使用它。 +特别地,这个字符串只是一个普通的遵循 JSON 格式的多行 Python 字符串,我们将在示例中使用它。 ```python data_JSON = """ @@ -167,16 +167,16 @@ data_JSON = """ """ ``` -JSON字符串 +JSON 字符串 -- 我们使用三重引号在Python中定义多行字符串。 +- 我们使用三重引号在 Python 中定义多行字符串。 - 然后我们将字符串赋给变量`data_JSON`。 💡 **提示:**[Python Style Guide](https://www.python.org/dev/peps/pep-0008/#string-quotes)建议三重引号字符串使用双引号。 -### JSON字符串到Python字典 +### JSON 字符串到 Python 字典 -我们将使用此JSON格式的字符串来创建一个可以访问、使用和修改的Python字典。 +我们将使用此 JSON 格式的字符串来创建一个可以访问、使用和修改的 Python 字典。 为此,我们将使用`json`模块的`loads()`函数,并将字符串作为参数传递进去。 @@ -214,7 +214,7 @@ data_dict = json.loads(data_JSON) data_dict = json.loads(data_JSON) ``` -- `json.loads(data_json)`使用JSON字符串的键-值对创建一个新字典,并返回这个字典。 +- `json.loads(data_json)`使用 JSON 字符串的键-值对创建一个新字典,并返回这个字典。 - 然后返回的字典被赋值给变量`data_dict`。 **太棒了!**如果我们打印这个字典,会看到以下输出: @@ -223,9 +223,9 @@ data_dict = json.loads(data_JSON) {'size': 'Medium', 'price': 15.67, 'toppings': ['Mushrooms', 'Extra Cheese', 'Pepperoni', 'Basil'], 'client': {'name': 'Jane Doe', 'phone': '455-344-234', 'email': 'janedoe@email.com'}} ``` -字典中已填充了JSON字符串的数据,每个键值对都被成功添加到字典里。 +字典中已填充了 JSON 字符串的数据,每个键值对都被成功添加到字典里。 -现在让我们尝试使用与访问常规Python字典相同的语法,来访问键-值对的值,看看会发生什么: +现在让我们尝试使用与访问常规 Python 字典相同的语法,来访问键-值对的值,看看会发生什么: ```python print(data_dict["size"]) @@ -245,25 +245,25 @@ Medium 每个键都可以用来访问其对应的值,正如我们所期望的那样。 -💡 **提示:**我们可以像使用任何其它Python字典一样来使用此字典。例如,我们可以调用字典的方法,添加、更新和删除键-值对,以及其它等等操作,我们甚至可以在for循环中使用它。 +💡 **提示:**我们可以像使用任何其它 Python 字典一样来使用此字典。例如,我们可以调用字典的方法,添加、更新和删除键-值对,以及其它等等操作,我们甚至可以在 for 循环中使用它。 -### JSON到Python:类型转换 +### JSON 到 Python:类型转换 -当使用`loads()`从JSON字符串来创建Python字典时,你会注意到一些值将被转换为Python中对应的值和数据类型。 +当使用`loads()`从 JSON 字符串来创建 Python 字典时,你会注意到一些值将被转换为 Python 中对应的值和数据类型。 -[Python文档](https://docs.python.org/3/library/json.html#encoders-and-decoders)上的这个`json`模块表格总结了JSON数据类型和Python数据类型的对应关系: +[Python 文档](https://docs.python.org/3/library/json.html#encoders-and-decoders)上的这个`json`模块表格总结了 JSON 数据类型和 Python 数据类型的对应关系: ![image-79](https://www.freecodecamp.org/news/content/images/2020/10/image-79.png) -这是官方[json模块文档](https://docs.python.org/3/library/json.html#encoders-and-decoders)中的表格 +这是官方[json 模块文档](https://docs.python.org/3/library/json.html#encoders-and-decoders)中的表格 -**💡 提示:**当我们处理JSON文件时,转换表同样也适用。 +**💡 提示:**当我们处理 JSON 文件时,转换表同样也适用。 -### Python字典到JSON字符串 +### Python 字典到 JSON 字符串 -现在你知道了如何用JSON格式的字符串创建Python字典。 +现在你知道了如何用 JSON 格式的字符串创建 Python 字典。 -但有时我们可能需要做相反的事情,即用对象(例如字典)创建JSON格式的字符串,以便打印、显示、存储,或者将其作为字符串使用。 +但有时我们可能需要做相反的事情,即用对象(例如字典)创建 JSON 格式的字符串,以便打印、显示、存储,或者将其作为字符串使用。 为此,我们可以使用`json`模块的`dumps`函数,该函数要求将(要转换的)对象作为参数传递: @@ -271,7 +271,7 @@ Medium **💡 提示:**此函数将返回一个字符串。 -这是一个例子,我们将Python字典`client`转换为JSON格式的字符串,并将其赋值给变量: +这是一个例子,我们将 Python 字典`client`转换为 JSON 格式的字符串,并将其赋值给变量: ```python # Python Dictionary @@ -293,7 +293,7 @@ client_JSON = json.dumps(client) client_JSON = json.dumps(client) ``` -- `json.dumps(client)`创建并返回一个包含字典中所有键-值对的JSON格式的字符串。 +- `json.dumps(client)`创建并返回一个包含字典中所有键-值对的 JSON 格式的字符串。 - 然后将此字符串赋值给`client_JSON`变量。 如果我们打印这个字符串,会看到如下输出: @@ -302,7 +302,7 @@ client_JSON = json.dumps(client) {"name": "Nora", "age": 56, "id": "45355", "eye_color": "green", "wears_glasses": false} ``` -💡 **提示:**请注意,最后一个值(`false`)已更改。在Python字典中,此值为`False`,但在JSON中等效值为`false`,这有助于我们确认原始字典现在确实已经表示为JSON格式的字符串。 +💡 **提示:**请注意,最后一个值(`false`)已更改。在 Python 字典中,此值为`False`,但在 JSON 中等效值为`false`,这有助于我们确认原始字典现在确实已经表示为 JSON 格式的字符串。 如果我们检查此变量(`client_JSON`)的数据类型,我们会看到: @@ -312,15 +312,15 @@ client_JSON = json.dumps(client) 所以这个函数的返回值确实是一个字符串。 -### Python到JSON:类型转换 +### Python 到 JSON:类型转换 -当我们将字典转换为JSON字符串时,也会发生类型转换过程。来自[Python文档](https://docs.python.org/3/library/json.html#json.JSONEncoder)的这张表显示了二者相对应的值: +当我们将字典转换为 JSON 字符串时,也会发生类型转换过程。来自[Python 文档](https://docs.python.org/3/library/json.html#json.JSONEncoder)的这张表显示了二者相对应的值: ![image-81](https://www.freecodecamp.org/news/content/images/2020/10/image-81.png) -表格来自[官方json模块文档](https://docs.python.org/3/library/json.html#json.JSONEncoder)。 +表格来自[官方 json 模块文档](https://docs.python.org/3/library/json.html#json.JSONEncoder)。 -### 如何使用缩进打印JSON数据 +### 如何使用缩进打印 JSON 数据 如果我们使用`dumps`函数打印上一示例中得到的字符串,我们会看到: @@ -330,9 +330,9 @@ client_JSON = json.dumps(client) 但是这样可读性不是很高,对吧? -我们可以通过添加**缩进**来提高JSON字符串的可读性。 +我们可以通过添加**缩进**来提高 JSON 字符串的可读性。 -我们只需传递第二个参数来指定要用于JSON字符串缩进的空格数,(`dumps`函数)就会自动执行此操作: +我们只需传递第二个参数来指定要用于 JSON 字符串缩进的空格数,(`dumps`函数)就会自动执行此操作: ![image-111](https://www.freecodecamp.org/news/content/images/2020/10/image-111.png) @@ -372,7 +372,7 @@ client_JSON = json.dumps(client, indent=4) client_JSON = json.dumps(client, sort_keys=True) ``` -将会返回键按字母顺序排序的JSON字符串: +将会返回键按字母顺序排序的 JSON 字符串: ```python {"age": 56, "eye_color": "green", "id": "45355", "name": "Nora", "wears_glasses": false} @@ -380,7 +380,7 @@ client_JSON = json.dumps(client, sort_keys=True) ### 如何同时按字母排序和使用缩进 -要生成(键)按字母顺序和有缩进的JSON字符串,只需要传递两个参数: +要生成(键)按字母顺序和有缩进的 JSON 字符串,只需要传递两个参数: ![image-104](https://www.freecodecamp.org/news/content/images/2020/10/image-104.png) @@ -398,19 +398,19 @@ client_JSON = json.dumps(client, sort_keys=True) **💡 提示:**可以按任何顺序(相对于彼此)传递这两个参数,但(要进行转换的)对象必须是第一个参数。 -太棒了,现在你已经知道如何使用JSON字符串,那就让我们看看如何在Python程序中处理JSON文件。 +太棒了,现在你已经知道如何使用 JSON 字符串,那就让我们看看如何在 Python 程序中处理 JSON 文件。 -## 🔸 JSON和文件 +## 🔸 JSON 和文件 -JSON通常用于将数据存储在文件中,因此Python为我们提供了在程序中读取这些类型的文件、处理文件的数据以及编写新数据所需的工具。 +JSON 通常用于将数据存储在文件中,因此 Python 为我们提供了在程序中读取这些类型的文件、处理文件的数据以及编写新数据所需的工具。 -**💡 提示:**JSON文件有一个`.json`扩展名: +**💡 提示:**JSON 文件有一个`.json`扩展名: ![image-62](https://www.freecodecamp.org/news/content/images/2020/10/image-62.png) -来看看如何在Python中处理`.json`文件。 +来看看如何在 Python 中处理`.json`文件。 -### 在Python中如何读取JSON文件 +### 在 Python 中如何读取 JSON 文件 假设我们创建了一个 `orders.json` 文件,文件中有披萨店两个订单的数据: @@ -447,14 +447,14 @@ JSON通常用于将数据存储在文件中,因此Python为我们提供了在 orders.json -请花点时间分析此JSON文件的结构。 +请花点时间分析此 JSON 文件的结构。 以下是一些提示: - 请注意值的数据类型、缩进和文件的整体结构。 -- 主键`"orders"`的值是一个JSON对象数组(这个数组在Python中表示列表),(数组里)每个JSON对象都保存了披萨订单的数据。 +- 主键`"orders"`的值是一个 JSON 对象数组(这个数组在 Python 中表示列表),(数组里)每个 JSON 对象都保存了披萨订单的数据。 -如果我们想在Python中读取此文件,只需要使用`with`语句: +如果我们想在 Python 中读取此文件,只需要使用`with`语句: ![image-87](https://www.freecodecamp.org/news/content/images/2020/10/image-87.png) @@ -466,12 +466,12 @@ orders.json data = json.load(file) ``` -- `json.load(file)`创建并返回一个新的包含JSON文件中键-值对的Python字典。 +- `json.load(file)`创建并返回一个新的包含 JSON 文件中键-值对的 Python 字典。 - 然后将该字典赋值给`data`变量。 💡 **提示:**请注意,我们使用的是`load()`而不是`loads()`,这是`json`模块中的不同函数。你将在本文的末尾了解更多它们的差异。 -一旦我们将JSON文件的内容作为字典存储在`data`变量中,我们就可以使用它做想要做的任何事情。 +一旦我们将 JSON 文件的内容作为字典存储在`data`变量中,我们就可以使用它做想要做的任何事情。 ### 例子 @@ -483,9 +483,9 @@ print(len(data["orders"])) 输出为`2`,因为主键`orders`的值是一个包含两个元素的列表。 -我们还可以使用键访问其对应的值,即处理JSON文件时通常要做的事情。 +我们还可以使用键访问其对应的值,即处理 JSON 文件时通常要做的事情。 -例如,要访问第一个订单的toppings,我们执行: +例如,要访问第一个订单的 toppings,我们执行: ``` data["orders"][0]["toppings"] @@ -505,21 +505,21 @@ data["orders"][0]["toppings"] ['mushrooms', 'pepperoni', 'basil'] ``` -这正是我们所期望的。你只需要通过使用必要的键和索引来“深入”了解字典的结构(可以使用原始JSON文件或字符串作为视觉参考),就可以访问、修改或删除任何值。 +这正是我们所期望的。你只需要通过使用必要的键和索引来“深入”了解字典的结构(可以使用原始 JSON 文件或字符串作为视觉参考),就可以访问、修改或删除任何值。 -**💡 提示:**请记住,我们正在使用新创建的字典,对此字典所做的更改不会影响原JSON文件。要更新文件的内容,需要写入文件中。 +**💡 提示:**请记住,我们正在使用新创建的字典,对此字典所做的更改不会影响原 JSON 文件。要更新文件的内容,需要写入文件中。 -### 如何写一个JSON文件 +### 如何写一个 JSON 文件 -让我们看看如何写一个JSON文件。 +让我们看看如何写一个 JSON 文件。 -第一行的`with`语句(和读JSON文件)非常相似,唯一的改变是需要以`'w'`(即写入)模式打开文件,这样才能修改文件。 +第一行的`with`语句(和读 JSON 文件)非常相似,唯一的改变是需要以`'w'`(即写入)模式打开文件,这样才能修改文件。 ![image-105](https://www.freecodecamp.org/news/content/images/2020/10/image-105.png) **💡 提示:**如果当前工作目录(文件夹)中文件不存在,则会自动创建该文件。如果文件存在,通过使用`'w'`模式,我们将替换文件的全部内容。 -在`with`语句中,有两种写入JSON文件的方法: +在`with`语句中,有两种写入 JSON 文件的方法: - `dump` - `dumps` @@ -530,12 +530,12 @@ data["orders"][0]["toppings"] 这是一个有两个参数的函数: -- 将以JSON格式存储的对象(例如字典)。 -- 将存储该JSON字符串的文件(即文件对象)。 +- 将以 JSON 格式存储的对象(例如字典)。 +- 将存储该 JSON 字符串的文件(即文件对象)。 ![image-91](https://www.freecodecamp.org/news/content/images/2020/10/image-91.png) -如果披萨店想从JSON文件中删除客户的数据,并创建一个新版本的`orders_new.json`文件。 +如果披萨店想从 JSON 文件中删除客户的数据,并创建一个新版本的`orders_new.json`文件。 我们可以通过以下代码完成此操作: @@ -602,11 +602,11 @@ orders\_new.json 然而,这个文件中缺少了一些东西,对吗? -请花点时间思考一下…可能是什么? +请花点时间思考一下……可能是什么? 当然是缩进! -该文件实际上看起来不像JSON文件,但我们可以通过将参数`indentation=4`传递给`dump()`来轻松解决这个问题。 +该文件实际上看起来不像 JSON 文件,但我们可以通过将参数`indentation=4`传递给`dump()`来轻松解决这个问题。 ![image-92](https://www.freecodecamp.org/news/content/images/2020/10/image-92.png) @@ -639,9 +639,9 @@ orders\_new.json orders\_new.json -多么大的变化啊!这正是我们期望的JSON文件的样子。 +多么大的变化啊!这正是我们期望的 JSON 文件的样子。 -现在你已经知道如何使用`load()`和`dump`来读取和写入JSON文件,就让我们看看这两个函数和用来处理JSON字符串的函数之间的区别。 +现在你已经知道如何使用`load()`和`dump`来读取和写入 JSON 文件,就让我们看看这两个函数和用来处理 JSON 字符串的函数之间的区别。 ## 🔹 load() vs. loads() @@ -659,20 +659,20 @@ orders\_new.json 💡 **提示:**将`dumps()`视为"dump string",这有助于记住函数处理的目标。 -## 🔹 JSON中的重要术语 +## 🔹 JSON 中的重要术语 -最后,使用JSON需要了解两个重要术语: +最后,使用 JSON 需要了解两个重要术语: -- **序列化:** 将一个对象转换为JSON字符串 -- **反序列化:** 将一个JSON字符串转换为对象 +- **序列化:** 将一个对象转换为 JSON 字符串 +- **反序列化:** 将一个 JSON 字符串转换为对象 ## 🔸 总结 - JSON(JavaScript Object Notation)是一种用于表示和存储的数据格式。 - 它通常用于存储配置信息和在网络上传输数据。 -- JSON文件有一个`.json`扩展名。 -- 可以将JSON字符串转换为Python对象,反之亦然。 -- 可以读取JSON文件并用其键-值对创建Python对象。 -- 可以以JSON格式存储Python对象的内容,并将其写入JSON文件。 +- JSON 文件有一个`.json`扩展名。 +- 可以将 JSON 字符串转换为 Python 对象,反之亦然。 +- 可以读取 JSON 文件并用其键-值对创建 Python 对象。 +- 可以以 JSON 格式存储 Python 对象的内容,并将其写入 JSON 文件。 -**我真的希望你喜欢我的文章,并觉得它很有帮助。**现在你已经知道如何在Python中使用JSON了。在Twitter上可以关注我[@EstefaniaCassN](https://twitter.com/EstefaniaCassN)和[查看我的在线课程](https://www.udemy.com/user/estefania-cn/)。 +**我真的希望你喜欢我的文章,并觉得它很有帮助。**现在你已经知道如何在 Python 中使用 JSON 了。在 Twitter 上可以关注我[@EstefaniaCassN](https://twitter.com/EstefaniaCassN)和[查看我的在线课程](https://www.udemy.com/user/estefania-cn/)。 diff --git a/chinese/articles/python-vs-javascript-what-are-the-key-differences-between-the-two-popular-programming-languages.md b/chinese/articles/python-vs-javascript-what-are-the-key-differences-between-the-two-popular-programming-languages.md index e156864cf..8dc136a6d 100644 --- a/chinese/articles/python-vs-javascript-what-are-the-key-differences-between-the-two-popular-programming-languages.md +++ b/chinese/articles/python-vs-javascript-what-are-the-key-differences-between-the-two-popular-programming-languages.md @@ -183,7 +183,7 @@ firstName ![](https://www.freecodecamp.org/news/content/images/2021/01/image-178.png) -## Python和 JavaScript 里的常量 +## Python 和 JavaScript 里的常量 可。现在已经了解了变量,现在来聊一聊常量。常量的值在程序执行期间不可改变。 @@ -247,7 +247,7 @@ const TAX_RATE_PERCENTAGE = 32; 根据 MDN Web 文档的 [Number][7]: -> JavaScript 代码中的数字如 `37` 是浮点值,而不是整数。 日常使用中没有单独的整数类型。(JavaScript 现在有了 [BigInt][8] 类型,但并非旨在用于替换日常使用的 Number 。`37` 仍然是Number而不是BigInt。) +> JavaScript 代码中的数字如 `37` 是浮点值,而不是整数。 日常使用中没有单独的整数类型。(JavaScript 现在有了 [BigInt][8] 类型,但并非旨在用于替换日常使用的 Number 。`37` 仍然是 Number 而不是 BigInt。) ### None vs. null @@ -390,7 +390,7 @@ False ![](https://www.freecodecamp.org/news/content/images/2021/01/image-150.png) -在JavaScript中,要检查值**和**数据类型是否相等,我们需要使用此运算符 `===`(严格相等运算符)。 +在 JavaScript 中,要检查值**和**数据类型是否相等,我们需要使用此运算符 `===`(严格相等运算符)。 现在我们得到了预期的结果: @@ -398,7 +398,7 @@ False 棒! -**💡提示:** Python中的 `==` 等价于JavaScript中的 `===` 运算符。 +**💡提示:** Python 中的 `==` 等价于 JavaScript 中的 `===` 运算符。 ### 逻辑运算符 @@ -433,7 +433,7 @@ False ![](https://www.freecodecamp.org/news/content/images/2021/01/image-184.png) -对于 JavaScript,可以打开Chrome Developer工具并在控制台中输入以下代码: +对于 JavaScript,可以打开 Chrome Developer 工具并在控制台中输入以下代码: ![](https://www.freecodecamp.org/news/content/images/2021/01/image-163.png) @@ -554,7 +554,7 @@ const object = { a: 1, b: 2, c: 3 }; ![](https://www.freecodecamp.org/news/content/images/2021/01/image-168.png) -### While循环 +### While 循环 While 循环在 Python 和 JavaScript 中非常相似。 diff --git a/chinese/articles/react-and-googlesheets.md b/chinese/articles/react-and-googlesheets.md index f0cf751a2..f8441dd3c 100644 --- a/chinese/articles/react-and-googlesheets.md +++ b/chinese/articles/react-and-googlesheets.md @@ -202,7 +202,7 @@ axios.post('url', this.state) submitHandler 方法 -用上面的代码替换 SubmitHandler 函数。 在这里,我们使用 Axios 将数据发送到URL,并通过 **.then** 关键字在控制台中获取响应。 +用上面的代码替换 SubmitHandler 函数。 在这里,我们使用 Axios 将数据发送到 URL,并通过 **.then** 关键字在控制台中获取响应。 把 axios.post('url') 中的 URL 替换成 **sheet.best** 中的 CONNECTION URL。 @@ -218,7 +218,7 @@ axios.post('https://sheet.best/api/sheets/a6e67deb-2f00-43c3-89d3-b331341d53ed', submitHandler 函数 -现在,打开Google表格,然后填写第一列,即姓name、age、salary 和 hobby。请仔细填写,否则将无法正常工作。 应该区分大小写。 +现在,打开 Google 表格,然后填写第一列,即姓 name、age、salary 和 hobby。请仔细填写,否则将无法正常工作。 应该区分大小写。 ![](https://www.freecodecamp.org/news/content/images/2021/02/Screenshot-2021-02-15-02-43-12.png) diff --git a/chinese/articles/react-interview-questions-and-answers.md b/chinese/articles/react-interview-questions-and-answers.md index 4b3ca24d6..8dec753f1 100644 --- a/chinese/articles/react-interview-questions-and-answers.md +++ b/chinese/articles/react-interview-questions-and-answers.md @@ -5,71 +5,71 @@ ![React Interview Questions – Interview Prep with Answers and Examples](https://www.freecodecamp.org/news/content/images/size/w2000/2022/06/cover-template.jpg) -React是一个用于创建用户界面的JavaScript库。它被用于超过3万个实时网站,并拥有超过7万个GitHub星。 +React 是一个用于创建用户界面的 JavaScript 库。它被用于超过 3 万个实时网站,并拥有超过 7 万个 GitHub 星。 -根据[2021年Stack Overflow开发者调查报告](https://insights.stackoverflow.com/survey/2021#section-most-popular-technologies-web-frameworks)的数据显示:React已经超越jQuery成为最流行的 Web框架,占据了大约40.14%的市场份额。除此之外,React也是最受欢迎的库,每四个开发人员中就有一个在使用它。 +根据[2021 年 Stack Overflow 开发者调查报告](https://insights.stackoverflow.com/survey/2021#section-most-popular-technologies-web-frameworks)的数据显示:React 已经超越 jQuery 成为最流行的 Web 框架,占据了大约 40.14%的市场份额。除此之外,React 也是最受欢迎的库,每四个开发人员中就有一个在使用它。 -包括 LinkedIn、Twitter和AirBnB在内的8千多个行业领导者使用React。 +包括 LinkedIn、Twitter 和 AirBnB 在内的 8 千多个行业领导者使用 React。 -React开发者在美国的平均年薪是12万美元,许多企业和公司都使用React,所有它们在不断寻找新的React人才。 +React 开发者在美国的平均年薪是 12 万美元,许多企业和公司都使用 React,所有它们在不断寻找新的 React 人才。 -在这篇文章中,我们将介绍一些React基础知识,看一看相关面试问题及其正确答案,以帮助你在React面试中取得成功。 +在这篇文章中,我们将介绍一些 React 基础知识,看一看相关面试问题及其正确答案,以帮助你在 React 面试中取得成功。 -## React是什么? +## React 是什么? -React是一个用于创建用户界面的开源前端JavaScript库。其声明性的、高效的、灵活的特点,以及坚持基于组件的方法,使得我们可以使用它创建可复用的UI组件。 +React 是一个用于创建用户界面的开源前端 JavaScript 库。其声明性的、高效的、灵活的特点,以及坚持基于组件的方法,使得我们可以使用它创建可复用的 UI 组件。 -开发人员主要使用React来创建单页应用程序,该库只专注于[MVC](https://zh.m.wikipedia.org/zh-hans/MVC)的视图层。它的速度也非常快。 +开发人员主要使用 React 来创建单页应用程序,该库只专注于[MVC](https://zh.m.wikipedia.org/zh-hans/MVC)的视图层。它的速度也非常快。 -## React的特点 +## React 的特点 -React的许多特性使其与众不同,这里有几个亮点: +React 的许多特性使其与众不同,这里有几个亮点: -- React采用了虚拟DOM,而不是真实/浏览器DOM。 -- React采用单向数据绑定。 -- React即可用于开发web应用程序,也可以使用[React Native](https://reactnative.dev/)来开发移动端应用,所以可以使用React创建跨平台应用。 +- React 采用了虚拟 DOM,而不是真实/浏览器 DOM。 +- React 采用单向数据绑定。 +- React 即可用于开发 web 应用程序,也可以使用[React Native](https://reactnative.dev/)来开发移动端应用,所以可以使用 React 创建跨平台应用。 -## 如何使用React开发一个新项目 +## 如何使用 React 开发一个新项目 -我们可以通过初始化一个项目并安装所有依赖项来从头创建一个React应用。 但最简单的办法是通过以下命令——[create-react-app](https://reactjs.org/docs/create-a-new-react-app.html#create-react-app)来创建: +我们可以通过初始化一个项目并安装所有依赖项来从头创建一个 React 应用。 但最简单的办法是通过以下命令——[create-react-app](https://reactjs.org/docs/create-a-new-react-app.html#create-react-app)来创建: ```bash npx create-react-app my-app ``` -**注意:** my-app是我们创建的应用的名称,你可以根据自己的喜好修改。 +**注意:** my-app 是我们创建的应用的名称,你可以根据自己的喜好修改。 更多相关信息可以查阅[这篇文章](https://www.freecodecamp.org/news/get-started-with-react-for-beginners/)。 -## DOM代表什么? +## DOM 代表什么? -术语“DOM”代表了文档对象模型(**D**ocument **O**bject **M**odel),指的是将web应用的整个用户界面表示为一个树状数据结构。 +术语“DOM”代表了文档对象模型(**D**ocument **O**bject **M**odel),指的是将 web 应用的整个用户界面表示为一个树状数据结构。 -### DOM的种类 +### DOM 的种类 -DOM的种类有两种:虚拟DOM和真实DOM。 +DOM 的种类有两种:虚拟 DOM 和真实 DOM。 -## React的优缺点 +## React 的优缺点 -以下是React的优缺点: +以下是 React 的优缺点: -### React的优点 +### React 的优点 -1. 对于JavaScript 开发人员来说,React的学习曲线更短,并且由于社区活跃,有大量的手册、教程和培训材料。 -2. React易使用搜索引擎搜索。 -3. React降低了创建丰富的 UI 和自定义组件的难度。 -4. React渲染速度快。 +1. 对于 JavaScript 开发人员来说,React 的学习曲线更短,并且由于社区活跃,有大量的手册、教程和培训材料。 +2. React 易使用搜索引擎搜索。 +3. React 降低了创建丰富的 UI 和自定义组件的难度。 +4. React 渲染速度快。 5. 使用 JSX 可以让我们编写更简单、更吸引人且更易于理解的代码。 -### React的缺点 +### React 的缺点 -1. React只是一个前端库,所以需要搭配其他语言和库来构建一个完整的应用程序。 -2. JSX对于没有经验的程序员来说可能很难上手。 +1. React 只是一个前端库,所以需要搭配其他语言和库来构建一个完整的应用程序。 +2. JSX 对于没有经验的程序员来说可能很难上手。 3. 由于开发周期短,现有文档很快就会过时。 -## 什么是JSX? +## 什么是 JSX? -JavaScript XML被缩写为JSX。JSX使得在React创建HTML更加容易,标记可读性更强,更容易被理解。 +JavaScript XML 被缩写为 JSX。JSX 使得在 React 创建 HTML 更加容易,标记可读性更强,更容易被理解。 例如: @@ -83,25 +83,25 @@ const App = () => { } ``` -想了解更多关于React中的JSX的信息,[可以阅读这篇文章](https://www.freecodecamp.org/news/jsx-in-react-introduction/)。 +想了解更多关于 React 中的 JSX 的信息,[可以阅读这篇文章](https://www.freecodecamp.org/news/jsx-in-react-introduction/)。 -## 为什么浏览器不能读取JSX? +## 为什么浏览器不能读取 JSX? -JSX并不是有效的JavaScript代码,浏览器没有内置读取和理解JSX的功能。我们需要将JSX编译成浏览器可以理解的JavaScript代码。所以我们使用[Babel](https://babeljs.io/),一个JavaScript的编译器/转译器来实现这个功能。 +JSX 并不是有效的 JavaScript 代码,浏览器没有内置读取和理解 JSX 的功能。我们需要将 JSX 编译成浏览器可以理解的 JavaScript 代码。所以我们使用[Babel](https://babeljs.io/),一个 JavaScript 的编译器/转译器来实现这个功能。 -注意: [create-react-app](https://github.com/facebook/create-react-app)的内部使用Babel进行JSX到JavaScript的转换,但你也可以使用Webpack设置自己的babel配置。 +注意: [create-react-app](https://github.com/facebook/create-react-app)的内部使用 Babel 进行 JSX 到 JavaScript 的转换,但你也可以使用 Webpack 设置自己的 babel 配置。 -想了解更多React中的JSX的信息,[可以阅读这篇文章](https://www.freecodecamp.org/news/jsx-in-react-introduction/)。 +想了解更多 React 中的 JSX 的信息,[可以阅读这篇文章](https://www.freecodecamp.org/news/jsx-in-react-introduction/)。 ## 组件是什么? -组件是一个独立的、可复用的代码块,它将用户界面分成更小的部分,而不是在单个文件中构建整个UI。 +组件是一个独立的、可复用的代码块,它将用户界面分成更小的部分,而不是在单个文件中构建整个 UI。 -React 中有两种组件:函数式组件和class组件。 +React 中有两种组件:函数式组件和 class 组件。 -### 什么是class组件? +### 什么是 class 组件? -class组件是返回JSX的ES6类,必须从React继承。因为在React的早期版本(16.8)中不能在函数组件内使用状态,所以函数组件只用于渲染UI。 +class 组件是返回 JSX 的 ES6 类,必须从 React 继承。因为在 React 的早期版本(16.8)中不能在函数组件内使用状态,所以函数组件只用于渲染 UI。 例如: @@ -118,13 +118,13 @@ export default class App extends Component { } ``` -你可以阅读[这篇文章](https://www.freecodecamp.org/news/react-components-jsx-props-for-beginners/)了解更多React组件和组件类型。 +你可以阅读[这篇文章](https://www.freecodecamp.org/news/react-components-jsx-props-for-beginners/)了解更多 React 组件和组件类型。 ### 什么是函数组件? -一个返回React元素的JavaScript/ES6函数被称为函数组件(JSX)。 +一个返回 React 元素的 JavaScript/ES6 函数被称为函数组件(JSX)。 -自从引入React Hooks以来,我们已经能够在函数式组件中使用状态,因为语法更简洁,很多人都采用这个方法。 +自从引入 React Hooks 以来,我们已经能够在函数式组件中使用状态,因为语法更简洁,很多人都采用这个方法。 例如: @@ -140,31 +140,31 @@ const App = () => { export default App; ``` -你可以阅读[这篇文章](https://www.freecodecamp.org/news/react-components-jsx-props-for-beginners/)了解更多React组件和组件类型。 +你可以阅读[这篇文章](https://www.freecodecamp.org/news/react-components-jsx-props-for-beginners/)了解更多 React 组件和组件类型。 -### 函数组件和class组件之间的区别 +### 函数组件和 class 组件之间的区别 <table style="width: 100%;border-spacing: 0;border: 1px solid #c1c7cd;word-break: break-word;"><tbody><tr><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 0;border-bottom-width: 0;border-right-width: 0;border-left-width: 0;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">                       </span><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4"><strong>  函数组件     </strong></span><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">             </span></div></td><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 0;border-bottom-width: 0;border-right-width: 0;border-left-width: 1px;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">                        </span><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4"><strong> class组件                </strong></span></div></td></tr><tr><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 0;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">函数式组件是一个JavaScript/ES6函数,它接受一个参数、props并返回JSX。</span></div></td><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 1px;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">要创建一个一个class组件,必须从React中继承。创建一个组件和一个返回React元素的渲染函数。</span></div></td></tr><tr><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 0;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">函数组件中没有渲染方法</span></div></td><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 1px;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">必须使用render()方法返回JSX</span></div></td></tr><tr><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 0;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">函数组件从上至下运行,一旦返回就无法保持活动状态。</span></div></td><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 1px;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">class组件被实例化,各种生命周期方法根据类组件的阶段保持活跃运行并且被调用。</span></div></td></tr><tr><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 0;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">它们也被称为无状态组件,因为它们只接受数据并以某种形式显示,它们主要负责UI渲染。</span></div></td><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 1px;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">它们也被称为有状态组件,因为它们实现了逻辑和状态。</span></div></td></tr><tr><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 0;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">不能在函数组件中使用React生命周期方法。</span></div></td><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 1px;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">可以在class组件中使用React生命周期方法。</span></div></td></tr><tr><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 0;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">Hooks如</span><span class="inline-code author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4"><span style="font-family: monospace; background-color: #f7f9fa; color: #1b2733; border: 1px solid rgba(208,212,217,0.5); margin: 0 0 0 -1px;">useState()</span></span><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">被采用以使得函数组件有状态。</span></div></td><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 1px;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">在class组件中要使用另外的语法来实现hook。</span></div></td></tr><tr><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 0;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">不使用构造函数(constructor)。</span></div></td><td style="border-color: #c1c7cd;border-style: solid;border-top-width: 1px;border-bottom-width: 0;border-right-width: 0;border-left-width: 1px;min-width: 50px;min-height: 20px;padding: 5px 8px;word-break: normal;vertical-align: top;"><div dir="auto" style="" class="ace-line gutter-author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4 ace-ltr"><span class=" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qowz82zpihz77zcerkden8iz78zz86zz75zz66zz71z0z85zz87zz69zqrz72zz85z0z77zz81z4">用构造函数(constructor)来存储状态 </span></div></td></tr></tbody></table> -## 如何在React中使用CSS +## 如何在 React 中使用 CSS -使用CSS设置React应用程序样式的方法有3种: +使用 CSS 设置 React 应用程序样式的方法有 3 种: - 内联样式 - 外部样式 -- JS中的CSS +- JS 中的 CSS -更多信息,可以阅读[这篇关于设置React样式的文章](https://www.freecodecamp.org/news/how-to-style-react-apps-with-css/)。 +更多信息,可以阅读[这篇关于设置 React 样式的文章](https://www.freecodecamp.org/news/how-to-style-react-apps-with-css/)。 -## React中的render()的用途是什么? +## React 中的 render()的用途是什么? -在class组件中使用`Render()`,来返回在组件中显示的HTML。 它被用作读取props和state并将JSX代码返回到应用的根组件。 +在 class 组件中使用`Render()`,来返回在组件中显示的 HTML。 它被用作读取 props 和 state 并将 JSX 代码返回到应用的根组件。 -## 什么是Props? +## 什么是 Props? -Props也被称作属性。它们将数据从一个组件传递到另一个组件(从父组件到子组件)。它们通常被用来呈现动态数据。 +Props 也被称作属性。它们将数据从一个组件传递到另一个组件(从父组件到子组件)。它们通常被用来呈现动态数据。 -注意:子组件永远不能将props发送到父组件,因为此流程是单向的(父到子)。 +注意:子组件永远不能将 props 发送到父组件,因为此流程是单向的(父到子)。 例如: @@ -181,13 +181,13 @@ function App({name, hobby}) { export default App; ``` -向更多信息,可以阅读[这篇讲解React Props是如何运作的文章](https://www.freecodecamp.org/news/how-to-use-props-in-react/)。 +向更多信息,可以阅读[这篇讲解 React Props 是如何运作的文章](https://www.freecodecamp.org/news/how-to-use-props-in-react/)。 -## React中的State是什么? +## React 中的 State 是什么? -State是一个React的内置对象,用于在组件中创建和管理数据。它与props的不同之处在于它用于存储数据而不是传递数据。 +State 是一个 React 的内置对象,用于在组件中创建和管理数据。它与 props 的不同之处在于它用于存储数据而不是传递数据。 -State是可变的(数据可以更改)并且可以通过`this.state()`访问。 +State 是可变的(数据可以更改)并且可以通过`this.state()`访问。 例如: @@ -210,11 +210,11 @@ class App extends React.Component { } ``` -## 如何更新一个React组件的State +## 如何更新一个 React 组件的 State -需要知道一个重要的信息是,当我们直接更新state时,它不会重新渲染组件——这意味着我们看不到更新。 +需要知道一个重要的信息是,当我们直接更新 state 时,它不会重新渲染组件——这意味着我们看不到更新。 -如果需要重新渲染,我们要使用 `setState()`方法,它会更新组件的state对象并重新渲染组件。 +如果需要重新渲染,我们要使用 `setState()`方法,它会更新组件的 state 对象并重新渲染组件。 例如: @@ -245,15 +245,15 @@ class App extends React.Component { 更多信息可以参考[这里](https://www.freecodecamp.org/news/react-js-for-beginners-props-state-explained/)。 -## 如何区分State和Props +## 如何区分 State 和 Props -State和props是具有不同功能的JavaScript对象。 +State 和 props 是具有不同功能的 JavaScript 对象。 -props用于将数据从父组件传输到子组件,而state是对本地数据的存储,仅对当前组件可用,不能与其他组件共享。 +props 用于将数据从父组件传输到子组件,而 state 是对本地数据的存储,仅对当前组件可用,不能与其他组件共享。 -## React中的事件是什么? +## React 中的事件是什么? -在React中,事件是一个可以由用户行为或系统生成的事件触发的动作。鼠标点击、网页加载、按键、窗口大小调整、滚动和其他交互都是事件。 +在 React 中,事件是一个可以由用户行为或系统生成的事件触发的动作。鼠标点击、网页加载、按键、窗口大小调整、滚动和其他交互都是事件。 例子: @@ -265,13 +265,13 @@ props用于将数据从父组件传输到子组件,而state是对本地数据 <button type="button" onClick={changeName} >Change Name</button> ``` -## 如何在React中处理事件 +## 如何在 React 中处理事件 -React中的事件处理方式与DOM元素类似。但值得注意的是对事件的命名,在React中事件是用小驼峰式(camelCase)而不是纯小写。 +React 中的事件处理方式与 DOM 元素类似。但值得注意的是对事件的命名,在 React 中事件是用小驼峰式(camelCase)而不是纯小写。 例子: -### class组件 +### class 组件 ```javascript class App extends Component { @@ -326,7 +326,7 @@ const App = () => { export default App; ``` -在class组件中,我们可以这样做: +在 class 组件中,我们可以这样做: ```javascript class App extends React.Component { @@ -344,25 +344,25 @@ class App extends React.Component { export default App; ``` -## 什么是Redux? +## 什么是 Redux? -Redux是一个流行的开源JavaScript库,用于集中管理应用程序的状态。它通常与React或其他视图库一起使用。 +Redux 是一个流行的开源 JavaScript 库,用于集中管理应用程序的状态。它通常与 React 或其他视图库一起使用。 -想了解更多[redux信息可以阅读这篇文章](https://www.freecodecamp.org/news/redux-tutorial-for-beginners/#:~:text=Redux%20is%20a%20popular%20open,you%20how%20to%20use%20Redux.)。 +想了解更多[redux 信息可以阅读这篇文章](https://www.freecodecamp.org/news/redux-tutorial-for-beginners/#:~:text=Redux%20is%20a%20popular%20open,you%20how%20to%20use%20Redux.)。 -## 什么是React Hooks(钩子)? +## 什么是 React Hooks(钩子)? -React Hooks在16.8版中被加入,使我们能够在不编写class组件的情况下,使用state和其他React功能。 +React Hooks 在 16.8 版中被加入,使我们能够在不编写 class 组件的情况下,使用 state 和其他 React 功能。 -它们不在class组件中运行,而是辅助函数组件和React状态和生命周期特性相勾连。 +它们不在 class 组件中运行,而是辅助函数组件和 React 状态和生命周期特性相勾连。 -### 我们从什么时候开始在React中使用hooks? +### 我们从什么时候开始在 React 中使用 hooks? -React团队在2018年10月下旬的React Conf(React年度大会)期间首次向全世界介绍了React Hooks,然后在 2019 年 2 月上旬 React v16.8.0中开始使用hooks。 +React 团队在 2018 年 10 月下旬的 React Conf(React 年度大会)期间首次向全世界介绍了 React Hooks,然后在 2019 年 2 月上旬 React v16.8.0 中开始使用 hooks。 -## 解释useState() +## 解释 useState() -useState Hook是一个可以在函数组件中使用状态变量的存储。你可以把初始状态传给这个函数,它将返回一个包含当前状态值(不一定是初始状态)的变量和另一个更新这个值的函数。 +useState Hook 是一个可以在函数组件中使用状态变量的存储。你可以把初始状态传给这个函数,它将返回一个包含当前状态值(不一定是初始状态)的变量和另一个更新这个值的函数。 例子: @@ -380,11 +380,11 @@ const App = () => { } ``` -## 解释useEffect() +## 解释 useEffect() -useEffect Hook允许你在组件中执行副作用,例如数据获取、直接更新DOM、使用setTimeout()之类的计时器等等。 +useEffect Hook 允许你在组件中执行副作用,例如数据获取、直接更新 DOM、使用 setTimeout()之类的计时器等等。 -这个hook接受两个参数:回调函数和依赖项,它们允许您控制何时执行副作用。 +这个 hook 接受两个参数:回调函数和依赖项,它们允许您控制何时执行副作用。 注意:第二个参数是可选的。 @@ -413,9 +413,9 @@ const App = () => { export default App; ``` -## useMemo() hook的用途是什么? +## useMemo() hook 的用途是什么? - `useMemo()` hook在函数组件中使用来记忆昂贵的函数,以便它们仅在设定的输入更改时调用,而不是每次渲染都调用。 + `useMemo()` hook 在函数组件中使用来记忆昂贵的函数,以便它们仅在设定的输入更改时调用,而不是每次渲染都调用。 例子: @@ -423,16 +423,16 @@ export default App; const result = useMemo(() => expensivesunction(input), [input]); ``` -它类似于useCallback hook,用于优化React函数组件的渲染行为。 useMemo用于记忆昂贵的函数,以便不必在每次渲染时调用它们。 +它类似于 useCallback hook,用于优化 React 函数组件的渲染行为。 useMemo 用于记忆昂贵的函数,以便不必在每次渲染时调用它们。 -## useRef hook是什么? +## useRef hook 是什么? -`useRef()`hook,也被称为References hook,用于存储在更新时不需要重新渲染的可变值。也被用来存储特定React元素或组件的引用,当我们需要测量DOM或直接向组件添加方法时能派上用场。 +`useRef()`hook,也被称为 References hook,用于存储在更新时不需要重新渲染的可变值。也被用来存储特定 React 元素或组件的引用,当我们需要测量 DOM 或直接向组件添加方法时能派上用场。 -useRefs的使用场景: +useRefs 的使用场景: - 调整焦点,在文本和媒体播放之间进行选择。 -- 使用第三方DOM库。 +- 使用第三方 DOM 库。 - 启动命令式动画。 例子: @@ -457,23 +457,23 @@ const App = () => { export default App; ``` -## 什么是自定义hook? +## 什么是自定义 hook? -自定义hook式你自己编写的JavaScript函数,来在不同的组件之间共享某种逻辑,以前的React组件并不支持这个功能。 +自定义 hook 式你自己编写的 JavaScript 函数,来在不同的组件之间共享某种逻辑,以前的 React 组件并不支持这个功能。 -如果你对它是如何运作的感兴趣,可以参考这篇文章——[分步骤指导你构建自己的自定义hook](https://www.freecodecamp.org/news/how-to-create-react-hooks/)。 +如果你对它是如何运作的感兴趣,可以参考这篇文章——[分步骤指导你构建自己的自定义 hook](https://www.freecodecamp.org/news/how-to-create-react-hooks/)。 -## React的context(上下文)是什么? +## React 的 context(上下文)是什么? -上下文的目的是为React组件树共享 "全局 "数据,允许数据向下传递,并在不使用props的情况下,让React应用中任何需要该数据的组件中使用(消费)。它使得组件之间分享数据(状态)更容易。 +上下文的目的是为 React 组件树共享 "全局 "数据,允许数据向下传递,并在不使用 props 的情况下,让 React 应用中任何需要该数据的组件中使用(消费)。它使得组件之间分享数据(状态)更容易。 -你可以在这篇[React上下文指南](https://www.freecodecamp.org/news/react-context-for-beginners/)中获取更多信息。 +你可以在这篇[React 上下文指南](https://www.freecodecamp.org/news/react-context-for-beginners/)中获取更多信息。 -## 什么是React Router? +## 什么是 React Router? -React Router是React应用程序中使用的标准库,用于处理路由并在各种组件的视图之间导航。 +React Router 是 React 应用程序中使用的标准库,用于处理路由并在各种组件的视图之间导航。 -将此库安装到你的React项目中就像在终端中输入以下命令一样简单: +将此库安装到你的 React 项目中就像在终端中输入以下命令一样简单: ```bash npm install – -save react-router-dom @@ -481,6 +481,6 @@ npm install – -save react-router-dom ## 总结 -在这个教程中,我们回顾了一些React面试问题,以帮助你准备面试。FreeCodeCamp的所有工作人员都祝愿你在面试中取得好成绩。 +在这个教程中,我们回顾了一些 React 面试问题,以帮助你准备面试。FreeCodeCamp 的所有工作人员都祝愿你在面试中取得好成绩。 -与其同时对各种各样的课程浅尝则止,不如找一个教程并且完成它,这样你将收获得更多,并且掌握React,在面试环节披荆斩棘。如果有兴趣的话,可以尝试freeCodeCamp的[2022年免费React课程](https://www.freecodecamp.org/news/free-react-course-2022/)或者[学习React - 初学者完全课程](https://www.freecodecamp.org/news/learn-react-course/)。 +与其同时对各种各样的课程浅尝则止,不如找一个教程并且完成它,这样你将收获得更多,并且掌握 React,在面试环节披荆斩棘。如果有兴趣的话,可以尝试 freeCodeCamp 的[2022 年免费 React 课程](https://www.freecodecamp.org/news/free-react-course-2022/)或者[学习 React - 初学者完全课程](https://www.freecodecamp.org/news/learn-react-course/)。 diff --git a/chinese/articles/relational-vs-nonrelational-databases-difference-between-sql-db-and-nosql-db.md b/chinese/articles/relational-vs-nonrelational-databases-difference-between-sql-db-and-nosql-db.md index bf6bcbf75..706008c99 100644 --- a/chinese/articles/relational-vs-nonrelational-databases-difference-between-sql-db-and-nosql-db.md +++ b/chinese/articles/relational-vs-nonrelational-databases-difference-between-sql-db-and-nosql-db.md @@ -268,7 +268,7 @@ BASE 是以下的首字母缩写词: 以下是它们特征的快速摘要,可帮助决定哪一个可能更适合。 -### 何时使用 SQL 数据库: +### 何时使用 SQL 数据库 - 需要分布在多个表中的高度结构化的数据。需要数据遵守严格的、可预测的、预定义的和已经计划好的模式。 - 数据将保持相对不变。如果不打算频繁更改数据库的结构并且不需要定期更新项目,SQL 数据库会很方便。请记住,它们提供的灵活性很小。 @@ -284,7 +284,7 @@ SQL 数据库的一个缺点是它们是垂直扩展的。 需要增加处理能力和内存存储来处理增加的负载以提高性能。 -### 何时使用 NoSQL 数据库: +### 何时使用 NoSQL 数据库 - 在一个快速的开发环境中工作,需要经常调整需求并不断更改数据库结构。 - 正在处理大量性质不同但不需要大量结构或准确性的数据。 diff --git a/chinese/articles/rest-api-design-best-practices-build-a-rest-api.md b/chinese/articles/rest-api-design-best-practices-build-a-rest-api.md index d51fa5af3..3b877d2cb 100644 --- a/chinese/articles/rest-api-design-best-practices-build-a-rest-api.md +++ b/chinese/articles/rest-api-design-best-practices-build-a-rest-api.md @@ -5,19 +5,19 @@ ![REST API Design Best Practices – How to Build a REST API with JavaScript, Node.js, and Express.js](https://www.freecodecamp.org/news/content/images/size/w2000/2022/05/rest-api-design-course-header.png) -在过去几年我创建和使用过不少API,期间我遇到过优秀的实践方式,也遭遇过极其不好的实践方式,但曙光总是存在。 +在过去几年我创建和使用过不少 API,期间我遇到过优秀的实践方式,也遭遇过极其不好的实践方式,但曙光总是存在。 -网上有许多最佳实践相关的文章,但是他们大多数都缺乏实用性。通过少量示例来了解理论是一个好办法,但是我一直都在思考如何通过更实际的例子来展现API的应用。 +网上有许多最佳实践相关的文章,但是他们大多数都缺乏实用性。通过少量示例来了解理论是一个好办法,但是我一直都在思考如何通过更实际的例子来展现 API 的应用。 简单的例子确实可以帮助概念的理解,也省去了复杂度。但实际情况往往并不简单,我确信你对此也深有体会。 😁 -这就是我决定写这个教程的原因。我将过去好的坏的学习经验都融入了到这边文章之中,并提供例子,使文章易读易懂。我们就会通过一个又一个最佳实践来创建一个完整的API。 +这就是我决定写这个教程的原因。我将过去好的坏的学习经验都融入了到这边文章之中,并提供例子,使文章易读易懂。我们就会通过一个又一个最佳实践来创建一个完整的 API。 开始之前的注意事项: 如你所想,最佳实践并不是必须遵从的具体规则。它们是人们逐渐总结出来的有效的惯例,确实有一些成为现在的标准,但这并不意味着你需要百分之一百的采用这些实践。 -最佳实践指导如何使API更加符合用户(使用API的人和其他工程师)的使用习惯、更加安全和提高性能。 +最佳实践指导如何使 API 更加符合用户(使用 API 的人和其他工程师)的使用习惯、更加安全和提高性能。 请记住不同的项目有不同的实践方法,肯定存在一些情况下你无法遵守这些规范,每一个工程师都应该自己决定使用什么方法。 @@ -29,17 +29,17 @@ - [前提条件](#prerequisites) - [结构](#architecture) - [基础设置](#basic-setup) -- [REST API最佳实践](#rest-api-best-practices) +- [REST API 最佳实践](#rest-api-best-practices) - [版本](#versioning) - [用复数形式命名资源](#name-resources-in-plural) - - [以JSON格式接受和响应数据](#accept-and-respond-with-data-in-json-format) - - [响应标准HTTP错误代码](#respond-with-standard-http-error-codes) + - [以 JSON 格式接受和响应数据](#accept-and-respond-with-data-in-json-format) + - [响应标准 HTTP 错误代码](#respond-with-standard-http-error-codes) - [避免在端点使用动词](#avoid-verbs-in-endpoint-names) - [帮相关资源放在一起](#group-associated-resources-together-logical-nesting-) - [集成过滤排序和分页功能](#integrate-filtering-sorting-pagination) - [使用数据缓存提升性能](#use-data-caching-for-performance-improvements) - [好的安全实践](#good-security-practices) - - [编写API合适的文档](#document-your-api-properly) + - [编写 API 合适的文档](#document-your-api-properly) - [总结](#conclusion) <h2 id="our-example-project">示例项目</h2> @@ -50,39 +50,39 @@ 在正式开始在示例中应用最佳实践前,我先简单介绍一下我们要创建什么。 -我们将为交叉训练应用创建REST API。交叉训练是一种健身方式,融合了竞技类运动和高强度训练,以及各种各样的运动元素(奥林匹克举重、体操等)。 +我们将为交叉训练应用创建 REST API。交叉训练是一种健身方式,融合了竞技类运动和高强度训练,以及各种各样的运动元素(奥林匹克举重、体操等)。 -这个应用可以创建、读取、更新和删除**WOD**(**W**orkout **o**f the **D**ay即每日训练),帮助用户(健身馆主)指定和维护已有的健身计划。除此之外,还可以在一些重要的训练旁批注一些训练建议。 +这个应用可以创建、读取、更新和删除**WOD**(**W**orkout **o**f the **D**ay 即每日训练),帮助用户(健身馆主)指定和维护已有的健身计划。除此之外,还可以在一些重要的训练旁批注一些训练建议。 -我们的工作就是设计和实现这个应用的API。 +我们的工作就是设计和实现这个应用的 API。 <h3 id="prerequisites">前提条件</h3> -在学习这门教程之前,你必须使用过JavaScript, Node.js, Express.js以及后端架构,熟悉REST和API这类术语,并且了解[主从式架构(客户端/服务器架构)](https://en.wikipedia.org/wiki/Client%E2%80%93server_model)。 +在学习这门教程之前,你必须使用过 JavaScript, Node.js, Express.js 以及后端架构,熟悉 REST 和 API 这类术语,并且了解[主从式架构(客户端/服务器架构)](https://en.wikipedia.org/wiki/Client%E2%80%93server_model)。 当然你不需要成为这些话题的专家,熟悉并且有这些实际操作经验就足够了。 即便你不符合上述条件,也不是跳过这篇教程的理由。你还是可以从这篇文章中学到很多东西,具备上述条件可以帮助你更轻松地阅读这篇文章。 -虽然这里的API是用JavaScript和Express写的,但不表示这些最佳实践仅适用于此。你也可以在其他的编程语言和框架中应用这些最佳实践。 +虽然这里的 API 是用 JavaScript 和 Express 写的,但不表示这些最佳实践仅适用于此。你也可以在其他的编程语言和框架中应用这些最佳实践。 <h3 id="architecture">结构</h3> -如前所述,我会是用Express.js来搭建API。我不想弄得太复杂,所以我会使用 **3层结构:** +如前所述,我会是用 Express.js 来搭建 API。我不想弄得太复杂,所以我会使用 **3 层结构:** ![Bildschirmfoto-2022-04-25-um-14.33.24-1](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-25-um-14.33.24-1.png) -在 **控制器** 我们将处理所有HTTP相关的内容,也就是说我们在这里处理端点的请求和响应。在这层之上是Express的**路由**把请求传递给相应的控制器。 +在 **控制器** 我们将处理所有 HTTP 相关的内容,也就是说我们在这里处理端点的请求和响应。在这层之上是 Express 的**路由**把请求传递给相应的控制器。 所有业务逻辑都在**服务层**,服务层导出特定服务(方法)供控制层器用。 -第三层是 **数据访问层**, 在这里处理数据库。我们将导出一些处理数据的方法,如创建WOD,供服务层使用。 +第三层是 **数据访问层**, 在这里处理数据库。我们将导出一些处理数据的方法,如创建 WOD,供服务层使用。 -在我们的教学示例中,我们不会使用 _真实的_ 数据库,如MongoDB或者PostgreSQL,因为我想专注于最佳实践本身。因此我们会使用本地JSON文件来模拟数据库,但是使用逻辑可以迁移到其他的数据库。 +在我们的教学示例中,我们不会使用 _真实的_ 数据库,如 MongoDB 或者 PostgreSQL,因为我想专注于最佳实践本身。因此我们会使用本地 JSON 文件来模拟数据库,但是使用逻辑可以迁移到其他的数据库。 <h3 id="basic-setup">基础设置</h3> -现在我们开始配置API的基础设置。不会太复杂,我们只创建一个简单、有组织的结构。 +现在我们开始配置 API 的基础设置。不会太复杂,我们只创建一个简单、有组织的结构。 首先,我们创建一个总文件目录结构,包含所有必须的文件和依赖项。创建完了之后,我们将快速地检查一下一切是否运行正常。 @@ -124,7 +124,7 @@ npm i -D nodemon npm i express ``` -在你最喜欢使用的文字处理器中打开我们的项目,然后配置Express: +在你最喜欢使用的文字处理器中打开我们的项目,然后配置 Express: ```javascript // 在src/index.js中 @@ -143,7 +143,7 @@ app.listen(PORT, () => { }); ``` -在package.json中添加 **"dev"** 脚本: +在 package.json 中添加 **"dev"** 脚本: ```json { @@ -166,7 +166,7 @@ app.listen(PORT, () => { } ``` -nodemon可以确保每次你保存更改的时候,重新启动开发服务器。 +nodemon 可以确保每次你保存更改的时候,重新启动开发服务器。 启动开发服务器: @@ -188,13 +188,13 @@ npm run dev 图片作者[Constantin Wenning](https://unsplash.com/@conniwenningsimages?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) 来源[Unsplash](https://unsplash.com/s/photos/handshake?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) -很好!我们已经完成了Express的基础设置,现在我们可以根据最佳实践来扩展API了。 +很好!我们已经完成了 Express 的基础设置,现在我们可以根据最佳实践来扩展 API 了。 -我们从最简单的基础CRUD端点开始,之后我们将使用最佳实践来扩展API。 +我们从最简单的基础 CRUD 端点开始,之后我们将使用最佳实践来扩展 API。 <h3 id="versioning">版本</h3> -稍等一下,在我们编写具体的API代码之前,我们要关注一下版本。和其他所有应用一样,我们的API也需要迭代、更新功能,所以给我们的API制定版本十分重要。 +稍等一下,在我们编写具体的 API 代码之前,我们要关注一下版本。和其他所有应用一样,我们的 API 也需要迭代、更新功能,所以给我们的 API 制定版本十分重要。 这样做最大的优势是当我们在创建新功能的时候并不影响客户端继续运行旧版本。 @@ -202,7 +202,7 @@ npm run dev 当下版本和新版本并行运行,互不干扰。 -那我们如何区分不同的版本呢?一种不错的做法是在URL添加**v1****v2**这样的路径段。 +那我们如何区分不同的版本呢?一种不错的做法是在 URL 添加**v1****v2**这样的路径段。 ```javascript // 版本1 @@ -216,13 +216,13 @@ npm run dev 这就是我们暴露给外部,其他开发者也可以使用的部分。同时,我们也需要调整项目结构来区分不同的版本。 -管理Express API版本的方法各式各样。本教程中我将在**src**目录下创建一个版本目录,如**v1**: +管理 Express API 版本的方法各式各样。本教程中我将在**src**目录下创建一个版本目录,如**v1**: ```bash mkdir src/v1 ``` -现在我们将路由目录移动到新的v1目录下: +现在我们将路由目录移动到新的 v1 目录下: ```bash # 获取当前路径(复制) @@ -232,7 +232,7 @@ pwd mv {pwd}/src/routes {pwd}/src/v1 ``` -新目录 **/src/v1/routes** 将存储版本1的所有路由。之后我们会在里面添加“真实”的内容,但现在我们简单添加一个**index.js**文件来简单测试一下。 +新目录 **/src/v1/routes** 将存储版本 1 的所有路由。之后我们会在里面添加“真实”的内容,但现在我们简单添加一个**index.js**文件来简单测试一下。 ```bash # 在/src/v1/routes @@ -253,7 +253,7 @@ router.route("/").get((req, res) => { module.exports = router; ``` -现在我们将在v1内部的根入口点src/index.js接上路由: +现在我们将在 v1 内部的根入口点 src/index.js 接上路由: ```javascript // 在src/index.js @@ -281,21 +281,21 @@ app.listen(PORT, () => { ![Bildschirmfoto-2022-04-30-um-11.22.28](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-30-um-11.22.28.png) -祝贺你!你已经调整好了项目结构以适应不同版本。现在我们通过版本1的路由来传入请求,之后每一个请求会连接相应的控制器方式。 +祝贺你!你已经调整好了项目结构以适应不同版本。现在我们通过版本 1 的路由来传入请求,之后每一个请求会连接相应的控制器方式。 再继续下一步之前,我想强调一些内容。 -我们把路由目录迁移到了v1目录下,其他目录如控制器和服务器仍在src目录下。因为我们搭建的API比较小,所以这么做没有问题,每一个版本我们使用相同的控制器和服务器。 +我们把路由目录迁移到了 v1 目录下,其他目录如控制器和服务器仍在 src 目录下。因为我们搭建的 API 比较小,所以这么做没有问题,每一个版本我们使用相同的控制器和服务器。 -当API逐渐壮大,比方说2版本需要使用不同的控制方法的话,最好还是把控制器目录放在v2目录下,这样就打包了这个版本所有的特定逻辑。 +当 API 逐渐壮大,比方说 2 版本需要使用不同的控制方法的话,最好还是把控制器目录放在 v2 目录下,这样就打包了这个版本所有的特定逻辑。 另一个这样做的原因是,我们可能在其他版本中想要改变某个服务器,但我们并不想要中断除此之外的版本。所以推荐把服务器目录也迁移到特定版本目录。 -在我们的例子当中仅区分路由的版本是可行的。尽管如此。切记当API壮大需要改变的时候,拥有一个清晰的目录结构十分重要。 +在我们的例子当中仅区分路由的版本是可行的。尽管如此。切记当 API 壮大需要改变的时候,拥有一个清晰的目录结构十分重要。 <h3 id="name-resources-in-plural">用复数形式命名资源</h3> -设置完结构后我们就进入了真正的API搭建了。我希望从基础的CRUD端点开始。 +设置完结构后我们就进入了真正的 API 搭建了。我希望从基础的 CRUD 端点开始。 也就是说,我们从实现创建、读取、更新和删除交叉训练端点开始。 @@ -313,7 +313,7 @@ touch src/v1/routes/workoutRoutes.js 我们可以将端点命名为 **/api/v1/workout**,因为我们只添加一个交叉训练,对不对?虽说这样做基本上没什么问题,但是这样可能会造成误解。 -谨记:你的API会被其他的**人类**使用,所以必须精准。这一规则也适用于给资源命名。 +谨记:你的 API 会被其他的**人类**使用,所以必须精准。这一规则也适用于给资源命名。 我通常会把资源看作一个盒子。在我们的例子中,这个盒子存储了**训练**集合。 @@ -351,7 +351,7 @@ module.exports = router; 我们可以删除 **src/v1/routes**中的测试文件**index.js**文件。 -现在让我们回到入口接点连接版本1.0的路由。 +现在让我们回到入口接点连接版本 1.0 的路由。 ```javascript // 在 src/index.js @@ -375,7 +375,7 @@ app.listen(PORT, () => { }); ``` -进展的很顺利!现在我们就可以通过版本1的训练路由捕捉到来自 **/api/v1/workouts**的所有请求。 +进展的很顺利!现在我们就可以通过版本 1 的训练路由捕捉到来自 **/api/v1/workouts**的所有请求。 在路由当中,我们将调用控制器的方法来处理不同的端点。 @@ -438,15 +438,15 @@ module.exports = router; ![Bildschirmfoto-2022-04-30-um-11.29.19](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-30-um-11.29.19.png) -我们成功了!API结构的第一层就搭建完毕。让我们用另个最佳实践来创建服务层。 +我们成功了!API 结构的第一层就搭建完毕。让我们用另个最佳实践来创建服务层。 <h3 id="accept-and-respond-with-data-in-json-format">以JSON格式接受和响应数据</h3> -和API交互的时候,我们会通过请求发送特定数据,或者通过响应接受数据。市面上有各种各样的数据格式,但是JSON(JavaScript Object Notation)是一个标准格式。 +和 API 交互的时候,我们会通过请求发送特定数据,或者通过响应接受数据。市面上有各种各样的数据格式,但是 JSON(JavaScript Object Notation)是一个标准格式。 -虽然在JSON的全称中有 **JavaScript** ,但两者并没有绑定。你也可以使用Java或者Python来编写你的API,它们也可以处理JSON。 +虽然在 JSON 的全称中有 **JavaScript** ,但两者并没有绑定。你也可以使用 Java 或者 Python 来编写你的 API,它们也可以处理 JSON。 -由于这样的标准化,API应该接受和响应JSON格式的数据。 +由于这样的标准化,API 应该接受和响应 JSON 格式的数据。 让我们回到我们的代码,看看如何把这一点融入到我们的最佳实践。 @@ -535,7 +535,7 @@ module.exports = { 在服务的方法中,我们处理了业务逻辑,如改变数据结构以及和数据层交互。 -为此我们需要创建一个数据层和一组处理与数据库交互的方法。我们的数据库将为简单的训练JSON文件。 +为此我们需要创建一个数据层和一组处理与数据库交互的方法。我们的数据库将为简单的训练 JSON 文件。 ```bash # 在src/database中创建一个新的名为 db.json的文件 @@ -545,7 +545,7 @@ touch src/database/db.json touch src/database/Workout.js ``` -将这些内容复制粘贴到db.json: +将这些内容复制粘贴到 db.json: ```json { @@ -618,7 +618,7 @@ touch src/database/Workout.js } ``` -可以看到上面添加了三组训练数据。每组训练包含id, name, mode, equipment, exercises, createdAt, updatedAt和trainerTips。 +可以看到上面添加了三组训练数据。每组训练包含 id, name, mode, equipment, exercises, createdAt, updatedAt 和 trainerTips。 我们从最简单的开始,返回所有存储的训练,在访问数据层建立对应的方法(src/database/Workout.js)。 @@ -673,7 +673,7 @@ module.exports = { }; ``` -返回所有的训练十分简单,我们不需要改变数据格式,因为数据已经是一个JSON文件了。我们暂时也不需要传入参数,现在做的事情非常简单直白,待会儿我们会重新回到这里。 +返回所有的训练十分简单,我们不需要改变数据格式,因为数据已经是一个 JSON 文件了。我们暂时也不需要传入参数,现在做的事情非常简单直白,待会儿我们会重新回到这里。 在我们的训练控制器中,已经接受到了 `workoutService.getAllWorkouts()`的返回值,并作为响应发送给客户端。我们完成了数据库从服务层到控制器的响应循环。 @@ -716,15 +716,15 @@ module.exports = { }; ``` -在浏览器访问 **localhost:3000/api/v1/workouts**,你将看到响应的JSON。 +在浏览器访问 **localhost:3000/api/v1/workouts**,你将看到响应的 JSON。 ![Bildschirmfoto-2022-04-30-um-11.38.14](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-30-um-11.38.14.png) -一切都进展得很顺利,我们将数据以JSON的形式返回。但如何接受来自客户端的数据呢?假设我们需要一个端点来接受来自客户端的JSON,在这个端点客户端创建和更新训练数据。 +一切都进展得很顺利,我们将数据以 JSON 的形式返回。但如何接受来自客户端的数据呢?假设我们需要一个端点来接受来自客户端的 JSON,在这个端点客户端创建和更新训练数据。 -在控制器中,我们提取了请求体来创建一个新的训练,并传入训练服务层。在训练服务层,我们插入了DB.json并且将新创建的训练返回到客户端。 +在控制器中,我们提取了请求体来创建一个新的训练,并传入训练服务层。在训练服务层,我们插入了 DB.json 并且将新创建的训练返回到客户端。 -要想在请求体中解析JSON,我们需要首先安装**body-parser**并配置。 +要想在请求体中解析 JSON,我们需要首先安装**body-parser**并配置。 ```bash npm i body-parser @@ -749,9 +749,9 @@ app.listen(PORT, () => { }); ``` -现在我们就可以在控制器的**req.body**中接受JSON格式的数据。 +现在我们就可以在控制器的**req.body**中接受 JSON 格式的数据。 -可以打开你最喜欢的HTTP服务器(我使用的是Postman)来进行测试,创建一个路由为localhost:3000/api/v1/workouts的POST请求,并且将请求体设置为JSON格式: +可以打开你最喜欢的 HTTP 服务器(我使用的是 Postman)来进行测试,创建一个路由为 localhost:3000/api/v1/workouts 的 POST 请求,并且将请求体设置为 JSON 格式: ```javascript { @@ -775,9 +775,9 @@ app.listen(PORT, () => { } ``` -你可能注意到了"id"、"createdAt"、"updatedAt"这些属性不存在。添加这些属性是我们API的工作,我们会在训练服务层中处理相关内容。 +你可能注意到了"id"、"createdAt"、"updatedAt"这些属性不存在。添加这些属性是我们 API 的工作,我们会在训练服务层中处理相关内容。 -在训练控制器的 **createNewWorkout** 方法中,我们可以在请求体中提取body,并做一些验证,并作为参数传入训练服务层。 +在训练控制器的 **createNewWorkout** 方法中,我们可以在请求体中提取 body,并做一些验证,并作为参数传入训练服务层。 ```javascript // 在src/controllers/workoutController.js @@ -814,11 +814,11 @@ const createNewWorkout = (req, res) => { 通常会使用第三方包来来提升请求验证性能,如:[express-validator](https://express-validator.github.io/docs/). -训练服务层接受来自createdNewWorkout方法传入的数据。 +训练服务层接受来自 createdNewWorkout 方法传入的数据。 -之后我们将缺失的属性传入对象,并将这个对象作为新的方法传入数据访问层,再存入DB中。 +之后我们将缺失的属性传入对象,并将这个对象作为新的方法传入数据访问层,再存入 DB 中。 -首先我们要创建一个简单的Util函数,来覆盖JSON文件以实时更新数据。 +首先我们要创建一个简单的 Util 函数,来覆盖 JSON 文件以实时更新数据。 ```bash # 在data目录下创建util文件 @@ -838,7 +838,7 @@ const saveToDatabase = (DB) => { module.exports = { saveToDatabase }; ``` -我们可以在Workout.js文件中使用这个函数 +我们可以在 Workout.js 文件中使用这个函数 ```javascript // 在src/database/Workout.js @@ -923,13 +923,13 @@ module.exports = { }; ``` -一切还不错,对不对?现在你可以去HTTP客户端,重新发送POST请求,就会接受到新的JSON格式的训练。 +一切还不错,对不对?现在你可以去 HTTP 客户端,重新发送 POST 请求,就会接受到新的 JSON 格式的训练。 -如果你尝试再次添加同样的训练,你仍会得到201状态码,但是不会插入新的内容。 +如果你尝试再次添加同样的训练,你仍会得到 201 状态码,但是不会插入新的内容。 -也就是说我们的数据库方法取消了插入,什么都不返回。这是因为if声明检查了是否已经存在同样名称的内容,暂时这么处理,我们会在下一个最佳实践中讲解如何优化。 +也就是说我们的数据库方法取消了插入,什么都不返回。这是因为 if 声明检查了是否已经存在同样名称的内容,暂时这么处理,我们会在下一个最佳实践中讲解如何优化。 -现在向 **localhost:3000/api/v1/workouts**发出GET请求,读取所有的训练。 我选择使用浏览器来操作,你会看到我们的训练成功地插入了: +现在向 **localhost:3000/api/v1/workouts**发出 GET 请求,读取所有的训练。 我选择使用浏览器来操作,你会看到我们的训练成功地插入了: ![Bildschirmfoto-2022-04-30-um-11.57.23](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-30-um-11.57.23.png) 你可以选择自行编写其他的方法,或者直接复制我的: @@ -1127,7 +1127,7 @@ module.exports = { <h3 id="respond-with-standard-http-error-codes">响应标准HTTP错误代码</h3> -我们已经完成了不少内容的搭建,但还没结束呢。现在我们的API已经可以处理CRUD并且存储数据,这样很棒!但还不够。 +我们已经完成了不少内容的搭建,但还没结束呢。现在我们的 API 已经可以处理 CRUD 并且存储数据,这样很棒!但还不够。 为什么?让我来解释。 @@ -1135,19 +1135,19 @@ module.exports = { 你或许也认为从一开始就没有任何错误是一种奇怪的感觉,这样确实很棒也让人享受,但作为一个开发者,我们应该更习惯与错误共处。 😁 -API也是这样,我们需要处理出现问题或者报错的情况。这也可以使我门的API更强大。 +API 也是这样,我们需要处理出现问题或者报错的情况。这也可以使我门的 API 更强大。 -出现问题时(不论是在请求中还是在我们API内部),我们返回HTTP错误代码。我见过并使用过一些API始终返回400错误代码,并且不附带任何具体的信息说明为什么错误会出现,错误是什么。这样调试起来就很痛苦。 +出现问题时(不论是在请求中还是在我们 API 内部),我们返回 HTTP 错误代码。我见过并使用过一些 API 始终返回 400 错误代码,并且不附带任何具体的信息说明为什么错误会出现,错误是什么。这样调试起来就很痛苦。 -这就是为什么针对不同的情况返回合适的HTTP代码是一种最佳实践。这能够使正在使用或者构建API的工程师更轻松地识别问题。 +这就是为什么针对不同的情况返回合适的 HTTP 代码是一种最佳实践。这能够使正在使用或者构建 API 的工程师更轻松地识别问题。 为了提升体验,我们还可以在返回错误的同时快速发送一个错误信息。但正如在文章开头说的那样,这一做法并不是万精油,还需要工程师自己来权衡。 例如,是否应该向用户返回 **"该用户名已经注册"**这类信息是需要深思熟虑的,因为或许这样就给用户提供了本该隐藏的数据。 -可以浏览一遍交叉训练API中的创建(CRUD中的C)端点,看看会出现什么问题,我们能怎么解决。在这一部分最后部分有其他端点的完整实现。 +可以浏览一遍交叉训练 API 中的创建(CRUD 中的 C)端点,看看会出现什么问题,我们能怎么解决。在这一部分最后部分有其他端点的完整实现。 -我们先从训练控制器的createNewWorkout方法开始: +我们先从训练控制器的 createNewWorkout 方法开始: ```javascript // 在src/controllers/workoutController.js @@ -1180,7 +1180,7 @@ const createNewWorkout = (req, res) => { 我们的代码已经可以捕获请求体属性不完整的情况。 -在返回400时,附带一条返回错误信息是一个不错的选择。 +在返回 400 时,附带一条返回错误信息是一个不错的选择。 ```javascript // 在src/controllers/workoutController.js @@ -1220,11 +1220,11 @@ const createNewWorkout = (req, res) => { ... ``` -如果我们想要添加一个新的训练,但是忘记在请求体提供"mode"属性,我们会在400报错的同时看到错误信息。 +如果我们想要添加一个新的训练,但是忘记在请求体提供"mode"属性,我们会在 400 报错的同时看到错误信息。 ![Bildschirmfoto-2022-04-30-um-15.17.21](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-30-um-15.17.21.png) -这样的话,使用这个API的开发者就更知道自己需要什么。他们马上就知道应该在请求体中找答案,并且看看他们缺失了哪一个必须的属性。 +这样的话,使用这个 API 的开发者就更知道自己需要什么。他们马上就知道应该在请求体中找答案,并且看看他们缺失了哪一个必须的属性。 在我们的例子中使用通用的错误信息没有问题。一般情况下可以使用一个模式验证器来处理这个问题。 @@ -1248,7 +1248,7 @@ const createNewWorkout = (newWorkout) => { ... ``` -在 **Workout.createNewWorkout()** 中的插入数据可能出现问题,我想将他们打包在try/catch代码块中,来捕获错误。 +在 **Workout.createNewWorkout()** 中的插入数据可能出现问题,我想将他们打包在 try/catch 代码块中,来捕获错误。 ```javascript // 在src/services/workoutService.js @@ -1272,9 +1272,9 @@ const createNewWorkout = (newWorkout) => { ... ``` -Workout.createNewWorkout()方法中的所有错误都会被catch代码块捕获。我们抛出这个错误之后就可以在控制器中调整响应。 +Workout.createNewWorkout()方法中的所有错误都会被 catch 代码块捕获。我们抛出这个错误之后就可以在控制器中调整响应。 -让我们在Workout.js中定义错误: +让我们在 Workout.js 中定义错误: ```javascript // 在src/database/Workout.js @@ -1303,7 +1303,7 @@ const createNewWorkout = (newWorkout) => { 如你所见,一个错误包含了状态和信息两个内容。 此处我使用了 **throw**关键字来抛出一个数据结构而不是一条字符串, **throw new Error()**必须这么写。 -使用throw的缺点是无法得到栈追踪。但基本上抛出错误由第三方库来处理(如果你使用MongoDB数据库的话就是Mongoose),在本教程中,我们现在做的就足够了。 +使用 throw 的缺点是无法得到栈追踪。但基本上抛出错误由第三方库来处理(如果你使用 MongoDB 数据库的话就是 Mongoose),在本教程中,我们现在做的就足够了。 现在我们就可以在服务和数据访问层来抛出和捕获错误了。我们现在进入训练控制层,来编写抛出错误和对应的消息。 @@ -1352,7 +1352,7 @@ const createNewWorkout = (req, res) => { ... ``` -你可以通过添加同样名字的训练,或者不在请求体中提供必需的属性来测试。你会接受对应的HTTP错误代码以及错误信息。 +你可以通过添加同样名字的训练,或者不在请求体中提供必需的属性来测试。你会接受对应的 HTTP 错误代码以及错误信息。 在结束这一篇并且进入下一个最佳实践之前,让我们复制其他的实现代码,或者你可以尝试自己编写: @@ -1656,11 +1656,11 @@ module.exports = { <h3 id="avoid-verbs-in-endpoint-names">避免在端点使用动词</h3> -在端点中使用动词实际上没有任何作用。大体上URL和资源(想想我们前文提到的“盒子”)是一一对应的。 +在端点中使用动词实际上没有任何作用。大体上 URL 和资源(想想我们前文提到的“盒子”)是一一对应的。 -在URL中使用动词,相当于展示了资源本身并没有的行为。 +在 URL 中使用动词,相当于展示了资源本身并没有的行为。 -我们已经在不使用动词的情况下正确地编写好了URL,但让我们看看,如果使用动词,URL会是什么样。 +我们已经在不使用动词的情况下正确地编写好了 URL,但让我们看看,如果使用动词,URL 会是什么样。 ```javascript // 现在的样子(没有动词) @@ -1678,35 +1678,35 @@ PATCH "/api/v1/updateWorkout/:workoutId" DELETE "/api/v1/deleteWorkout/:workoutId" ``` -你看到区别了吗?给每一个行为分配不同的URL,会让人困惑并且十分复杂。 +你看到区别了吗?给每一个行为分配不同的 URL,会让人困惑并且十分复杂。 -假设我们有300个不同的端点。为每个端点分配单独的URL可能造成开销(和文档)地狱。 +假设我们有 300 个不同的端点。为每个端点分配单独的 URL 可能造成开销(和文档)地狱。 -另一个我不推荐在URL中使用动词的原因是,HTTP动词已经表明了响应的动作。 +另一个我不推荐在 URL 中使用动词的原因是,HTTP 动词已经表明了响应的动作。 如 **"GET /api/v1/getAllWorkouts"** 和 **"DELETE api/v1/deleteWorkout/workoutId"**就很没有必要。 -你会发现我们的实现非常清晰,因为我们只使用两个不同的URL,而实际的行为是通过HTTP动词以及对应的请求有效载荷来实现。 +你会发现我们的实现非常清晰,因为我们只使用两个不同的 URL,而实际的行为是通过 HTTP 动词以及对应的请求有效载荷来实现。 -我认为HTTP动词是来定义行为的(我们也希望这样),而URL(指向资源)是目标。 **"GET /api/v1/workouts"** 这句话即便是人类的语言中也更通顺。 +我认为 HTTP 动词是来定义行为的(我们也希望这样),而 URL(指向资源)是目标。 **"GET /api/v1/workouts"** 这句话即便是人类的语言中也更通顺。 <h3 id="group-associated-resources-together-logical-nesting-">把相关的资源放在一起(逻辑嵌套)</h3> -当你在设计API的时候,会出现资源之间相互关联的情况。一个好的实践方式是将资源整合和嵌套到一个端点。 +当你在设计 API 的时候,会出现资源之间相互关联的情况。一个好的实践方式是将资源整合和嵌套到一个端点。 -在我们的API中,有一系列的会员注册了交叉训练盒子(此处的“盒子”是交叉训练健身房的名字),为了鼓励会员,我们记录了每一次训练的所有记录。 +在我们的 API 中,有一系列的会员注册了交叉训练盒子(此处的“盒子”是交叉训练健身房的名字),为了鼓励会员,我们记录了每一次训练的所有记录。 假设有一组训练包含一定顺序的练习,你想要尽快做完。我们记录了所有会员完成这项训练的时间。 -这时,前端就需要一个端点来响应一个特定训练的所有时间记录,并且在UI上呈现。 +这时,前端就需要一个端点来响应一个特定训练的所有时间记录,并且在 UI 上呈现。 训练、会员还有训练记录存储在不同的数据库里。所以在这里我们需要使用盒中盒(训练中的记录),对不对? -这个端点的URI会是 **/api/v1/workouts/:workoutId/records**. 这便是一个在URL中实现逻辑嵌套的好实践。URL本身不需要反应数据结构。 +这个端点的 URI 会是 **/api/v1/workouts/:workoutId/records**. 这便是一个在 URL 中实现逻辑嵌套的好实践。URL 本身不需要反应数据结构。 让我们来实现这个端点。 -首先我们要在db.json中添加一组叫"memebers"的数据,放在"workouts"下面。 +首先我们要在 db.json 中添加一组叫"memebers"的数据,放在"workouts"下面。 ```json { @@ -1784,7 +1784,7 @@ DELETE "/api/v1/deleteWorkout/:workoutId" } ``` -为了确保同一id下的训练相同,我也复制了一些训练到workouts中: +为了确保同一 id 下的训练相同,我也复制了一些训练到 workouts 中: ```json { @@ -1889,9 +1889,9 @@ DELETE "/api/v1/deleteWorkout/:workoutId" 在创建交叉内容的结构之前,建议先创建另一个控制器、服务层和数据组合方法来负责训练记录。 -我们很有可能需要为训练记录实现CRUD端点,因为在未来我们也会添加、更新和删除记录。但这不是现在的首要任务。 +我们很有可能需要为训练记录实现 CRUD 端点,因为在未来我们也会添加、更新和删除记录。但这不是现在的首要任务。 -我们也需要一个记录的路由来捕获对应的请求。这是你练习自己实现CRUD的绝好机会。 +我们也需要一个记录的路由来捕获对应的请求。这是你练习自己实现 CRUD 的绝好机会。 ```bash # 创建记录控制器 @@ -1927,7 +1927,7 @@ const getRecordForWorkout = (workoutId) => { module.exports = { getRecordForWorkout }; ``` -很直接对不对,我们通过查询参数过滤出和训练id相关的记录数据 +很直接对不对,我们通过查询参数过滤出和训练 id 相关的记录数据 接下来是记录的服务层: @@ -1977,11 +1977,11 @@ module.exports = router; 真棒!让我们在浏览器中测试一下。 -首先我们抓取所有训练记录,来获得一个训练id。 +首先我们抓取所有训练记录,来获得一个训练 id。 ![Bildschirmfoto-2022-04-30-um-15.36.48](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-30-um-15.36.48.png) -让我们来看看能不能获得这个id下的所有记录。 +让我们来看看能不能获得这个 id 下的所有记录。 ![Bildschirmfoto-2022-04-30-um-15.36.32](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-30-um-15.36.32.png) @@ -1991,13 +1991,13 @@ module.exports = router; 想象一下,前端还需要一个端点来获取到底是哪个会员持有当前记录的信息,并希望接受这个会员的所有原始信息。 -你当然可以使用下面的URI: +你当然可以使用下面的 URI: ```javascript GET /api/v1/workouts/:workoutId/records/members/:memberId ``` -嵌套越多,端点就越不容易管理。因此,将接受会员信息的URI直接存储在记录中是一个好的做法。 +嵌套越多,端点就越不容易管理。因此,将接受会员信息的 URI 直接存储在记录中是一个好的做法。 可以这样修改数据库: @@ -2022,31 +2022,31 @@ GET /api/v1/workouts/:workoutId/records/members/:memberId 前端只需要调用 **GET /api/v1/workouts/:workoutId/records**便可以获得所有和训练相关的数据。 -除此之外,我们可以由会员id来获取会员的信息,就可以避免更深入的嵌套。 +除此之外,我们可以由会员 id 来获取会员的信息,就可以避免更深入的嵌套。 当然,这一切实现的前提是处理"/members/:memberId"请求。😁 这听上去是锻炼你自己实现能力的好机会! <h3 id="integrate-filtering-sorting-pagination">集成过滤、排序和分页功能</h3> -现在我们的API已经可以完成很多工作,取得了相当大的进展,但是这还不够。 +现在我们的 API 已经可以完成很多工作,取得了相当大的进展,但是这还不够。 -在上一部分我们聚焦在如何提高开发者的体验,以及我们的API如何交互。但是API的整体性能也是一个关键部分,需要我们努力提高。 +在上一部分我们聚焦在如何提高开发者的体验,以及我们的 API 如何交互。但是 API 的整体性能也是一个关键部分,需要我们努力提高。 这就是为什么在我的待办清单中集成过滤、排序和分页功能也是非常关键的。 -假设我们的DB中有2000个训练,450条记录和500个会员。当我们调用端点来获取训练的时候,我们不希望一次性获得所有2000个训练。这样的响应速度会比较慢,导致系统崩溃(崩溃可能需要200000条记录 😁)。 +假设我们的 DB 中有 2000 个训练,450 条记录和 500 个会员。当我们调用端点来获取训练的时候,我们不希望一次性获得所有 2000 个训练。这样的响应速度会比较慢,导致系统崩溃(崩溃可能需要 200000 条记录 😁)。 这就是为什么过滤和分页十分重要。过滤正如这个名称一样,可以帮助我们在整个数据集中获取我们需要的数据。例如所有具备“时间”模式的训练。 -分页是另一种可以拆分数据集的机制,比方说我们可以把数据分成每页二十个训练的“页面”。这个技术确保我们一次返回不超过20个训练。 +分页是另一种可以拆分数据集的机制,比方说我们可以把数据分成每页二十个训练的“页面”。这个技术确保我们一次返回不超过 20 个训练。 -排序可以变得非常复杂,所以直接在我们的API排序后,再向用户发送数据更高效。 +排序可以变得非常复杂,所以直接在我们的 API 排序后,再向用户发送数据更高效。 -我们首先在API中整合一些过滤机制。我们将发送所有训练的这个端点升级,让这个端点接受过滤参数。通常在GET请求中,我们使用查询参数来添加过滤条件。 +我们首先在 API 中整合一些过滤机制。我们将发送所有训练的这个端点升级,让这个端点接受过滤参数。通常在 GET 请求中,我们使用查询参数来添加过滤条件。 -当我们只获取训练状态(mode)为"AMRAP"(尽可能多地训练 **A**s **M**any **R**ounds **A**s **P**ossible)时,我们新的URI会是这样: **/api/v1/workouts?mode=amrap**。 +当我们只获取训练状态(mode)为"AMRAP"(尽可能多地训练 **A**s **M**any **R**ounds **A**s **P**ossible)时,我们新的 URI 会是这样: **/api/v1/workouts?mode=amrap**。 -为了让实现更有趣,我们可以添加更多的训练。请在db.json中的"workouts"数据集中添加以下代码: +为了让实现更有趣,我们可以添加更多的训练。请在 db.json 中的"workouts"数据集中添加以下代码: ```json { @@ -2131,11 +2131,11 @@ const getAllWorkouts = (req, res) => { ... ``` -我们在req.query对象中提取“mode”,并用作workoutService.getAllWorkouts的参数。这个对象包含了所有过滤参数。 +我们在 req.query 对象中提取“mode”,并用作 workoutService.getAllWorkouts 的参数。这个对象包含了所有过滤参数。 -这里我使用了简写语法,来创建一个名为"mode"的新键,这个键位于对象内部,其值可以是任意"req.query.mode"的值。可以为一个真值或者如果没有一个参数为“mode”的参数则为undefined。我们可以在对象内扩充更多过滤参数。 +这里我使用了简写语法,来创建一个名为"mode"的新键,这个键位于对象内部,其值可以是任意"req.query.mode"的值。可以为一个真值或者如果没有一个参数为“mode”的参数则为 undefined。我们可以在对象内扩充更多过滤参数。 -在workoutService中传入数据处理方法: +在 workoutService 中传入数据处理方法: ```javascript // 在src/services/workoutService.js @@ -2180,9 +2180,9 @@ const getAllWorkouts = (filterParams) => { 简单明了!我们在这里做的工作就是检查"filterParams"中是否存在键"mode"的真值,如果存在,则过滤出所有包含同样"mode"的训练,如果不存在,则返回所有训练, -此处我们使用"let"来定义"workouts"变量是因为如果我们使用if表达式来添加更多过滤器的话,会覆盖掉"workouts"并且串联过滤器。 +此处我们使用"let"来定义"workouts"变量是因为如果我们使用 if 表达式来添加更多过滤器的话,会覆盖掉"workouts"并且串联过滤器。 -在浏览器中可以登陆3000/api/v1/workouts?mode=amrap,会接受到所有包含 "AMRAP"的训练: +在浏览器中可以登陆 3000/api/v1/workouts?mode=amrap,会接受到所有包含 "AMRAP"的训练: ![Bildschirmfoto-2022-04-30-um-15.48.57](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-30-um-15.48.57.png) @@ -2193,14 +2193,14 @@ const getAllWorkouts = (filterParams) => { 排序和分页的参数页遵行同样的原理,我们来看看我们需要实现的一些功能: - 接受所有需要杠铃的训练: **/api/v1/workouts?equipment=barbell** -- 接受5组训练: **/api/v1/workouts?length=5** +- 接受 5 组训练: **/api/v1/workouts?length=5** - 使用分页时,返回第二页:**/api/v1/workouts?page=2** - 给训练排序,并且以创建时间为标准降序来响应训练: **/api/v1/workouts?sort=-createdAt** -- 你也可以合并参数,获取最近更新的10个训练:**/api/v1/workouts?sort=-updatedAt&length=10** +- 你也可以合并参数,获取最近更新的 10 个训练:**/api/v1/workouts?sort=-updatedAt&length=10** <h3 id="use-data-caching-for-performance-improvements">使用数据缓存提升性能</h3> -使用数据缓存也是一个提升API整体使用体验和性能的优秀实践。 +使用数据缓存也是一个提升 API 整体使用体验和性能的优秀实践。 当一段数据经常被请求,或者这个数据太大了需要比较长的时间加载的时候,可以使用缓存来提供数据。 @@ -2208,11 +2208,11 @@ const getAllWorkouts = (filterParams) => { 但必须记住的是,使用缓存来提供数据的话,这段数据很有可能过期。所以必须确保缓存中的数据保持更新。 -有各种实现来实现缓存的方式,一种是使用[redis](https://www.npmjs.com/package/redis)或者express的中间件[apicache](https://www.npmjs.com/package/apicache). +有各种实现来实现缓存的方式,一种是使用[redis](https://www.npmjs.com/package/redis)或者 express 的中间件[apicache](https://www.npmjs.com/package/apicache). -我准备使用apicache,但如果你想使用Redis,我强烈推荐你阅读他们的[文档](https://docs.redis.com/latest/rs/references/client_references/client_nodejs/)。 +我准备使用 apicache,但如果你想使用 Redis,我强烈推荐你阅读他们的[文档](https://docs.redis.com/latest/rs/references/client_references/client_nodejs/)。 -我们思考一下在API中使用缓存的场景。我认为使用缓存来返回所有训练会更加有效。 +我们思考一下在 API 中使用缓存的场景。我认为使用缓存来返回所有训练会更加有效。 首先,让我们安装中间件: @@ -2252,17 +2252,17 @@ module.exports = router; 很简单!我们可以将新的缓存命名为**apicache.middleware**,并在路由中当作中间件来使用。仅需在实际的路径和训练控制器之间放置这个参数。 -你可以在中间件内部定义你需要保存缓存多久。在这篇教程中我选择2分钟。保存时间一般取决于你存储的数据多久更新一次。 +你可以在中间件内部定义你需要保存缓存多久。在这篇教程中我选择 2 分钟。保存时间一般取决于你存储的数据多久更新一次。 让我们测试一下! -在Postman或者另外的HTTP客户端中,定义一个新的请求,获取所有的训练。之前我都是在浏览器中操作,但是这次我想给你更直观的感受,所以使用Postman。 +在 Postman 或者另外的 HTTP 客户端中,定义一个新的请求,获取所有的训练。之前我都是在浏览器中操作,但是这次我想给你更直观的感受,所以使用 Postman。 让我们第一次请求数据: ![Bildschirmfoto-2022-04-26-um-15.36.46-1](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-26-um-15.36.46-1.png) -你可以看到我们的API花了22.93毫秒来响应。一旦缓存被清空(2分钟后),又回重新抓取数据保存到缓存,我们第一次获取数据的时候就将数据存储到了缓存。 +你可以看到我们的 API 花了 22.93 毫秒来响应。一旦缓存被清空(2 分钟后),又回重新抓取数据保存到缓存,我们第一次获取数据的时候就将数据存储到了缓存。 在上述例子中,数据并不是有由缓存提供。而是通过“普通”方式来抓去数据库保存到缓存。 @@ -2303,31 +2303,31 @@ app.listen(PORT, () => { - 必须保证缓存中的数据是更新的,你可不想提供过期的数据 - 当第一个请求在执行的过程中,数据被保存到缓存,也有更多地请求进来,你必须决定是延迟其他的请求,从缓存中提供数据,还是其他的请求也如第一次请求一样从数据库来获取数据 -- 如果使用分布式缓存如Redis,缓存会是你的结构中的一个组件,所以你必须考虑一下是否有必要使用缓存 +- 如果使用分布式缓存如 Redis,缓存会是你的结构中的一个组件,所以你必须考虑一下是否有必要使用缓存 我常常这么做: -当我在搭建的时候我希望一切从简,API同理。 +当我在搭建的时候我希望一切从简,API 同理。 -首次搭建API的时候没有特别的原因使用缓存,我会等使用了一段时间之后,有理由使用缓存后再使用缓存。 +首次搭建 API 的时候没有特别的原因使用缓存,我会等使用了一段时间之后,有理由使用缓存后再使用缓存。 <h3 id="good-security-practices">好的安全实践</h3> -这是一段不错的旅行,我们讲了许多API相关的重要观点,并且扩充了我们的API。 +这是一段不错的旅行,我们讲了许多 API 相关的重要观点,并且扩充了我们的 API。 -我们已经讲了提升API使用和性能的最佳实践。安全也是API重要的一环。如果你创建出一个绝佳的API,但是在服务器上运行的时候却十分脆弱,那这个API就变得无用且危险。 +我们已经讲了提升 API 使用和性能的最佳实践。安全也是 API 重要的一环。如果你创建出一个绝佳的 API,但是在服务器上运行的时候却十分脆弱,那这个 API 就变得无用且危险。 -首先必须使用的是SSL/TLS,因为这是当今互联网通讯的一个标准。特别是当API需要在客户端和服务器之间传输私人数据的时候。 +首先必须使用的是 SSL/TLS,因为这是当今互联网通讯的一个标准。特别是当 API 需要在客户端和服务器之间传输私人数据的时候。 如果你需要给验证客户提供数据,必须使用验证手段来保护数据。 -在Express中,我们可以像在缓存中那样在路由中插入特定的中间件来检查请求的真实性再获取资源。 +在 Express 中,我们可以像在缓存中那样在路由中插入特定的中间件来检查请求的真实性再获取资源。 -API中的一些资源和交互是你可能不希望所有用户都可以请求的。这是就需要一个角色系统。在路由中添加一个检查逻辑来验证用户是否有权利来获取这些数据。 +API 中的一些资源和交互是你可能不希望所有用户都可以请求的。这是就需要一个角色系统。在路由中添加一个检查逻辑来验证用户是否有权利来获取这些数据。 用户角色在我们的用例中也同样适用。比方说我们需要特定用户(教练)来使用创建、更新和删除训练和记录的功能。所有用户可以读取(同样可成为“普通”用户)。 -这可以通过在路由中插入中间件来实现。如在我们的/api/v1/workouts的POST请求中插入。 +这可以通过在路由中插入中间件来实现。如在我们的/api/v1/workouts 的 POST 请求中插入。 在第一个中间件中我们检查用户是不是真实的,如果为真,就进入下一个中间件来检查用户角色,如果用户符合获取资源的角色,就移交到对应的控制器。 @@ -2350,23 +2350,23 @@ router.post("/", authenticate, authorize, workoutController.createNewWorkout); <h3 id="document-your-api-properly">给API编写合适的文档</h3> -对于开发者来说编写文档确实不是一件让他们乐意干的活儿,但是是必须要做的事。特别是API的文档。 +对于开发者来说编写文档确实不是一件让他们乐意干的活儿,但是是必须要做的事。特别是 API 的文档。 有人说过: -> “API得和文档一样优秀“ +> “API 得和文档一样优秀“ -我认为这句话挺有道理,因为如果API的文档不好,这个API就不好使用。文档帮助开发者更方便地使用API。 +我认为这句话挺有道理,因为如果 API 的文档不好,这个 API 就不好使用。文档帮助开发者更方便地使用 API。 -永远记住文档是API使用者和API交互的第一环节。用户能够更快读懂文档,就能够更快使用API。 +永远记住文档是 API 使用者和 API 交互的第一环节。用户能够更快读懂文档,就能够更快使用 API。 所以我们必须编写良好精确的文档。有一些比较好用的工具可以帮助我们实现。 -和其他计算机科学领域一样,API文档也有标准,查看[OpenAPI细则](https://swagger.io/specification/). +和其他计算机科学领域一样,API 文档也有标准,查看[OpenAPI 细则](https://swagger.io/specification/). 让我们来看看如何遵循这份规则来创建文档。 我们将使用[swagger-ui-express](https://www.npmjs.com/package/swagger-ui-express) 和[swagger-jsdoc](https://www.npmjs.com/package/swagger-jsdoc) 工具包。你马上就会为这两个工具包能够做到的事感到惊奇。 -首先我们设置好文档的基础结构。因为我们会有不同版本的API,所以文档会有些许不同,这就是为什么我会创建swagger文件,来处理不同版本的文档。 +首先我们设置好文档的基础结构。因为我们会有不同版本的 API,所以文档会有些许不同,这就是为什么我会创建 swagger 文件,来处理不同版本的文档。 ```bash # 安装必须的NPM包 @@ -2410,11 +2410,11 @@ const swaggerDocs = (app, port) => { module.exports = { swaggerDocs }; ``` -设置很简单,我们定义了API的基本数据,创建了JSON格式的文档,并创建了函数使文档可用。 +设置很简单,我们定义了 API 的基本数据,创建了 JSON 格式的文档,并创建了函数使文档可用。 为了检查一切可以运行,我们在控制台打印一个简单的信息。 -这是我们在根文件中会使用到的函数,在根文件中我们也创建了Express服务器,确保文档也被启动。 +这是我们在根文件中会使用到的函数,在根文件中我们也创建了 Express 服务器,确保文档也被启动。 ```javascript // 在src/index.js @@ -2441,15 +2441,15 @@ app.listen(PORT, () => { ![Bildschirmfoto-2022-04-28-um-20.23.51-1](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-28-um-20.23.51-1.png) -当你登陆localhost:3000/api/v1/docs,你会看到我们的文档已经准备好了: +当你登陆 localhost:3000/api/v1/docs,你会看到我们的文档已经准备好了: ![Bildschirmfoto-2022-04-28-um-20.25.00-1](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-28-um-20.25.00-1.png) 每次我都会感叹运作得如此顺畅。现在基本结构已经设置好,我们可以来实现文档的端点了,让我们开始吧! -当你查看swagger.js文件中的 **options.apis** ,你会发现我们已经预留了处理训练路由和数据库中训练文件的路径。 这就是让魔法实现最重要的环节。 +当你查看 swagger.js 文件中的 **options.apis** ,你会发现我们已经预留了处理训练路由和数据库中训练文件的路径。 这就是让魔法实现最重要的环节。 -在swagger中有这些选项使得我们可以使用评论来引用OpenAPI,并且使用类似yaml的语法来编写文档,这就是设置文档的所有必须条件了。 +在 swagger 中有这些选项使得我们可以使用评论来引用 OpenAPI,并且使用类似 yaml 的语法来编写文档,这就是设置文档的所有必须条件了。 现在我们就可以开始来创建我们文档的第一个端点了,让我们开始吧! @@ -2484,13 +2484,13 @@ router.get("/", cache("2 minutes"), workoutController.getAllWorkouts); ... ``` -这基本上就是使用swagger文档来添加端点的所有魔法了,你可以在他们的[文档](https://swagger.io/docs/specification/about/)中查看所有细则。 +这基本上就是使用 swagger 文档来添加端点的所有魔法了,你可以在他们的[文档](https://swagger.io/docs/specification/about/)中查看所有细则。 当你重新加载文档页面,你会看到如下: ![Bildschirmfoto-2022-04-29-um-07.21.51-1](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-29-um-07.21.51-1.png) -如果你熟悉OpenAPI文档的话,这个画面对于你来说就不陌生。在这个页面中我们会看到所有端点,并且包含每一个端点的信息。 +如果你熟悉 OpenAPI 文档的话,这个画面对于你来说就不陌生。在这个页面中我们会看到所有端点,并且包含每一个端点的信息。 ![Bildschirmfoto-2022-04-29-um-07.41.46-1](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-29-um-07.41.46-1.png) @@ -2593,7 +2593,7 @@ router.get("/", cache("2 minutes"), workoutController.getAllWorkouts); 很不错!你可能会认为“编写这些评论很繁琐”。 -这或许是真的,但是可以这样想,写在代码中的评论里可以帮助你作为一个API开发者更好地了解你的API。当你想要了解某一个端点的时候,你不要阅读所有文档,你可以直接在代码中找到。 +这或许是真的,但是可以这样想,写在代码中的评论里可以帮助你作为一个 API 开发者更好地了解你的 API。当你想要了解某一个端点的时候,你不要阅读所有文档,你可以直接在代码中找到。 为端点写文档也可以帮助你更好的了解这些端点,“逼迫”自己去思考在实现的过程当中缺失了什么。 @@ -2660,7 +2660,7 @@ router.get("/", cache("2 minutes"), workoutController.getAllWorkouts); ![Bildschirmfoto-2022-04-29-um-08.03.00-1](https://www.freecodecamp.org/news/content/images/2022/04/Bildschirmfoto-2022-04-29-um-08.03.00-1.png) -现阶段展示可能出现的错误,我们只用抛出5XX报错就行。所以在"responses"内部,我们可以定义另一个文档。 +现阶段展示可能出现的错误,我们只用抛出 5XX 报错就行。所以在"responses"内部,我们可以定义另一个文档。 现在我们的文档页面如下: @@ -2668,9 +2668,9 @@ router.get("/", cache("2 minutes"), workoutController.getAllWorkouts); 很棒!我们已经给一个端点创建了完整的文档,我强烈建议你为剩下的端点创建对应的文档,在这个过程中你会学到很多东西。 -你或许也体会到了为API写文档并不总是一件头疼的事,使用我介绍给你的工具可以减轻你不少负担,搭建过程也十分简单明了。 +你或许也体会到了为 API 写文档并不总是一件头疼的事,使用我介绍给你的工具可以减轻你不少负担,搭建过程也十分简单明了。 -这样你就可以把注意力集中在重要的事情上,编写文档内容。swagger和OpenAPI的文档非常不错,在网络上你也可以找到其他的优秀例子。 +这样你就可以把注意力集中在重要的事情上,编写文档内容。swagger 和 OpenAPI 的文档非常不错,在网络上你也可以找到其他的优秀例子。 那么因为太多“额外”工作而不写文档这个理由现在就不成立了。 @@ -2680,7 +2680,7 @@ router.get("/", cache("2 minutes"), workoutController.getAllWorkouts); 这些最佳实践中有一些可能很重要。另一些可能不适用于你现在的情况。没关系,正如我一开始说的那样,对于开发者来说最重要的是能够根据情况挑选出最适合自己的方法。 -我尽力把所有最佳实践融汇到这个API项目中,我从中获得非常多的乐趣。 +我尽力把所有最佳实践融汇到这个 API 项目中,我从中获得非常多的乐趣。 我十分乐意接受各种反馈,任何你想要告诉我的事情(好的或坏的),别迟疑,请告诉我: diff --git a/chinese/articles/rest-api-tutorial.md b/chinese/articles/rest-api-tutorial.md index 01a8ebfdc..5624dac5a 100644 --- a/chinese/articles/rest-api-tutorial.md +++ b/chinese/articles/rest-api-tutorial.md @@ -43,7 +43,7 @@ REST 是什么?维基百科: 这里的 `https://official-joke-api.appspot.com/random_joke` 被称为 API 端点,服务端会监听指向这个端点的请求。 -## REST 剖析: +## REST 剖析 现在我们知道了客户端可以向服务端发送携带数据的请求、服务端会返回适当的响应,再来深入了解一下如何构造一个请求。 @@ -448,7 +448,7 @@ fetch("/add", { 我在 glitch 上部署了一个[小应用][15]供你测试。 -## 总结: +## 总结 通过本文,我们学习了 REST 架构和 REST 请求的相关知识,我们一起构建了一个简单的能够处理 `GET` 和 `POST` 请求的 REST 服务器,还构建了一个 web 应用作为 REST 客户端来计算两数之和。 diff --git a/chinese/articles/reveal-on-scroll-in-react-using-the-intersection-observer-api.md b/chinese/articles/reveal-on-scroll-in-react-using-the-intersection-observer-api.md index b31faebc6..8d789ad0c 100644 --- a/chinese/articles/reveal-on-scroll-in-react-using-the-intersection-observer-api.md +++ b/chinese/articles/reveal-on-scroll-in-react-using-the-intersection-observer-api.md @@ -36,7 +36,7 @@ This section consists of three parts: 2. Adding boilerplates and styles 3. Implementing the review on scroll functionality -### How to set up your React environment: +### How to set up your React environment First, you'll need to set up a React environment by running `npx create-react-app [your project name]`, either on your native terminal or an IDE’s terminal. diff --git a/chinese/articles/schofields-laws-of-computing.md b/chinese/articles/schofields-laws-of-computing.md index d707baf60..5e3e5fa20 100644 --- a/chinese/articles/schofields-laws-of-computing.md +++ b/chinese/articles/schofields-laws-of-computing.md @@ -7,7 +7,7 @@ 斯科菲尔德的计算法则是任何与计算机打交道的人都应用知道的法则。它们的重点是数据的可移植性、完整性和安全性。 -[杰克-斯科菲尔德](https://wikipedia.org/wiki/Jack_Schofield_(journalist)) 是一位多产的记者,为《卫报》撰稿,接近40年。在此期间, 他写了三篇特别的文章,被称为 "斯科菲尔德的计算法则". +[杰克-斯科菲尔德](https://wikipedia.org/wiki/Jack_Schofield_(journalist)) 是一位多产的记者,为《卫报》撰稿,接近 40 年。在此期间, 他写了三篇特别的文章,被称为 "斯科菲尔德的计算法则". 杰克并不是一下子就创造了这些原则,而是在他整个职业生涯中遇到的`发现`的积累。 @@ -28,10 +28,10 @@ 例如: -- LastPass将免费用户限制在一种类型的设备上。([更多信息](https://wikipedia.org/wiki/LastPass#Reception)) -- ArtStation被Epic Games收购。([更多信息](https://wikipedia.org/wiki/Epic_Games#Acquisitions)) -- Adobe转向软件即服务的商业模式(SaaS)。([更多信息](https://wikipedia.org/wiki/Adobe_Creative_Cloud#Criticism)) -- Megaupload被关闭并被管理机构查封。([更多信息](https://wikipedia.org/wiki/Megaupload#2012_indictments_by_the_United_States)) +- LastPass 将免费用户限制在一种类型的设备上。([更多信息](https://wikipedia.org/wiki/LastPass#Reception)) +- ArtStation 被 Epic Games 收购。([更多信息](https://wikipedia.org/wiki/Epic_Games#Acquisitions)) +- Adobe 转向软件即服务的商业模式(SaaS)。([更多信息](https://wikipedia.org/wiki/Adobe_Creative_Cloud#Criticism)) +- Megaupload 被关闭并被管理机构查封。([更多信息](https://wikipedia.org/wiki/Megaupload#2012_indictments_by_the_United_States)) 数据可移植性是软件和服务的一个基本特征。你的主要解决方案需要避免供应商的锁定或必须为迁移过程做昂贵的预算的。 @@ -49,11 +49,11 @@ ## 数据可迁移的权利 -[通用数据保护条例](https://wikipedia.org/wiki/General_Data_Protection_Regulation) (GDPR)有助于提高数据可迁移的权利。它促使像Discord、Instagram和Twitter这样的公司为用户添加自动化工具来导出他们的内容。 +[通用数据保护条例](https://wikipedia.org/wiki/General_Data_Protection_Regulation) (GDPR)有助于提高数据可迁移的权利。它促使像 Discord、Instagram 和 Twitter 这样的公司为用户添加自动化工具来导出他们的内容。 -GDPR的第20条是 "数据可迁移性权利",即有办法将你的个人数据从数据控制者那里转移到你可以给另一个数据控制者的标准格式的权利。 +GDPR 的第 20 条是 "数据可迁移性权利",即有办法将你的个人数据从数据控制者那里转移到你可以给另一个数据控制者的标准格式的权利。 -尽管GDPR是专门针对个人数据的,但这已经促进了通用的数据可迁移性,包括用户生成的内容。虽然在欧盟之外执行GDPR的难度值得商榷,但这些工具通常也可以让其他司法管辖区(欧盟外)的成员使用。 +尽管 GDPR 是专门针对个人数据的,但这已经促进了通用的数据可迁移性,包括用户生成的内容。虽然在欧盟之外执行 GDPR 的难度值得商榷,但这些工具通常也可以让其他司法管辖区(欧盟外)的成员使用。 如果你代表一个组织或自由职业者,负责挑选软件,请记住这一点! @@ -83,19 +83,19 @@ GDPR的第20条是 "数据可迁移性权利",即有办法将你的个人数 斯科菲尔德第二法则 不仅针对你拥有的数据,还包括你保存在云端的数据。例如,你应该把你的云存储、电子邮件和媒体内容也存放在第二个地方。 -当使用那些不需要备份你的数据,或在一段时间不活动后关闭你的账户的服务时,这一点尤其重要。这在那些提供真正免费服务的公司中很常见,它们不需要成本,也不需要跟踪,比如Nextcloud供应商或Tutanota。 +当使用那些不需要备份你的数据,或在一段时间不活动后关闭你的账户的服务时,这一点尤其重要。这在那些提供真正免费服务的公司中很常见,它们不需要成本,也不需要跟踪,比如 Nextcloud 供应商或 Tutanota。 -不要因为你的数据在云中就认为它是安全的。最近的一个例子是OVH的一个数据中心发生火灾,导致数据丢失。OVH公共云提供非管理型服务器,这意味着用户负责管理和备份他们的服务器。([更多信息](https://wikipedia.org/wiki/OVHcloud#Incidents)) +不要因为你的数据在云中就认为它是安全的。最近的一个例子是 OVH 的一个数据中心发生火灾,导致数据丢失。OVH 公共云提供非管理型服务器,这意味着用户负责管理和备份他们的服务器。([更多信息](https://wikipedia.org/wiki/OVHcloud#Incidents)) 此外,还存在着漏洞的风险,允许未经授权的人访问你的账户。 -例如,去年黑客获得了对Twitter管理工具的访问权,使他们能够访问许多知名的账户。使用这样的工具,要删除以前的帖子和媒体也是很容易的。([更多信息](https://wikipedia.org/wiki/2020_Twitter_account_hijacking)) +例如,去年黑客获得了对 Twitter 管理工具的访问权,使他们能够访问许多知名的账户。使用这样的工具,要删除以前的帖子和媒体也是很容易的。([更多信息](https://wikipedia.org/wiki/2020_Twitter_account_hijacking)) ## 同步你的数据 你可以通过使用在你的电脑和服务器之间同步数据的解决方案来解决其中的一些问题。这提供了保护,防止任何与硬件有关的故障,或物理损坏。 -如果数据丢失会有不利的后果,应该加密并同步到云存储。一些软件,如Bitwarden或Thunderbird,本身也依赖于同步,所以即使服务器消失了,你的设备上仍然有一个最新的副本。 +如果数据丢失会有不利的后果,应该加密并同步到云存储。一些软件,如 Bitwarden 或 Thunderbird,本身也依赖于同步,所以即使服务器消失了,你的设备上仍然有一个最新的副本。 然而,同步并不能解决所有问题--最理想的是也有一个独立的备份。同步将自动发送所有的变化,包括用户错误,甚至是恶意软件或勒索软件所做的修改。有定期的冷存储备份对这样的情况会很方便。 @@ -117,9 +117,9 @@ GDPR的第20条是 "数据可迁移性权利",即有办法将你的个人数 - [伪造凭证](https://wikipedia.org/wiki/Credential_stuffing) – 押注于密码的重复使用,因为这让密码使用者比管理多个密码更方便。 - [字典攻击](https://wikipedia.org/wiki/Dictionary_attack) – 猜常用密码,因为它对于密码使用者更容易回忆。 -此外,这些攻击基于用户没有启用2FA,以及另一方的数据没有被混淆或加密。 +此外,这些攻击基于用户没有启用 2FA,以及另一方的数据没有被混淆或加密。 -你或你的组织应该使用一个密码管理器,在所有系统上执行2FA,并在可能的情况下,在数据被发送到第三方服务器之前进行加密。 +你或你的组织应该使用一个密码管理器,在所有系统上执行 2FA,并在可能的情况下,在数据被发送到第三方服务器之前进行加密。 甚至更好,删除不再相关的数据(无用的数据)。删除数据总是比担心保护数据要好。 @@ -127,8 +127,8 @@ GDPR的第20条是 "数据可迁移性权利",即有办法将你的个人数 # 总结 -这些法则是在10多年前确立的,今天比以往任何时候都更适用。 +这些法则是在 10 多年前确立的,今天比以往任何时候都更适用。 技术和开放标准已经发生了演变,所以它们更容易被遵守。但随着云计算基础设施的发展,我们越来越信任第三方来处理我们的数据。在许多情况下,我们可能对它被留在哪里以及谁能接触到它感到太舒服(太信任)了。 -不幸的是,杰克-斯科菲尔德于2020年3月去世,但他支持了科技界的许多人。我希望通过分享他的经验,其他人可以继续从中学习。 \ No newline at end of file +不幸的是,杰克-斯科菲尔德于 2020 年 3 月去世,但他支持了科技界的许多人。我希望通过分享他的经验,其他人可以继续从中学习。 \ No newline at end of file diff --git a/chinese/articles/scientific-computing-in-golang-using-gonum.md b/chinese/articles/scientific-computing-in-golang-using-gonum.md index 9f7648fbf..3ca683cff 100644 --- a/chinese/articles/scientific-computing-in-golang-using-gonum.md +++ b/chinese/articles/scientific-computing-in-golang-using-gonum.md @@ -7,7 +7,7 @@ In this article, I'll introduce you to Gonum, a package you can use to perform scientific computations in the Go programming language. -### Here's what we'll cover in this intermediate tutorial: +### Here's what we'll cover in this intermediate tutorial - What is Gonum? - Why use Gonum @@ -16,7 +16,7 @@ In this article, I'll introduce you to Gonum, a package you can use to perform s - How to perform matrix operations using Gonum - Other scientific computations supported by Gonum. -### Prerequisites: +### Prerequisites - Knowledge of functional programming in Golang. - A Golang IDE with Go installed (I use Goland and Go 1.17.6, but you can use any other) @@ -118,7 +118,7 @@ Gonum supports matrix operations in the [`mat` package](https://pkg.go.dev/gonum import “gonum.org/v1/gonum/mat” ``` -### How to Create a Matrix: +### How to Create a Matrix `mat.NewDense` is the method for creating a matrix. It takes in the dimensions of the matrix and the data to be passed in, which could be nil (a matrix with all entities equal to zero). diff --git a/chinese/articles/secure-application-basics.md b/chinese/articles/secure-application-basics.md index 340fcebf6..8da8f6916 100644 --- a/chinese/articles/secure-application-basics.md +++ b/chinese/articles/secure-application-basics.md @@ -5,7 +5,7 @@ ![How to make your app's architecture secure right now: separation, configuration, and access](https://www.freecodecamp.org/news/content/images/size/w2000/2019/09/cover-2.png) -### 如果你是一个工作繁忙的开发人员,那么本文将是你构建安全的应用程序体系架构的一个很好的起点。 +### 如果你是一个工作繁忙的开发人员,那么本文将是你构建安全的应用程序体系架构的一个很好的起点 与过去相比,如今的开发人员可以更专注于构建软件,这是一件好事儿。 @@ -78,7 +78,7 @@ application/ 尽管花费时间进行定制化会影响生产率,但配置的设置还是你需要进行定制化的部分。 -[安全性配置错误][4]被列举在 OWASP Top 10中。大量的安全时间的发生都是由于服务器、防火墙或者管理员账号的相关信息以默认配置运行在生产环境中。在实际的工作中,你也要格外注意这些安全隐患。 +[安全性配置错误][4]被列举在 OWASP Top 10 中。大量的安全时间的发生都是由于服务器、防火墙或者管理员账号的相关信息以默认配置运行在生产环境中。在实际的工作中,你也要格外注意这些安全隐患。 ![](https://www.freecodecamp.org/news/content/images/2019/09/defaultkey.png) diff --git a/chinese/articles/software-testing-beginners-guide.md b/chinese/articles/software-testing-beginners-guide.md index 7a2d0fafd..eaaf417e5 100644 --- a/chinese/articles/software-testing-beginners-guide.md +++ b/chinese/articles/software-testing-beginners-guide.md @@ -11,7 +11,7 @@ But software testing is a complex topic and can be a bit difficult to understand In this article, I'll explain the major topics in software testing and how this practice can help you. -### Table of Contents: +### Table of Contents - [What is Software Testing?](#what-is-software-testing) - [Types of Software Testing](#types-of-software-testing) @@ -38,7 +38,7 @@ Other benefits of testing your code include preventing bugs, reducing cost, and There are two general types of software testing: -### Functional Testing: +### Functional Testing Functional Testing is a software testing method that validates the system against the customer's requirements or specifications. @@ -48,7 +48,7 @@ For examples, let's say you write a test case to test creating a user. The test Functional testing checks that everything is functioning properly by emulating business scenarios based on applicable requirements. -### Non-functional Testing: +### Non-functional Testing Non-functional testing is a software testing method that tests for end-user experiences, such as performance and reliability under load. This could either make or break a user experience. @@ -66,7 +66,7 @@ ADVERTISEMENT if (!isAuthenticated) (adsbygoogle = window.adsbygoogle || \[\]).push({}); -### Unit Testing: +### Unit Testing Unit testing is a type of software testing that validates how each software unit performs and whether that specific piece of code does what it should. A unit is the smallest testable component of an application. @@ -82,19 +82,19 @@ Code Snippet of a unit test case in Python Code Snippet of a unit test case in Java -### Integration Testing: +### Integration Testing Integration Testing is software testing which helps ensure that software components or functions work together properly. This is the second phase of the software testing process that comes after unit testing. In this type of testing, units or individual software components are tested in groups. This testing method mainly focuses on exposing defects in interactions between integrated components and units. -### System Testing: +### System Testing System testing involves the process of testing integrated software. The aim is to evaluate the system's compliance with specify requirements. In system testing, the quality assurance team evaluates how each component of the application or software work together in a full, integrated environment. -### Acceptance Testing: +### Acceptance Testing Acceptance testing is a software testing method where a system is tested or checked for acceptability. It evaluates the system's compatibility with the business requirements and assesses whether it is acceptable for delivery. @@ -102,7 +102,7 @@ It is also known as formal testing performed to fit user needs, requirements, an Acceptance testing is the last stage of software testing done after system testing and before making the system available for public use. -### Regression Testing: +### Regression Testing Regression testing ensures that a component continues working as it should, after including additional components in the program. You perform regression testing when something changes, such as adding a new module to the program. @@ -112,7 +112,7 @@ ADVERTISEMENT if (!isAuthenticated) (adsbygoogle = window.adsbygoogle || \[\]).push({}); -### Alpha Testing and Beta Testing: +### Alpha Testing and Beta Testing Alpha testing is also known as initial validation testing. It is an aspect of acceptance testing done before the product is given to the consumers or users. QA (Quality Assurance) testers usually do this. Alpha testing is done internally by the QA team. @@ -126,19 +126,19 @@ Everything in tech has principles. These are guidelines to help you build better Here are some software testing principles you should follow when writing tests for your code: -### Testing aims to show the presence of defects, not the absence: +### Testing aims to show the presence of defects, not the absence Software testing aims to spot software failures. This reduces the presence of faults and errors. Software testing ensures defects are visible to the developer but doesn't guarantee defect-free software. Multiple types of testing can't even ensure error-free software. Testing can only decrease the number of errors. -### Exhaustive testing is not possible: +### Exhaustive testing is not possible Exhaustive Testing is the process of testing software for all valid and invalid inputs and pre-conditions. This method of testing is not realistic because test cases presume that the software is correct and it produces the correct output in every test case. If you truly try to test every aspect and test case in your software, it will take too much time and effort, and it's not practical. -### Perform early testing: +### Perform early testing Testing your software at an early phase helps avoid minor bugs or errors. When you can spot errors at an early stage of the Software Development Life Cycle(SDLC), it's always less expensive. It is best to start software testing from the beginning of the project. @@ -146,19 +146,19 @@ ADVERTISEMENT if (!isAuthenticated) (adsbygoogle = window.adsbygoogle || \[\]).push({}); -### Defect clustering: +### Defect clustering Defect clustering refers to when most of the problems you find occur in just a few parts of the application or software. If you can identify the modules or areas where these defects occur, you can focus most of your testing efforts on them. Keep the Pareto Principle in mind when testing your code: 80% of software defects tend to come from 20% of the modules. -### Beware of the Pesticide paradox: +### Beware of the Pesticide paradox This principle is based on a theory – "the more you use pesticide on a crop, the more immune the crop will eventually grow, and the pesticide will not be effective." When you repeat particular test cases over and over, you will see fewer and fewer new bugs. So to find new bugs, update your test cases and run them once you add new test cases. -### Testing is context-dependent: +### Testing is context-dependent Testing is context-dependent, which means that you should test your software based on its needs, functionalities, and requirements. @@ -168,7 +168,7 @@ For instance, when testing an eCommerce web app, you will focus on its functiona You wouldn't necessarily use the same test cases for both – that is what it means that testing is context-dependent. -### The absence of errors is a fallacy: +### The absence of errors is a fallacy If you build software that is 99% bug-free, but it doesn't follow user requirements, it is not usable for end-users. @@ -182,19 +182,19 @@ ADVERTISEMENT if (!isAuthenticated) (adsbygoogle = window.adsbygoogle || \[\]).push({}); -### Software testing improves security: +### Software testing improves security When building software, security is a crucial part of your planning. This is because vulnerable software could jeopardize you users and their information, as hackers can use stolen info for malicious purposes. As a product undergoes testing, the end-user can count on the fact that they will be getting a reliable product and their details will be secured and safe. So users are more likely to get a product that is free from vulnerabilities with the help of software testing. -### Software testing improves product quality: +### Software testing improves product quality You want your software or product to be bug-free, low-risk, and effective at what it should do. And you can achieve this by including test cases and other testing methods when building out the code. In addition, you won't know how good your product is until you test it. This helps you provide the best product version before it gets released (and discover any inconsistencies or pain points along the way – so you can improve them). -### Software testing improves customer satisfaction: +### Software testing improves customer satisfaction For instance, let's say you download a new app and try to use some of its functionality – but it shows an error. This will probably frustrate you, and you might not want to use the app again, right? @@ -202,7 +202,7 @@ This is exactly why software testing is important. It can help you discover such By investing in software testing early in the development stage, you are letting the users know that you care about their experience. It could also help you create a solid long-term customer relationship. -### Software testing saves money: +### Software testing saves money Software testing can save you a lot of money – but how? diff --git a/chinese/articles/solid-principles.md b/chinese/articles/solid-principles.md index b50165b0b..3f2ed9a1d 100644 --- a/chinese/articles/solid-principles.md +++ b/chinese/articles/solid-principles.md @@ -17,9 +17,9 @@ SOLID 原则是面向对象 class 设计的五条原则。他们是设计 class ## 背景 -SOLID 原则首先由著名的计算机科学家 Robert C·Martin (著名的Bob大叔)由 2000 年在他的[论文][1]中提出。但是 SOLID 缩略词是稍晚由 Michael Feathers 先使用的。 +SOLID 原则首先由著名的计算机科学家 Robert C·Martin (著名的 Bob 大叔)由 2000 年在他的[论文][1]中提出。但是 SOLID 缩略词是稍晚由 Michael Feathers 先使用的。 -Bob大叔也是畅销书《代码整洁之道》和《架构整洁之道》的作者,也是 ["Agile Alliance"][2] 的成员。 +Bob 大叔也是畅销书《代码整洁之道》和《架构整洁之道》的作者,也是 ["Agile Alliance"][2] 的成员。 因此,代码整洁、面向对象架构、设计模式彼此互补并以这种方式连接就不足为奇了。 @@ -47,7 +47,7 @@ Bob大叔也是畅销书《代码整洁之道》和《架构整洁之道》的 其次,单一职责更容易版本管理,比如,有一个持久化 class 处理数据库操作,我们在 GitHub 看到某个文件上有一处修改。如果遵循 SRP 原则,根据文件就能判断这是关于存储或者数据库相关的提交。 -另一个例子是合并冲突,当不同的团队修改同一个文件时,如果遵循 SRP原则,冲突很少会发生,因为文件只有一个变化的原因,即使出现冲突也会很容易解决。 +另一个例子是合并冲突,当不同的团队修改同一个文件时,如果遵循 SRP 原则,冲突很少会发生,因为文件只有一个变化的原因,即使出现冲突也会很容易解决。 ### 常见错误和反面教材 @@ -132,7 +132,7 @@ public class Invoice { 第一处是 **printInvoice** 方法,因为里面包含了打印逻辑。SRP 描述 class 应该只有一个变化的原因,这个变化原因应该是 class 里的发票计算。 -在这个架构里,如果我们想要改变打印格式,我们需要修改这个 class。我们不能把打印逻辑和业务逻辑混合在一个class 里。 +在这个架构里,如果我们想要改变打印格式,我们需要修改这个 class。我们不能把打印逻辑和业务逻辑混合在一个 class 里。 在 class 里面还有一个方法违背了 SRP: **saveToFile** 方法。这也是一个很常见的错误,把持久化逻辑和业务逻辑混合在了一起。 @@ -261,7 +261,7 @@ public class FilePersistence implements InvoicePersistence { class 结构如下: ![](https://erinc.io/wp-content/uploads/2020/08/SOLID-Tutorial-1-1024x554.jpeg) -现在持久化逻辑更易于扩展了,如果老板要求我们添加另一个数据库,有了两种不同类型的数据库如 MySQL和 MongoDB ,可以更快搞定了。 +现在持久化逻辑更易于扩展了,如果老板要求我们添加另一个数据库,有了两种不同类型的数据库如 MySQL 和 MongoDB ,可以更快搞定了。 你可能会想,我们只需创建多个 class 给每个都添加一个 save 方法而无需接口。 @@ -475,7 +475,7 @@ public class FreeParking implements ParkingLot { ## 结论 -在本文中,先介绍了SOLID 原则的历史,接着尝试解释清楚为什么使用以及怎么使用各个原则。我们甚至重构了一个简单的发票应用来遵循 SOLID 原则。 +在本文中,先介绍了 SOLID 原则的历史,接着尝试解释清楚为什么使用以及怎么使用各个原则。我们甚至重构了一个简单的发票应用来遵循 SOLID 原则。 感谢你花时间阅读本文,希望上面的概念已经解释清楚了。 diff --git a/chinese/articles/solve-sudoku-using-azure-ai.md b/chinese/articles/solve-sudoku-using-azure-ai.md index 3b2b0295f..6954d60f3 100644 --- a/chinese/articles/solve-sudoku-using-azure-ai.md +++ b/chinese/articles/solve-sudoku-using-azure-ai.md @@ -421,8 +421,8 @@ import { NgMaterialModule } from './ng-material/ng-material.module'; 这个命令将创建一个名为 services 的文件夹,然后在里面创建以下两个文件: -- form-recognizer.service.ts - 服务类文件 -- form-recognizer.service.spec.ts - 服务的单元测试文件 +- form-recognizer.service.ts- 服务类文件 +- form-recognizer.service.spec.ts -服务的单元测试文件 将下面的代码放入 `form-recognizer.service.ts` 文件中。 diff --git a/chinese/articles/stopping-credit-card-fraud-and-saving-our-nonprofit.md b/chinese/articles/stopping-credit-card-fraud-and-saving-our-nonprofit.md index 1f5f61d7d..229a390fc 100644 --- a/chinese/articles/stopping-credit-card-fraud-and-saving-our-nonprofit.md +++ b/chinese/articles/stopping-credit-card-fraud-and-saving-our-nonprofit.md @@ -133,7 +133,7 @@ I tried another refund. "Refund Pending." Oh geez. -# They're not going to make this easy for me, are they. +# They're not going to make this easy for me, are they I immediately tabbed back to Stripe's support page and requested another callback. They explained to me that I could no longer refund transactions because we didn't have any more money in our Stripe account. @@ -269,7 +269,7 @@ If I hadn't continued to press for Stripe for a better solution than wiring them It pays to be polite but insistent. -### Lesson #4: There are some real bastards out there. +### Lesson #4: There are some real bastards out there > "Security in IT is like locking your house or car – it doesn't stop the bad guys,  but if it's good enough they may move on to an easier target." - Paul Herbka diff --git a/chinese/articles/structuring-a-flask-restplus-web-service-for-production-builds.md b/chinese/articles/structuring-a-flask-restplus-web-service-for-production-builds.md index 61bc12b6c..d2e76efd1 100644 --- a/chinese/articles/structuring-a-flask-restplus-web-service-for-production-builds.md +++ b/chinese/articles/structuring-a-flask-restplus-web-service-for-production-builds.md @@ -87,7 +87,7 @@ mkproject name_of_your_project 这里使用[功能性结构](http://exploreflask.com/zh-CN/latest/blueprints.html#functional-structure)通过文件的功能来组织项目文件。在功能结构里,模板、静态文件、视图在三个不同的目录中。 -在项目目录中,创建一个名为 `app` 的新包。 在 `app` 内部,创建两个包 ` main` 和 `test`。 目录结构如下。 +在项目目录中,创建一个名为 `app` 的新包。 在 `app` 内部,创建两个包 `main` 和 `test`。 目录结构如下。 ``` . @@ -591,7 +591,7 @@ class User(Resource): > _具体资源应从此 class 扩展并暴露每个支持的 HTTP 方法。如果使用不支持的 HTTP 方法调用资源,则 API 将返回状态为 405 Method Not Allowe 的响应。否则,将调用适当的方法并在将资源添加到 API 实例时传递所有的 URL 参数_。 -上面 ` line 7`中的 `api` 命名空间为 controller 提供了多个装饰器,包括但不限于以下几种: +上面 `line 7`中的 `api` 命名空间为 controller 提供了多个装饰器,包括但不限于以下几种: - api.**route**: __route 资源的装饰器__ - api.**marshal\_with**: __一个用来指定需要序列化字段的装饰器 (就是用到的之前创建的 `__userDto__` )__ @@ -649,7 +649,7 @@ app.app_context().push() python manage.py run ``` -现在,在浏览器中打开URL [http://127.0.0.1:5000](http://127.0.0.1:5000/)。 应该可以看到 swagger 的文档。 +现在,在浏览器中打开 URL [http://127.0.0.1:5000](http://127.0.0.1:5000/)。 应该可以看到 swagger 的文档。 ![](https://cdn-media-1.freecodecamp.org/images/1*Us_S2WLR3AQAyfOvkzZ38Q.png) @@ -663,7 +663,7 @@ python manage.py run #### 安全与认证 -创建一个 ` blacklistToken` model 来存储列入黑名单的 tokens。 在 `models` 包中,创建具有以下内容的 `blacklist.py`文件: +创建一个 `blacklistToken` model 来存储列入黑名单的 tokens。 在 `models` 包中,创建具有以下内容的 `blacklist.py`文件: ```python from .. import db @@ -1006,7 +1006,7 @@ api.add_namespace(auth_ns) 使用 `python manage.py run` 运行应用程序,然后在浏览器中打开网址 [http://127.0.0.1:5000](http://127.0.0.1:5000/)。 -swagger 文档现在应该展示出新创建的带有 `login` 和 `logout` 接口的 `auth `命名空间。 +swagger 文档现在应该展示出新创建的带有 `login` 和 `logout` 接口的 `auth`命名空间。 ![](https://cdn-media-1.freecodecamp.org/images/1*K4ZVMOwsOIIzBOV8bfqJew.png) diff --git a/chinese/articles/tcp-vs-udp-which-is-faster.md b/chinese/articles/tcp-vs-udp-which-is-faster.md index 6d812cfac..2a2d551fe 100644 --- a/chinese/articles/tcp-vs-udp-which-is-faster.md +++ b/chinese/articles/tcp-vs-udp-which-is-faster.md @@ -105,7 +105,7 @@ TCP 报头的长度必须至少为 20 字节且不超过 60 字节。 ![](https://www.freecodecamp.org/news/content/images/2021/05/Screenshot-2021-05-31-at-10.56.59-AM.png) -#### TCP 的报头包括: +#### TCP 的报头包括 1. **源端口** - 表示发送设备的源端口。占 16 位。 2. **目的端口** - 表示接收设备上的目的端口。占 16 位。 @@ -122,7 +122,7 @@ TCP 报头的长度必须至少为 20 字节且不超过 60 字节。 ![](https://www.freecodecamp.org/news/content/images/2021/05/Screenshot-2021-05-31-at-10.57.27-AM.png) -#### UDP 的报头包括: +#### UDP 的报头包括 1. **源端口** - 表示发送设备的源端口。占 16 位。 2. **目的端口** - 表示接收设备上的目的端口。占 16 位。 diff --git a/chinese/articles/technical-blogging-basics.md b/chinese/articles/technical-blogging-basics.md index b37e522fe..f5fafcbaa 100644 --- a/chinese/articles/technical-blogging-basics.md +++ b/chinese/articles/technical-blogging-basics.md @@ -48,7 +48,7 @@ 💡 **小提示:** 创建一个私人 GitHub 仓库,以及一个 markdown 文件。当你遇到新事物时,在这个文件中添加注释(如果需要的话还可以加代码)。 -这个文件的内容将作为你今后文章的优秀资源。出于这个目的,我在维护一个名为 TIL\_2021.md 的文件(记录了我2021年学到的东西)。 +这个文件的内容将作为你今后文章的优秀资源。出于这个目的,我在维护一个名为 TIL\_2021.md 的文件(记录了我 2021 年学到的东西)。 当决定写博客时,我打算通过分享知识来学习。如果你想深入学习一些东西,请开始教别人。写博客就是一个很棒的方式。 @@ -270,5 +270,5 @@ SERP 生成器 - [如何毫不费力地找到博客内容创意?](https://blog.greenroots.info/how-to-find-blog-content-ideas-effortlessly-ckghrjv5200o7rhs1ewn40102) - [从哪里开始?给新手的一些实用技巧](https://blog.greenroots.info/where-to-begin-some-practical-tips-from-a-beginner-ckcu5llil00ncw8s11dr1fh2w) - [作为开发者,你为什么需要做业余项目?](https://blog.greenroots.info/why-do-you-need-to-do-side-projects-as-a-developer-ckhn5m5km05teajs1fvjd7u5f) -- [你可能觉得有用的16个业余项目的 GitHub 仓库](https://blog.greenroots.info/16-side-project-github-repositories-you-may-find-useful-ckk50hic406quhls1dui2d6sd) +- [你可能觉得有用的 16 个业余项目的 GitHub 仓库](https://blog.greenroots.info/16-side-project-github-repositories-you-may-find-useful-ckk50hic406quhls1dui2d6sd) - [作为一个软件开发者,如何每天学点新东西](/news/learn-something-new-every-day-as-a-software-developer/) diff --git a/chinese/articles/technical-writing-for-beginners.md b/chinese/articles/technical-writing-for-beginners.md index 953616c96..04f7c889c 100644 --- a/chinese/articles/technical-writing-for-beginners.md +++ b/chinese/articles/technical-writing-for-beginners.md @@ -30,7 +30,7 @@ 技术写作是一门提供以细节为导向的指导的艺术,以帮助用户了解特定的技能或产品。 -而技术作家就是写这些说明的人,也就是所谓的技术文档或教程。这可能包括用户手册、在线支持文章或编码员/API开发人员的内部文档。 +而技术作家就是写这些说明的人,也就是所谓的技术文档或教程。这可能包括用户手册、在线支持文章或编码员/API 开发人员的内部文档。 技术作家的沟通方式是介绍技术信息,使读者能够理解这些信息。 @@ -151,7 +151,7 @@ This process includes a number of steps, which we'll go over now. 下面是一个**被动语态**的例子: 这文档应该被网络开发人员每年阅读六次。 -这里有一个**主动语态**的例子: 每个网络开发人员都应该每年读6次这个文档。 +这里有一个**主动语态**的例子: 每个网络开发人员都应该每年读 6 次这个文档。 #### 谨慎地选词 @@ -167,9 +167,9 @@ This process includes a number of steps, which we'll go over now. **下面是一个例子**: -不清晰: **PWAs**确实被认为是多平台开发的未来。它们在安卓和iOS上的可用性使它们成为未来的应用程序。 +不清晰: **PWAs**确实被认为是多平台开发的未来。它们在安卓和 iOS 上的可用性使它们成为未来的应用程序。 -改进版: **Progressive Web Applications (PWAs)** 是真正的多平台开发的未来。它们在Android和iOS上的可用性使**PWAs**成为未来的应用程序。 +改进版: **Progressive Web Applications (PWAs)** 是真正的多平台开发的未来。它们在 Android 和 iOS 上的可用性使**PWAs**成为未来的应用程序。 #### 使用通俗易懂的说法 @@ -193,7 +193,7 @@ This process includes a number of steps, which we'll go over now. ![step1-1](https://www.freecodecamp.org/news/content/images/2020/11/step1-1.PNG) -在你的文章中添加图片,使内容更有亲和力,更容易理解。除了图片,你还可以在必要时使用gif、emoji、embeds(社交媒体、代码)和代码片段。 +在你的文章中添加图片,使内容更有亲和力,更容易理解。除了图片,你还可以在必要时使用 gif、emoji、embeds(社交媒体、代码)和代码片段。 深思熟虑的格式、模板和图像或图表也会使你的文本对读者更有帮助。你可以查看下面的参考资料,了解@Bolajiayodeji 的技术写作模板。 @@ -201,7 +201,7 @@ This process includes a number of steps, which we'll go over now. 任何类型的好文章都必须没有拼写和语法错误。这些错误可能看起来很明显,但并不总是容易发现它们(尤其是在长篇文件中)。 -在点击 "发布 "之前,一定要仔细检查你的拼写(你知道,点你的Is和交叉你的Ts,从英文的角度来讲,`I`与`T`,看起来挺近似的,注意拼写错误)。 +在点击 "发布 "之前,一定要仔细检查你的拼写(你知道,点你的 Is 和交叉你的 Ts,从英文的角度来讲,`I`与`T`,看起来挺近似的,注意拼写错误)。 有许多免费的工具,如[Grammarly](https://grammarly.com/)和[Hemingway app](http://www.hemingwayapp.com/),你可以用它们来检查语法和拼写错误。你也可以把你的文章草稿与别人分享,以便在发表前进行校对。 @@ -225,9 +225,9 @@ This process includes a number of steps, which we'll go over now. ![freecodecamp](https://www.freecodecamp.org/news/content/images/2020/11/freecodecamp.PNG) -**[Hackernoon](https://hackernoon.com)** 有超过7000名作家,可能是一个伟大的平台,让你开始向社区中每天超过20万的读者发布你的文章。 +**[Hackernoon](https://hackernoon.com)** 有超过 7000 名作家,可能是一个伟大的平台,让你开始向社区中每天超过 20 万的读者发布你的文章。 -Hacker Noon为作家提供帮助,在他们的文章在平台上发布之前进行校对,帮助他们避免常见错误。 +Hacker Noon 为作家提供帮助,在他们的文章在平台上发布之前进行校对,帮助他们避免常见错误。 ![hackernoon](https://www.freecodecamp.org/news/content/images/2020/11/hackernoon.PNG) @@ -240,8 +240,8 @@ Hacker Noon为作家提供帮助,在他们的文章在平台上发布之前进 以下是一些技术写作课程,你可以去看看: - [谷歌技术写作课程](https://developers.google.com/tech-writing) (Free) -- [Udemy技术写作课程](https://www.udemy.com/topic/technical-writing/) (Paid) -- [Hashnode技术写作训练营](https://hashnode.com/bootcamp/batch-2) (Free) +- [Udemy 技术写作课程](https://www.udemy.com/topic/technical-writing/) (Paid) +- [Hashnode 技术写作训练营](https://hashnode.com/bootcamp/batch-2) (Free) ## Technical Writing Forums and Communities @@ -263,7 +263,7 @@ Hacker Noon为作家提供帮助,在他们的文章在平台上发布之前进 这些作家是我仰望的对象,被我视为技术写作的虚拟导师。有时,他们传授的技术写作技巧让我觉得很有帮助,并从中学到了很多。 -以下是这些作家中的一些人(他们的twitter账号): +以下是这些作家中的一些人(他们的 twitter 账号): - [Quincy Larson](https://twitter.com/ossia) - [Edidiong Asikpo](https://twitter.com/didicodes) @@ -276,11 +276,11 @@ Hacker Noon为作家提供帮助,在他们的文章在平台上发布之前进 ## 结语 -你不需要有技术写作的学位就可以开始发布技术内容。你可以开始在你的个人博客和公共GitHub项目上写作,同时建立你的作品集并获得实践经验。 +你不需要有技术写作的学位就可以开始发布技术内容。你可以开始在你的个人博客和公共 GitHub 项目上写作,同时建立你的作品集并获得实践经验。 **真的--只要开始写作。** -通过为现有的程序或项目创建新的文档来练习。在GitHub上有许多开源项目,你可以查看并添加到他们的文档中。 +通过为现有的程序或项目创建新的文档来练习。在 GitHub 上有许多开源项目,你可以查看并添加到他们的文档中。 是否有一个你喜欢使用的应用,但其文档写得很差?写下你自己的,并在网上分享以获得反馈。你也可以在[hashnode](https://hashnode.com)上快速建立你的博客并开始写作。 diff --git a/chinese/articles/test-driven-development-tutorial-how-to-test-javascript-and-reactjs-app.md b/chinese/articles/test-driven-development-tutorial-how-to-test-javascript-and-reactjs-app.md index d58c016a1..508aa83c8 100644 --- a/chinese/articles/test-driven-development-tutorial-how-to-test-javascript-and-reactjs-app.md +++ b/chinese/articles/test-driven-development-tutorial-how-to-test-javascript-and-reactjs-app.md @@ -7,23 +7,23 @@ 想要成为高产的软件开发工程师,了解测试驱动的开发必不可少。测试是创建可靠程序的基石。 -这篇教程会帮助你在JavaScript和React应用中实现测试驱动的开发。 +这篇教程会帮助你在 JavaScript 和 React 应用中实现测试驱动的开发。 ## 目录 1. [什么是测试驱动开发](#what-is-test-driven-development) -2. [测试驱动开发工作流的JavaScript示例](#javascript-example-of-a-test-driven-development-workflow) -3. [如何使用Jest来测试执行](#how-to-use-jest-as-a-test-implementation-tool) -4. [在Jest中使用es6模块须知](#important-stuff-to-know-about-using-es6-modules-with-jest) +2. [测试驱动开发工作流的 JavaScript 示例](#javascript-example-of-a-test-driven-development-workflow) +3. [如何使用 Jest 来测试执行](#how-to-use-jest-as-a-test-implementation-tool) +4. [在 Jest 中使用 es6 模块须知](#important-stuff-to-know-about-using-es6-modules-with-jest) 5. [测试驱动的开发有什么好处?](#what-are-the-advantages-of-test-driven-development) 6. [测试驱动开发中的单元测试是什么?](#what-is-a-unit-test-in-test-driven-development)? 7. [测试驱动开发中的集成测试是什么?](#what-is-an-integration-test-in-test-driven-development)? 8. [测试驱动开发中的端到端测试是什么?](#what-is-an-end-to-end-test-in-test-driven-development)? 9. [测试驱动开发中的测试替身是什么?](#what-are-test-doubles-in-test-driven-development)? 10. [阶段性总结测试驱动开发](#quick-overview-of-test-driven-development-so-far) -11. [如何测试React组件](#how-to-test-react-components) -12. [测试运行工具vsReact组件测试工具:区别是什么?](#test-runner-vs-react-component-testing-tool-what-s-the-difference) -13. [项目:React测试如何运行](#project-how-react-testing-works) +11. [如何测试 React 组件](#how-to-test-react-components) +12. [测试运行工具 vsReact 组件测试工具:区别是什么?](#test-runner-vs-react-component-testing-tool-what-s-the-difference) +13. [项目:React 测试如何运行](#project-how-react-testing-works) 14. [总结](#overview) 话不多说,让我们开始从了解什么是测试驱动开发开始吧! @@ -32,13 +32,13 @@ **测试驱动开发(TDD)** 是一种编程实践,你先写出你预期的程序会产生的结果,再编写程序。 -也就是说,TDD需要你预先构思好程序的输出,来通过你展望想实现的功能的测试。 +也就是说,TDD 需要你预先构思好程序的输出,来通过你展望想实现的功能的测试。 -所以,一种高效实践TDD的方法是你首先编写测试你预期结果的程序。 +所以,一种高效实践 TDD 的方法是你首先编写测试你预期结果的程序。 然后,你创建可以通过测试的程序。 -举个例子,假设你想要创建一个加法计算器,TDD方法如图: +举个例子,假设你想要创建一个加法计算器,TDD 方法如图: ![Test-driven development workflow diagram](https://www.freecodecamp.org/news/content/images/2022/07/test-driven-development-tdd-workflow-diagram-codesweetly.png) @@ -51,11 +51,11 @@ 5. 重构程序(如有必要)。 6. 重复循环,直至计算器符合你的预期。 -让我们来看一个用JavaScript实现的例子 +让我们来看一个用 JavaScript 实现的例子 <h2 id="javascript-example-of-a-test-driven-development-workflow">测试驱动开发工作流的JavaScript示例</h2> -让我们用一个简单的JavaScript程序,来分步骤实现测试驱动编程的工作流: +让我们用一个简单的 JavaScript 程序,来分步骤实现测试驱动编程的工作流: ### 1\. 编写测试 @@ -89,7 +89,7 @@ function additionCalculator(a, b) { additionCalculatorTester(); ``` -[**在StackBlitz查看示例**](https://stackblitz.com/edit/js-ciui1u?devToolsHeight=33&file=index.js) +[**在 StackBlitz 查看示例**](https://stackblitz.com/edit/js-ciui1u?devToolsHeight=33&file=index.js) ### 4\. 重构测试 @@ -121,25 +121,25 @@ const additionCalculator = (a, b) => a + b; additionCalculatorTester(); ``` -[**在StackBlitz查看示例**](https://stackblitz.com/edit/js-xp732h?devToolsHeight=33&file=index.js) +[**在 StackBlitz 查看示例**](https://stackblitz.com/edit/js-xp732h?devToolsHeight=33&file=index.js) 注意在以上例子中,我们没有使用任何第三方库。 其实你可以使用强大的第三方库来执行测试,如:[Jasmine](https://jasmine.github.io/)、 [Mocha](https://mochajs.org/)、 [Tape](https://github.com/substack/tape)和[Jest](https://jestjs.io/),这些库可以使你的测试运行得更加快速、简洁并充满乐趣。 -让我们一起看看如何使用Jest。 +让我们一起看看如何使用 Jest。 <h2 id="#how-to-use-jest-as-a-test-implementation-tool">如何使用Jest来测试执行</h2> -在使用Jest工具之前,你需要执行以下步骤: +在使用 Jest 工具之前,你需要执行以下步骤: -### 第一步:使用正确的Node和NPM版本 +### 第一步:使用正确的 Node 和 NPM 版本 -确保你的系统上装有Node 10.16 (或者更高版本) 和 NPM 5.6 (或者更高版本)。 +确保你的系统上装有 Node 10.16 (或者更高版本) 和 NPM 5.6 (或者更高版本)。 -你可以在[Node.js](https://nodejs.org/en/)官网下载最新的LTS。 +你可以在[Node.js](https://nodejs.org/en/)官网下载最新的 LTS。 -如果你更倾向于使用Yarn,确保你使用[Yarn 0.25 (或者更高版本)](https://yarnpkg.com/)。 +如果你更倾向于使用 Yarn,确保你使用[Yarn 0.25 (或者更高版本)](https://yarnpkg.com/)。 ### 第二步: 创建一个项目目录 @@ -165,29 +165,29 @@ cd path/to/addition-calculator-jest-project npm init -y ``` -如果你的[包管理器](https://codesweetly.com/package-manager-explained)是Yarn,执行: +如果你的[包管理器](https://codesweetly.com/package-manager-explained)是 Yarn,执行: ```bash yarn init -y ``` -### 第五步:安装Jest +### 第五步:安装 Jest -把Jest作为开发依赖包安装 +把 Jest 作为开发依赖包安装 ```bash npm install jest --save-dev ``` -如果你使用的是Yarn,执行: +如果你使用的是 Yarn,执行: ```bash yarn add jest --dev ``` -### 第六步:设置Jest为项目测试运行工具 +### 第六步:设置 Jest 为项目测试运行工具 -打开`package.json`文件,并把Jest添加到`test`区域。 +打开`package.json`文件,并把 Jest 添加到`test`区域。 ```json { @@ -213,7 +213,7 @@ touch additionCalculator.js touch additionCalculator.test.js ``` -**注意:** 测试文件的结尾必须是 `.test.js`,这样Jest才能够分辨出来这个文件是测试文件。 +**注意:** 测试文件的结尾必须是 `.test.js`,这样 Jest 才能够分辨出来这个文件是测试文件。 ### 第九步:编写测试案例 @@ -238,12 +238,12 @@ test("addition of 4 and 6 to equal 10", () => { **注意:** -- [`test()`](https://jestjs.io/docs/api#testname-fn-timeout)是Jest的全局方法,接受三个参数: +- [`test()`](https://jestjs.io/docs/api#testname-fn-timeout)是 Jest 的全局方法,接受三个参数: 1. 测试名 (`"addition of 4 and 6 to equal 10"`) 2. 一个包含你期望测试结果的函数 - 3. 一个可选的timeout参数 -- [`expect()`](https://jestjs.io/docs/expect#expectvalue)是一个测试代码输出的Jest方法。 -- [`toBe()`](https://jestjs.io/docs/expect#tobevalue) 是一个[Jest匹配器](https://jestjs.io/docs/using-matchers)函数,可以对比 `expect()`参数和原始值。 + 3. 一个可选的 timeout 参数 +- [`expect()`](https://jestjs.io/docs/expect#expectvalue)是一个测试代码输出的 Jest 方法。 +- [`toBe()`](https://jestjs.io/docs/expect#tobevalue) 是一个[Jest 匹配器](https://jestjs.io/docs/using-matchers)函数,可以对比 `expect()`参数和原始值。 假设你现在执行这个测试,测试将不会通过,因为你还没有编写程序,让我们现在开始吧! @@ -273,7 +273,7 @@ module.exports = additionCalculator; npm run test ``` -也可以使用Yarn: +也可以使用 Yarn: ```bash yarn test @@ -285,13 +285,13 @@ yarn test npm run test additionCalculator.test.js ``` -如果使用Yarn的话是这样: +如果使用 Yarn 的话是这样: ```bash yarn test additionCalculator.test.js ``` -一旦你启动了测试,Jest会在你的编辑器控制台打印出通过或者不通过的消息,消息如下: +一旦你启动了测试,Jest 会在你的编辑器控制台打印出通过或者不通过的消息,消息如下: ```bash $ jest @@ -306,7 +306,7 @@ Ran all test suites. Done in 7.80s. ``` -如果你希望Jest自动执行测试,可以在`package.json`的`test`区域添加 `--watchAll` 选项。 +如果你希望 Jest 自动执行测试,可以在`package.json`的`test`区域添加 `--watchAll` 选项。 **例子:** @@ -318,7 +318,7 @@ Done in 7.80s. } ``` -添加`--watchAll`后,重新执行`npm run test` (或 `yarn test`)命令,Jest会在每次保存后重新执行测试。 +添加`--watchAll`后,重新执行`npm run test` (或 `yarn test`)命令,Jest 会在每次保存后重新执行测试。 **注意:** 你可以使用键盘上的**Q**键退出监视(watch)模式。 @@ -375,8 +375,8 @@ module.exports = additionCalculator; 在代码块中我们做了这些事情: -1. `...numbers`代码使用了JavaScript中的[展开操作符](https://codesweetly.com/javascript-rest-operator) (`...`) ,将函数的参数转化为一个数组。 -2. `numbers.reduce((sum, item) => sum + item, 0)`代码使用JavaScript的[reduce()](https://codesweetly.com/javascript-reduce-method)方法,求和了`numbers`数组中的所有元素。 +1. `...numbers`代码使用了 JavaScript 中的[展开操作符](https://codesweetly.com/javascript-rest-operator) (`...`) ,将函数的参数转化为一个数组。 +2. `numbers.reduce((sum, item) => sum + item, 0)`代码使用 JavaScript 的[reduce()](https://codesweetly.com/javascript-reduce-method)方法,求和了`numbers`数组中的所有元素。 ### 第十四步:重新执行测试 @@ -384,33 +384,33 @@ module.exports = additionCalculator; ### 结束 -恭喜你!你成功的使用Jest,并借助测试驱动开发的方法创建了一个计算器程序。 🎉 +恭喜你!你成功的使用 Jest,并借助测试驱动开发的方法创建了一个计算器程序。 🎉 <h2 id="important-stuff-to-know-about-using-es6-modules-with-jest">在Jest中使用es6模块须知</h2> -目前,Jest不能识别ES6模块。 +目前,Jest 不能识别 ES6 模块。 -假设,你习惯使用ES6的import/export声明,你必须采取以下步骤: +假设,你习惯使用 ES6 的 import/export 声明,你必须采取以下步骤: -### 1\. 安装Babel作为依赖包 +### 1. 安装 Babel 作为依赖包 ```bash npm install @babel/preset-env --save-dev ``` -或者使用Yarn: +或者使用 Yarn: ```bash yarn add @babel/preset-env --dev ``` -### 2\. 在项目的root创建 `.babelrc`文件: +### 2. 在项目的 root 创建 `.babelrc`文件 ```bash touch .babelrc ``` -### 3\. 打开 `.babelrc` 文件,并且复制以下代码: +### 3\. 打开 `.babelrc` 文件,并且复制以下代码 ```json { "presets": ["@babel/preset-env"] } @@ -440,7 +440,7 @@ module.exports = additionCalculator; export default additionCalculator; ``` -**注意:** Jest在[使用Babel](https://jestjs.io/docs/getting-started#using-babel)文档中,指定了类似说明。 +**注意:** Jest 在[使用 Babel](https://jestjs.io/docs/getting-started#using-babel)文档中,指定了类似说明。 ### 4\. 重新执行测试 @@ -456,19 +456,19 @@ export default additionCalculator; 测试驱动的开发可以帮助你理解程序的目的。 -也就是说,因为你在编写实际的程序前已经编写了测试,所以TDD可以促使你去思考你想要程序做什么事。 +也就是说,因为你在编写实际的程序前已经编写了测试,所以 TDD 可以促使你去思考你想要程序做什么事。 在你通过一到两个测试记录下来你的程序的目的之后,你可以自信地去创建程序。 -因此,TDD可以有效地帮助你记录下来你希望程序产生的结果。 +因此,TDD 可以有效地帮助你记录下来你希望程序产生的结果。 ### 2\. 信心助推器 -TDD是了解你的程序是否如预期工作的的一个基准。它给予你信心,相信自己的程序正确执行。 +TDD 是了解你的程序是否如预期工作的的一个基准。它给予你信心,相信自己的程序正确执行。 -所以无论之后你的代码库会有什么变化,TDD都可以有效地确保你的程序能够执行。 +所以无论之后你的代码库会有什么变化,TDD 都可以有效地确保你的程序能够执行。 -让我们现在来讨论一下TDD的术语: "单元测试(unit test)"、 "集成测试(integration test)"、 "端对端(E2E)"、和 "测试替身(test doubles)"。 +让我们现在来讨论一下 TDD 的术语: "单元测试(unit test)"、 "集成测试(integration test)"、 "端对端(E2E)"、和 "测试替身(test doubles)"。 <h2 id="what-is-a-unit-test-in-test-driven-development">测试驱动开发中的单元测试是什么</h2> @@ -478,7 +478,7 @@ TDD是了解你的程序是否如预期工作的的一个基准。它给予你 第十步里的`additionalCalculator()`测试是一个独立的函数,不依赖任何外部代码。 -注意单元测试首要目的并不是检查是否有bug,而是检查程序的一个独立片段(被称作单元)是否在不同的情况下按照预期工作。 +注意单元测试首要目的并不是检查是否有 bug,而是检查程序的一个独立片段(被称作单元)是否在不同的情况下按照预期工作。 <h2 id="what-is-an-integration-test-in-test-driven-development">测试驱动开发中的集成测试是什么?</h2> @@ -486,30 +486,30 @@ TDD是了解你的程序是否如预期工作的的一个基准。它给予你 我们为 `additionalCalculator`程序编写的第十三步的测试就是一个很好的例子。 -第十三步的`additionalCalculator()`的测试一个例子是因为这个程序是一个依赖函数,依赖了JavaScript的[reduce()](https://codesweetly.com/javascript-reduce-method)方法。 +第十三步的`additionalCalculator()`的测试一个例子是因为这个程序是一个依赖函数,依赖了 JavaScript 的[reduce()](https://codesweetly.com/javascript-reduce-method)方法。 也就是说,我们使用事先编写好的测试案例来测试 `additionalCalculator()`和`reduce()`。 -因此,如果JavaScript把`reduce()`规定为一个过时的方法,那么在这个案例中,`additionalCalculator`会因为`reduce()`方法而无法通过测试。 +因此,如果 JavaScript 把`reduce()`规定为一个过时的方法,那么在这个案例中,`additionalCalculator`会因为`reduce()`方法而无法通过测试。 <h2 id="what-is-an-end-to-end-test-in-test-driven-development">测试驱动开发中的端到端测试是什么?</h2> -**端到端(E2E)测试**访问用户接口(UI)的功能,也就是说E2E检查UI是否按照意图工作。 +**端到端(E2E)测试**访问用户接口(UI)的功能,也就是说 E2E 检查 UI 是否按照意图工作。 -可以观看[Max的Youtube频道](https://youtu.be/r9HdJ8P6GQI?t=1755)了解更多。 +可以观看[Max 的 Youtube 频道](https://youtu.be/r9HdJ8P6GQI?t=1755)了解更多。 <h2 id="what-are-test-doubles-in-test-driven-development">测试驱动开发中的测试替身是什么?</h2> -**测试替身(test doubles)**是模仿对象,用于模仿如数据库、库、网络和API等真实的依赖项。 +**测试替身(test doubles)**是模仿对象,用于模仿如数据库、库、网络和 API 等真实的依赖项。 使用测试替身可以绕过程序真实的依赖对象,你可以独立于任何依赖项来测试你的代码。 -假设你需要测试应用的一个错误是由外部API还是你自己的代码引起的。 +假设你需要测试应用的一个错误是由外部 API 还是你自己的代码引起的。 -但这个API仅在生产阶段,而不在开发阶段提供服务。所以,你有两种选择: +但这个 API 仅在生产阶段,而不在开发阶段提供服务。所以,你有两种选择: 1. 一直等到应用投入使用(这可能要等上数月); -2. 克隆API,这样不论这个依赖项是否可用,你都可以继续测试。 +2. 克隆 API,这样不论这个依赖项是否可用,你都可以继续测试。 使用测试替身来克隆项目依赖项,能够帮助你在不打断进度的情况下进行应用测试。 @@ -525,13 +525,13 @@ TDD是了解你的程序是否如预期工作的的一个基准。它给予你 **模拟(mock)** 是用于模仿外部依赖项的测试替身,使用模拟可以在开发的过程中不考虑依赖项的返回。 -假设你的应用依赖第三方API(如:Facebook),而这个API不可以在开发模式中被访问。使用模拟可以绕过这个API,这样你可以在不考虑Facebook的API是否可以访问的情况下进行测试。 +假设你的应用依赖第三方 API(如:Facebook),而这个 API 不可以在开发模式中被访问。使用模拟可以绕过这个 API,这样你可以在不考虑 Facebook 的 API 是否可以访问的情况下进行测试。 ### 测试驱动开发中的桩(stub)是什么? **桩(stub)** 使用手动输入的值来模仿外部依赖项的返回值。你可以使用不同的返回值来测试应用的性能。 -假设你的应用依赖于第三方API(如:Facebook),而这个API不可以在开发模式中被访问。桩模仿Facebook的返回值让你可以绕开这个API做测试。 +假设你的应用依赖于第三方 API(如:Facebook),而这个 API 不可以在开发模式中被访问。桩模仿 Facebook 的返回值让你可以绕开这个 API 做测试。 因此,桩可以帮助你获取不同响应场景的应用行为。 @@ -545,22 +545,22 @@ TDD是了解你的程序是否如预期工作的的一个基准。它给予你 我们学习了测试驱动开发如何在创建程序前记录程序的行为。 -我们也实践了一个简单的JavaScript测试,并且使用Jest来作为测试的工具。 +我们也实践了一个简单的 JavaScript 测试,并且使用 Jest 来作为测试的工具。 -现在让我们一起来看看如何测试React组件。 +现在让我们一起来看看如何测试 React 组件。 <h2 id="how-to-test-react-components">如何测试React组件</h2> -两个主要的测试React组件的工具是: +两个主要的测试 React 组件的工具是: 1. 测试运行工具 -2. React组件测试工具 +2. React 组件测试工具 -测试运行工具和React组件测试工具的主要区别是什么? +测试运行工具和 React 组件测试工具的主要区别是什么? <h2 id="test-runner-vs-react-component-testing-tool-what-s-the-difference">测试运行工具 vs React组件测试工具:区别是什么?</h2> -以下是测试运行工具和React组件测试工具的主要区别: +以下是测试运行工具和 React 组件测试工具的主要区别: ### 什么是测试运行? @@ -572,39 +572,39 @@ TDD是了解你的程序是否如预期工作的的一个基准。它给予你 典型的测试运行工具有:[Jasmine](https://jasmine.github.io/)、 [Mocha](https://mochajs.org/)、 [Tape](https://github.com/substack/tape)和[Jest](https://jestjs.io/)。 -### 什么是React组件测试工具? +### 什么是 React 组件测试工具? -**React组件测试工具**提供强大的API来定义组件测试案例。 +**React 组件测试工具**提供强大的 API 来定义组件测试案例。 -假设你需要测试你的项目的`<App />`组件,你可以使用React组件测试工具来定义组件的测试案例。 +假设你需要测试你的项目的`<App />`组件,你可以使用 React 组件测试工具来定义组件的测试案例。 -也就是说,这个测试工具提供API来编写组件的测试案例。 +也就是说,这个测试工具提供 API 来编写组件的测试案例。 典型的组件测试工具有: [Enzyme](https://enzymejs.github.io/enzyme/) 和 [React Testing Library](https://testing-library.com/docs/react-testing-library/intro)。 -现在你已经知道了测试运行工具和React组件测试工具是什么,让我们来利用一个简单的项目例子进一步了解React测试是如何运行的。 +现在你已经知道了测试运行工具和 React 组件测试工具是什么,让我们来利用一个简单的项目例子进一步了解 React 测试是如何运行的。 <h2 id="project-how-react-testing-works">项目:React测试如何运行</h2> -在接下来的例子中,我们将使用[Jest](https://en.wikipedia.org/wiki/Jest_(JavaScript_framework))和[React Testing Library](https://testing-library.com/docs/react-testing-library/intro) (文档由Kent C. Dodds编写)来学习React测试是如何运行的。 +在接下来的例子中,我们将使用[Jest](https://en.wikipedia.org/wiki/Jest_(JavaScript_framework))和[React Testing Library](https://testing-library.com/docs/react-testing-library/intro) (文档由 Kent C. Dodds 编写)来学习 React 测试是如何运行的。 -**注意:** React官方文档[推荐](https://reactjs.org/docs/testing.html#tools)结合Jest和React Testing Library一起来测试React组件。 +**注意:** React 官方文档[推荐](https://reactjs.org/docs/testing.html#tools)结合 Jest 和 React Testing Library 一起来测试 React 组件。 -### 第一步:获取正确的Node和NPM版本 +### 第一步:获取正确的 Node 和 NPM 版本 -确保你的系统安装的是[Node 10.16](https://codesweetly.com/package-manager-explained#how-to-check-the-installed-node-version) (或者更高版本)以及NPM 5.6 (或者更高版本)。 +确保你的系统安装的是[Node 10.16](https://codesweetly.com/package-manager-explained#how-to-check-the-installed-node-version) (或者更高版本)以及 NPM 5.6 (或者更高版本)。 -如果你倾向于使用Yarn,确保你安装的是Yarn 0.25 (或者更高版本). +如果你倾向于使用 Yarn,确保你安装的是 Yarn 0.25 (或者更高版本). -### 第二步:创建一个新的React应用 +### 第二步:创建一个新的 React 应用 -使用NPM的[create-react-app](https://create-react-app.dev/)包来创建一个名为`react-testing-project`的项目: +使用 NPM 的[create-react-app](https://create-react-app.dev/)包来创建一个名为`react-testing-project`的项目: ```bash npx create-react-app react-testing-project ``` -同样,你可以使用Yarn来创建: +同样,你可以使用 Yarn 来创建: ```bash yarn create react-app react-testing-project @@ -631,25 +631,25 @@ cd react-testing-project 现在让我们讲解一下这些测试包的作用: -#### 什么是Jest? +#### 什么是 Jest? [jest](https://www.npmjs.com/package/jest)是个测试运行工具,我们可以使用这个工具来运行测试脚本,并将结果打印在命令行。 #### 什么是@testing-library/react? -[@testing-library/react](https://www.npmjs.com/package/@testing-library/react)是一个React测试库,提供为React组件编写测试案例的API。 +[@testing-library/react](https://www.npmjs.com/package/@testing-library/react)是一个 React 测试库,提供为 React 组件编写测试案例的 API。 #### 什么是@testing-library/jest-dom? -[@testing-library/jest-dom](https://www.npmjs.com/package/@testing-library/jest-dom)提供定制的Jest匹配器来测试DOM的状态。 +[@testing-library/jest-dom](https://www.npmjs.com/package/@testing-library/jest-dom)提供定制的 Jest 匹配器来测试 DOM 的状态。 -**注意:** Jest已经包含很多匹配器,所以使用`jest-dom`是可选的。 `jest-dom`只是扩展了Jest匹配器,使得测试更加声明式、易阅读以及更容易维护。 +**注意:** Jest 已经包含很多匹配器,所以使用`jest-dom`是可选的。 `jest-dom`只是扩展了 Jest 匹配器,使得测试更加声明式、易阅读以及更容易维护。 #### 什么是@testing-library/user-event? -[@testing-library/user-event](https://www.npmjs.com/package/@testing-library/user-event)提供`userEvent`API来模拟在web上用户和应用的交互。 +[@testing-library/user-event](https://www.npmjs.com/package/@testing-library/user-event)提供`userEvent`API 来模拟在 web 上用户和应用的交互。 -**注意:** `@testing-library/user-event`比[fireEvent](https://testing-library.com/docs/dom-testing-library/api-events/#fireevent) API更好用。 +**注意:** `@testing-library/user-event`比[fireEvent](https://testing-library.com/docs/dom-testing-library/api-events/#fireevent) API 更好用。 ### 第五步: 清空`src`文件夹 @@ -665,7 +665,7 @@ cd react-testing-project ### 第七步:渲染`App`组件 -打开`index.js`文件,并在DOM渲染`App`组件: +打开`index.js`文件,并在 DOM 渲染`App`组件: ```js // index.js @@ -701,16 +701,16 @@ test("codesweetly test heading", () => { 上面的测试代码片段主要做了这些事: 1. 引入了测试案例需要的包 -2. 编写了测试案例,希望 `<App />`组件可以渲染一个head元素包含 `"codesweetly test"`文本。 +2. 编写了测试案例,希望 `<App />`组件可以渲染一个 head 元素包含 `"codesweetly test"`文本。 -- [`test()`](https://jestjs.io/docs/api#testname-fn-timeout)是Jest的一个全局方法。我们使用它运行测试案例。这个方法接受三个参数: +- [`test()`](https://jestjs.io/docs/api#testname-fn-timeout)是 Jest 的一个全局方法。我们使用它运行测试案例。这个方法接受三个参数: - 测试名 (`"codesweetly test heading"`) - 包含期望测试结果的函数 - - 可选的timeout参数 -- [`render()`](https://testing-library.com/docs/react-testing-library/api/#render)是React Testing library的一个API,我们使用它来渲染我们希望测试的组件。 -- [`expect()`](https://jestjs.io/docs/expect#expectvalue)是一个测试代码结果的Jest方法。 -- [`screen`](https://testing-library.com/docs/queries/about/#screen)是一个包含多种搜寻页面元素方法的React Testing Library对象。 -- [`getByRole()`](https://testing-library.com/docs/queries/about/#priority)是搜寻页面元素的一个React Testing Library的请求方法。 + - 可选的 timeout 参数 +- [`render()`](https://testing-library.com/docs/react-testing-library/api/#render)是 React Testing library 的一个 API,我们使用它来渲染我们希望测试的组件。 +- [`expect()`](https://jestjs.io/docs/expect#expectvalue)是一个测试代码结果的 Jest 方法。 +- [`screen`](https://testing-library.com/docs/queries/about/#screen)是一个包含多种搜寻页面元素方法的 React Testing Library 对象。 +- [`getByRole()`](https://testing-library.com/docs/queries/about/#priority)是搜寻页面元素的一个 React Testing Library 的请求方法。 - [`toHaveTextContent()`](https://github.com/testing-library/jest-dom#tohavetextcontent)是 `jest-dom`的一个定制匹配器,可以使用它来确认特定节点存在文本内容。 - `/codesweetly test/i` 是一个[正则表达式](https://codesweetly.com/javascript-regular-expression-object) 语法,用于表达搜索不区分大小写的`codesweetly test`。 @@ -729,7 +729,7 @@ expect(screen.getByRole("heading", { name: /codesweetly test/i })).toBeInTheDocu **提示:** -可以添加`level`选项到`getByRole()`方法,来标注head的层级。 +可以添加`level`选项到`getByRole()`方法,来标注 head 的层级。 **例子:** @@ -744,7 +744,7 @@ test("codesweetly test heading", () => { 假设你现在运行测试,会测试失败,因为还没有编写组件,所以我们现在开始编写: -### 第九步:开发你的React组件 +### 第九步:开发你的 React 组件 打开`App.js`文件来开发一个可以通过测试的组件 @@ -770,13 +770,13 @@ export default App; npm test App.test.js ``` -也可以使用Yarn: +也可以使用 Yarn: ```bash yarn test App.test.js ``` -初始化测试后,Jest会在你的编辑器的控制台打印通过或者失败的消息: +初始化测试后,Jest 会在你的编辑器的控制台打印通过或者失败的消息: ```bash $ jest @@ -790,7 +790,7 @@ Time: 3.146 s Ran all test suites related to changed files. ``` -**注意:** `create-react-app`默认在[watch mode](https://codesweetly.com/javascript-module-bundler/#what-is-webpack---progress---watch)配置Jest。所以,在执行 `npm test` (或者`yarn test`)之后,你当前打开的终端会继续执行`test`命令的活动。在`test`执行的过程中,你将没办法在终端输入任何内容,但是你可以同时期开启一个新的终端窗口来执行`test`。 +**注意:** `create-react-app`默认在[watch mode](https://codesweetly.com/javascript-module-bundler/#what-is-webpack---progress---watch)配置 Jest。所以,在执行 `npm test` (或者`yarn test`)之后,你当前打开的终端会继续执行`test`命令的活动。在`test`执行的过程中,你将没办法在终端输入任何内容,但是你可以同时期开启一个新的终端窗口来执行`test`。 也就是说,使用一个窗口来执行`test`,另一个来输入命令。 @@ -802,7 +802,7 @@ Ran all test suites related to changed files. npm start ``` -如果你的[包管理工具](https://codesweetly.com/package-manager-explained) 是Yarn,执行: +如果你的[包管理工具](https://codesweetly.com/package-manager-explained) 是 Yarn,执行: ```bash yarn start @@ -812,7 +812,7 @@ yarn start ### 第十二步:重构测试代码 -假设你希望当用户点击按钮的时候改变head的文字。你可以模拟一个按钮来测试这个用户交互是否成立。 +假设你希望当用户点击按钮的时候改变 head 的文字。你可以模拟一个按钮来测试这个用户交互是否成立。 **例子:** @@ -846,18 +846,18 @@ describe("App component", () => { 上面的测试代码片段的重要内容是: 1. 引入了测试案例需要的包。 -2. 编写了测试案例,希望 `<App />`组件可以渲染一个head元素包含 `"codesweetly test"`文本。 -3. 编写了另一个测试案例,模仿用户和应用按钮元素的互动。 也就是说,我们希望一旦用户点击按钮, `<App />`的head就会更新`"a codesweetly project"`文本。 +2. 编写了测试案例,希望 `<App />`组件可以渲染一个 head 元素包含 `"codesweetly test"`文本。 +3. 编写了另一个测试案例,模仿用户和应用按钮元素的互动。 也就是说,我们希望一旦用户点击按钮, `<App />`的 head 就会更新`"a codesweetly project"`文本。 **注意:** -- [`describe()`](https://jestjs.io/docs/api#describename-fn)是Jest的一个全局方法。这是一个可选的方法,用户将相关的测试案例分组到一起。 `describe()`接受两个参数: +- [`describe()`](https://jestjs.io/docs/api#describename-fn)是 Jest 的一个全局方法。这是一个可选的方法,用户将相关的测试案例分组到一起。 `describe()`接受两个参数: - 你希望测试案例组被命名的名称,如: `"App component"`. - 包含测试案例的函数。 -- [`userEvent`](https://www.npmjs.com/package/@testing-library/user-event) 包含许多模拟用户与应用交互方法的一个React Testing Library包。例如在代码块中,我们使用 `userEvent`的`click()`方法来模拟按钮元素的点击事件。 -- 每次测试案例我们都会渲染`<App />`,因为每次测试后,React测试库都会卸载掉已经渲染的组件。假设你的组件有多个测试案例, 使用Jest的[`beforeEach()`](https://jestjs.io/docs/api#beforeeachfn-timeout)方法来渲染你文件中的`render(<App />)`(或者`describe` 代码块中)的测试。 +- [`userEvent`](https://www.npmjs.com/package/@testing-library/user-event) 包含许多模拟用户与应用交互方法的一个 React Testing Library 包。例如在代码块中,我们使用 `userEvent`的`click()`方法来模拟按钮元素的点击事件。 +- 每次测试案例我们都会渲染`<App />`,因为每次测试后,React 测试库都会卸载掉已经渲染的组件。假设你的组件有多个测试案例, 使用 Jest 的[`beforeEach()`](https://jestjs.io/docs/api#beforeeachfn-timeout)方法来渲染你文件中的`render(<App />)`(或者`describe` 代码块中)的测试。 -### 第十三步:重构React组件 +### 第十三步:重构 React 组件 我们已经重构了测试代码,现在我们来重构`App`组件: @@ -890,7 +890,7 @@ export default App; 1. `App`的`heading`初始状态是`"CodeSweetly Test"`字符串。 2. 编写了一个`handleClick`函数来处理`heading`状态。 -3. 在DOM渲染一个 `<h1>`和一个`<button>`元素。 +3. 在 DOM 渲染一个 `<h1>`和一个`<button>`元素。 注意以下几点: @@ -905,25 +905,25 @@ export default App; ### 就这么多! -恭喜你!你成功的使用Jest和React测试库来测试React组件! 🎉 +恭喜你!你成功的使用 Jest 和 React 测试库来测试 React 组件! 🎉 <h2 id="overview">总结</h2> -本文探讨了在JavaScript和ReactJS应用中如何使用测试驱动的开发。 +本文探讨了在 JavaScript 和 ReactJS 应用中如何使用测试驱动的开发。 -我们还学习了如何使用Jest和React测试库使得测试更加简单快速。 +我们还学习了如何使用 Jest 和 React 测试库使得测试更加简单快速。 感谢阅读! -### **这里还有一些有用的ReactJS的资源:** +### **这里还有一些有用的 ReactJS 的资源** -我编写了一本React相关的书籍! +我编写了一本 React 相关的书籍! - 初学者友好 ✔ - 包含代码片段 ✔ - 包含可以扩展的项目 ✔ - 和非常多好理解的例子 ✔ -[React Explained Clearly](https://www.amazon.com/dp/B09KYGDQYW)是你了解ReactJS的敲门砖。 +[React Explained Clearly](https://www.amazon.com/dp/B09KYGDQYW)是你了解 ReactJS 的敲门砖。 [![React Explained Clearly Book Now Available at Amazon](https://www.freecodecamp.org/news/content/images/2022/01/Twitter-React_Explained_Clearly-CodeSweetly-Oluwatobi_Sofela.jpg)](https://www.amazon.com/dp/B09KYGDQYW) diff --git a/chinese/articles/the-basic-design-patterns-all-developers-need-to-know.md b/chinese/articles/the-basic-design-patterns-all-developers-need-to-know.md index e6ba131c5..41c8237cd 100644 --- a/chinese/articles/the-basic-design-patterns-all-developers-need-to-know.md +++ b/chinese/articles/the-basic-design-patterns-all-developers-need-to-know.md @@ -4,13 +4,13 @@ 设计模式是一些设计级别的解决方案。这些解决方案是为了解决软件工程师经常遇到的问题。它不是代码,我再说一遍,它**不是代码**。 它就像一种描述,描述如何解决这些问题、如何设计解决方案。 -使用这些模式被认为是一种很好的实践,因为解决方案的设计经过了充分的尝试和测试,从而使最终代码具有更高的可读性。设计模式经常被面向对象语言(如Java)创建和使用。(接下来的大多数例子将会以 Java 编写) +使用这些模式被认为是一种很好的实践,因为解决方案的设计经过了充分的尝试和测试,从而使最终代码具有更高的可读性。设计模式经常被面向对象语言(如 Java)创建和使用。(接下来的大多数例子将会以 Java 编写) # 设计模式的类型 -目前发现了大约26种模式(我几乎不认为我会完成所有这些模式……)。 +目前发现了大约 26 种模式(我几乎不认为我会完成所有这些模式……)。 -这26种可分为3种类型: +这 26 种可分为 3 种类型: 1. 创建型:这些模式是为类实例化而设计的。它们可以是类创建模式或对象创建模式。 @@ -20,7 +20,7 @@ 在这篇文章中,我们将针对每种分类类型介绍一种基本设计模式。 -## 类型1:创建型 - 单例设计模式 +## 类型 1:创建型 - 单例设计模式 单例设计模式是一种创建型模式,它的目标是限制一个类只能创建一个实例对象,并且只为该实例对象提供唯一一个全局访问点。Java 中这种类的一个常用示例是 Calendar ,它使用自己的 `getInstance()` 方法来获取要使用的实例对象,你无法为 Calendar 类再创建一个实例对象。 @@ -127,13 +127,13 @@ public static ThreadSafeSingleton getInstanceDoubleLocking() { 现在进入下一个分类。 -## 类型2:结构型 - 装饰器设计模式 +## 类型 2:结构型 - 装饰器设计模式 我将为你提供一个小场景,用来给出一个更好的上下文用来说明为什么以及在何处使用修饰器模式。 话说你拥有一家咖啡店,就像所有新手一样,刚开始你只需要家常咖啡和焦炒咖啡两种普通咖啡。在你的结算系统中,有一个类用于不同的混合咖啡,它继承了饮料抽象类。人们走进店里,准备喝你美妙(虽然苦涩)的咖啡。这时,有个咖啡新人想要加糖或牛奶,(很无语)真是对咖啡的嘲弄。 -现在你需要将这两个配料放在菜单和结算系统上,你的IT人员别出心裁地为每种咖啡分别添加两个子类,一种加糖,一种加牛奶。这时,由于客户总是对的,其中一个又说这些可怕的话: +现在你需要将这两个配料放在菜单和结算系统上,你的 IT 人员别出心裁地为每种咖啡分别添加两个子类,一种加糖,一种加牛奶。这时,由于客户总是对的,其中一个又说这些可怕的话: “请来一杯加糖的牛奶咖啡。” @@ -141,15 +141,15 @@ public static ThreadSafeSingleton getInstanceDoubleLocking() { 望着你的结算系统,尴尬而又不失礼貌的微笑再次挂在了你的脸上。 好吧,回到绘图板…… -然后,IT人员将加糖的牛奶咖啡作为子类添加到每个父咖啡类中。本月剩下的时间一帆风顺,人们排队等候你的咖啡,你实际上赚钱了。 +然后,IT 人员将加糖的牛奶咖啡作为子类添加到每个父咖啡类中。本月剩下的时间一帆风顺,人们排队等候你的咖啡,你实际上赚钱了。 但等等,这还不够! -整个世界又怼你,一个竞争对手在街对面开业,不仅有4种咖啡,还有超过10种配料! +整个世界又怼你,一个竞争对手在街对面开业,不仅有 4 种咖啡,还有超过 10 种配料! 为了自己的咖啡卖得更好,对手有的没有的你都买了,这时候你想起了忘记更新那个讨厌的结算系统。你几乎不可能为所有的配料组合添加无穷无尽的子类,更不用说最终系统的大小。 -是时候接入正确的结算系统了。你找到了懂行的IT人员,他们说:“如果使用装饰器模式,这将更容易和更小。” +是时候接入正确的结算系统了。你找到了懂行的 IT 人员,他们说:“如果使用装饰器模式,这将更容易和更小。” ### 装饰器模式究竟是什么? @@ -157,19 +157,19 @@ public static ThreadSafeSingleton getInstanceDoubleLocking() { 让我们给**数学**一个登场机会(不寒而栗?),来把这一切都带入视野: -4种混合咖啡和10种配料。如果我们坚持为每一种类型的咖啡的所有配料的每一种不同组合生成子类。那是: +4 种混合咖啡和 10 种配料。如果我们坚持为每一种类型的咖啡的所有配料的每一种不同组合生成子类。那是: (10–1)² = 9² = 81 个子类 -我们从10中减去1,因为你不能将同一种配料混合,糖加糖听起来太蠢了,这只是一种混合咖啡。81乘以4,你会得到惊人的324个不同的子类!再想想那些涉及到的代码…… +我们从 10 中减去 1,因为你不能将同一种配料混合,糖加糖听起来太蠢了,这只是一种混合咖啡。81 乘以 4,你会得到惊人的 324 个不同的子类!再想想那些涉及到的代码…… -但是在这种情况下,装饰器模式只需要16个类。敢打赌吗? +但是在这种情况下,装饰器模式只需要 16 个类。敢打赌吗? ![Decorator Design Pattern Class diagram](https://www.freecodecamp.org/news/content/images/2019/07/decorator-class-diagram.png) ![Class diagram according to coffee shop scenario](https://www.freecodecamp.org/news/content/images/2019/07/decorator-coffee-class-diagram.png) -如果我们根据上面的类图绘制出我们的场景,我们得到4个混合咖啡类,10个配料类,1个抽象组件,1个抽象装饰器。看!16个!现在交出100美元。(开玩笑别当真, 当然如果你愿意给,我也不会拒绝……嘛,说说而已) +如果我们根据上面的类图绘制出我们的场景,我们得到 4 个混合咖啡类,10 个配料类,1 个抽象组件,1 个抽象装饰器。看!16 个!现在交出 100 美元。(开玩笑别当真, 当然如果你愿意给,我也不会拒绝……嘛,说说而已) 从上面可以看出,正如实现混合咖啡类是饮料抽象类的子类一样,配料抽象类也从饮料抽象类继承了方法。配料类作为饮料抽象类的子类,能在需要时用自己的新方法向基础对象添加功能。 @@ -297,7 +297,7 @@ public class CoffeeShop { 来,看看最后一类。 -## 类型3:行为型 - 命令设计模式 +## 类型 3:行为型 - 命令设计模式 行为设计模式侧重于类、对象如何相互通信。命令模式的主要焦点是在所涉及的各个类之间灌输更高程度的低耦合。 @@ -317,7 +317,7 @@ public class CoffeeShop { 技术方面的操作流程是,你点菜形成了一个订单(这个订单 `Order` 具体实现了命令接口要求厨师完成一道菜)。你把订单交个服务员(服务员 `Waiter` 就是调用者,他知道何时发出此命令)。服务员把订单交给厨师(收到特定订单时,厨师 `Chef` 是唯一知道具体该做什么的人,这就实现了低耦合)。如此,当调用者执行 `execute` 方法时,它调用订单对象的 `execute` 方法,而订单对象的 `execute` 方法又调用接收者对应的方法,从而完成必要的操作。 -### 我们需要实现: +### 我们需要实现 1. 一个接口 `Command` 2. 一个具体实现 `Command` 接口的 `Order` 类 diff --git a/chinese/articles/the-beginners-guide-to-sass.md b/chinese/articles/the-beginners-guide-to-sass.md index 24d54ee31..8a70115f5 100644 --- a/chinese/articles/the-beginners-guide-to-sass.md +++ b/chinese/articles/the-beginners-guide-to-sass.md @@ -5,35 +5,35 @@ ![Sass初学者指南](https://www.freecodecamp.org/news/content/images/size/w2000/2022/04/The-Beginner-s-Guide-to-SASS.png) -你是否好奇SASS到底是什么?或者你已经知道SASS,但还没来得及去学习和使用。 +你是否好奇 SASS 到底是什么?或者你已经知道 SASS,但还没来得及去学习和使用。 -不论你是首次学习Sass,还是更新这个主题的知识,这篇文章都很适合你。 +不论你是首次学习 Sass,还是更新这个主题的知识,这篇文章都很适合你。 -在这篇文章中,你将学习到Sass的基础,Sass到底是什么,如果使用Sass超棒的功能来提速书写样式的过程。 +在这篇文章中,你将学习到 Sass 的基础,Sass 到底是什么,如果使用 Sass 超棒的功能来提速书写样式的过程。 ## 前提条件 这本文章默认你: -- 基本了解HTML和CSS -- 安装了代码编辑器(推荐使用VS Code。如果你尚未安装,可以在此处[下载](http://code.visualstudio.com/). -- 安装了浏览器(推荐使用Chrome或者Firefox) +- 基本了解 HTML 和 CSS +- 安装了代码编辑器(推荐使用 VS Code。如果你尚未安装,可以在此处[下载](http://code.visualstudio.com/). +- 安装了浏览器(推荐使用 Chrome 或者 Firefox) -## 究竟什么是Sass +## 究竟什么是 Sass -Sass(英文全称是Syntactically Awesome Style Sheets)是一种CSS预处理器,使用它可以使你的CSS拥有超能力。 +Sass(英文全称是 Syntactically Awesome Style Sheets)是一种 CSS 预处理器,使用它可以使你的 CSS 拥有超能力。 -你不得不承认:常常你会觉得用CSS写样式很困难,特别是现在用户交互界面(UI)变得越来越复杂。 +你不得不承认:常常你会觉得用 CSS 写样式很困难,特别是现在用户交互界面(UI)变得越来越复杂。 你常常会觉得自己在重复自己。 -Sass就可以解决这个问题,在你写样式的时候,帮助你坚守DRY(Do Not Repeat Yourself不重复自己)准则。 +Sass 就可以解决这个问题,在你写样式的时候,帮助你坚守 DRY(Do Not Repeat Yourself 不重复自己)准则。 -Sass的编译器可以让我们用两种语法来写样式表——缩进语法和SCSS。下文会逐个讲解。 +Sass 的编译器可以让我们用两种语法来写样式表——缩进语法和 SCSS。下文会逐个讲解。 ### 缩进语法 -缩进语法是Sass的原始语法,采用缩进的格式,但是取消了花括号和分号。 其文件扩展名为 `.sass`. +缩进语法是 Sass 的原始语法,采用缩进的格式,但是取消了花括号和分号。 其文件扩展名为 `.sass`. ```sass nav @@ -50,11 +50,11 @@ nav text-decoration: none ``` -### SCSS语法 +### SCSS 语法 -相比缩进语法,这个语法较新也更受欢迎。SCSS语法实质是CSS3语法的子集。这意味这你可以直接用常规的CSS加上一些新增的函数来书写SCSS。 +相比缩进语法,这个语法较新也更受欢迎。SCSS 语法实质是 CSS3 语法的子集。这意味这你可以直接用常规的 CSS 加上一些新增的函数来书写 SCSS。 -由于是在CSS的基础上提供高级功能,所以SCSS语法又被称作 _Sassy CSS_。其文件扩展名为 `.scss`. +由于是在 CSS 的基础上提供高级功能,所以 SCSS 语法又被称作 _Sassy CSS_。其文件扩展名为 `.scss`. ```scss nav { @@ -74,29 +74,29 @@ nav { } ``` -> 免责声明: 本文使用SCSS语法是因为SCSS语法被更多人使用。 +> 免责声明: 本文使用 SCSS 语法是因为 SCSS 语法被更多人使用。 -## Sass的工作机制 +## Sass 的工作机制 -当你写了一个扩展名为 `.scss`的文件,该文件会被编译成正常的CSS文件。然后CSS代码在浏览器加载。 -这就是为什么我们成Sass为预处理器。 +当你写了一个扩展名为 `.scss`的文件,该文件会被编译成正常的 CSS 文件。然后 CSS 代码在浏览器加载。 +这就是为什么我们成 Sass 为预处理器。 -## 为什么要使用Sass? +## 为什么要使用 Sass? -- **易于学习**: 如果你已经熟悉CSS,你会欣喜地发现Sass和CSS的语法很相似,所以看完这篇教程,你就可以开始使用Sass了;) -- **兼容性强**: Sass兼容所有版本的CSS,所以你可以使用所有CSS库。 -- **提高效率**: Sass强大的功能可以避免CSS的重复。 -- **可复用的代码**: Sass中可以使用变量和代码块(mixins),并且可以重复使用。这样就节约了时间,提高了编写代码的速度。 -- **有条理的代码**: 在Sass中可以使用片段(partials)来整理代码。 -- **跨浏览器兼容**: Sass会被编译成CSS,并且自动添加浏览器引擎前缀,这样你就不需要手动编写了。 +- **易于学习**: 如果你已经熟悉 CSS,你会欣喜地发现 Sass 和 CSS 的语法很相似,所以看完这篇教程,你就可以开始使用 Sass 了;) +- **兼容性强**: Sass 兼容所有版本的 CSS,所以你可以使用所有 CSS 库。 +- **提高效率**: Sass 强大的功能可以避免 CSS 的重复。 +- **可复用的代码**: Sass 中可以使用变量和代码块(mixins),并且可以重复使用。这样就节约了时间,提高了编写代码的速度。 +- **有条理的代码**: 在 Sass 中可以使用片段(partials)来整理代码。 +- **跨浏览器兼容**: Sass 会被编译成 CSS,并且自动添加浏览器引擎前缀,这样你就不需要手动编写了。 -## Sass的功能 +## Sass 的功能 -以下功能使得Sass成为拥有超能力的CSS: +以下功能使得 Sass 成为拥有超能力的 CSS: -### Sass中的变量 +### Sass 中的变量 -在Sass中可以声明变量,这是Sass其中一个强大的功能,因为我们可以根据各种属性来定义变量,并且在任何文件中使用这些变量。 +在 Sass 中可以声明变量,这是 Sass 其中一个强大的功能,因为我们可以根据各种属性来定义变量,并且在任何文件中使用这些变量。 使用变量的好处是,一旦值发生了变化,你只要编写一行代码进行修改。 @@ -114,11 +114,11 @@ button { } ``` -### Sass中的嵌套 +### Sass 中的嵌套 -在写CSS的时候,大多数情况下需要重复类选择器,在Sass中我们可以在父元素中嵌套来避免这样的重复。 +在写 CSS 的时候,大多数情况下需要重复类选择器,在 Sass 中我们可以在父元素中嵌套来避免这样的重复。 -在CSS中: +在 CSS 中: ```css nav { @@ -146,7 +146,7 @@ nav li a:hover { } ``` -在Sass,同样的代码可以写作: +在 Sass,同样的代码可以写作: ```scss nav { @@ -176,21 +176,21 @@ nav { ### 父选择器 -在上面Sass的代码片段中你可能注意到了和号 `&` 搭配了一个悬停的伪类。这就是父选择器。 +在上面 Sass 的代码片段中你可能注意到了和号 `&` 搭配了一个悬停的伪类。这就是父选择器。 -> 父选择器——`&`是Sass中一个特殊的选择器,一般用于嵌套选择器中,指代外层的选择器。 资源出处 – [Sass文档](https://sass-lang.com/documentation/style-rules/parent-selector) +> 父选择器——`&`是 Sass 中一个特殊的选择器,一般用于嵌套选择器中,指代外层的选择器。 资源出处 – [Sass 文档](https://sass-lang.com/documentation/style-rules/parent-selector) 所以在上面的代码片段中, `&`指代的父元素是锚点标签`a`. -> 你可以查阅我的[文章](https://israelmitolu.hashnode.dev/writing-cleaner-css-using-bem-methodology),该讲解了如何在Sass中应用BEM。 +> 你可以查阅我的[文章](https://israelmitolu.hashnode.dev/writing-cleaner-css-using-bem-methodology),该讲解了如何在 Sass 中应用 BEM。 -### Sass中片段 +### Sass 中片段 -这是Sass众多给你赋能的炫酷功能之一。 +这是 Sass 众多给你赋能的炫酷功能之一。 随着样式表越来越大,维护变得越来越困难。因此,把样式表拆分成更小的单元块变得有意义。换句话说,片段帮助你更好去组织和构建你的代码。 -声明一个片段,我们首先要创建一个文件,以下划线`_`打头为文件名,然后使用`@import`指令在其他Sass文件中引用这个片段。 +声明一个片段,我们首先要创建一个文件,以下划线`_`打头为文件名,然后使用`@import`指令在其他 Sass 文件中引用这个片段。 例如,我如果我们有`_globals.scss`、`_variables.scss`和`_buttons.scss`三个文件, 我们可以在 `main.scss`中引用这三个文件: @@ -200,13 +200,13 @@ nav { @import "buttons"; ``` -你可能注意到`.scss`后缀并没有被加入, 这是因为Sass会默认你添加的是`.sass`或`.scss`文件。 +你可能注意到`.scss`后缀并没有被加入, 这是因为 Sass 会默认你添加的是`.sass`或`.scss`文件。 -### Sass中的Mixins +### Sass 中的 Mixins -在CSS中另一个主要的问题是你会经常重复同一组样式,Mixin使你能够封装一组样式,并使用`@include`关键字在你代码的任意地方应用这组样式。 +在 CSS 中另一个主要的问题是你会经常重复同一组样式,Mixin 使你能够封装一组样式,并使用`@include`关键字在你代码的任意地方应用这组样式。 -下面的代码片段是使用Flexbox的时候,使用mixin的例子: +下面的代码片段是使用 Flexbox 的时候,使用 mixin 的例子: ```scss @mixin flex-container { @@ -226,17 +226,17 @@ nav { } ``` -### Sass中的函数和运算符 +### Sass 中的函数和运算符 -Sass提供一套工具帮助你编写更程序话的代码。 +Sass 提供一套工具帮助你编写更程序话的代码。 -Sass提供内置函数,可以实现计算和运算,并返回特定的值。 +Sass 提供内置函数,可以实现计算和运算,并返回特定的值。 内置函数的功能包含从颜色计算到数学运算,包括获取随机数、计算尺寸甚至是条件式。 -Sass还提供数学运算符号,包括 `+`、 `-`、 `\`、`*`、 `/` 和 `%`,可以搭配`calc`函数使用。 +Sass 还提供数学运算符号,包括 `+`、 `-`、 `\`、`*`、 `/` 和 `%`,可以搭配`calc`函数使用。 -以下代码片段是使用函数将像素转换成rem的例子: +以下代码片段是使用函数将像素转换成 rem 的例子: ```scss @function pxToRem($pxValue) { @@ -249,7 +249,7 @@ div { } ``` -> 但一定要强调的是,不推荐使用除法运算符 `/`, 在Dart Sass 2.0.0.中除法运算符会被去除。 更多内容可以阅读[文档](https://sass-lang.com/documentation/breaking-changes/slash-div). +> 但一定要强调的是,不推荐使用除法运算符 `/`, 在 Dart Sass 2.0.0.中除法运算符会被去除。 更多内容可以阅读[文档](https://sass-lang.com/documentation/breaking-changes/slash-div). 所以更推荐的写法是: @@ -265,7 +265,7 @@ div { } ``` -以下代码片段是在mixin里使用条件逻辑: +以下代码片段是在 mixin 里使用条件逻辑: ```scss @mixin body-theme($theme) { @@ -277,7 +277,7 @@ div { } ``` -Sass也提供`lighten`和`darken`函数实现按照百分比调整颜色 +Sass 也提供`lighten`和`darken`函数实现按照百分比调整颜色 如: @@ -289,44 +289,44 @@ a:visited { } ``` -## 如果在本地设置好Sass +## 如果在本地设置好 Sass -真棒!我们已经学习了Sass的“理论”部分,接下来我们用代码来深入理解Sass是如何工作的。 +真棒!我们已经学习了 Sass 的“理论”部分,接下来我们用代码来深入理解 Sass 是如何工作的。 在这个部分,你将要学习到如何设置一个本地环境,并且一步一步练习我准备好的登陆页。 实例在[Codesandbox](https://codesandbox.io/s/currying-river-44d7zr?file=/index.html) 代码库在[GitHub](https://github.com/israelmitolu/Getting-Started-with-SASS). -### 编译Sass的方法 +### 编译 Sass 的方法 -编译Sass的方法如下: +编译 Sass 的方法如下: -- VS Code拓展 -- 使用NPM全局安装 -- 使用开源app安装如:Compass.app、Live Reload、 and Koala -- 使用Homebrew安装(MacOs用户) +- VS Code 拓展 +- 使用 NPM 全局安装 +- 使用开源 app 安装如:Compass.app、Live Reload、 and Koala +- 使用 Homebrew 安装(MacOs 用户) -本教程将使用VS Code拓展,因为这是最容易的方法。 +本教程将使用 VS Code 拓展,因为这是最容易的方法。 -### 在VS Code中设置好Sass +### 在 VS Code 中设置好 Sass -#### 第一步:安装Live Sass Compiler +#### 第一步:安装 Live Sass Compiler -首先启动VS Code,启动完毕后,在左手边的侧栏找到拓展选择栏。 +首先启动 VS Code,启动完毕后,在左手边的侧栏找到拓展选择栏。 ![1](https://www.freecodecamp.org/news/content/images/2022/04/1.PNG) -VS Code中的拓展选择栏 +VS Code 中的拓展选择栏 -在搜索框中查找 “Live Sass Compiler“并安装,这个拓展会帮助我们编译Sass文件 — `.scss` (或`.sass`)成`.css`文件。 +在搜索框中查找 “Live Sass Compiler“并安装,这个拓展会帮助我们编译 Sass 文件 — `.scss` (或`.sass`)成`.css`文件。 #### 第二步:设置保存地址 -然后修改文件路径,这样Sass才能在`styles`文件夹内变异。 +然后修改文件路径,这样 Sass 才能在`styles`文件夹内变异。 这一步需要在 `settings.json`文件中操作。 -在VS Code中,选择File > Preferences > Settings。然后查找`live sass compile`来改变全局设置。 +在 VS Code 中,选择 File > Preferences > Settings。然后查找`live sass compile`来改变全局设置。 点击`Edit settings.json`。 @@ -364,9 +364,9 @@ VS Code中的拓展选择栏 ], ``` -#### 第三步:编译Sass +#### 第三步:编译 Sass -保存好设置后,回到Sass文件,点击窗口的最低端"Watch Sass"按钮。 +保存好设置后,回到 Sass 文件,点击窗口的最低端"Watch Sass"按钮。 ![2](https://www.freecodecamp.org/news/content/images/2022/04/2.PNG) @@ -374,11 +374,11 @@ VS Code中的拓展选择栏 点击按钮后,会在`styles`文件夹中生成两个文件: `.css`和`.css.map`。 -你不要随意更改这两个文件,因为他们帮助你在每次更新样式的时候把Sass编译成CSS。 +你不要随意更改这两个文件,因为他们帮助你在每次更新样式的时候把 Sass 编译成 CSS。 -#### 第四步:链接CSS文件 +#### 第四步:链接 CSS 文件 -然后将CSS文件链接到`index.html`,在我们例子中就是: +然后将 CSS 文件链接到`index.html`,在我们例子中就是: ```html <link rel="stylesheet" href="/styles/main.css" /> @@ -389,11 +389,11 @@ VS Code中的拓展选择栏 以下是对上一部分代码的详细解释: -- 在 `index.html`基本标记文件中包含一个header和一个home/hero部分 +- 在 `index.html`基本标记文件中包含一个 header 和一个 home/hero 部分 - - 包含了一个链接引入CSS文件,这个文件是VS Code拓展帮我们编译完成的 - - 以及一些JavaScript来实现菜单的响应切换 -- `main.scss`被编译, 编译结果为CSS文件 `main.css`并被引入到`index.html`: + - 包含了一个链接引入 CSS 文件,这个文件是 VS Code 拓展帮我们编译完成的 + - 以及一些 JavaScript 来实现菜单的响应切换 +- `main.scss`被编译, 编译结果为 CSS 文件 `main.css`并被引入到`index.html`: ```html <link rel="stylesheet" href="/styles/main.css" /> @@ -415,9 +415,9 @@ VS Code中的拓展选择栏 ## 总结 -恭喜你!如果你读到了这儿,说明你已经学习了Sass是如何工作的,Sass一些炫酷的功能,希望你可以马上开始使用Sass。 +恭喜你!如果你读到了这儿,说明你已经学习了 Sass 是如何工作的,Sass 一些炫酷的功能,希望你可以马上开始使用 Sass。 -如果你想要学习更多Sass的知识,我推荐你使用[freeCodeCamp's course](https://www.youtube.com/watch?v=aoQ6S1a32j8&t=3323s). +如果你想要学习更多 Sass 的知识,我推荐你使用[freeCodeCamp's course](https://www.youtube.com/watch?v=aoQ6S1a32j8&t=3323s). 如果你认为这篇文章帮助到了你(我确定帮助到了你😉), 请分享给你的朋友或者到社交网络上,欢迎在 [Twitter](https://twitter.com/israelmitolu) 或者[blog](https://israelmitolu.hashnode.dev) 上联系我,我会在这个上面分享文章和资源,帮助你成为更好的开发者。 diff --git a/chinese/articles/the-brain-inspired-approach-to-ai.md b/chinese/articles/the-brain-inspired-approach-to-ai.md index 6fed38d56..a6072615f 100644 --- a/chinese/articles/the-brain-inspired-approach-to-ai.md +++ b/chinese/articles/the-brain-inspired-approach-to-ai.md @@ -77,7 +77,7 @@ > "如果一个生理学问题的难度是数学性质的,那么十个不懂数学的生理学家与一个不懂数学的生理学家走得恰好一样远。" -1934年,韦纳和其他几位学者每月聚会,讨论涉及边界区域科学的论文。 +1934 年,韦纳和其他几位学者每月聚会,讨论涉及边界区域科学的论文。 ![https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc008a7-d0e0-4d6f-83ed-ab2a320263e0_2048x1251](https://substackcdn.com/image/fetch/w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0fc008a7-d0e0-4d6f-83ed-ab2a320263e0_2048x1251.jpeg) @@ -104,19 +104,19 @@ _Norman Weiner_ 其中一位科学家是一位名叫[Warren McCulloch](https://en.wikipedia.org/wiki/Warren_Sturgis_McCulloch)的神经生理学家。他从哈弗福德大学退学,去耶鲁大学学习哲学和心理学。在纽约参加一个科学会议时,他发现了同事写的关于生物反馈机制的论文。 -第二年,麦考洛克与他18岁的天才门生沃尔特-皮茨(Walter Pitts)合作,提出了一个关于大脑如何工作的理论。这一理论将有助于培养一种广泛的观念,即计算机和大脑的运作方式基本相同。 +第二年,麦考洛克与他 18 岁的天才门生沃尔特-皮茨(Walter Pitts)合作,提出了一个关于大脑如何工作的理论。这一理论将有助于培养一种广泛的观念,即计算机和大脑的运作方式基本相同。 -他们的结论是基于麦库洛赫对神经元处理二进制数的可能性的研究(计算机通过二进制数进行通信)。这一理论成为第一个人工神经网络模型的基础,该模型被命名为McCulloch-Pitts神经元(MCP)。 +他们的结论是基于麦库洛赫对神经元处理二进制数的可能性的研究(计算机通过二进制数进行通信)。这一理论成为第一个人工神经网络模型的基础,该模型被命名为 McCulloch-Pitts 神经元(MCP)。 -MCP成为创建第一个神经网络的基础,被称为[感知器](https://edemgold.substack.com/p/the-history-of-ai)。感知器是由心理学家[弗兰克-罗森布拉特](https://en.wikipedia.org/wiki/Frank_Rosenblatt)创造的。受大脑中突触的启发,他决定,既然人脑可以通过突触(神经元之间的交流)来处理和分类信息,那么数字计算机或许也可以通过神经网络做同样的事情。 +MCP 成为创建第一个神经网络的基础,被称为[感知器](https://edemgold.substack.com/p/the-history-of-ai)。感知器是由心理学家[弗兰克-罗森布拉特](https://en.wikipedia.org/wiki/Frank_Rosenblatt)创造的。受大脑中突触的启发,他决定,既然人脑可以通过突触(神经元之间的交流)来处理和分类信息,那么数字计算机或许也可以通过神经网络做同样的事情。 -感知器本质上将MCP神经元从一个人工神经元扩展为一个神经元网络。但不幸的是,感知器有一些技术挑战,限制了它的实际应用。这些限制中最明显的是它不能进行复杂的操作(比如在一个以上的项目之间进行分类--例如,感知器不能在猫、狗和鸟之间进行分类)。 +感知器本质上将 MCP 神经元从一个人工神经元扩展为一个神经元网络。但不幸的是,感知器有一些技术挑战,限制了它的实际应用。这些限制中最明显的是它不能进行复杂的操作(比如在一个以上的项目之间进行分类--例如,感知器不能在猫、狗和鸟之间进行分类)。 -1969年,[Marvin Minsky](https://en.wikipedia.org/wiki/Marvin_Minsky)和[Seymour Papert](https://en.wikipedia.org/wiki/Seymour_Papert)出版了一本名为_Perceptron_的书,详细阐述了Perceptron的缺陷。正因为如此,人工神经网络的研究停滞不前,直到[Paul Werbos](https://en.wikipedia.org/wiki/Paul_Werbos)提出了反向传播法。 +1969 年,[Marvin Minsky](https://en.wikipedia.org/wiki/Marvin_Minsky)和[Seymour Papert](https://en.wikipedia.org/wiki/Seymour_Papert)出版了一本名为_Perceptron_的书,详细阐述了 Perceptron 的缺陷。正因为如此,人工神经网络的研究停滞不前,直到[Paul Werbos](https://en.wikipedia.org/wiki/Paul_Werbos)提出了反向传播法。 逆向传播希望能够解决当时阻碍神经网络工业应用的复杂数据分类问题。它的灵感来自于突触可塑性--大脑修改神经元之间连接强度的方式,从而提高性能。 @@ -125,7 +125,7 @@ MCP成为创建第一个神经网络的基础,被称为[感知器](https://e 反向传播的设计是为了模仿大脑中通过一个称为权重调整的过程加强神经元之间的连接。 -尽管Paul Werbos提出了早期的建议,但反向传播的概念只是在[David Rumelheart](https://en.wikipedia.org/wiki/David_Rumelhart)、[Geoffrey Hinton](https://en.wikipedia.org/wiki/Geoffrey_Hinton)和[Ronald Williams](https://en.wikipedia.org/wiki/Ronald_J._Williams)等研究人员发表论文证明其对训练神经网络的有效性后才得到广泛的采纳。 +尽管 Paul Werbos 提出了早期的建议,但反向传播的概念只是在[David Rumelheart](https://en.wikipedia.org/wiki/David_Rumelhart)、[Geoffrey Hinton](https://en.wikipedia.org/wiki/Geoffrey_Hinton)和[Ronald Williams](https://en.wikipedia.org/wiki/Ronald_J._Williams)等研究人员发表论文证明其对训练神经网络的有效性后才得到广泛的采纳。 反向传播的实施导致了深度学习的产生,它为世界上大多数的人工智能系统提供了动力。 @@ -149,7 +149,7 @@ MCP成为创建第一个神经网络的基础,被称为[感知器](https://e ###大脑如何工作--简化描述 -人类的大脑基本上是通过使用神经元来处理思想。一个神经元由3个核心部分组成:树突、轴突和躯干。 +人类的大脑基本上是通过使用神经元来处理思想。一个神经元由 3 个核心部分组成:树突、轴突和躯干。 树突负责接收来自其他神经元的信号。躯干负责处理从树突收到的信息,而轴突负责将处理过的信息传递给下一个树突。 @@ -177,7 +177,7 @@ MCP成为创建第一个神经网络的基础,被称为[感知器](https://e - **大脑模拟:**人工智能系统已被用于[模拟](https://www.frontiersin.org/articles/10.3389/fncom.2020.00016/full)人脑,研究其与物理世界的互动。例如,研究人员利用机器学习技术来模拟参与视觉处理的生物神经元的活动。其结果使人们对大脑如何处理视觉信息有了深入了解。 -- 对大脑的见解:**研究人员已经开始使用机器学习算法来分析和获得对大脑数据和fMRI扫描的见解。这些洞察力有助于识别模式和关系,否则这些模式和关系将被隐藏起来。这些见解可以帮助我们了解内部认知功能、记忆和决策。它们还有助于治疗大脑原生疾病,如阿尔茨海默氏症。 +- 对大脑的见解:**研究人员已经开始使用机器学习算法来分析和获得对大脑数据和 fMRI 扫描的见解。这些洞察力有助于识别模式和关系,否则这些模式和关系将被隐藏起来。这些见解可以帮助我们了解内部认知功能、记忆和决策。它们还有助于治疗大脑原生疾病,如阿尔茨海默氏症。 ## ###大脑启发的人工智能方法背后的核心原则 @@ -266,7 +266,7 @@ MCP成为创建第一个神经网络的基础,被称为[感知器](https://e ###复杂度 -这是一个相当艰巨的挑战。大脑启发式人工智能的方法是基于对大脑的建模,并根据该模型构建人工智能系统。但人脑是一个固有的复杂系统,有1000亿个神经元和大约600万亿个突触连接(每个神经元平均有10000个与其他神经元的突触连接)。这些突触不断地以动态和不可预知的方式进行互动。 +这是一个相当艰巨的挑战。大脑启发式人工智能的方法是基于对大脑的建模,并根据该模型构建人工智能系统。但人脑是一个固有的复杂系统,有 1000 亿个神经元和大约 600 万亿个突触连接(每个神经元平均有 10000 个与其他神经元的突触连接)。这些突触不断地以动态和不可预知的方式进行互动。 建立旨在模仿甚至超越这种复杂性的人工智能系统本身就是一个挑战,需要同样复杂的统计模型。 @@ -274,16 +274,16 @@ MCP成为创建第一个神经网络的基础,被称为[感知器](https://e ###训练大型模型的数据要求 -Open AI的GPT 4,目前是基于文本的AI模型的最前沿,需要47G字节的数据。相比之下,其前身GPT3是在17千兆字节的数据上训练的,大约低了3个数量级。想象一下,GPT5将在多少数据上进行训练。 +Open AI 的 GPT 4,目前是基于文本的 AI 模型的最前沿,需要 47G 字节的数据。相比之下,其前身 GPT3 是在 17 千兆字节的数据上训练的,大约低了 3 个数量级。想象一下,GPT5 将在多少数据上进行训练。 -为了获得可接受的结果,大脑启发的人工智能系统需要大量的任务数据,特别是听觉和视觉任务。这就对数据收集管道的建立给予了很大的重视。例如,特斯拉有7.8亿英里的驾驶数据,其数据收集管道每10小时就会增加一百万英里。 +为了获得可接受的结果,大脑启发的人工智能系统需要大量的任务数据,特别是听觉和视觉任务。这就对数据收集管道的建立给予了很大的重视。例如,特斯拉有 7.8 亿英里的驾驶数据,其数据收集管道每 10 小时就会增加一百万英里。 ###能源效率 -建立模仿大脑能效的大脑启发式人工智能系统是一个巨大的挑战。人类的大脑大约消耗20瓦特的能量。相比之下,特斯拉的自动驾驶仪,在专门的芯片上,每秒消耗约2500瓦,[它需要大约](https://ts2.space/en/exploring-the-environmental-footprint-of-gpt-4-energy-consumption-and-sustainability/#:~:text=The%20paper%20found%20the,hours%20(MWh)%20%20energy。) 7.5兆瓦时(MWh)来训练一个ChatGPT大小的人工智能模型。 +建立模仿大脑能效的大脑启发式人工智能系统是一个巨大的挑战。人类的大脑大约消耗 20 瓦特的能量。相比之下,特斯拉的自动驾驶仪,在专门的芯片上,每秒消耗约 2500 瓦,[它需要大约](https://ts2.space/en/exploring-the-environmental-footprint-of-gpt-4-energy-consumption-and-sustainability/#:~:text=The%20paper%20found%20the,hours%20(MWh)%20%20energy。) 7.5 兆瓦时(MWh)来训练一个 ChatGPT 大小的人工智能模型。 ###可解释性问题 diff --git a/chinese/articles/the-complete-guide-to-full-stack-development-with-supabas.md b/chinese/articles/the-complete-guide-to-full-stack-development-with-supabas.md index 25bb72ba7..6bec33b82 100644 --- a/chinese/articles/the-complete-guide-to-full-stack-development-with-supabas.md +++ b/chinese/articles/the-complete-guide-to-full-stack-development-with-supabas.md @@ -5,61 +5,61 @@ ![使用 Next.js 和 Supabase 进行全栈开发](https://www.freecodecamp.org/news/content/images/size/w2000/2021/06/supanext.png) -[Supabase](https://twitter.com/supabase_io) 是一个Firebase的开源替代品,让你在不到两分钟的时间内创建一个实时后台。 +[Supabase](https://twitter.com/supabase_io) 是一个 Firebase 的开源替代品,让你在不到两分钟的时间内创建一个实时后台。 -在过去的几个月里,Supabase继续通过我的网站获得开发人员的宣传和采用。和我交流过的很多人都喜欢它利用 SQL 风格的数据库这一事实,他们也喜欢它是开源的。 +在过去的几个月里,Supabase 继续通过我的网站获得开发人员的宣传和采用。和我交流过的很多人都喜欢它利用 SQL 风格的数据库这一事实,他们也喜欢它是开源的。 -当你创建一个项目时,Supabase会自动给你一个Postgres SQL数据库、用户认证和API。从那里你可以很容易地实现额外的功能,如实时订阅和文件存储。 +当你创建一个项目时,Supabase 会自动给你一个 Postgres SQL 数据库、用户认证和 API。从那里你可以很容易地实现额外的功能,如实时订阅和文件存储。 在本指南中,你将学习如何构建一个全栈应用程序,实现大多数应用程序需要的核心功能--如路由、数据库、API、认证、授权、实时数据和细粒度访问控制。我们将使用一个现代堆栈,包括[React](https://reactjs.org/docs/getting-started.html), [Next.js](https://nextjs.org/), 和 [TailwindCSS](https://tailwindcss.com/). -我试图把我自己在使用Supabase的过程中所学到的东西提炼成一个尽可能简短的指南,这样你也可以开始用这个框架构建全栈应用。 +我试图把我自己在使用 Supabase 的过程中所学到的东西提炼成一个尽可能简短的指南,这样你也可以开始用这个框架构建全栈应用。 -我们将建立的应用程序是一个多用户博客应用程序,其中包含了你在许多现代应用程序中看到的所有类型的功能。这将使我们超越基本的CRUD,实现文件存储以及授权和细粒度的访问控制等功能。 +我们将建立的应用程序是一个多用户博客应用程序,其中包含了你在许多现代应用程序中看到的所有类型的功能。这将使我们超越基本的 CRUD,实现文件存储以及授权和细粒度的访问控制等功能。 -> 你可以找到我们将要建立的应用程序的代码,[在这里](https://github.com/dabit3/supabase-next.js)。 +> 你可以找到我们将要建立的应用程序的代码,[在这里](https://github.com/dabit3/supabase-next.js)。 通过学习如何将所有这些功能结合在一起,你应该能够利用你在这里学到的东西,建立你自己的想法。了解基本结构本身,你就可以在将来带着这些知识,以你认为合适的方式使用它。 -## Supabase概述 +## Supabase 概述 ### 如何构建全栈式应用程序 我对全栈式无服务器框架非常着迷,因为它们为希望构建完整应用的开发者提供了大量的支持和敏捷性。 -Supabase将强大的后端服务与易于使用的客户端库和SDK结合起来,提供了一个端到端的解决方案。 +Supabase 将强大的后端服务与易于使用的客户端库和 SDK 结合起来,提供了一个端到端的解决方案。 这种组合使你不仅可以在后端建立起必要的个别功能和服务,而且可以通过利用同一团队维护的客户端库,在前端轻松地将它们整合在一起。 -由于Supabase是开源的,你可以选择自我托管或将你的后端部署为托管服务。而且,正如你所看到的,这对我们来说将很容易在一个不需要信用卡的,免费的基础设施上开始做。 +由于 Supabase 是开源的,你可以选择自我托管或将你的后端部署为托管服务。而且,正如你所看到的,这对我们来说将很容易在一个不需要信用卡的,免费的基础设施上开始做。 -## 为什么使用Supabase? +## 为什么使用 Supabase? -我曾在AWS领导前端Web和移动开发者倡导团队,并写了一本关于构建这些类型的应用程序的书。所以我在这个领域有相当多的建设经验。 +我曾在 AWS 领导前端 Web 和移动开发者倡导团队,并写了一本关于构建这些类型的应用程序的书。所以我在这个领域有相当多的建设经验。 -我认为Supabase带来了一些非常强大的功能,当我开始使用它进行构建时,这些功能立即让我感到震惊。 +我认为 Supabase 带来了一些非常强大的功能,当我开始使用它进行构建时,这些功能立即让我感到震惊。 ### 数据访问方式 -我过去使用的一些工具和框架的最大限制之一是缺乏查询功能。我非常喜欢Supabase,因为它是建立在Postgres之上的,它能够实现一套极其丰富的、开箱即用的高性能查询功能,而不需要编写任何额外的后端代码。 +我过去使用的一些工具和框架的最大限制之一是缺乏查询功能。我非常喜欢 Supabase,因为它是建立在 Postgres 之上的,它能够实现一套极其丰富的、开箱即用的高性能查询功能,而不需要编写任何额外的后端代码。 -客户端SDK提供了易于使用的[filters](https://supabase.io/docs/reference/javascript/using-filters) 和[modifiers](https://supabase.io/docs/reference/javascript/using-modifiers),以实现几乎无限的数据访问模式的组合。 +客户端 SDK 提供了易于使用的[filters](https://supabase.io/docs/reference/javascript/using-filters) 和[modifiers](https://supabase.io/docs/reference/javascript/using-modifiers),以实现几乎无限的数据访问模式的组合。 -因为数据库是SQL,关系型数据很容易配置和查询,而且客户端库把它作为第一等公民来考虑。 +因为数据库是 SQL,关系型数据很容易配置和查询,而且客户端库把它作为第一等公民来考虑。 ### 权限 -当你写好 "hello world "时,许多类型的框架和服务很快就会被淘汰。这是因为大多数真实世界的需要远远超出了你经常看到的这些工具所提供的基本CRUD功能。 +当你写好 "hello world "时,许多类型的框架和服务很快就会被淘汰。这是因为大多数真实世界的需要远远超出了你经常看到的这些工具所提供的基本 CRUD 功能。 一些框架和管理服务的问题是,它们创建的抽象没有足够的扩展性,无法轻松修改配置或定制业务逻辑。这些限制往往使其难以考虑到在现实世界中构建应用程序时出现的许多一次性的用例。 -除了实现广泛的数据访问模式外,Supabase还使配置授权和精细的访问控制变得容易。这是因为它是简单的Postgres,使你能够直接从内置的SQL编辑器中实现任何你想要的[row-level security policies(行级安全策略)](https://www.postgresql.org/docs/10/ddl-rowsecurity.html) 你可以直接从内置的SQL编辑器中获取你想要的信息(我们将在这里讨论这个问题)。 +除了实现广泛的数据访问模式外,Supabase 还使配置授权和精细的访问控制变得容易。这是因为它是简单的 Postgres,使你能够直接从内置的 SQL 编辑器中实现任何你想要的[row-level security policies(行级安全策略)](https://www.postgresql.org/docs/10/ddl-rowsecurity.html) 你可以直接从内置的 SQL 编辑器中获取你想要的信息(我们将在这里讨论这个问题)。 ### UI 组件 -除了由构建其他Supabase工具的同一个团队维护的客户端库之外,他们还维护了一个[UI 组件库](https://ui.supabase.io/) (beta)使你能够使用各种UI元素来启动和运行。 +除了由构建其他 Supabase 工具的同一个团队维护的客户端库之外,他们还维护了一个[UI 组件库](https://ui.supabase.io/) (beta)使你能够使用各种 UI 元素来启动和运行。 -最强大的是[Auth](https://ui.supabase.io/components/auth),它与你的Supabase项目集成,以快速启动一个用户认证流程(我将在本教程中使用它)。 +最强大的是[Auth](https://ui.supabase.io/components/auth),它与你的 Supabase 项目集成,以快速启动一个用户认证流程(我将在本教程中使用它)。 ### 多种认证方式 @@ -80,18 +80,18 @@ Supabase enables all of the following types of authentication mechanisms: 它最大的优点之一是[完全开源](https://github.com/supabase) (是的,后端也是)。 这意味着,你可以选择无服务器托管的方式,也可以自己托管。 -这意味着,如果你愿意,你可以[用Docker运行Supabase,托管自己的应用程序](https://supabase.io/docs/guides/self-hosting) 在 AWS, GCP, or Azure上。这将消除你在使用Supabase时,避免云服务商锁定问题。 -## 如何开始使用Supabase +这意味着,如果你愿意,你可以[用 Docker 运行 Supabase,托管自己的应用程序](https://supabase.io/docs/guides/self-hosting) 在 AWS, GCP, or Azure 上。这将消除你在使用 Supabase 时,避免云服务商锁定问题。 +## 如何开始使用 Supabase ### 项目设置 -为了开始,让我们首先创建Next.js应用程序。 +为了开始,让我们首先创建 Next.js 应用程序。 ```sh npx create-next-app next-supabase ``` -接下来,进入目录并使用NPM或Yarn安装我们的应用程序所需的依赖。 +接下来,进入目录并使用 NPM 或 Yarn 安装我们的应用程序所需的依赖。 ```sh @@ -99,13 +99,13 @@ npm install @supabase/supabase-js @supabase/ui react-simplemde-editor easymde re npm install tailwindcss@latest @tailwindcss/typography postcss@latest autoprefixer@latest ``` -接下来,创建必要的Tailwind配置文件: +接下来,创建必要的 Tailwind 配置文件: ```sh npx tailwindcss init -p ``` -现在更新**tailwind.config.js**,将Tailwind typography插件添加到插件数组中。我们将使用这个插件来为我们的博客设计标记。 +现在更新**tailwind.config.js**,将 Tailwind typography 插件添加到插件数组中。我们将使用这个插件来为我们的博客设计标记。 ``` plugins: [ require('@tailwindcss/typography') @@ -120,25 +120,25 @@ plugins: [ @tailwind utilities; ``` -### Supabase项目初始化 +### Supabase 项目初始化 -现在项目已经在本地创建,让我们来创建Supabase项目。 +现在项目已经在本地创建,让我们来创建 Supabase 项目。 -为此,请前往[Supabase.io](https://supabase.io/),点击**Start Your Project**。通过GitHub账号完成认证,然后在账户中提供给你的组织下创建一个新项目。 +为此,请前往[Supabase.io](https://supabase.io/),点击**Start Your Project**。通过 GitHub 账号完成认证,然后在账户中提供给你的组织下创建一个新项目。 ![](https://www.freecodecamp.org/news/content/images/2021/06/myaccountsorg.jpg) 给项目一个**Name**和**Password**,然后点击**Create new project**。 -你的项目将需要大约2分钟的时间来创建。 +你的项目将需要大约 2 分钟的时间来创建。 -### 如何在Supabase中创建一个数据库表 +### 如何在 Supabase 中创建一个数据库表 当你创建了你的项目,让我们继续为我们的应用程序创建表,以及我们需要的所有权限。要做到这一点,请点击左边菜单中的**SQL**链接。 ![](https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-06-06-at-6.07.00-PM.png) -在这个视图中,点击**Query-1**下的 **Open queries**,粘贴以下SQL查询,并点击 **RUN**。 +在这个视图中,点击**Query-1**下的 **Open queries**,粘贴以下 SQL 查询,并点击 **RUN**。 ``` CREATE TABLE posts ( @@ -168,7 +168,7 @@ create policy "Posts are public." on posts for 这将创建我们将在应用程序中使用的`posts`表。它还启用了一些行级(row)权限: - 所有用户都可以查询帖子 -- 只有已登录的用户可以创建帖子,他们的用户ID必须与传入参数的用户ID一致 +- 只有已登录的用户可以创建帖子,他们的用户 ID 必须与传入参数的用户 ID 一致 - 只有帖子的主人可以更新或删除它 现在,如果我们点击**Table editor**链接,我们应该看到我们的新表以适当的模式创建。 @@ -179,9 +179,9 @@ create policy "Posts are public." on posts for ### Next.js Supabase 配置 -现在项目已经创建,我们需要一种方法让我们的Next.js应用程序知道我们刚刚为它创建的后端服务。 +现在项目已经创建,我们需要一种方法让我们的 Next.js 应用程序知道我们刚刚为它创建的后端服务。 -对我们来说,配置的最好方法是使用环境变量。Next.js允许通过在项目根部创建一个名为 **.env.local** 的文件来设置环境变量,并将它们存储在那里。 +对我们来说,配置的最好方法是使用环境变量。Next.js 允许通过在项目根部创建一个名为 **.env.local** 的文件来设置环境变量,并将它们存储在那里。 为了向浏览器暴露一个变量,你必须在变量前加上 `NEXT_PUBLIC_`。 @@ -192,7 +192,7 @@ NEXT_PUBLIC_SUPABASE_URL=https://app-id.supabase.co NEXT_PUBLIC_SUPABASE_ANON_KEY=your-public-api-key ``` -你可以在Supabase仪表板的设置中找到你的API URL和API密钥的值: +你可以在 Supabase 仪表板的设置中找到你的 API URL 和 API 密钥的值: ![](https://www.freecodecamp.org/news/content/images/2021/06/appurls.jpg) @@ -209,7 +209,7 @@ export const supabase = createClient( 现在我们将能够导入`supabase`实例并在我们的应用程序中的任何地方使用它。 -下面是使用Supabase JavaScript客户端与API互动的概况。 +下面是使用 Supabase JavaScript 客户端与 API 互动的概况。 **查询数据:** @@ -256,7 +256,7 @@ const { user, session, error } = await supabase.auth.signIn({ }) ``` -在我们的案例中,我们不会手工编写主要的认证逻辑,我们将使[Supabase UI](https://ui.supabase.io/components/auth)中的Auth组件。 +在我们的案例中,我们不会手工编写主要的认证逻辑,我们将使[Supabase UI](https://ui.supabase.io/components/auth)中的 Auth 组件。 ## 如何构建应用程序 @@ -420,21 +420,21 @@ function CreatePost() { export default CreatePost ``` -这个组件渲染了一个Markdown编辑器,允许用户创建新的帖子。 +这个组件渲染了一个 Markdown 编辑器,允许用户创建新的帖子。 `createNewPost`函数将使用`supabase`实例,使用本地表单状态创建新的帖子。 -你可能注意到,我们没有传入任何消息头(headers)。这是因为如果用户已经登录,Supabase客户端库会自动将访问令牌包含在已登录用户的消息头(headers)中。 +你可能注意到,我们没有传入任何消息头(headers)。这是因为如果用户已经登录,Supabase 客户端库会自动将访问令牌包含在已登录用户的消息头(headers)中。 ### 如何查看单个帖子 我们需要配置一个页面来查看单个帖子。 -这个页面使用`getStaticPaths`来在构建时根据从API回来的帖子动态地创建页面。 +这个页面使用`getStaticPaths`来在构建时根据从 API 回来的帖子动态地创建页面。 -我们还使用`fallback`标志来启用动态SSG页面生成的返回路径。 +我们还使用`fallback`标志来启用动态 SSG 页面生成的返回路径。 -我们使用`getStaticProps`使帖子数据被获取,然后在构建时作为props传入页面。 +我们使用`getStaticProps`使帖子数据被获取,然后在构建时作为 props 传入页面。 在**pages**目录下创建一个名为**posts**的新文件夹, 并在该文件夹中创建一个名为 **[id\].js**的文件。在**pages/posts/[id\].js**,添加以下代码。 @@ -614,7 +614,7 @@ export default function MyPosts() { 接下来,在**pages**目录下创建一个名为**edit-post**的新文件夹。然后,在这个文件夹中创建一个名为 **[id\].js** 的文件。 -在这个文件中,我们将从一个路由参数中获取访问帖子的`id'。当组件加载时,我们将使用来自路由的帖子ID来获取帖子数据,并使其可用于编辑。 +在这个文件中,我们将从一个路由参数中获取访问帖子的`id'。当组件加载时,我们将使用来自路由的帖子 ID 来获取帖子数据,并使其可用于编辑。 在这个文件中,添加以下代码: @@ -700,9 +700,9 @@ export default EditPost 现在,我们已经运行了应用程序,添加实时更新是很容易的。 -默认情况下,数据库中的Realtime是禁用的。让我们为**posts**表打开Realtime。 +默认情况下,数据库中的 Realtime 是禁用的。让我们为**posts**表打开 Realtime。 -要做到这一点,打开应用程序仪表板并点击 **Databases** -> **Replication** -> **0 Tables** (Source下面)。 打开 **posts** 表Realtime功能。 [这里](https://supabase.io/docs/guides/api#managing-realtime) 是一个关于如何做的视频演练,以使其清晰明了。 +要做到这一点,打开应用程序仪表板并点击 **Databases** -> **Replication** -> **0 Tables** (Source 下面)。 打开 **posts** 表 Realtime 功能。 [这里](https://supabase.io/docs/guides/api#managing-realtime) 是一个关于如何做的视频演练,以使其清晰明了。 接下来,打开 **src/index.js**,用以下代码更新 `useEffect` hook: @@ -723,9 +723,9 @@ export default EditPost ## 下一步 -现在你应该对如何用Supabase和Next.js构建全栈应用有了一定的了解。 +现在你应该对如何用 Supabase 和 Next.js 构建全栈应用有了一定的了解。 -如果你想了解更多关于用Supabase构建全栈应用的信息,我建议查看以下资源。 +如果你想了解更多关于用 Supabase 构建全栈应用的信息,我建议查看以下资源。 - [Supabase 文档](https://supabase.io/docs) - [Supabase 示例项目](https://github.com/supabase/supabase/tree/master/examples) @@ -734,4 +734,4 @@ export default EditPost - [Build in Public 001 - 构建 Next.js + Supabase 教程](https://www.youtube.com/watch?v=p561ogKZ63o) - [身份验证深入了解](https://supabase.io/docs/learn/auth-deep-dive/auth-deep-dive-jwts) - [Supabase 和 Sveltekit](https://www.youtube.com/watch?v=j4AV2Liojk0) -- [ 在Replit上使用Supabase和nodejs](https://www.youtube.com/watch?v=lQ5iIxaYduI) \ No newline at end of file +- [在 Replit 上使用 Supabase 和 nodejs](https://www.youtube.com/watch?v=lQ5iIxaYduI) \ No newline at end of file diff --git a/chinese/articles/the-definitive-guide-to-contributing-to-open-source-900d5f9f2282.md b/chinese/articles/the-definitive-guide-to-contributing-to-open-source-900d5f9f2282.md index 616b9a9f7..73b4d9d78 100644 --- a/chinese/articles/the-definitive-guide-to-contributing-to-open-source-900d5f9f2282.md +++ b/chinese/articles/the-definitive-guide-to-contributing-to-open-source-900d5f9f2282.md @@ -210,7 +210,7 @@ A Buy me a Coffee button looks like this (you can click it if you feel I deserve [https://www.buymeacoffee.com/doomhammer](https://www.buymeacoffee.com/doomhammer) -Besides general purpose buttons, there are methods dedicated to Open Source projects, like Gittip/[Liberapay](https://en.liberapay.com/explore/repositories) (this one likes to change names quite often). Funny enough, one of the popular repositories supported by Liberapay is… [awesome](https://github.com/sindresorhus/awesome). +Besides general purpose buttons, there are methods dedicated to Open Source projects, like Gittip/[Liberapay](https://en.liberapay.com/explore/repositories) (this one likes to change names quite often). Funny enough, one of the popular repositories supported by Liberapay is…… [awesome](https://github.com/sindresorhus/awesome). The premise is similar to that of [Flattr](https://flattr.com/). You choose the monthly amount of money you want to spend on creations, and when you approve of some work, the author of the work gets a share of your budget. Say, you decided that you can afford to pay $5 each month towards Open Source projects. If you approve of four projects in a particular month (by starring them or clicking the Flattr button) each of the projects’ authors gets $1.25. Pretty simple. diff --git a/chinese/articles/the-definitive-node-js-handbook.md b/chinese/articles/the-definitive-node-js-handbook.md index cfe9e63f9..d653b659f 100644 --- a/chinese/articles/the-definitive-node-js-handbook.md +++ b/chinese/articles/the-definitive-node-js-handbook.md @@ -5,7 +5,7 @@ ![The definitive Node.js handbook](https://cdn-media-1.freecodecamp.org/images/1*7F50Qc-ysFgy6tCjUyruTA.jpeg) -注意:你可以得到这本手册的 [PDF、ePub或Mobi][1] 版本,以方便参考,或在你的 Kindle 或平板电脑上阅读。 +注意:你可以得到这本手册的 [PDF、ePub 或 Mobi][1] 版本,以方便参考,或在你的 Kindle 或平板电脑上阅读。 ### Node.js 简介 @@ -15,11 +15,11 @@ Node.js 是一个 **服务器** 上的 **JavaScript 运行环境**。 -Node.js 是开源的、跨平台的,自从2009年推出以来,它大受欢迎,现在在 Web 开发领域发挥着重要作用。如果 GitHub 的星星是一个流行的指示因素,那么拥有58000多颗星星就意味着非常流行。 +Node.js 是开源的、跨平台的,自从 2009 年推出以来,它大受欢迎,现在在 Web 开发领域发挥着重要作用。如果 GitHub 的星星是一个流行的指示因素,那么拥有 58000 多颗星星就意味着非常流行。 Node.js 在浏览器之外运行 V8 JavaScript 引擎,这是 Chrome 浏览器的核心。Node.js 能够利用那些使 Chrome 浏览器的 JavaScript 运行变得非常快的成果,这使得 Node.js 能够从 V8 执行的巨大性能改进和即时编译中受益。得益于此,在 Node.js 中运行的 JavaScript 代码可以变得非常有性能。 -一个 Node.js 应用程序是由一个单一的进程运行的(single process),不需要为每个请求创建一个新的线程(new thread)。Node在其标准库中提供了一套异步 I/O 原生语法,这将防止JavaScript 代码被阻塞,一般来说,Node.js 中的库是使用非阻塞范式编写的,使阻塞行为成为例外,而不是正常的。 +一个 Node.js 应用程序是由一个单一的进程运行的(single process),不需要为每个请求创建一个新的线程(new thread)。Node 在其标准库中提供了一套异步 I/O 原生语法,这将防止 JavaScript 代码被阻塞,一般来说,Node.js 中的库是使用非阻塞范式编写的,使阻塞行为成为例外,而不是正常的。 当 Node.js 需要执行 I/O 操作时,比如从网络中读取数据、访问数据库或文件系统,而不是阻塞线程,Node.js 会在响应回来时恢复操作,而不是浪费 CPU 来等待。 @@ -27,13 +27,13 @@ Node.js 在浏览器之外运行 V8 JavaScript 引擎,这是 Chrome 浏览器 Node.js 有一个独特的优势,因为数百万为浏览器编写 JavaScript 的前端开发人员现在能够运行服务器端代码和前端代码,而不需要学习一种完全不同的语言。 -在 Node.js 中,新的 ECMAScript 标准可以顺利使用,因为你不必等待所有用户更新他们的浏览器--你通过改变Node.js的版本来决定使用哪个 ECMAScript 版本,你还可以通过运行带有标志(flags)的 Node 来启用特定的实验性功能。 +在 Node.js 中,新的 ECMAScript 标准可以顺利使用,因为你不必等待所有用户更新他们的浏览器--你通过改变 Node.js 的版本来决定使用哪个 ECMAScript 版本,你还可以通过运行带有标志(flags)的 Node 来启用特定的实验性功能。 #### Node.js 有大量的库 -凭借其简单的结构,Node包管理器([NPM][2])帮助 Node.js 的生态系统激增。现在,[NPM registry][3] 托管了近50万个开源包,你可以自由使用。 +凭借其简单的结构,Node 包管理器([NPM][2])帮助 Node.js 的生态系统激增。现在,[NPM registry][3] 托管了近 50 万个开源包,你可以自由使用。 -### 一个Node.js应用程序的例子 +### 一个 Node.js 应用程序的例子 Node.js 最常见的例子 Hello World 是一个网络服务器: @@ -55,17 +55,17 @@ server.listen(port, hostname, () => { 要运行这个片段,将其保存为 `server.js` 文件,并在终端运行 `node server.js`。 -这段代码首先包括 Node.js [http模块][4]。 +这段代码首先包括 Node.js [http 模块][4]。 -Node.js有一个惊艳的 [标准库][5],包括对网络的一流支持。 +Node.js 有一个惊艳的 [标准库][5],包括对网络的一流支持。 -`http` 的 `createServer()` 方法创建一个新的HTTP服务器并返回。 +`http` 的 `createServer()` 方法创建一个新的 HTTP 服务器并返回。 该服务器被设置为在指定的端口和主机名上监听。当服务器准备好时,回调函数被调用,在这种情况下,通知我们服务器正在运行。 每当收到一个新的请求,[request event][6] 被调用,提供两个对象:一个请求(一个 [http.IncomingMessage][7] 对象)和一个响应(一个 [http.ServerResponse][8] 对象)。 -这两个对象对于处理HTTP调用是必不可少的。 +这两个对象对于处理 HTTP 调用是必不可少的。 第一个对象提供请求的细节。在这个简单的例子中,这个没有被使用,但是你可以访问请求头和请求数据。 @@ -93,20 +93,20 @@ res.end('Hello World\n') ### Node.js 框架和工具 -Node.js 是一个低代码(low-level)平台。为了让开发者更容易、更有趣,成千上万的库被建立在Node.js 之上。 +Node.js 是一个低代码(low-level)平台。为了让开发者更容易、更有趣,成千上万的库被建立在 Node.js 之上。 其中许多人随着时间的推移成为了流行的选择。这里有一个不全面的列表,列出了我认为非常相关和值得学习的那些: - [**Express**][9] 创建一个网络服务器的最简单而强大的方法之一。它的极简方法和对服务器核心功能的无偏见关注是其成功的关键。 - [**Meteor**][10] - 一个令人难以置信的强大的全栈框架,赋予你用JavaScript构建应用程序的同构方法,在客户端和服务器上共享代码。曾经是一个提供一切的现成工具,现在它与前端库如[React][11]、[Vue][12] 和 [Angular][13] 集成。Meteor也可以用来创建移动应用程序。 + 一个令人难以置信的强大的全栈框架,赋予你用 JavaScript 构建应用程序的同构方法,在客户端和服务器上共享代码。曾经是一个提供一切的现成工具,现在它与前端库如[React][11]、[Vue][12] 和 [Angular][13] 集成。Meteor 也可以用来创建移动应用程序。 - [**Koa**][14] - 由Express背后的同一个团队建立,Koa旨在更简单和更小,建立在多年的知识之上。这个新项目的诞生是由于需要在不破坏现有社区的情况下,创造不兼容的变化。 + 由 Express 背后的同一个团队建立,Koa 旨在更简单和更小,建立在多年的知识之上。这个新项目的诞生是由于需要在不破坏现有社区的情况下,创造不兼容的变化。 - [**Next.js**][15] 这是一个用于渲染服务器端的 [React][16] 应用程序的框架。 - [**Micro**][17] - 这是一个非常轻量级的服务器,用于创建异步的HTTP微服务。 + 这是一个非常轻量级的服务器,用于创建异步的 HTTP 微服务。 - [**Socket.io**][18] 这是一个实时通信引擎,用于构建网络应用。 @@ -116,11 +116,11 @@ Node.js 是一个低代码(low-level)平台。为了让开发者更容易、 信不信由你,Node.js 只有 9 年的历史。 -相比之下,JavaScript 有 23 年的历史,而我们所知的网络(在引入Mosaic之后)有25年的历史。 +相比之下,JavaScript 有 23 年的历史,而我们所知的网络(在引入 Mosaic 之后)有 25 年的历史。 -对于一项技术来说,9年的时间实在是太短了,但Node.js似乎已经存在了很久。 +对于一项技术来说,9 年的时间实在是太短了,但 Node.js 似乎已经存在了很久。 -我有幸从 Node.js 的早期就开始工作,当时它只有2年的历史,尽管信息很少,但你已经可以感觉到它是一个巨大的东西。 +我有幸从 Node.js 的早期就开始工作,当时它只有 2 年的历史,尽管信息很少,但你已经可以感觉到它是一个巨大的东西。 在这一节中,我想画出 Node.js 在历史上的大图景,把事情看清楚。 @@ -128,9 +128,9 @@ Node.js 是一个低代码(low-level)平台。为了让开发者更容易、 JavaScript 是一种编程语言,是在网景公司创建的,作为一种脚本工具,在他们的浏览器 [Netscape Navigator][19] 中操作网页。 -网景公司的部分商业模式是销售网络服务器,其中包括一个名为 "Netscape LiveWire" 的环境,它可以使用服务器端的 JavaScript 创建动态页面。因此,服务器端JavaScript的想法并不是由 Node.js引入的,它就像 JavaScript 一样古老--但在当时它并不成功。 +网景公司的部分商业模式是销售网络服务器,其中包括一个名为 "Netscape LiveWire" 的环境,它可以使用服务器端的 JavaScript 创建动态页面。因此,服务器端 JavaScript 的想法并不是由 Node.js 引入的,它就像 JavaScript 一样古老--但在当时它并不成功。 -导致 Node.js 崛起的一个关键因素是时机。几年前,JavaScript 开始被认为是一种严肃的语言,这要归功于 "Web 2.0 "应用程序,它们向世界展示了网络上的现代体验是什么样的(想想谷歌地图或GMail)。 +导致 Node.js 崛起的一个关键因素是时机。几年前,JavaScript 开始被认为是一种严肃的语言,这要归功于 "Web 2.0 "应用程序,它们向世界展示了网络上的现代体验是什么样的(想想谷歌地图或 GMail)。 由于浏览器的竞争,JavaScript 引擎的性能标准大大提高了,这种竞争仍在继续。每个主要浏览器背后的开发团队每天都在努力工作,为我们提供更好的性能,这对 JavaScript 这个平台来说是一个巨大的胜利。Chrome V8,即 Node.js 背后使用的引擎,就是其中之一,特别是它的 Chrome JavaScript 引擎。 @@ -138,7 +138,7 @@ JavaScript 是一种编程语言,是在网景公司创建的,作为一种脚 #### 2009 -Node.js的诞生 +Node.js 的诞生 第一种形式的 [npm][20] 的诞生 @@ -150,7 +150,7 @@ Node.js的诞生 #### 2011 -npm达到1.0版本 +npm 达到 1.0 版本 公司开始采用 Node。[LinkedIn][23], [Uber][24] @@ -168,7 +168,7 @@ npm达到1.0版本 #### 2014 -大事件: [IO.js][28] 是 Node.js 的一个重要分叉,目标是引入ES6支持,并快速推进。 +大事件: [IO.js][28] 是 Node.js 的一个重要分叉,目标是引入 ES6 支持,并快速推进。 #### 2015 @@ -176,9 +176,9 @@ npm达到1.0版本 IO.js 回归到 Node.js 中 -npm引入了私有模块 +npm 引入了私有模块 -[Node 4][30] 发布 (之前没有发布过1、2、3版本) +[Node 4][30] 发布 (之前没有发布过 1、2、3 版本) #### 2016 @@ -194,7 +194,7 @@ npm 更专注于安全: Node 8 发布 [V8][34] 在其测试套件中引入了 Node,正式将 Node 作为除 Chrome 之外的 JavaScript 引擎的目标。 -每周30亿次npm下载 +每周 30 亿次 npm 下载 #### 2018 @@ -214,7 +214,7 @@ Node.js 可以通过不同的方式进行安装。这篇文章强调了最常见 安装 Node.js 的一个非常方便的方法是通过包管理器。在这种情况下,每个操作系统都有自己的。 -在macOS上,[Homebrew][38] 是事实上的标准,而且一旦安装,就可以通过在 CLI 中运行这个命令,非常容易地安装 Node.js: +在 macOS 上,[Homebrew][38] 是事实上的标准,而且一旦安装,就可以通过在 CLI 中运行这个命令,非常容易地安装 Node.js: ```shell brew install node @@ -226,7 +226,7 @@ brew install node 它对于用旧的 Node.js 版本测试你的代码也非常有用。 -我的建议是,如果你刚刚开始,而且你还没有使用 Homebrew,就使用官方安装程序。否则,Homebrew是我最喜欢的解决方案。 +我的建议是,如果你刚刚开始,而且你还没有使用 Homebrew,就使用官方安装程序。否则,Homebrew 是我最喜欢的解决方案。 ### 使用 Node.js,你需要知道多少 JavaScript? @@ -251,7 +251,7 @@ brew install node - Template Literals (模板文字) - Semicolons (分号) - Strict Mode (严格模式) -- ECMAScript 6, 2016, 2017 (ES6 ES2016 ES2017标准) +- ECMAScript 6, 2016, 2017 (ES6 ES2016 ES2017 标准) 有了这些概念,你就可以在浏览器和 Node.js 中成为一名熟练的 JavaScript 开发者了。 @@ -264,7 +264,7 @@ brew install node - 闭包(Closures) - 事件循环(The Event Loop) -幸运的是,我写了一本免费的电子书,解释了所有这些主题,它叫做 [JavaScript基础知识][41]。这是你能找到的学习所有这些的最紧凑的资源。 +幸运的是,我写了一本免费的电子书,解释了所有这些主题,它叫做 [JavaScript 基础知识][41]。这是你能找到的学习所有这些的最紧凑的资源。 ### Node.js 和浏览器之间的差异 @@ -282,7 +282,7 @@ brew install node 生态系统的变化。 -在浏览器中,大多数时候你所做的是与DOM或其他网络平台 API(如 Cookies )进行交互。当然,这些并不存在于 Node.js 中。你没有 `document`、`window` 和所有其他由浏览器提供的对象。 +在浏览器中,大多数时候你所做的是与 DOM 或其他网络平台 API(如 Cookies )进行交互。当然,这些并不存在于 Node.js 中。你没有 `document`、`window` 和所有其他由浏览器提供的对象。 而且在浏览器中,我们没有 Node.js 通过其模块提供的所有好用的 API,如文件系统访问功能。 @@ -300,49 +300,49 @@ brew install node ### V8 JavaScript 引擎 -V8 是谷歌浏览器的 JavaScript 引擎的名字。在使用 Chrome 浏览器浏览时,它能接收我们的JavaScript 并执行它。 +V8 是谷歌浏览器的 JavaScript 引擎的名字。在使用 Chrome 浏览器浏览时,它能接收我们的 JavaScript 并执行它。 V8 提供了运行时环境,在其中执行 JavaScript。DOM 和其他网络平台 API 是由浏览器提供的。 -最酷的是,JavaScript 引擎是独立于它所承载的浏览器的。这一关键特征使 Node.js 的崛起成为可能。V8 早在2009年就被 Node.js 选择为引擎,随着 Node.js 的普及,V8成为现在为大量用JavaScript 编写的服务器端代码提供动力的引擎。 +最酷的是,JavaScript 引擎是独立于它所承载的浏览器的。这一关键特征使 Node.js 的崛起成为可能。V8 早在 2009 年就被 Node.js 选择为引擎,随着 Node.js 的普及,V8 成为现在为大量用 JavaScript 编写的服务器端代码提供动力的引擎。 -Node.js 的生态系统是巨大的,由于它的存在,V8也为桌面应用程序提供了动力,比如 [Electron][43] 等项目。 +Node.js 的生态系统是巨大的,由于它的存在,V8 也为桌面应用程序提供了动力,比如 [Electron][43] 等项目。 #### 其他 JS 引擎 其他浏览器有自己的 JavaScript 引擎: -- Firefox使用 [Spidermonkey][44] -- Safari使用 [JavaScriptCore][45] (也叫Nitro) -- Edge使用 [Chakra][46](译者注: 现在Edge放弃自己的引擎,使用chrome一样的引擎,即V8) +- Firefox 使用 [Spidermonkey][44] +- Safari 使用 [JavaScriptCore][45] (也叫 Nitro) +- Edge 使用 [Chakra][46](译者注: 现在 Edge 放弃自己的引擎,使用 chrome 一样的引擎,即 V8) -还有很多JavaScript引擎。 +还有很多 JavaScript 引擎。 -所有这些引擎都实现了ECMA ES-262标准,也叫ECMAScript,即JavaScript使用的标准。 +所有这些引擎都实现了 ECMA ES-262 标准,也叫 ECMAScript,即 JavaScript 使用的标准。 #### 对性能的追求 V8 是用 C++ 编写的,而且它在不断改进。它是可移植的,可以在 Mac、Windows、Linux 和其他一些系统上运行。 -在这个 V8 介绍中,我将忽略 V8 的实现细节。它们可以在更权威的网站上找到,包括 [V8官方网站][47],而且它们随着时间的推移而变化,往往是很大的变化。 +在这个 V8 介绍中,我将忽略 V8 的实现细节。它们可以在更权威的网站上找到,包括 [V8 官方网站][47],而且它们随着时间的推移而变化,往往是很大的变化。 -V8一直在发展,就像周围的其他JavaScript引擎一样,以加快网络和 Node.js 生态系统的发展。 +V8 一直在发展,就像周围的其他 JavaScript 引擎一样,以加快网络和 Node.js 生态系统的发展。 在网络上,有一场多年来一直在进行的性能竞赛,我们(作为用户和开发者)从这场竞争中获益良多,因为我们年复一年地得到更快和更优化的机器。 #### 编译(Compilation) -一般来说,JavaScript 被认为是一种解释语言,但现代的 JavaScript 引擎不再只是解释JavaScript,而是对其进行编译。 +一般来说,JavaScript 被认为是一种解释语言,但现代的 JavaScript 引擎不再只是解释 JavaScript,而是对其进行编译。 这发生在 2009 年,当时 SpiderMonkey JavaScript 编译器被添加到 Firefox 3.5 中,每个人都遵循这个想法。 -JavaScript 由V8内部编译,采用实时制(JIT)编译,以加快执行速度。 +JavaScript 由 V8 内部编译,采用实时制(JIT)编译,以加快执行速度。 -这可能看起来违反直觉,。但自从2004年谷歌地图问世以来,JavaScript 已经从一般执行几十行代码的语言发展到在浏览器中运行的几千到几十万行的完整应用程序。 +这可能看起来违反直觉,。但自从 2004 年谷歌地图问世以来,JavaScript 已经从一般执行几十行代码的语言发展到在浏览器中运行的几千到几十万行的完整应用程序。 我们的应用程序现在可以在浏览器中运行数小时,而不仅仅是一些表单验证规则或简单的脚本。 -在这个**新世界**里,编译JavaScript是非常有意义的,因为虽然可能需要多花一点时间来让JavaScript**就绪**,但一旦完成,它的性能就会比纯粹的解释代码高得多。 +在这个**新世界**里,编译 JavaScript 是非常有意义的,因为虽然可能需要多花一点时间来让 JavaScript**就绪**,但一旦完成,它的性能就会比纯粹的解释代码高得多。 ### 如何退出 Node.js 程序 @@ -352,7 +352,7 @@ JavaScript 由V8内部编译,采用实时制(JIT)编译,以加快执行 让我们从最激烈的一个开始,看看为什么你最好**不**使用它。 -`process` 核心模块提供了一个方便的方法,允许你以编程方式退出Node.js程序:`process.exit()`。 +`process` 核心模块提供了一个方便的方法,允许你以编程方式退出 Node.js 程序:`process.exit()`。 当 Node.js 运行这一行时,进程会立即被强制终止。 @@ -420,11 +420,11 @@ process.on('SIGTERM', () => { process.kill(process.pid, 'SIGTERM') ``` -或者从另一个正在运行的Node.js程序,或者在你的系统中运行的任何其他应用程序,知道你想终止的进程的PID。 +或者从另一个正在运行的 Node.js 程序,或者在你的系统中运行的任何其他应用程序,知道你想终止的进程的 PID。 ### 如何从 Node.js 读取环境变量 -Node的 `process` 核心模块提供了 `env` 属性,它承载了所有在进程启动时设置的环境变量。 +Node 的 `process` 核心模块提供了 `env` 属性,它承载了所有在进程启动时设置的环境变量。 下面是一个访问 `NODE_ENV` 环境变量的例子,该变量默认设置为 `development`。 @@ -472,7 +472,7 @@ process.env.NODE_ENV // "development" #### Serverless -无服务器(Serverless)是一种发布应用的方式,而且完全没有服务器需要管理。无服务器是一种范式,你把你的应用发布为**功能**,它们在网络端点上做出响应(也叫FAAS--功能即服务)。 +无服务器(Serverless)是一种发布应用的方式,而且完全没有服务器需要管理。无服务器是一种范式,你把你的应用发布为**功能**,它们在网络端点上做出响应(也叫 FAAS--功能即服务)。 非常受欢迎的解决方案有: @@ -497,19 +497,19 @@ PAAS 是 Platform As A Service 的缩写。这些平台解决了很多你在部 [Heroku][58] 是一个神奇的平台。 -这是一篇好文章,[在Heroku上开始使用Node.js][59]. +这是一篇好文章,[在 Heroku 上开始使用 Node.js][59]. #### Microsoft Azure [Azure][60] 是微软的云产品。 -查看 [在Azure中创建一个Node.js Web应用][61]. +查看 [在 Azure 中创建一个 Node.js Web 应用][61]. #### Google Cloud Platform [Google Cloud][62] 是你的应用程序的一个了不起的结构。 -他们有一个很好的[Node.js文档部分][63]. +他们有一个很好的[Node.js 文档部分][63]. #### Virtual Private Server(虚拟私有服务器) @@ -519,13 +519,13 @@ PAAS 是 Platform As A Service 的缩写。这些平台解决了很多你在部 - [Linode][65] - [Amazon Web Services][66], 我特别提到 Amazon Elastic Beanstalk,因为它抽象了一点 AWS 的复杂性。 -因为他们提供了一个空的Linux机器,你可以在上面工作,所以这些没有具体的教程。 +因为他们提供了一个空的 Linux 机器,你可以在上面工作,所以这些没有具体的教程。 -在VPS类别中还有很多选择,这些只是我使用的和我推荐的。 +在 VPS 类别中还有很多选择,这些只是我使用的和我推荐的。 #### Bare metal(裸金属) -另一个解决方案是获得一个 [裸机金属服务器][67],安装一个Linux发行版,把它连接到互联网上(或者每月租一个,比如你可以使用 [虚拟裸金属][68]服务)。 +另一个解决方案是获得一个 [裸机金属服务器][67],安装一个 Linux 发行版,把它连接到互联网上(或者每月租一个,比如你可以使用 [虚拟裸金属][68]服务)。 ### 如何使用 Node.js REPL @@ -552,7 +552,7 @@ node **提示**:如果你不确定如何打开你的终端,谷歌 “How to open terminal on <your operating system>”。 -REPL正在等待我们输入一些JavaScript代码。 +REPL 正在等待我们输入一些 JavaScript 代码。 从简单的开始,然后按下 `enter` 键: @@ -568,7 +568,7 @@ REPL正在等待我们输入一些JavaScript代码。 #### 通过使用 tab 键完成自动补全 -REPL最酷的地方是它是互动的。 +REPL 最酷的地方是它是互动的。 当你写代码时,如果你按下 `tab` 键,REPL 将尝试自动完成你写的内容,以匹配你已经定义的变量或预定义的变量。 @@ -576,13 +576,13 @@ REPL最酷的地方是它是互动的。 试着输入一个 JavaScript 类的名称,如 `Number`,加一个点,然后按 `tab`。 -REPL将打印出你可以访问该类的所有属性和方法: +REPL 将打印出你可以访问该类的所有属性和方法: ![](https://cdn-media-1.freecodecamp.org/images/MgYHCtgjD1rom1yKM43E-qBh7ansJuyglRWr) #### 探索全局对象(global objects) -你可以通过输入 "global. "并按 "tab "来检查你可以访问的globals对象: +你可以通过输入 "global. "并按 "tab "来检查你可以访问的 globals 对象: ![](https://cdn-media-1.freecodecamp.org/images/e2qWLuyjYC4DFZjEs2jYWK-NL9AXbpDiSdA7) @@ -592,17 +592,17 @@ REPL将打印出你可以访问该类的所有属性和方法: #### 点命令(Dot commands) -REPL有一些特殊的命令,都以点`.`开头。它们是 +REPL 有一些特殊的命令,都以点`.`开头。它们是 - `.help`: 显示点命令的帮助。 -- `.editor`: 启用更多的编辑器,可以轻松地编写多行JavaScript代码。一旦你进入这个模式,输入ctrl-D就可以运行你写的代码。 -- `.break`: 当输入一个多行表达式时,输入.break命令将中止继续输入。与按下ctrl-C相同。 -- `.clear`: 将REPL上下文重置为空对象,并清除当前正在输入的任何多行表达式。 -- `.load`: 加载一个JavaScript文件,相对于当前工作目录。 -- `.save`: 将你在REPL会话中输入的所有内容保存到一个文件(指定文件名) -- `.exit`: 退出repl(与按两次ctrl-C相同) +- `.editor`: 启用更多的编辑器,可以轻松地编写多行 JavaScript 代码。一旦你进入这个模式,输入 ctrl-D 就可以运行你写的代码。 +- `.break`: 当输入一个多行表达式时,输入.break 命令将中止继续输入。与按下 ctrl-C 相同。 +- `.clear`: 将 REPL 上下文重置为空对象,并清除当前正在输入的任何多行表达式。 +- `.load`: 加载一个 JavaScript 文件,相对于当前工作目录。 +- `.save`: 将你在 REPL 会话中输入的所有内容保存到一个文件(指定文件名) +- `.exit`: 退出 repl(与按两次 ctrl-C 相同) -REPL知道你什么时候在输入一个多行语句,而不需要调用`.editor`。 +REPL 知道你什么时候在输入一个多行语句,而不需要调用`.editor`。 例如,如果你开始键入一个迭代,像这样: @@ -700,7 +700,7 @@ args['name'] //flavio #### 使用控制台模块的基本输出 -Node.js提供了一个 [console模块][70],它提供了大量非常有用的方法来与命令行进行交互。 +Node.js 提供了一个 [console 模块][70],它提供了大量非常有用的方法来与命令行进行交互。 它基本上与你在浏览器中找到的 `console` 对象相同。 @@ -847,7 +847,7 @@ console.log(chalk.yellow('hi!')) [Progress][72] 是一个很棒的软件包,可以在控制台中创建一个进度条。使用 `npm install progress` 来安装它。 -这个片段创建了一个10步的进度条,每100毫秒完成一步。当进度条完成后,我们会清除间隔时间: +这个片段创建了一个 10 步的进度条,每 100 毫秒完成一步。当进度条完成后,我们会清除间隔时间: ```js const ProgressBar = require('progress') @@ -864,7 +864,7 @@ const timer = setInterval(() => { 如何使 Node.js CLI 程序具有交互性? -Node 从第7版开始就提供了 [readline模块][73] 来执行这个任务:从一个可读流中获取输入,比如 `process.stdin` 流,在 Node 程序的执行过程中,它就是终端输入,一次一个行。 +Node 从第 7 版开始就提供了 [readline 模块][73] 来执行这个任务:从一个可读流中获取输入,比如 `process.stdin` 流,在 Node 程序的执行过程中,它就是终端输入,一次一个行。 ```js const readline = require('readline') @@ -887,7 +887,7 @@ readline.question(`What's your name?\n`, (name) => { 如果你需要要求一个密码,现在最好是回显它,而是显示一个`*`符号。 -最简单的方法是使用 [readline-sync][74],它在API方面非常相似,可以开箱即用。 +最简单的方法是使用 [readline-sync][74],它在 API 方面非常相似,可以开箱即用。 一个更完整和抽象的解决方案是由 [Inquirer.js][75] 提供。 @@ -925,11 +925,11 @@ const library = require('./library') 文件中定义的任何其他对象或变量默认为私有,不向外界公开。 -这就是 [module 系统][76]提供的 `module.exports` API所允许我们做的。 +这就是 [module 系统][76]提供的 `module.exports` API 所允许我们做的。 当你把一个对象或一个函数指定为新的 `exports` 属性时,这就是被暴露的东西。因此,它可以被导入到你的应用程序的其他部分,或者其他应用程序中。 -你可以通过2种方式做到这一点。 +你可以通过 2 种方式做到这一点。 首先是给 `module.exports` 指定一个对象,这是一个由模块系统提供的开箱即用的对象,这将使你的文件只导出**那个对象**: @@ -976,9 +976,9 @@ const car = require('./items').car `npm` 是 **node 软件包管理器**。 -2017年1月,超过35万个软件包被报告列在 npm registry 中,使其成为地球上最大的单一语言代码库,你可以肯定有一个软件包用于(几乎!)一切。 +2017 年 1 月,超过 35 万个软件包被报告列在 npm registry 中,使其成为地球上最大的单一语言代码库,你可以肯定有一个软件包用于(几乎!)一切。 -它开始时是一种下载和管理Node.js包的依赖关系的方式,但后来它也成为了一个用于前端 JavaScript 的工具。 +它开始时是一种下载和管理 Node.js 包的依赖关系的方式,但后来它也成为了一个用于前端 JavaScript 的工具。 `npm` 做了很多事情。 @@ -1033,7 +1033,7 @@ npm update <package-name> 很多时候,你会发现一个库只与另一个库的主要版本兼容。 -或者一个库的最新版本中的一个bug,仍未被修复,导致了一个问题。 +或者一个库的最新版本中的一个 bug,仍未被修复,导致了一个问题。 指定一个库的明确版本也有助于让每个人都处于同一个确切的软件包版本上,这样整个团队就会运行同一个版本,直到 `package.json` 文件被更新。 @@ -1058,7 +1058,7 @@ npm <task-name> } ``` -使用这个功能来运行Webpack是非常普遍的: +使用这个功能来运行 Webpack 是非常普遍的: ```json { @@ -1078,9 +1078,9 @@ npm dev npm prod ``` -### npm在哪里安装软件包? +### npm 在哪里安装软件包? -当你使用`npm`(或 [yarn][77])安装一个软件包时,你可以执行2种类型的安装: +当你使用`npm`(或 [yarn][77])安装一个软件包时,你可以执行 2 种类型的安装: - a local install (本地安装) - a global install (全局安装) @@ -1107,7 +1107,7 @@ npm install -g lodash `npm root -g` 命令将告诉你该位置在你的机器上的确切位置。 -在MacOS或Linux上,这个位置可以是 `/usr/local/lib/node_modules`。 在 Windows 上应该是`C:\Users\YOU\AppData\Roaming\npm\node_modules` +在 MacOS 或 Linux 上,这个位置可以是 `/usr/local/lib/node_modules`。 在 Windows 上应该是`C:\Users\YOU\AppData\Roaming\npm\node_modules` 然而,如果你使用 `nvm` 来管理 Node.js 的版本,这个位置会有所不同。 @@ -1117,7 +1117,7 @@ npm install -g lodash #### 如何在你的代码中包含并使用安装在 node_modules 文件夹中的软件包 -当你使用 `npm` 安装一个包到你的 `node_modules` 文件夹,或者全局安装,你如何在你的Node代码中使用它? +当你使用 `npm` 安装一个包到你的 `node_modules` 文件夹,或者全局安装,你如何在你的 Node 代码中使用它? 假设你安装了 `lodash`,一个流行的 JavaScript 工具库,使用 @@ -1143,17 +1143,17 @@ cowsay 软件包提供了一个命令行程序,执行该程序可以让一头 当你使用 `npm install cowsay` 来安装这个包时,它将自己和一些依赖项安装在 node_modules 文件夹: -有一个隐藏的.bin文件夹,它包含cowsay二进制文件的符号链接。 +有一个隐藏的.bin 文件夹,它包含 cowsay 二进制文件的符号链接。 你如何执行这些? -你当然可以输入 `./node_modules/.bin/cowsay` 来运行它,它也可以工作,但是 [npx][79],包括在最近版本的 npm 中(从5.2开始),是一个更好的选择。你只需运行: +你当然可以输入 `./node_modules/.bin/cowsay` 来运行它,它也可以工作,但是 [npx][79],包括在最近版本的 npm 中(从 5.2 开始),是一个更好的选择。你只需运行: ```js npx cowsay ``` -而npx会找到软件包的位置。 +而 npx 会找到软件包的位置。 ### package.json 指南 @@ -1271,7 +1271,7 @@ package.json 文件是很多基于 Node.js 生态系统的应用代码库中的 - `description` 是对应用程序/包的简要描述 - `main` 设置应用程序的入口点 - `private` 如果设置为 `true` 可以防止应用程序/软件包被意外地发布到 `npm` -- `scripts` 定义了一组你可以运行的node脚本 +- `scripts` 定义了一组你可以运行的 node 脚本 - `dependencies` 设置一个作为依赖项安装的 `npm` 包的列表 - `devDependencies` 设置一个作为开发依赖的 `npm` 包的列表 - `engines` 设置该软件包/应用程序适用于哪些版本的 Node @@ -1295,9 +1295,9 @@ package.json 文件是很多基于 Node.js 生态系统的应用代码库中的 {"name": "test-project"} ``` -该名称必须少于214个字符,不能有空格,只能包含小写字母、连字符(`-`)或下划线(`_`)。 +该名称必须少于 214 个字符,不能有空格,只能包含小写字母、连字符(`-`)或下划线(`_`)。 -这是因为当一个软件包在 `npm` 上发布时,它会根据这个属性获得自己的URL。 +这是因为当一个软件包在 `npm` 上发布时,它会根据这个属性获得自己的 URL。 #### `author` @@ -1349,7 +1349,7 @@ package.json 文件是很多基于 Node.js 生态系统的应用代码库中的 #### `bugs` -链接到软件包的issues跟踪器,很可能是GitHub issues页面 +链接到软件包的 issues 跟踪器,很可能是 GitHub issues 页面 比如: @@ -1377,11 +1377,11 @@ package.json 文件是很多基于 Node.js 生态系统的应用代码库中的 {"version": "1.0.0"} ``` -这个属性遵循版本的语义版本(semver)符号,这意味着版本总是用3个数字表示。`x.x.x`。 +这个属性遵循版本的语义版本(semver)符号,这意味着版本总是用 3 个数字表示。`x.x.x`。 第一个数字是主要版本,第二个是次要版本,第三个是补丁版本。 -这些数字是有意义的:一个只修复bug的版本是补丁版本,一个引入了向后兼容的变化的版本是次要版本,一个主要版本可以有突破性的变化。 +这些数字是有意义的:一个只修复 bug 的版本是补丁版本,一个引入了向后兼容的变化的版本是次要版本,一个主要版本可以有突破性的变化。 #### `license` @@ -1403,7 +1403,7 @@ package.json 文件是很多基于 Node.js 生态系统的应用代码库中的 {"keywords": [ "email", "machine learning", "ai"]} ``` -这有助于人们在浏览类似的软件包或浏览npm官网时找到你的软件包。 +这有助于人们在浏览类似的软件包或浏览 npm 官网时找到你的软件包。 #### `description` @@ -1539,7 +1539,7 @@ yarn add --dev <PACKAGENAME> #### `engines` -设置该软件包/应用程序适用于哪些版本的Node.js和其他命令。 +设置该软件包/应用程序适用于哪些版本的 Node.js 和其他命令。 例如: @@ -1549,7 +1549,7 @@ yarn add --dev <PACKAGENAME> #### `browserslist` -是用来告诉你要支持哪些浏览器(以及它们的版本)。它被 Babel、Autoprefixer 和其他工具引用,只为你的目标浏览器添加所需的 polyfills(降级方案)和fallbacks(回退方案)。 +是用来告诉你要支持哪些浏览器(以及它们的版本)。它被 Babel、Autoprefixer 和其他工具引用,只为你的目标浏览器添加所需的 polyfills(降级方案)和 fallbacks(回退方案)。 例如: @@ -1557,7 +1557,7 @@ yarn add --dev <PACKAGENAME> {"browserslist": [ "> 1%", "last 2 versions", "not ie <= 8"]} ``` -这种配置意味着你要支持所有至少有1%使用量的浏览器的最后两个主要版本(来自 [CanIUse.com][83]统计),但IE8和更低版本除外([见更多][84] 浏览器列表)。 +这种配置意味着你要支持所有至少有 1%使用量的浏览器的最后两个主要版本(来自 [CanIUse.com][83]统计),但 IE8 和更低版本除外([见更多][84] 浏览器列表)。 #### Command-specific properties @@ -1571,7 +1571,7 @@ yarn add --dev <PACKAGENAME> 该符号指定了你的软件包接受哪些更新,来自该依赖关系。 -鉴于使用semver(语义版本管理),所有的版本都有3位数字,第一位是主要版本,第二位是次要版本,第三位是补丁版本,你有这些规则: +鉴于使用 semver(语义版本管理),所有的版本都有 3 位数字,第一位是主要版本,第二位是次要版本,第三位是补丁版本,你有这些规则: - `~`: 如果你写 `~0.13.0`, 你想只更新补丁版本。`0.13.1`可以,但`0.14.0`不可以。 - `^`: 如果你写 `^0.13.0`, 你想更新补丁和次要版本。`0.13.1`, `0.14.0`等等。 @@ -1586,17 +1586,17 @@ yarn add --dev <PACKAGENAME> - `no symbol`:你只接受你指定的那个特定版本 - `latest`:你想使用最新的可用版本 -你可以将上述大部分的范围结合起来,就像这样。`1.0.0 || >=1.1.0 < 1.2.0`,以使用1.0.0或1.1.0以上的一个版本,但低于1.2.0。 +你可以将上述大部分的范围结合起来,就像这样。`1.0.0 || >=1.1.0 < 1.2.0`,以使用 1.0.0 或 1.1.0 以上的一个版本,但低于 1.2.0。 ### package-lock.json 文件 -package-lock.json 文件是在安装 Nodo包时自动生成的。 +package-lock.json 文件是在安装 Nodo 包时自动生成的。 -在版本5中,NPM 引入了 `package-lock.json` 文件。 +在版本 5 中,NPM 引入了 `package-lock.json` 文件。 那是什么?你可能知道 `package.json` 文件,它更常见,存在的时间也更长。 -该文件的目的是跟踪每一个安装的软件包的确切版本,这样,即使软件包被维护者更新,产品也能以同样的方式100%重现。 +该文件的目的是跟踪每一个安装的软件包的确切版本,这样,即使软件包被维护者更新,产品也能以同样的方式 100%重现。 这解决了 `package.json` 未解决的一个非常具体的问题。在 package.json 中,你可以使用 **semver** 注解来设置你想升级到哪个版本(补丁或小版本),例如: @@ -1610,7 +1610,7 @@ package-lock.json 文件是在安装 Nodo包时自动生成的。 可能是你,也可能是另一个人在世界的另一端试图通过运行 `npm install` 来初始化这个项目。 -所以你的原始项目和新初始化的项目实际上是不同的。即使一个补丁或小版本不应该引入破坏性的变化,我们都知道bug可以(所以,他们会)潜入。 +所以你的原始项目和新初始化的项目实际上是不同的。即使一个补丁或小版本不应该引入破坏性的变化,我们都知道 bug 可以(所以,他们会)潜入。 `package-lock.json` 将你当前安装的每个软件包的版本**in stone**上,`npm` 在运行 `npm install` 时将使用这些确切的版本。 @@ -1653,7 +1653,7 @@ package-lock.json 文件是在安装 Nodo包时自动生成的。 它们按字母顺序被添加到文件中,每一个都有一个 `version` 字段,一个指向软件包位置的 `resolved` 字段,以及一个 `integrity` 字符串,我们可以用来验证该软件包。 -### 查找一个npm包的安装版本 +### 查找一个 npm 包的安装版本 查看所有安装的 npm 包的最新版本,包括它们的依赖关系: @@ -1704,7 +1704,7 @@ npm list └── minimist@0.0.10 ``` -如果你想查看npm仓库中软件包的最新可用版本,运行`npm view [package_name] version`: +如果你想查看 npm 仓库中软件包的最新可用版本,运行`npm view [package_name] version`: ```shell ❯ npm view cowsay version @@ -1718,7 +1718,7 @@ npm list 安装一个旧版本的 npm 包可能对解决兼容性问题有帮助。 -你可以使用 `@` 语法来安装一个npm包的旧版本: +你可以使用 `@` 语法来安装一个 npm 包的旧版本: ```shell npm install <package>@<;version> @@ -1730,9 +1730,9 @@ npm install <package>@<;version> npm install cowsay ``` -安装1.3.1版本(在撰写本文时)。 +安装 1.3.1 版本(在撰写本文时)。 -安装1.2.0 版本: +安装 1.2.0 版本: ```shell npm install cowsay@1.2.0 @@ -1758,9 +1758,9 @@ npm install -g webpack@4.16.4 当你使用 `npm install <packagename>` 安装一个软件包时,该软件包的最新可用版本会被下载并放在 `node_modules文件夹` 中,并且在你当前文件夹中的`package.json` 和 `package-lock.json` 文件中添加相应条目。 -npm会计算依赖关系,并安装那些最新的可用版本。 +npm 会计算依赖关系,并安装那些最新的可用版本。 -假设你安装了 `[cowsay][85]`,一个很酷的命令行工具,可以让你让cow(牛)说**东西**。 +假设你安装了 `[cowsay][85]`,一个很酷的命令行工具,可以让你让 cow(牛)说**东西**。 当你 `npm安装cowsay` 时,这个条目被添加到 `package.json` 文件中: @@ -1790,7 +1790,7 @@ npm会计算依赖关系,并安装那些最新的可用版本。 } ``` -现在这2个文件告诉我们,我们安装了cowsay的`1.3.1`版本,而我们的更新规则是 `^1.3.1`,对于npm的版本规则(后面会解释)意味着npm可以更新到补丁和小版本。`0.13.1`,`0.14.0`,以此类推。 +现在这 2 个文件告诉我们,我们安装了 cowsay 的`1.3.1`版本,而我们的更新规则是 `^1.3.1`,对于 npm 的版本规则(后面会解释)意味着 npm 可以更新到补丁和小版本。`0.13.1`,`0.14.0`,以此类推。 如果有一个新的次要版本或补丁版本,我们输入 `npm update`,安装的版本就会被更新,`package-lock.json` 文件就会勤奋地填上新的版本。 @@ -1834,9 +1834,9 @@ npm install 语义版本管理(Semantic Versioning)是一种用来为版本提供意义的惯例。 -如果说Node.js包中有什么了不起的东西,那就是所有的人都同意使用语义版本控制(Semantic Versioning)来进行版本编号。 +如果说 Node.js 包中有什么了不起的东西,那就是所有的人都同意使用语义版本控制(Semantic Versioning)来进行版本编号。 -语义版本控制的概念很简单:所有的版本都有3位数字。`x.y.z`。 +语义版本控制的概念很简单:所有的版本都有 3 位数字。`x.y.z`。 - 第一个数字是主版本(major version) - 第二位数字是次要版本 (minor version) @@ -1844,7 +1844,7 @@ npm install 当你制作一个新的版本时,你不会随心所欲地提高一个数字,而是有规则的: -- 当你对API进行不兼容的修改时,你要提高主版本的等级 +- 当你对 API 进行不兼容的修改时,你要提高主版本的等级 - 当你以向后兼容的方式增加功能时,你要提高次要版本的数量 - 当你进行向后兼容的 bug 修复时,你要提高补丁版本 @@ -1852,7 +1852,7 @@ npm install 为什么这么重要? -因为 `npm` 设置了一些规则,我们可以在 [package.json文件][87] 中使用,以便在运行 `npm update` 时,选择它可以将我们的软件包更新到哪些版本。 +因为 `npm` 设置了一些规则,我们可以在 [package.json 文件][87] 中使用,以便在运行 `npm update` 时,选择它可以将我们的软件包更新到哪些版本。 规则使用这些符号: @@ -1878,7 +1878,7 @@ npm install - `-`: 你接受一定范围的版本。例如: `2.1.0 - 2.6.2` - `||`: 你结合一组。例如: `< 2.1 || > 2.6` -你可以结合这些符号,例如使用 `1.0.0 || >=1.1.0 <1.2.0` 来表示使用 1.0.0 或 1.1.0 以上的一个版本,但低于1.2.0。 +你可以结合这些符号,例如使用 `1.0.0 || >=1.1.0 <1.2.0` 来表示使用 1.0.0 或 1.1.0 以上的一个版本,但低于 1.2.0。 也有其他规则: @@ -1941,7 +1941,7 @@ require('package-name') 所有的项目都有自己的本地包,即使这看起来是一种资源的浪费,但与可能产生的负面影响相比,它是微不足道的。 -当一个软件包提供了一个可执行的命令,你可以从shell(CLI)中运行,并且在不同的项目中重复使用时,它应该被全局地安装。 +当一个软件包提供了一个可执行的命令,你可以从 shell(CLI)中运行,并且在不同的项目中重复使用时,它应该被全局地安装。 你也可以在本地安装可执行命令,并使用 [npx][90] 运行它们,但有些软件包最好是全局安装。 @@ -1981,11 +1981,11 @@ npm list -g --depth 0 你需要设置 `--production` flag (`npm install --production`),以避免安装这些开发依赖项。 -### npx Node包运行器 +### npx Node 包运行器 `npx` 是一种非常酷的运行 Node.js 代码的方式,并提供了许多有用的功能。 -在本节中,我想介绍一个非常强大的命令,从2017年7月发布的5.2版本开始,**npm** 中就有这个命令,**npx**。 +在本节中,我想介绍一个非常强大的命令,从 2017 年 7 月发布的 5.2 版本开始,**npm** 中就有这个命令,**npx**。 如果你不想安装 npm,你可以将 npx 作为一个 [独立的包][91] 来安装。 @@ -2022,9 +2022,9 @@ Node.js 的开发者曾经将大多数可执行的命令作为全局包发布, || || ``` -现在,这需要你之前从npm全局安装了 `cowsay` 命令,否则当你试图运行该命令时,你会得到一个错误。 +现在,这需要你之前从 npm 全局安装了 `cowsay` 命令,否则当你试图运行该命令时,你会得到一个错误。 -`npx` 允许你在没有本地安装的情况下运行该npm命令: +`npx` 允许你在没有本地安装的情况下运行该 npm 命令: ```shell npx cowsay "Hello" @@ -2033,7 +2033,7 @@ npx cowsay "Hello" 现在,这是一个有趣的无用命令。其他情况包括: - 运行 `vue CLI` 工具来创建新的应用程序并运行它们:`npx vue create my-vue-app` -- 使用 `create-react-app` 创建一个新的React应用:`npx create-react-app my-react-app` +- 使用 `create-react-app` 创建一个新的 React 应用:`npx create-react-app my-react-app` 以及更多。 @@ -2052,7 +2052,7 @@ npx node@8 -v #v8.11.3 #### 直接从 URL 中运行任意的代码片段 -`npx` 并不仅能运行在npm注册表上发布的软件包。 +`npx` 并不仅能运行在 npm 注册表上发布的软件包。 你可以运行位于 [GitHub][92] gist 中的代码,例如: @@ -2064,11 +2064,11 @@ npx https://gist.github.com/zkat/4bc19503fe9e9309e2bfaa2c58074d32 ### The Event Loop(事件循环) -事件循环是了解JavaScript的最重要的方面之一。本节解释了JavaScript如何在单线程中工作的内部细节,以及它如何处理异步函数。 +事件循环是了解 JavaScript 的最重要的方面之一。本节解释了 JavaScript 如何在单线程中工作的内部细节,以及它如何处理异步函数。 -我已经用JavaScript编程多年了,但我从来没有 **完全** 理解过事情是如何在幕后运作的。不了解这个概念的细节是完全可以的。但是像往常一样,知道它是如何工作的是很有帮助的,而且在这一点上你可能只是有点好奇。 +我已经用 JavaScript 编程多年了,但我从来没有 **完全** 理解过事情是如何在幕后运作的。不了解这个概念的细节是完全可以的。但是像往常一样,知道它是如何工作的是很有帮助的,而且在这一点上你可能只是有点好奇。 -你的JavaScript代码是单线程(single threaded)运行的。每次只有一件事在发生。 +你的 JavaScript 代码是单线程(single threaded)运行的。每次只有一件事在发生。 这是一个实际上非常有帮助的限制,因为它简化了很多你编程的方式,而不用担心并发问题。 @@ -2076,13 +2076,13 @@ npx https://gist.github.com/zkat/4bc19503fe9e9309e2bfaa2c58074d32 一般来说,在大多数浏览器中,每个浏览器标签都有一个事件循环,以使每个进程都被隔离,避免一个有无限循环的网页或繁重的处理过程阻塞你整个浏览器。 -该环境管理着多个并发的事件循环,以处理例如API调用。[Web Workers][94] 也是在自己的事件循环中运行。 +该环境管理着多个并发的事件循环,以处理例如 API 调用。[Web Workers][94] 也是在自己的事件循环中运行。 你主要需要关注的是,**你的代码**会在一个事件循环中运行,写代码时要考虑到这个事情,避免阻塞它。 #### 阻塞事件循环 -任何 JavaScript 代码,如果需要花费太长的时间将控制权返回到事件循环中,就会阻断页面中任何 JavaScript 代码的执行,甚至阻断UI线程,用户就无法点击、滚动页面,等等。 +任何 JavaScript 代码,如果需要花费太长的时间将控制权返回到事件循环中,就会阻断页面中任何 JavaScript 代码的执行,甚至阻断 UI 线程,用户就无法点击、滚动页面,等等。 JavaScript 中几乎所有的 I/O 原生语句都是无阻塞的。如网络请求、Node.js 文件系统操作,等等。阻塞是个例外,这就是为什么 JavaScript 如此基于回调,以及最近的 promises 和 async/await 的原因。 @@ -2140,7 +2140,7 @@ baz #### 排列函数执行 -上面的例子看起来很正常,没有什么特别的地方。JavaScript找到要执行的东西,按顺序运行它们。 +上面的例子看起来很正常,没有什么特别的地方。JavaScript 找到要执行的东西,按顺序运行它们。 我们来看看如何推迟一个函数,直到堆栈清空。 @@ -2184,17 +2184,17 @@ bar #### 消息队列 -当 `setTimeout()` 被调用时,浏览器或Node.js开始计时。一旦定时器过期,在这种情况下,由于我们把 `0` 作为超时,回调函数就会被放到**消息队列**中。 +当 `setTimeout()` 被调用时,浏览器或 Node.js 开始计时。一旦定时器过期,在这种情况下,由于我们把 `0` 作为超时,回调函数就会被放到**消息队列**中。 消息队列也是用户启动的事件(如点击和键盘事件或获取响应)在您的代码有机会对其做出反应之前排队的地方。 或者像 `onLoad` 这样的 DOM 事件。 循环优先考虑调用栈。 它首先处理它在调用栈中找到的所有内容,一旦那里没有任何内容,它就会去获取消息队列中的内容。 -我们不必等待像 `setTimeout`、fetch或其他东西的函数来做它们自己的工作,因为它们是由浏览器提供的,而且它们生活在自己的线程上。例如,如果你把 `setTimeout` 的超时时间设为2秒,你就不必等待2秒,等待发生在其他地方。 +我们不必等待像 `setTimeout`、fetch 或其他东西的函数来做它们自己的工作,因为它们是由浏览器提供的,而且它们生活在自己的线程上。例如,如果你把 `setTimeout` 的超时时间设为 2 秒,你就不必等待 2 秒,等待发生在其他地方。 -#### ES6 Job Queue(ES6任务队列) +#### ES6 Job Queue(ES6 任务队列) -ECMAScript 2015引入了Job Queue的概念,Promises(也在ES6/ES2015中引入)也使用了这个概念。这是一种尽快执行异步函数结果的方法,而不是放在调用栈的最后。 +ECMAScript 2015 引入了 Job Queue 的概念,Promises(也在 ES6/ES2015 中引入)也使用了这个概念。这是一种尽快执行异步函数结果的方法,而不是放在调用栈的最后。 在当前函数结束之前解析的 Promise 将在当前函数之后立即执行。 @@ -2227,15 +2227,15 @@ baz should be right after foo, before barbar ``` -这是Promises(以及建立在Promises 之上的 `async/await`)和通过 `setTimeout()` 或其他平台API的普通异步函数之间的巨大区别。 +这是 Promises(以及建立在 Promises 之上的 `async/await`)和通过 `setTimeout()` 或其他平台 API 的普通异步函数之间的巨大区别。 -### 理解process.nextTick() +### 理解 process.nextTick() -当你试图理解Node.js的事件循环时,它的一个重要部分是 `process.nextTick()`。它以一种特殊的方式与事件循环互动。 +当你试图理解 Node.js 的事件循环时,它的一个重要部分是 `process.nextTick()`。它以一种特殊的方式与事件循环互动。 -每当事件循环进行一次完整的旅行,我们称它为tick。 +每当事件循环进行一次完整的旅行,我们称它为 tick。 -当我们向 `process.nextTick()` 传递一个函数时,我们指示引擎在当前操作结束时,在下一个事件循环tick开始前调用这个函数: +当我们向 `process.nextTick()` 传递一个函数时,我们指示引擎在当前操作结束时,在下一个事件循环 tick 开始前调用这个函数: ```js process.nextTick(() => { @@ -2245,11 +2245,11 @@ process.nextTick(() => { 事件循环正忙于处理当前的函数代码。 -当这个操作结束时,JavaScript引擎会运行该操作期间传递给 `nextTick` 调用的所有函数。 +当这个操作结束时,JavaScript 引擎会运行该操作期间传递给 `nextTick` 调用的所有函数。 -这是我们可以告诉JavaScript引擎异步处理一个函数的方法(在当前函数之后),但要尽快,不要排队。 +这是我们可以告诉 JavaScript 引擎异步处理一个函数的方法(在当前函数之后),但要尽快,不要排队。 -调用 `setTimeout(() => {}, 0)` 将在下一个tick中执行该函数,比使用 `nextTick()` 时晚得多。 +调用 `setTimeout(() => {}, 0)` 将在下一个 tick 中执行该函数,比使用 `nextTick()` 时晚得多。 当你想确保在下一个事件循环迭代中,代码已经被执行时,使用 `nextTick()`。 @@ -2265,19 +2265,19 @@ setImmediate(() => { 作为 `setImmediate()` 参数传递的函数都是一个回调,会在事件循环的下一次迭代中执行。 -`setImmediate()` 与 `setTimeout(() => {}, 0)`(传递0ms的超时)和 `from process.nextTick()` 有何不同? +`setImmediate()` 与 `setTimeout(() => {}, 0)`(传递 0ms 的超时)和 `from process.nextTick()` 有何不同? 传递给 `process.nextTick()` 的函数将在事件循环的当前迭代中执行,在当前操作结束后。这意味着它将总是在 `setTimeout()` 和 `setImmediate()` 之前执行。 -具有0ms延迟的 `setTimeout()` 回调与 `setImmediate()` 非常相似。执行顺序取决于各种因素,但它们都将在事件循环的下一次迭代中运行。 +具有 0ms 延迟的 `setTimeout()` 回调与 `setImmediate()` 非常相似。执行顺序取决于各种因素,但它们都将在事件循环的下一次迭代中运行。 ### 定时器 -在编写JavaScript代码时,你可能想延迟一个函数的执行。学习如何使用 `setTimeout()` 和 `setInterval()` 来安排未来的函数。 +在编写 JavaScript 代码时,你可能想延迟一个函数的执行。学习如何使用 `setTimeout()` 和 `setInterval()` 来安排未来的函数。 #### `setTimeout()` -在编写JavaScript代码时,你可能想延迟一个函数的执行。这就是 `setTimeout` 的工作。 +在编写 JavaScript 代码时,你可能想延迟一个函数的执行。这就是 `setTimeout` 的工作。 你可以指定一个稍后执行的回调函数,以及一个表达你希望它多长时间运行的值,单位是毫秒: @@ -2302,7 +2302,7 @@ const myFunction = (firstParam, secondParam) => { setTimeout(myFunction, 2000, firstParam, secondParam)// runs after 2 seconds ``` -`setTimeout()` 返回定时器ID。这通常是不使用的,但你可以存储这个ID,如果你想删除这个预定函数的执行,可以清除它: +`setTimeout()` 返回定时器 ID。这通常是不使用的,但你可以存储这个 ID,如果你想删除这个预定函数的执行,可以清除它: ```js const id = setTimeout(() => { @@ -2325,7 +2325,7 @@ console.log(' before ') 将打印出 `before after`. -这对于避免在密集型任务上阻塞CPU,并在执行繁重的计算时让其他函数被执行,通过在调度器中排队的函数特别有用。 +这对于避免在密集型任务上阻塞 CPU,并在执行繁重的计算时让其他函数被执行,通过在调度器中排队的函数特别有用。 一些浏览器(IE 和 Edge)实现了一个 `setImmediate()` 方法,可以实现这个完全相同的功能,但它不是标准的,[在其他浏览器上不可用][95]。但它是 Node.js 的一个标准函数。 @@ -2339,7 +2339,7 @@ setInterval(() => { }, 2000) ``` -上面的函数每2秒运行一次,除非你用 `clearInterval` 告诉它停止,并把 `setInterval` 返回的定时器id传给它: +上面的函数每 2 秒运行一次,除非你用 `clearInterval` 告诉它停止,并把 `setInterval` 返回的定时器 id 传给它: ```js const id = setInterval(() => { @@ -2372,7 +2372,7 @@ const interval = setInterval(function() { ![](https://cdn-media-1.freecodecamp.org/images/I9kJc6l-BIT850OGlNDJre80RcsLp7N4amvy) -为了避免这种情况,你可以安排一个递归的setTimeout,当回调函数完成时被调用: +为了避免这种情况,你可以安排一个递归的 setTimeout,当回调函数完成时被调用: ```js const myFunction = () => { // do something @@ -2386,9 +2386,9 @@ setTimeout(myFunction()}, 1000) ![](https://cdn-media-1.freecodecamp.org/images/B2kod2dFuR5U1uwaaW9SGiC1zX5gIUEaiJ8A) -`setTimeout` 和 `setInterval` 在 Node.js 中也可以使用,通过 [Timer模块][96]。 +`setTimeout` 和 `setInterval` 在 Node.js 中也可以使用,通过 [Timer 模块][96]。 -Node.js还提供了 `setImmediate()`,相当于使用 `setTimeout(() => {}, 0)`,主要用于与 Node.js 事件循环一起工作。 +Node.js 还提供了 `setImmediate()`,相当于使用 `setTimeout(() => {}, 0)`,主要用于与 Node.js 事件循环一起工作。 ### 异步编程(Asynchronous Programming)和回调(Callbacks) @@ -2445,7 +2445,7 @@ document.getElementById('button') 这就是所谓的**回调**。 -回调是一个简单的函数,它作为一个值传递给另一个函数,只有当事件发生时才会被执行。我们可以这样做,因为JavaScript有的函数是一等公民,它可以被分配给变量并传递给其他函数(称为**高阶函数**)。 +回调是一个简单的函数,它作为一个值传递给另一个函数,只有当事件发生时才会被执行。我们可以这样做,因为 JavaScript 有的函数是一等公民,它可以被分配给变量并传递给其他函数(称为**高阶函数**)。 常见的做法是将所有的客户端代码包裹在 "window "对象的 "load "事件监听器中,只有当页面准备好时才运行回调函数: @@ -2453,7 +2453,7 @@ document.getElementById('button') window.addEventListener('load', () => {})//window loaded //do what you want ``` -回调无处不在,不仅仅是在DOM事件中使用。 +回调无处不在,不仅仅是在 DOM 事件中使用。 一个常见的例子是通过使用定时器: @@ -2463,7 +2463,7 @@ setTimeout(() => { }, 2000) ``` -[XHR请求][97] 也接受回调,在这个例子中,通过给一个属性分配一个函数,当一个特定的事件发生时(在这个例子中,请求的状态改变),该函数将被调用: +[XHR 请求][97] 也接受回调,在这个例子中,通过给一个属性分配一个函数,当一个特定的事件发生时(在这个例子中,请求的状态改变),该函数将被调用: ```js const xhr = new XMLHttpRequest() @@ -2509,13 +2509,13 @@ window.addEventListener('load', () => { }) ``` -这只是一个简单的4级代码,但我见过更多级别的嵌套,这很不好玩。 +这只是一个简单的 4 级代码,但我见过更多级别的嵌套,这很不好玩。 我们如何解决这个问题呢? #### 回调的替代方案 -从ES6开始,JavaScript 引入了几个功能,帮助我们处理不涉及使用回调的异步代码: +从 ES6 开始,JavaScript 引入了几个功能,帮助我们处理不涉及使用回调的异步代码: - Promises (ES6) - Async/Await (ES8) @@ -2532,11 +2532,11 @@ Promises 是处理 JavaScript 中异步代码的一种方式,而不需要在 **异步函数(Async functions)**使用 Promise API 作为其构建模块,因此,即使在较新的代码中,你可能会使用异步函数而不是 Promise,理解它们也是基本的。 -#### Promises是如何工作的 +#### Promises 是如何工作的 一旦一个 Promises 被调用,它将开始处于**待定状态**。这意味着调用者函数继续执行,同时等待 Promises 做它自己的处理,并给调用者函数一些反馈。 -在这一点上,调用者函数等待它在**解决的状态(resolved state)**下返回promises,或者在**拒绝的状态(rejected state)**下返回 Promises,但是你知道 JavaScript 是异步的,所以函数继续执行,而承诺在做它的工作。 +在这一点上,调用者函数等待它在**解决的状态(resolved state)**下返回 promises,或者在**拒绝的状态(rejected state)**下返回 Promises,但是你知道 JavaScript 是异步的,所以函数继续执行,而承诺在做它的工作。 #### 哪些 JS API 使用 Promises? @@ -2573,7 +2573,7 @@ const isItDoneYet = new Promise( (resolve, reject) => { 在上一节中,我们介绍了如何创建一个 Promise。 -现在让我们来看看promise如何被**消费**或使用。 +现在让我们来看看 promise 如何被**消费**或使用。 ```js const isItDoneYet = new Promise()//... @@ -2592,9 +2592,9 @@ const checkIfItsDone = () => { [Fetch API][100] 给出了一个很好的 Promise 链的例子,它是 `XMLHttpRequest` API 上面的一层,我们可以用它来获取一个资源,并在获取资源的时候排队执行一连串的 Promise。 -Fetch API是一个基于promise的机制,调用 `fetch()` 等同于使用 `new Promise()` 定义我们自己的 Promise。 +Fetch API 是一个基于 promise 的机制,调用 `fetch()` 等同于使用 `new Promise()` 定义我们自己的 Promise。 -#### Promises链的例子 +#### Promises 链的例子 ```js const status = (response) => { @@ -2616,12 +2616,12 @@ fetch('/todos.json') 运行 `fetch()` 返回一个 [response][101],它有许多属性,在这些属性中我们引用了: -- `status`, 一个代表HTTP状态代码的数字值 +- `status`, 一个代表 HTTP 状态代码的数字值 - `statusText`, 一个状态信息,如果请求成功,就是 `OK` `response` 也有一个 `json()` 方法,它返回一个 Promise,该 Promise 将对 body 的内容进行处理并转化为 JSON。 -所以在这些前提下,会发生这样的事情:链中的第一个 Promise是我们定义的一个函数,叫做`status()`,它检查响应状态,如果不是一个成功的响应(在200和299之间),它拒绝这个 Promise(rejects the promise)。 +所以在这些前提下,会发生这样的事情:链中的第一个 Promise 是我们定义的一个函数,叫做`status()`,它检查响应状态,如果不是一个成功的响应(在 200 和 299 之间),它拒绝这个 Promise(rejects the promise)。 这个操作将导致承诺链跳过所有列出的链式 Promise,直接跳到底部的 `catch()` 语句,记录 `Request failed` 文本和错误信息。 @@ -2684,7 +2684,7 @@ Promise.all([f1, f2]) .catch((err) => {console.error(err)}) ``` -[ES2015的析构赋值][102] 语法允许你也这样做: +[ES2015 的析构赋值][102] 语法允许你也这样做: ```js Promise.all([f1, f2]) @@ -2723,7 +2723,7 @@ Promise.race([first, second]).then((result) => { 探索 JavaScript 中异步函数的现代方法。 -JavaScript在很短的时间内从回调(callback)发展到了 Promise(ES2015),而从ES2017开始,异步 JavaScript 通过 async/await 语法变得更加简单。 +JavaScript 在很短的时间内从回调(callback)发展到了 Promise(ES2015),而从 ES2017 开始,异步 JavaScript 通过 async/await 语法变得更加简单。 异步函数是 Promises 和生成器(generators)的结合,基本上,它们是 Promises 的更高层次的抽象。让我重复一遍:`async/await` 是建立在 Promises 之上的。 @@ -2793,7 +2793,7 @@ I did something //after 3s 在任何函数前加上 `async` 关键字意味着该函数将返回一个 promise。 -即使它没有明确地这样做,也会在内部使其返回一个promise。 +即使它没有明确地这样做,也会在内部使其返回一个 promise。 这就是为什么这段代码是有效的: @@ -2819,7 +2819,7 @@ aFunction().then(alert) // This will alert 'test' 而这只是一个非常简单的例子,主要的好处将出现在代码更复杂的时候。 -例如,这里是你如何获得一个JSON资源并解析它,使用 Promises: +例如,这里是你如何获得一个 JSON 资源并解析它,使用 Promises: ```js const getFirstUserData = () => { @@ -2890,7 +2890,7 @@ I did something and I watched and I watched as well 如果你在浏览器中使用 JavaScript,你就知道用户的大部分交互是通过事件处理的:鼠标点击、键盘按键、对鼠标移动的反应等等。 -在后端,Node.js为我们提供了使用 [events模块][103] 建立类似系统的选择。 +在后端,Node.js 为我们提供了使用 [events 模块][103] 建立类似系统的选择。 这个模块特别提供了 `EventEmitter` 类,我们将用它来处理我们的事件。 @@ -2945,7 +2945,7 @@ EventEmitter 对象还公开了其他几个与事件互动的方法,比如: - `removeListener()` / `off()`: 从一个事件中删除一个事件监听器 - `removeAllListeners()`: 删除一个事件的所有监听器。 -### HTTP请求如何工作 +### HTTP 请求如何工作 当你在浏览器中输入一个 URL 时,从开始到结束会发生什么? @@ -2961,19 +2961,19 @@ EventEmitter 对象还公开了其他几个与事件互动的方法,比如: 这是很少变化的技术,它为人类有史以来最复杂、最广泛的生态系统之一提供动力。 -### HTTP协议 +### HTTP 协议 我只分析 URL 请求。 -现代浏览器有能力知道你在地址栏中写的东西是一个实际的URL还是一个搜索词,如果它不是一个有效的URL,它们将使用默认的搜索引擎。 +现代浏览器有能力知道你在地址栏中写的东西是一个实际的 URL 还是一个搜索词,如果它不是一个有效的 URL,它们将使用默认的搜索引擎。 我假设你输入了一个实际的 URL。 -当你输入URL并按下回车键时,浏览器首先建立完整的URL。 +当你输入 URL 并按下回车键时,浏览器首先建立完整的 URL。 #### 与 MacOS/Linux 有关的事情 -仅供参考。Windows可能会对一些事情的处理方式略有不同。 +仅供参考。Windows 可能会对一些事情的处理方式略有不同。 #### DNS 查询阶段 @@ -2983,19 +2983,19 @@ EventEmitter 对象还公开了其他几个与事件互动的方法,比如: 首先,它检查 DNS 的本地缓存,看这个域名最近是否已经被解析。 -**_Chrome 有一个方便的DNS缓存可视化工具,你可以在这个网址上看到:chrome://net-internals/#dns(复制并粘贴到Chrome浏览器地址栏)_** +**_Chrome 有一个方便的 DNS 缓存可视化工具,你可以在这个网址上看到:chrome://net-internals/#dns(复制并粘贴到 Chrome 浏览器地址栏)_** -如果没有找到,浏览器就使用DNS解析器,使用 `gethostbyname` POSIX 系统调用来检索主机信息。 +如果没有找到,浏览器就使用 DNS 解析器,使用 `gethostbyname` POSIX 系统调用来检索主机信息。 #### gethostbyname `gethostbyname`:浏览器首先查找本地主机文件,在 macOS 或 Linux 上,该文件位于 `/etc/hosts`,以查看系统是否在本地提供了该信息。 -如果这没有提供任何关于域名的信息,系统会向DNS服务器发出请求。 +如果这没有提供任何关于域名的信息,系统会向 DNS 服务器发出请求。 -DNS服务器的地址存储在系统偏好中。 +DNS 服务器的地址存储在系统偏好中。 -这些是2个流行的 DNS 服务器: +这些是 2 个流行的 DNS 服务器: - `8.8.8.8`: 谷歌公共 DNS 服务器 - `1.1.1.1`: CloudFlare DNS 服务器 @@ -3036,7 +3036,7 @@ TLD DNS 服务器将拥有我们正在寻找的域名的权威性域名服务器 DNS 解析器从第一个开始,试图询问你要找的域名(也包括子域名)的 IP。 -这就是IP地址的最终真实来源。 +这就是 IP 地址的最终真实来源。 现在我们有了 IP 地址,我们可以继续我们的旅程了。 @@ -3052,7 +3052,7 @@ TCP 连接在完全初始化之前需要进行一些握手,然后就可以开 请求是一个纯文本文件,以通信协议确定的精确方式结构化。 -它由3个部分组成: +它由 3 个部分组成: - 请求行 - 请求头 @@ -3156,7 +3156,7 @@ server.listen(port, () => { })s ``` -让我们简单地分析一下。我们包括 [http模块][104]。 +让我们简单地分析一下。我们包括 [http 模块][104]。 我们使用该模块来创建一个 HTTP 服务器。 @@ -3366,7 +3366,7 @@ const countBreeds = async () => { countBreeds() ``` -#### 在GET请求中添加参数 +#### 在 GET 请求中添加参数 一个 GET 响应可以在 URL 中包含参数,像这样 [https://site.com/?foo=bar][111]。 @@ -3408,7 +3408,7 @@ WebSockets 是网络应用中 HTTP 通信的替代方案。 所有现代浏览器都支持 WebSockets。 -### WebSockets与HTTP有什么不同 +### WebSockets 与 HTTP 有什么不同 HTTP 是一个非常不同的协议,并且有不同的通信方式。 @@ -3915,7 +3915,7 @@ const fs = require('fs') 一旦你这样做,你就可以使用它的所有方法,其中包括: -- `fs.access()`: 检查文件是否存在,并且Node可以用其权限访问它。 +- `fs.access()`: 检查文件是否存在,并且 Node 可以用其权限访问它。 - `fs.appendFile()`: 将数据追加到文件中。如果文件不存在,就创建它 - `fs.chmod()`: 改变一个由文件名指定的文件的权限。相关的: `fs.lchmod()`, `fs.fchmod()` - `fs.chown()`: 改变由文件名指定的文件的所有者和组。相关的: `fs.fchown()`, `fs.lchown()` @@ -3953,7 +3953,7 @@ const fs = require('fs') 这对你的应用流程有很大的影响。 -Node 10 包括对基于 Promise 的API的 [实验性支持][117]。 +Node 10 包括对基于 Promise 的 API 的 [实验性支持][117]。 例如,让我们检查 `fs.rename()` 方法。异步 API 是用一个回调来实现的: @@ -3989,7 +3989,7 @@ try { const path = require('path') ``` -这个模块提供了 `path.sep`,它提供了路径段的分隔符(在 Windows下为 `/`,在 Linux/MacOS下为 `/`),以及 `path.delimiter`,它提供了路径分隔符(在 Windows 下为`;`,在 Linux/MacOS下为`:`)。 +这个模块提供了 `path.sep`,它提供了路径段的分隔符(在 Windows 下为 `/`,在 Linux/MacOS 下为 `/`),以及 `path.delimiter`,它提供了路径分隔符(在 Windows 下为`;`,在 Linux/MacOS 下为`:`)。 这些是 `路径` 方法。 @@ -4110,7 +4110,7 @@ const os = require('os') 有几个有用的属性告诉我们一些与处理文件有关的关键事情: -`os.EOL` 给出了行的定界符序列。在 Linux和MacOS 上是 `\n`,而在 Windows 上是 `\r\n`。 +`os.EOL` 给出了行的定界符序列。在 Linux 和 MacOS 上是 `\n`,而在 Windows 上是 `\r\n`。 当我说 Linux 和 MacOS 时,我指的是 POSIX 平台。为了简单起见,我排除了其他不太流行的操作系统,Node 可以在上面运行。 @@ -4338,7 +4338,7 @@ const door = new EventEmitter() #### `emitter.eventNames()` -返回一个字符串数组,代表在当前EventListener上注册的事件: +返回一个字符串数组,代表在当前 EventListener 上注册的事件: ```js door.eventNames() @@ -4448,7 +4448,7 @@ const http = require('http') #### `http.METHODS` -此属性列出了所有支持的HTTP方法: +此属性列出了所有支持的 HTTP 方法: ```js > require('http').METHODS @@ -4457,7 +4457,7 @@ const http = require('http') #### `http.STATUS_CODES` -此属性列出了所有的HTTP状态代码及其描述: +此属性列出了所有的 HTTP 状态代码及其描述: ```js > require('http').STATUS_CODES @@ -4492,7 +4492,7 @@ const server = http.createServer((req, res) => {})//handle every single request #### Classes -HTTP模块提供了5个类(classes): +HTTP 模块提供了 5 个类(classes): - `http.Agent` - `http.ClientRequest` @@ -4502,7 +4502,7 @@ HTTP模块提供了5个类(classes): #### `http.Agent` -Node创建了一个 `http.Agent` 类的全局实例来管理HTTP客户端的连接持久性和重复使用,这是 Node HTTP 网络的一个关键组成部分。 +Node 创建了一个 `http.Agent` 类的全局实例来管理 HTTP 客户端的连接持久性和重复使用,这是 Node HTTP 网络的一个关键组成部分。 这个对象确保每一个向服务器发出的请求都是排队的,并且一个套接字被重复使用。 @@ -4526,7 +4526,7 @@ Node创建了一个 `http.Agent` 类的全局实例来管理HTTP客户端的连 一旦你有了一个服务器对象,你就可以访问它的方法: - `close()` 停止服务器接受新的连接 -- `listen()` 启动HTTP服务器并监听连接 +- `listen()` 启动 HTTP 服务器并监听连接 #### `http.ServerResponse` @@ -4579,7 +4579,7 @@ response.statusMessage = 'Internal Server Error' 由于`http.IncomingMessage` 实现了可读流接口,所以数据可以使用流访问。 -### Node.js流(Streams) +### Node.js 流(Streams) 流是支持 Node.js 应用程序的基本概念之一。 @@ -4591,7 +4591,7 @@ response.statusMessage = 'Internal Server Error' 使用流,你会一块一块地读取它,处理它的内容,而不把它全部保留在内存中。 -Node.js的 [stream模块][121] 提供了所有流媒体API的基础。 +Node.js 的 [stream 模块][121] 提供了所有流媒体 API 的基础。 #### 为什么是流? @@ -4604,7 +4604,7 @@ Node.js的 [stream模块][121] 提供了所有流媒体API的基础。 一个典型的例子是从磁盘上读取文件的例子。 -使用Node.js `fs` 模块,你可以读取一个文件,并在与你的 `http` 服务器建立新的连接时通过 HTTP 提供服务: +使用 Node.js `fs` 模块,你可以读取一个文件,并在与你的 `http` 服务器建立新的连接时通过 HTTP 提供服务: ```js const http = require('http') @@ -4783,7 +4783,7 @@ Node.js 生态系统有几个不同的包,允许你与 MySQL 接口,存储 我们将使用 [mysqljs/mysql][122],这个包在 GitHub 上有超过 12,000 颗星,已经存在多年。 -#### 安装Node.js MySql包 +#### 安装 Node.js MySql 包 安装命令: @@ -4837,11 +4837,11 @@ const options = { 你还可以使用很多,包括: - `host`, 数据库主机名,默认为 `localhost` -- `port`, MySQL服务器端口号,默认为 3306 +- `port`, MySQL 服务器端口号,默认为 3306 - `socketPath`, 用于指定 Unix 套接字,而不是主机和端口 - `debug`, 默认为禁用,可用于调试 - `trace`, 默认为启用,当发生错误时打印堆栈跟踪 -- `ssl`, 用于设置与服务器的SSL连接(不在本教程范围内) +- `ssl`, 用于设置与服务器的 SSL 连接(不在本教程范围内) #### 执行一个 SELECT 查询 @@ -4883,7 +4883,7 @@ connection.query('SELECT * FROM todos WHERE id = ? AND author = ?', [id, author] }) ``` -#### 执行一个INSERT 语句 +#### 执行一个 INSERT 语句 你可以传递一个对象: @@ -4925,7 +4925,7 @@ connection.end() 你可以为生产和开发环境进行不同的配置。 -Node.js假定它总是在开发环境中运行。你可以通过设置 `NODE_ENV=production` 环境变量向 Node.js 发出信号,表明你正在生产环境中运行。 +Node.js 假定它总是在开发环境中运行。你可以通过设置 `NODE_ENV=production` 环境变量向 Node.js 发出信号,表明你正在生产环境中运行。 这通常是通过执行以下命令来完成的: @@ -4948,9 +4948,9 @@ NODE_ENV=production node app.js - 日志记录保持在最小的、必要的水平上 - 更多的缓存级别,以优化性能 -例如 [Pug][123],Express使用的模板库,如果 `NODE_ENV` 没有设置为 `production`,则在开发(development)模式下进行编译。在开发模式下,Express 视图在每个请求中都被编译,而在生产(production)模式下,它们被缓存起来。还有很多例子。 +例如 [Pug][123],Express 使用的模板库,如果 `NODE_ENV` 没有设置为 `production`,则在开发(development)模式下进行编译。在开发模式下,Express 视图在每个请求中都被编译,而在生产(production)模式下,它们被缓存起来。还有很多例子。 -Express提供了特定于环境的配置钩子,这些钩子根据`NODE_ENV`变量值自动调用: +Express 提供了特定于环境的配置钩子,这些钩子根据`NODE_ENV`变量值自动调用: ```js app.configure('development', () => {})//... diff --git a/chinese/articles/the-difference-between-a-framework-and-a-library.md b/chinese/articles/the-difference-between-a-framework-and-a-library.md index ae57bb8f1..dd24a7d56 100644 --- a/chinese/articles/the-difference-between-a-framework-and-a-library.md +++ b/chinese/articles/the-difference-between-a-framework-and-a-library.md @@ -9,7 +9,7 @@ 框架和库都是由其他人编写的代码,用于帮助解决常见问题。 -例如,假设你有一个程序,你打算处理字符串。你决定保持你的代码DRY(不要重复自己),并写一些像这样的可重复使用的函数: +例如,假设你有一个程序,你打算处理字符串。你决定保持你的代码 DRY(不要重复自己),并写一些像这样的可重复使用的函数: ```js function getWords(str) { @@ -38,7 +38,7 @@ function createSentence(words) { 当你使用一个库时,你负责应用程序的流程。你要选择何时何地调用库。当你使用一个框架时,框架负责程序的流程。它提供了一些地方让你插入你的代码,但是它根据需要调用你插入的代码。 -让我们看一个使用jQuery(一个库)和Vue.js(一个框架)的例子。 +让我们看一个使用 jQuery(一个库)和 Vue.js(一个框架)的例子。 想象一下,我们想在出现错误时显示一个错误信息。在我们的例子中,我们将点击一个按钮,并假装发生了错误。 @@ -74,9 +74,9 @@ $('#myButton').on('click', () => { }); ``` -注意我们如何使用jQuery。我们告诉我们的程序我们想在哪里调用它。这很像去一个实体图书馆,从书架上抽出某些我们想要的书。 +注意我们如何使用 jQuery。我们告诉我们的程序我们想在哪里调用它。这很像去一个实体图书馆,从书架上抽出某些我们想要的书。 -这并不是说jQuery的函数不需要某些输入,当我们调用它们,但jQuery本身就是这些函数的一个库。我们是负责的。 +这并不是说 jQuery 的函数不需要某些输入,当我们调用它们,但 jQuery 本身就是这些函数的一个库。我们是负责的。 #### 使用 Vue.js @@ -109,7 +109,7 @@ const vm = new Vue({ }); ``` -有了Vue,我们就得填空了。Vue的构造函数是一个具有某些属性的对象。它告诉我们它需要什么,然后在幕后,Vue决定何时需要它。Vue 主导了对程序的控制。我们把我们的代码插入Vue中。Vue是负责的。 +有了 Vue,我们就得填空了。Vue 的构造函数是一个具有某些属性的对象。它告诉我们它需要什么,然后在幕后,Vue 决定何时需要它。Vue 主导了对程序的控制。我们把我们的代码插入 Vue 中。Vue 是负责的。 无论是库还是框架,其区别在于是否存在控制反转。 @@ -119,7 +119,7 @@ const vm = new Vue({ 框架比非框架更有约束,因为--根据定义--控制反转,对应用设计的自由度会变小。 -同样,某件事的程度是主观的。例如,我个人认为Angular是一个高度有约束的框架,而Vue.js是一个不太有约束的框架。 +同样,某件事的程度是主观的。例如,我个人认为 Angular 是一个高度有约束的框架,而 Vue.js 是一个不太有约束的框架。 ### 总结 diff --git a/chinese/articles/the-javascript-array-handbook.md b/chinese/articles/the-javascript-array-handbook.md index 5d97a8444..34b9e5e2c 100644 --- a/chinese/articles/the-javascript-array-handbook.md +++ b/chinese/articles/the-javascript-array-handbook.md @@ -835,7 +835,7 @@ junkFoodILove.at(10); // undefined 在 `at()` 方法加入 JavaScript 语言之前,你可以使用这个 [polyfill](https://github.com/es-shims/Array.prototype.at) 来获得它的功能。查看这个 GitHub 仓库以获取 `at()` 方法的示例:[https://github.com/atapas/js-array-at-method](https://github.com/atapas/js-array-at-method)。 -# 结束之前... +# 结束之前 希望你觉得这篇文章有价值,也希望它能够帮助你更好地理解 JavaScript 数组。请多多练习文中的示例,以便更好地掌握它们。你可以在[我的 GitHub 仓库](https://github.com/atapas/js-handbook-examples#%EF%B8%8F-list-of-content)中找到所有代码示例。 diff --git a/chinese/articles/the-linux-commands-handbook.md b/chinese/articles/the-linux-commands-handbook.md index 557da610c..94d3b8ae3 100644 --- a/chinese/articles/the-linux-commands-handbook.md +++ b/chinese/articles/the-linux-commands-handbook.md @@ -1179,7 +1179,7 @@ chmod og-r filename #其他人,和用户组无法读取文件。 如果你正在编辑一个文件夹,你可以使用 `-r`(递归)参数将权限应用到该文件夹中的每个文件。 -使用数字参数速度更快,但我认为当你不是每天都使用的话,是很难记住它们的。数字在此代表任一角色的权限。这个数字值最大可以是7,它是这样计算的: +使用数字参数速度更快,但我认为当你不是每天都使用的话,是很难记住它们的。数字在此代表任一角色的权限。这个数字值最大可以是 7,它是这样计算的: - 拥有执行权限,记为 `1` - 拥有写入权限,记为 `2` @@ -1226,7 +1226,7 @@ chmod 644 filename 和所有者位于同一用户组的用户(`g`)拥有对文件的读取和执行权限,除此之外的其他用户(`o`)也一样。 -在数字符号中,我们通常会改变最后3位数字。 +在数字符号中,我们通常会改变最后 3 位数字。 以下列表给出了这些数字的含义: @@ -1619,7 +1619,7 @@ cat todelete.txt | xargs rm ![Screen-Shot-2020-09-08-at-07.46.39](https://www.freecodecamp.org/news/content/images/2020/10/Screen-Shot-2020-09-08-at-07.46.39.png) -它的工作方式是:`xargs` 会运行 `rm` 2次,为 `cat` 返回的每一行运行一次。 +它的工作方式是:`xargs` 会运行 `rm` 2 次,为 `cat` 返回的每一行运行一次。 这是 `xargs` 最简单的用法。我们还可以使用以下的一些参数。 diff --git a/chinese/articles/the-most-important-skill-a-programmer-can-learn.md b/chinese/articles/the-most-important-skill-a-programmer-can-learn.md index 2504e86ed..de33a3de0 100644 --- a/chinese/articles/the-most-important-skill-a-programmer-can-learn.md +++ b/chinese/articles/the-most-important-skill-a-programmer-can-learn.md @@ -89,7 +89,7 @@ 这就是如果你一直对所有事情都说 "是" 会发生什么。清楚地知道什么时候不需要编码。从你的项目中消除所有不必要的代码。这将使你的生活更轻松,使你的软件更持久。 -> 我最有成就感的一天是删除了1000行的代码。 — Ken Thompson +> 我最有成就感的一天是删除了 1000 行的代码。 — Ken Thompson 我知道,知道什么时候不写代码是如此困难。即使对高级程序员来说也是如此。也许我在这篇文章中写的内容对初级程序员来说很难理解,这也没关系,可以理解。 diff --git a/chinese/articles/the-nginx-handbook.md b/chinese/articles/the-nginx-handbook.md index a1a5b938d..beafb4cc7 100644 --- a/chinese/articles/the-nginx-handbook.md +++ b/chinese/articles/the-nginx-handbook.md @@ -13,7 +13,7 @@ ![](https://www.freecodecamp.org/news/content/images/2021/04/177962736_1410222585999736_5618677227291897851_n.jpg) -相信我,搞懂它并不难...... +相信我,搞懂它并不难…… 我并不是说复制代码不好,但在千万不要在不理解的情况下复制代码。 @@ -29,7 +29,7 @@ ## 先决条件 -- 熟悉Linux终端和常用Unix程序,如 `ls`、`cat`、`ps`、`grep`、`find`、`nproc`、`ulimit` 和 `nano`。 +- 熟悉 Linux 终端和常用 Unix 程序,如 `ls`、`cat`、`ps`、`grep`、`find`、`nproc`、`ulimit` 和 `nano`。 - 一台可以以运行虚拟机的计算机或 5 美元的虚拟专用服务器。 - 了解 Web 应用程序和编程语言,如 JavaScript 或 PHP。 @@ -1034,7 +1034,7 @@ curl -i http://nginx-handbook.test/about_page# HTTP/1.1 200 OK# Server: nginx/1. events {}http { include /etc/nginx/mime.types; server { listen 80; server_name nginx-handbook.test; root /srv/nginx-handbook-projects/static-demo; try_files /the-nginx-handbook.jpg /not_found; location /not_found { return 404 "sadly, you've hit a brick wall buddy!\n"; } }} ``` -如你所见,添加了一个新的 `try_files` 指令。 通过编写`try_files /the-nginx-handbook.jpg /not_found;`,可以指示NGINX 在收到请求时在根目录中查找名为 the-nginx-handbook.jpg 的文件。如果它不存在,则转到 `/not_found` 位置。 +如你所见,添加了一个新的 `try_files` 指令。 通过编写`try_files /the-nginx-handbook.jpg /not_found;`,可以指示 NGINX 在收到请求时在根目录中查找名为 the-nginx-handbook.jpg 的文件。如果它不存在,则转到 `/not_found` 位置。 所以现在如果你访问服务器,你会看到图像: @@ -1294,7 +1294,7 @@ To use FastCGI instead of HTTP, update your configuration as follows: events {}http { include /etc/nginx/mime.types; server { listen 80; server_name nginx-handbook.test; root /srv/nginx-handbook-projects/php-demo; index index.php; location / { try_files $uri $uri/ =404; } location ~ \.php$ { fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; } }} ``` -让我们从新的 `index` 指令开始。 如你所知,NGINX 默认会查找 index.html 文件来提供服务。但在演示项目中,它是 index.php。 因此,编写 `index index.php`,指示NGINX 以 root 用户身份使用 index.php 文件。 +让我们从新的 `index` 指令开始。 如你所知,NGINX 默认会查找 index.html 文件来提供服务。但在演示项目中,它是 index.php。 因此,编写 `index index.php`,指示 NGINX 以 root 用户身份使用 index.php 文件。 该指令可以接受多个参数。对于 `index index.php index.html`,NGINX 会首先寻找 index.php。如果它没有找到这个文件,它会寻找 index.html 文件。 @@ -1783,6 +1783,6 @@ sudo nginx -t# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok# 我衷心感谢您花时间阅读本文。 我希望你享受你的学习时间并学习了 NGINX 的所有基本知识。 -如果你喜欢我的作品,你可以在 [https://www.freecodecamp.org/news/author/farhanhasin/](/news/author/farhanhasin/) 上找到我的其他书籍,个人博客 [ https://www.farhan.info/](https://www.farhan.info/blogs/) 同步更新。 +如果你喜欢我的作品,你可以在 [https://www.freecodecamp.org/news/author/farhanhasin/](/news/author/farhanhasin/) 上找到我的其他书籍,个人博客 [https://www.farhan.info/](https://www.farhan.info/blogs/) 同步更新。 你可以在 Twitter 上关注我 [@frhnhsin](https://twitter.com/frhnhsin) 或在 LinkedIn 上与我联系 [/in/farhanhasin](https://www.linkedin.com/in/farhanhasin/) 。 diff --git a/chinese/articles/the-php-handbook.md b/chinese/articles/the-php-handbook.md index 27fa40637..012c67b7d 100644 --- a/chinese/articles/the-php-handbook.md +++ b/chinese/articles/the-php-handbook.md @@ -668,7 +668,7 @@ As with strings and numbers, PHP provides lots of very useful functions for arra ### How to Use Associative Arrays in PHP -So far we’ve used arrays with an incremental, numeric index: 0, 1, 2… +So far we’ve used arrays with an incremental, numeric index: 0, 1, 2…… You can also use arrays with named indexes (keys), and we call them associative arrays: @@ -2410,7 +2410,7 @@ Here’s an example of a JSON representation of an object that contains a string PHP offers us two utility functions to work with JSON: - `json_encode()` to encode a variable into JSON -- `json_decode()` to decode a JSON string into a data type (object, array…) +- `json_decode()` to decode a JSON string into a data type (object, array……) Example: diff --git a/chinese/articles/the-python-handbook.md b/chinese/articles/the-python-handbook.md index bca30d73d..594918aa2 100644 --- a/chinese/articles/the-python-handbook.md +++ b/chinese/articles/the-python-handbook.md @@ -5,109 +5,109 @@ ![The Python Handbook](https://www.freecodecamp.org/news/content/images/size/w2000/2021/03/book.png) -这本Python编程手册遵循“80/20定律”:使用20%的时间学习80%的内容。 +这本 Python 编程手册遵循“80/20 定律”:使用 20%的时间学习 80%的内容。 -我认为这种方法可以为学习者提供一个对Python全面的了解。 +我认为这种方法可以为学习者提供一个对 Python 全面的了解。 -本手册并没有涵盖与Python相关的全部内容。它专注于这门编程语言的核心主题,并且试图简化那些复杂的内容。 +本手册并没有涵盖与 Python 相关的全部内容。它专注于这门编程语言的核心主题,并且试图简化那些复杂的内容。 -我希望这本手册可以帮助您实现:**学习Python的基础** +我希望这本手册可以帮助您实现:**学习 Python 的基础** -> Note: 您可以获取这本手册的[PDF、ePub或者Mobi版本](https://flaviocopes.com/page/python-handbook/) +> Note: 您可以获取这本手册的[PDF、ePub 或者 Mobi 版本](https://flaviocopes.com/page/python-handbook/) Enjoy it! <h2 id="summary">目录</h2> -- [Python介绍](#introduction-to-python) -- [如何安装Python](#how-to-install-python) -- [如何运行Python程序](#how-to-run-python-programs) +- [Python 介绍](#introduction-to-python) +- [如何安装 Python](#how-to-install-python) +- [如何运行 Python 程序](#how-to-run-python-programs) - [Python 2 vs Python 3](#python2-vs-python3) -- [Python基础](#python-basics) -- [Python数据类型](#data-types-in-python) -- [Python运算符](#operators) -- [Python三元运算符](#the-ternary-operator-in-python) -- [Python字符串](#strings-in-python) -- [Python布尔值](#booleans-in-python) -- [Python数字](#numbers-in-python) -- [Python常量](#constants-in-python) -- [Python枚举](#enums-in-python) -- [Python用户输入](#user-input-in-python) -- [Python控制语句](#control-statements-in-python) -- [Python列表](#lists-in-python) -- [Python元组](#tuples-in-python) -- [Python字典](#dictionaries-in-python) -- [Python集合](#sets-in-python) -- [Python函数](#functions-in-python) -- [Pytho对象](#objects-in-python) -- [Python循环](#loops-in-python) -- [Python类](#classes-in-python) -- [Python模块](#modules-in-python) -- [Python标准库](#the-python-standard-library) -- [Python PEP8风格指导](#the-pep8-python-style-guide) -- [Python代码调试](#debugging-in-python) -- [Python变量作用域](#variable-scope-in-python) -- [Python接收从命令行传入的参数](#how-to-accept-arguments-from-the-command-line-in-python) -- [Python的Lambda函数](#lambda-functions-in-python) -- [Python递归](#recursion-in-python) -- [Python嵌套函数](#nested-functions-in-python) -- [Python闭包](#closures-in-python) -- [Python装饰器](#decorators-in-python) -- [Python文档字符串](#docstrings-in-python) -- [Python反射](#introspection-in-python) -- [Python注解](#annotations-in-python) -- [Python异常](#exceptions-in-python) -- [Python中with语句](#the-with-statement-in-python) -- [Python如何使用pip安装第三方包](#how-to-install-3rd-party-packages-in-python-using-pip) -- [Python列表推导式](#list-comprehensions-in-python) -- [Python多态](#polymorphism-in-python) -- [Python运算符重载](#operator-overloading-in-python) -- [Python虚拟环境](#virtual-environments-in-python) +- [Python 基础](#python-basics) +- [Python 数据类型](#data-types-in-python) +- [Python 运算符](#operators) +- [Python 三元运算符](#the-ternary-operator-in-python) +- [Python 字符串](#strings-in-python) +- [Python 布尔值](#booleans-in-python) +- [Python 数字](#numbers-in-python) +- [Python 常量](#constants-in-python) +- [Python 枚举](#enums-in-python) +- [Python 用户输入](#user-input-in-python) +- [Python 控制语句](#control-statements-in-python) +- [Python 列表](#lists-in-python) +- [Python 元组](#tuples-in-python) +- [Python 字典](#dictionaries-in-python) +- [Python 集合](#sets-in-python) +- [Python 函数](#functions-in-python) +- [Pytho 对象](#objects-in-python) +- [Python 循环](#loops-in-python) +- [Python 类](#classes-in-python) +- [Python 模块](#modules-in-python) +- [Python 标准库](#the-python-standard-library) +- [Python PEP8 风格指导](#the-pep8-python-style-guide) +- [Python 代码调试](#debugging-in-python) +- [Python 变量作用域](#variable-scope-in-python) +- [Python 接收从命令行传入的参数](#how-to-accept-arguments-from-the-command-line-in-python) +- [Python 的 Lambda 函数](#lambda-functions-in-python) +- [Python 递归](#recursion-in-python) +- [Python 嵌套函数](#nested-functions-in-python) +- [Python 闭包](#closures-in-python) +- [Python 装饰器](#decorators-in-python) +- [Python 文档字符串](#docstrings-in-python) +- [Python 反射](#introspection-in-python) +- [Python 注解](#annotations-in-python) +- [Python 异常](#exceptions-in-python) +- [Python 中 with 语句](#the-with-statement-in-python) +- [Python 如何使用 pip 安装第三方包](#how-to-install-3rd-party-packages-in-python-using-pip) +- [Python 列表推导式](#list-comprehensions-in-python) +- [Python 多态](#polymorphism-in-python) +- [Python 运算符重载](#operator-overloading-in-python) +- [Python 虚拟环境](#virtual-environments-in-python) - [总结](#conclusion) <h2 id="introduction-to-python">Python介绍</h2> -Python正在逐步“占领”编程世界。它的受欢迎度和使用度正在以计算机历史中前所未有的方式实现增长。 +Python 正在逐步“占领”编程世界。它的受欢迎度和使用度正在以计算机历史中前所未有的方式实现增长。 -Python在各种应用场景下都表现出色——**Shell 脚本**、**自动化的任务**和**Web 开发**只是其基本的应用。 +Python 在各种应用场景下都表现出色——**Shell 脚本**、**自动化的任务**和**Web 开发**只是其基本的应用。 -Python是做**数据分析**和**机器学习**的首选语言,但是它也可以用来做游戏或者在嵌入式设备上工作。 +Python 是做**数据分析**和**机器学习**的首选语言,但是它也可以用来做游戏或者在嵌入式设备上工作。 -最重要的是,Python是世界上多所大学介绍**计算机科学课程**时选择的编程语言。 +最重要的是,Python 是世界上多所大学介绍**计算机科学课程**时选择的编程语言。 -许多学生选择Python作为自己的第一门编程语言来学习。很多人正在学习Python,将来还会有更多人学习它。并且对于学习者中的大部分人来说,Python将是他们唯一需要的编程语言。 +许多学生选择 Python 作为自己的第一门编程语言来学习。很多人正在学习 Python,将来还会有更多人学习它。并且对于学习者中的大部分人来说,Python 将是他们唯一需要的编程语言。 -基于其独特的情况,Python在未来很有可能会更快地增长。 +基于其独特的情况,Python 在未来很有可能会更快地增长。 -Python这门编程语言的特点是简单易上手,表示丰富,非常直接,易于理解。 +Python 这门编程语言的特点是简单易上手,表示丰富,非常直接,易于理解。 -Python的生态系统非常庞大,可能需要一个图书馆才能容纳你所想象到的一切。 +Python 的生态系统非常庞大,可能需要一个图书馆才能容纳你所想象到的一切。 -因为其直观的语法、庞大的社区和充满活力的生态系统,Python是一门适合编程初学者的高级编程语言。 +因为其直观的语法、庞大的社区和充满活力的生态系统,Python 是一门适合编程初学者的高级编程语言。 -Python也受到不同领域的专家赞赏。 +Python 也受到不同领域的专家赞赏。 -从技术上讲,Python是一种解释型语言,它不像编译型语言(例如C或Java)那样具有中间编译阶段。 +从技术上讲,Python 是一种解释型语言,它不像编译型语言(例如 C 或 Java)那样具有中间编译阶段。 -和许多解释型语言一样,Python是动态类型的,这意味着您不必声明所使用的变量的类型,并且变量不必为特定类型。 +和许多解释型语言一样,Python 是动态类型的,这意味着您不必声明所使用的变量的类型,并且变量不必为特定类型。 这有利有弊。特别是,您编写程序的速度会更快,但另一方面,您从工具中获得防止出现可能错误的帮助会较少。这意味着您只有在执行程序时才能发现某些问题。 -Python支持多种编程范式,包括面向过程编程、面向对象编程和函数式编程。它足够灵活,可以适应不同的需求。 +Python 支持多种编程范式,包括面向过程编程、面向对象编程和函数式编程。它足够灵活,可以适应不同的需求。 -自从Python由Guido van Rossum于1991年创建后,它便越来越受欢迎——尤其是在过去5年中,正如这张Google趋势信息图所示: +自从 Python 由 Guido van Rossum 于 1991 年创建后,它便越来越受欢迎——尤其是在过去 5 年中,正如这张 Google 趋势信息图所示: ![Screen-Shot-2020-11-09-at-19.22.38](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-09-at-19.22.38.png) -开始Python编程非常容易。您只需从[python.org](https://www.python.org/)选择适用于Windows、macOS或Linux的官方软件包安装,然后就可以开始使用Python了。 +开始 Python 编程非常容易。您只需从[python.org](https://www.python.org/)选择适用于 Windows、macOS 或 Linux 的官方软件包安装,然后就可以开始使用 Python 了。 -如果您是编程新手,我将会在接下来的内容中引导您从零开始成为一名Python程序员。 +如果您是编程新手,我将会在接下来的内容中引导您从零开始成为一名 Python 程序员。 -即使您目前是一名专门研究另一种编程语言的程序员,Python也值得您了解,因为我认为它只会继续发展壮大。 +即使您目前是一名专门研究另一种编程语言的程序员,Python 也值得您了解,因为我认为它只会继续发展壮大。 -像C++和Rust这样相对于Python来说更“低级”的语言,对于专业程序员来说可能很棒,但它们从一开始就令人生畏,而且需要很长时间才能掌握。 +像 C++和 Rust 这样相对于 Python 来说更“低级”的语言,对于专业程序员来说可能很棒,但它们从一开始就令人生畏,而且需要很长时间才能掌握。 -另一方面,Python是一种适用于任何人——学生、使用Excel完成日常工作的人、科学家等等——的编程语言。 +另一方面,Python 是一种适用于任何人——学生、使用 Excel 完成日常工作的人、科学家等等——的编程语言。 **这是每个对编程感兴趣的人都应该首先学习的语言**。 @@ -117,13 +117,13 @@ Python支持多种编程范式,包括面向过程编程、面向对象编程 ![Screen-Shot-2020-11-09-at-13.57.36-1](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-09-at-13.57.36-1.png) -请确保遵循关于您电脑所用的操作系统的特定说明。在macOS上,您可以在[https://flaviocopes.com/python-installation-macos/](https://flaviocopes.com/python-installation-macos/) 上找到详细指南。 +请确保遵循关于您电脑所用的操作系统的特定说明。在 macOS 上,您可以在[https://flaviocopes.com/python-installation-macos/](https://flaviocopes.com/python-installation-macos/) 上找到详细指南。 <h2 id="how-to-run-python-programs">如何运行Python程序</h2> -您可以使用几种不同的方式来运行Python程序。 +您可以使用几种不同的方式来运行 Python 程序。 -特别地,使用交互式环境(输入Python代码后,便立即执行它),和将Python程序保存到文件中,然后再执行它,这二者之间存在区别。 +特别地,使用交互式环境(输入 Python 代码后,便立即执行它),和将 Python 程序保存到文件中,然后再执行它,这二者之间存在区别。 让我们从交互式环境开始。 @@ -131,9 +131,9 @@ Python支持多种编程范式,包括面向过程编程、面向对象编程 ![Screen-Shot-2020-11-10-at-13.44.07](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-13.44.07.png) -这是Python REPL(交互式解释器,即读取-评估-打印-循环)。 +这是 Python REPL(交互式解释器,即读取-评估-打印-循环)。 -注意`>>>`符号和之后的光标。,您可以在此处输入任何Python代码,然后按 `enter` 键运行它。 +注意`>>>`符号和之后的光标。,您可以在此处输入任何 Python 代码,然后按 `enter` 键运行它。 例如尝试定义一个新变量 @@ -149,19 +149,19 @@ print(name) ![Screen-Shot-2020-11-10-at-14.11.57](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.11.57.png) -> 请注意:在REPL中,您也可以只输入`name`,然后按 `enter` 键,您会看到`name`的值。但是在写到文件中的程序里,如果这样做,您将看不到任何输出——您需要使用 `print()` 代替这种写法。 +> 请注意:在 REPL 中,您也可以只输入`name`,然后按 `enter` 键,您会看到`name`的值。但是在写到文件中的程序里,如果这样做,您将看不到任何输出——您需要使用 `print()` 代替这种写法。 -您在此处编写的任何Python代码行都将立即执行。 +您在此处编写的任何 Python 代码行都将立即执行。 -输入`quit()`可以退出这个Python REPL。 +输入`quit()`可以退出这个 Python REPL。 -您可以使用Python自动安装的IDLE应用程序使用相同的交互式环境: +您可以使用 Python 自动安装的 IDLE 应用程序使用相同的交互式环境: ![Screen-Shot-2020-11-10-at-14.13.25](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.13.25.png) 这对您来说可能更方便,因为与使用终端相比,使用鼠标可以更轻松地四处移动和复制/粘贴。 -以上是Python默认附带的基础内容。不过我建议您安装[IPython](https://ipython.org/),它可能是您能找到的最好的Python命令行REPL应用程序。 +以上是 Python 默认附带的基础内容。不过我建议您安装[IPython](https://ipython.org/),它可能是您能找到的最好的 Python 命令行 REPL 应用程序。 使用下面的命令安装它 @@ -169,13 +169,13 @@ print(name) pip install ipython ``` -上面的命令需要确保pip可执行文件的路径在您的环境变量中,安装好之后运行`ipython`: +上面的命令需要确保 pip 可执行文件的路径在您的环境变量中,安装好之后运行`ipython`: ![Screen-Shot-2020-11-11-at-09.36.29](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-11-at-09.36.29.png) -`ipython`是另一个让您使用Python REPL的接口,并提供了一些不错的功能,如语法突出显示、代码完成以及等等。 +`ipython`是另一个让您使用 Python REPL 的接口,并提供了一些不错的功能,如语法突出显示、代码完成以及等等。 -运行Python程序的第二种方法是将Python程序代码写入文件,例如`program.py`: +运行 Python 程序的第二种方法是将 Python 程序代码写入文件,例如`program.py`: ![Screen-Shot-2020-11-10-at-14.01.24](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.01.24.png) @@ -183,15 +183,15 @@ pip install ipython ![Screen-Shot-2020-11-10-at-14.01.32](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.01.32.png) -> 请注意,我们约定使用`.py`扩展名保存Python程序文件。 +> 请注意,我们约定使用`.py`扩展名保存 Python 程序文件。 在这种情况下,程序作为一个整体被执行,而不是一次运行一行。而这就是我们运行程序的典型方式。 -我们使用REPL进行快速的代码原型设计和学习。 +我们使用 REPL 进行快速的代码原型设计和学习。 -在Linux和macOS上,也可以将Python程序文件转换为shell脚本,方法是在文件最前面加上一个特殊行,用来指示使用哪个可执行文件来运行它。 +在 Linux 和 macOS 上,也可以将 Python 程序文件转换为 shell 脚本,方法是在文件最前面加上一个特殊行,用来指示使用哪个可执行文件来运行它。 -在我的系统上,Python解释器的路径是`/usr/bin/python3`,所以我在第一行输入`#!/usr/bin/python3`: +在我的系统上,Python 解释器的路径是`/usr/bin/python3`,所以我在第一行输入`#!/usr/bin/python3`: ![Screen-Shot-2020-11-10-at-14.17.26](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.17.26.png) @@ -211,15 +211,15 @@ chmod u+x program.py 这在您编写与终端交互的脚本时特别有用。 -我们还有许多其它方式可以运行Python程序。 +我们还有许多其它方式可以运行 Python 程序。 -一种方法是使用VS Code,尤其是Microsoft官方的Python扩展插件: +一种方法是使用 VS Code,尤其是 Microsoft 官方的 Python 扩展插件: ![Screen-Shot-2020-11-10-at-14.23.32](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.23.32.png) -安装好此扩展插件后,您将可以使用Python代码自动补全、语法错误检查、自动格式化和使用`pylint`进行代码检查,以及一些特殊命令,包括: +安装好此扩展插件后,您将可以使用 Python 代码自动补全、语法错误检查、自动格式化和使用`pylint`进行代码检查,以及一些特殊命令,包括: -**Python: Start REPL** 用于在VS Code的集成终端中运行REPL: +**Python: Start REPL** 用于在 VS Code 的集成终端中运行 REPL: ![Screen-Shot-2020-11-10-at-14.31.36](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.31.36.png) @@ -231,19 +231,19 @@ chmod u+x program.py ![Screen-Shot-2020-11-10-at-14.30.02-1](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.30.02-1.png) -以及很多其它命令。只需打开命令面板(查看 -> 命令面板,或按下Cmd+Shift+P)并输入`python`,即可查看所有与Python相关的命令: +以及很多其它命令。只需打开命令面板(查看 -> 命令面板,或按下 Cmd+Shift+P)并输入`python`,即可查看所有与 Python 相关的命令: ![Screen-Shot-2020-11-10-at-14.30.02](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.30.02.png) -另一种轻松运行Python代码的方法是repl.it,这是一个非常不错的网站,它提供了一个编程环境,您可以使用任何语言创建并运行程序,包括Python: +另一种轻松运行 Python 代码的方法是 repl.it,这是一个非常不错的网站,它提供了一个编程环境,您可以使用任何语言创建并运行程序,包括 Python: ![Screen-Shot-2020-11-10-at-14.33.58](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.33.58.png) -使用这个网站要先注册(免费注册),然后在“create a repl”下单击Python: +使用这个网站要先注册(免费注册),然后在“create a repl”下单击 Python: ![Screen-Shot-2020-11-10-at-14.46.34](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.46.34.png) -然后您将看到一个带有`main.py`文件的编辑器,这样就已经准备好了编写Python代码: +然后您将看到一个带有`main.py`文件的编辑器,这样就已经准备好了编写 Python 代码: ![Screen-Shot-2020-11-10-at-14.47.15](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.47.15.png) @@ -251,7 +251,7 @@ chmod u+x program.py ![Screen-Shot-2020-11-10-at-14.48.09](https://www.freecodecamp.org/news/content/images/2021/03/Screen-Shot-2020-11-10-at-14.48.09.png) -我认为repl.it很方便,因为: +我认为 repl.it 很方便,因为: - 您只需分享链接即可轻松分享代码 - 它允许多人处理相同的代码 @@ -261,23 +261,23 @@ chmod u+x program.py <h2 id="python2-vs-python3">Python 2 vs Python 3</h2> -我们一开始就应该讨论的一个关键主题是Python 2与Python 3。 +我们一开始就应该讨论的一个关键主题是 Python 2 与 Python 3。 -Python 3于2008年被推出,其后作为主要的Python版本一直在被持续开发,而Python 2则通过错误修复和安全补丁进行维护,直到2020年初。 +Python 3 于 2008 年被推出,其后作为主要的 Python 版本一直在被持续开发,而 Python 2 则通过错误修复和安全补丁进行维护,直到 2020 年初。 -在那一天,对Python 2的支持停止。 +在那一天,对 Python 2 的支持停止。 -许多程序仍然使用Python 2编写,并且组织仍在积极致力于这些程序,因为迁移到Python 3并非易事,升级这些程序需要大量工作。并且重要文件的大型迁移总是会引入新的bug。 +许多程序仍然使用 Python 2 编写,并且组织仍在积极致力于这些程序,因为迁移到 Python 3 并非易事,升级这些程序需要大量工作。并且重要文件的大型迁移总是会引入新的 bug。 -但是对应新的代码程序,除非您必须遵守组织设置的强制使用Python 2的规则,否则应使用Python 3进行编写。 +但是对应新的代码程序,除非您必须遵守组织设置的强制使用 Python 2 的规则,否则应使用 Python 3 进行编写。 > 本书重点介绍 Python 3。 <h2 id="python-basics">Python 基础</h2> -### Python中的变量 +### Python 中的变量 -我们可以通过使用赋值运算符`=`为“标签”赋值,从而创建一个新的Python变量。 +我们可以通过使用赋值运算符`=`为“标签”赋值,从而创建一个新的 Python 变量。 在下面这个示例中,我们将字符串`"Roger"`分配给变量`name`: @@ -310,11 +310,11 @@ test! name% ``` -除此之外,任何输入都是有效的变量名,除非它是Python的**关键字**,如`for`、`if`、`while`、`import`等就是关键字。 +除此之外,任何输入都是有效的变量名,除非它是 Python 的**关键字**,如`for`、`if`、`while`、`import`等就是关键字。 -无需记住它们,因为如果您使用其中任何一个关键字作为变量名,Python都会提醒您,并且您会逐渐视它们为Python语法的一部分。 +无需记住它们,因为如果您使用其中任何一个关键字作为变量名,Python 都会提醒您,并且您会逐渐视它们为 Python 语法的一部分。 -### Python表达式和语句 +### Python 表达式和语句 我们可以_构造_任意一个有返回值的表达式代码,例如 @@ -323,7 +323,7 @@ name% "Roger" ``` -另一方面,语句是对值的操作。例如,下面是2个语句: +另一方面,语句是对值的操作。例如,下面是 2 个语句: ```python name = "Roger" @@ -338,7 +338,7 @@ name = "Roger"; print(name) ### 注释 -在Python程序中,井号之后的所有内容都被忽略,并被视为注释: +在 Python 程序中,井号之后的所有内容都被忽略,并被视为注释: ```python #this is a commented line @@ -346,9 +346,9 @@ name = "Roger"; print(name) name = "Roger" # this is an inline comment ``` -### Python中的缩进 +### Python 中的缩进 -Python中的缩进是有意义的。 +Python 中的缩进是有意义的。 您不能像这样随意缩进: @@ -357,7 +357,7 @@ name = "Flavio" print(name) ``` -对于其它一些语言,空格是没有意义的,但是在Python中,缩进很重要。 +对于其它一些语言,空格是没有意义的,但是在 Python 中,缩进很重要。 在上面这种情况下,如果您尝试运行这个程序,您会得到一个`IndentationError: unexpected indent`错误,因为缩进有特殊的含义。 @@ -365,7 +365,7 @@ name = "Flavio" <h2 id="data-types-in-python">Python数据类型</h2> -Python有几种内置类型。 +Python 有几种内置类型。 如果您创建`name`变量并为其分配值"Roger",则此变量现在自动表示**String**数据类型。 @@ -387,7 +387,7 @@ name = "Roger" isinstance(name, str) # True ``` -> 请注意,要在REPL之外查看`True`值,您需要将此代码包装在`print()`中,但为了清楚起见,我避免使用它。 +> 请注意,要在 REPL 之外查看`True`值,您需要将此代码包装在`print()`中,但为了清楚起见,我避免使用它。 我们在这里使用了`str`,但这种方法同样适用于其它数据类型。 @@ -410,7 +410,7 @@ name = "Flavio" age = 20 ``` -Python自动从变量值检测数据类型。 +Python 自动从变量值检测数据类型。 您还可以通过向类构造器传递字面值或变量名,来创建特定类型的变量: @@ -419,7 +419,7 @@ name = str("Flavio") anotherName = str(name) ``` -您还可以使用类构造器,将一种类型转换为另一种类型。Python将尝试转换为正确的值,例如从字符串中提取数字: +您还可以使用类构造器,将一种类型转换为另一种类型。Python 将尝试转换为正确的值,例如从字符串中提取数字: ```python age = int("20") @@ -432,7 +432,7 @@ print(intFraction) # 0 这称为**casting**。当然,这种转换并不总是有效,具体取决于传递的值。如果您在上面的字符串中写了`test`而不是`20`,您会得到一个`ValueError: invalid literal for int() with base 10: 'test'`错误。 -这些只是基础的数据类型。Python中有更多其它数据类型: +这些只是基础的数据类型。Python 中有更多其它数据类型: - `complex` 复数 - `bool` 布尔值 @@ -448,7 +448,7 @@ print(intFraction) # 0 <h2 id="operators">Python运算符</h2> -我们使用Python运算符来对值和变量进行运算操作。 +我们使用 Python 运算符来对值和变量进行运算操作。 我们可以根据它们执行操作的类型来划分运算符: @@ -460,7 +460,7 @@ print(intFraction) # 0 再加上一些其它有趣的运算符,比如`is`和`in`。 -### Python赋值运算符 +### Python 赋值运算符 赋值运算符用于为变量赋值: @@ -475,11 +475,11 @@ age = 8 anotherVariable = age ``` -从Python 3.8开始,可以使用_海象运算符_`:=`为变量赋值,同时该运算可作为另一个操作的一部分。例如在`if`或循环的条件部分。这个稍后再谈。 +从 Python 3.8 开始,可以使用_海象运算符_`:=`为变量赋值,同时该运算可作为另一个操作的一部分。例如在`if`或循环的条件部分。这个稍后再谈。 -### Python算术运算符 +### Python 算术运算符 -Python有许多算术运算符:`+`、`-`、`*`、`/`(除法)、`%`(取余)、`**`(求幂)和 `//`(向下取整除法) : +Python 有许多算术运算符:`+`、`-`、`*`、`/`(除法)、`%`(取余)、`**`(求幂)和 `//`(向下取整除法) : ```python 1 + 1 # 2 @@ -523,9 +523,9 @@ age += 1 # age is now 9 ``` -### Python比较运算符 +### Python 比较运算符 -Python定义了一些比较运算符: +Python 定义了一些比较运算符: - `==` - `!=` @@ -546,9 +546,9 @@ a > b # False a <= b # True ``` -### Python布尔运算符 +### Python 布尔运算符 -Python为我们提供了以下布尔运算符: +Python 为我们提供了以下布尔运算符: - `not` - `and` @@ -577,7 +577,7 @@ print([] or False) # 'False' print(False or []) # '[]' ``` -Python文档将其(x or y)描述为`如果x为假,则为y,否则为x`。(译者:`or`碰到真值就停,没有真值就走到最后) +Python 文档将其(x or y)描述为`如果x为假,则为y,否则为x`。(译者:`or`碰到真值就停,没有真值就走到最后) `and`运算操作仅在第一个操作数为真时,才计算第二个操作数。因此,如果第一个操作数是假值(假值:`False`、`0`、`''`、`[]`..),它会返回那个操作数。否则,它就会计算第二个操作数: @@ -590,9 +590,9 @@ print([] and False ) # [] print(False and [] ) # False ``` -Python文档将其(x and y)描述为`如果x为假,则为x,否则为y`。(译者:`or`碰到假值就停,没有假值就走到最后) +Python 文档将其(x and y)描述为`如果x为假,则为x,否则为y`。(译者:`or`碰到假值就停,没有假值就走到最后) -### Python位运算符 +### Python 位运算符 一些运算符用于处理位和二进制数: @@ -605,17 +605,17 @@ Python文档将其(x and y)描述为`如果x为假,则为x,否则为y` 一般很少使用位运算符,仅在非常特定的情况下使用,但是值得一提。 -### Python中的`is`和`in` +### Python 中的`is`和`in` -`is`被称为**identity operator**(验证运算符),用于比较两个对象,如果两者是同一个对象,则返回true。稍后将详细介绍对象。 +`is`被称为**identity operator**(验证运算符),用于比较两个对象,如果两者是同一个对象,则返回 true。稍后将详细介绍对象。 `in`被称为**membership operator**(成员运算符),用于判断一个值是否包含在一个列表或序列中。稍后将详细介绍列表和其他序列数据类型。 <h2 id="the-ternary-operator-in-python">Python三元运算符</h2> -使用Python三元运算符,您可以快速定义条件语句。 +使用 Python 三元运算符,您可以快速定义条件语句。 -假设您有一个函数,它将`age`变量与`18`进行比较,并根据结果返回True或False。 +假设您有一个函数,它将`age`变量与`18`进行比较,并根据结果返回 True 或 False。 可以不这样写: @@ -643,7 +643,7 @@ def is_adult(age): <h2 id="strings-in-python">Python字符串</h2> -Python中的字符串是用单引号或双引号括起来的一串字符: +Python 中的字符串是用单引号或双引号括起来的一串字符: ```python "Roger" @@ -683,7 +683,7 @@ str(8) # "8" print("Roger is " + str(8) + " years old") # Roger is 8 years old ``` -当使用特殊语法定义时,字符串可以是多行的,将字符串括在一组3个引号中: +当使用特殊语法定义时,字符串可以是多行的,将字符串括在一组 3 个引号中: ```python print("""Roger is @@ -756,7 +756,7 @@ print("ger" in name) # True name = "Roger" ``` -`"Ro"Ger"`将不起作用,因为Python会认为字符串以`"Ro"`结尾。 +`"Ro"Ger"`将不起作用,因为 Python 会认为字符串以`"Ro"`结尾。 方法是使用`\`反斜杠字符转义字符串内的双引号: @@ -766,7 +766,7 @@ name = "Ro\"ger" 这也适用于单引号`\'`,以及其它特殊格式字符,如制表符`\t`、换行符`\n`和反斜杠`\\`。 -给定一个字符串,并给定一个索引(从0开始),您就可以使用方括号获取指定位置上的字符,从而获取特定内容: +给定一个字符串,并给定一个索引(从 0 开始),您就可以使用方括号获取指定位置上的字符,从而获取特定内容: ```python name = "Roger" @@ -793,7 +793,7 @@ name[2:] # "ger" <h2 id="booleans-in-python">Python布尔值</h2> -Python提供了`bool`类型,它可以有两个值:`True` 和 `False`(首字母大写)。 +Python 提供了`bool`类型,它可以有两个值:`True` 和 `False`(首字母大写)。 ```python done = False @@ -824,7 +824,7 @@ done = True type(done) == bool # True ``` -或者使用`isinstance()`,需要传递2个参数:变量和`bool`类: +或者使用`isinstance()`,需要传递 2 个参数:变量和`bool`类: ```python done = True @@ -851,9 +851,9 @@ ready_to_serve = all([ingredients_purchased, meal_cooked]) # False <h2 id="numbers-in-python">Python数字</h2> -Python中的数字有3种类型:`int`、`float`和`complex`。 +Python 中的数字有 3 种类型:`int`、`float`和`complex`。 -### Python整数 +### Python 整数 整数使用`int`表示,您可以使用字面值定义整数: @@ -873,7 +873,7 @@ age = int(8) type(age) == int # True ``` -### Python浮点数 +### Python 浮点数 浮点数(分数)的类型为`float`,您可以使用字面值定义浮点数: @@ -893,7 +893,7 @@ fraction = float(0.1) type(fraction) == float # True ``` -### Python复数 +### Python 复数 复数属于`complex`类型。 @@ -922,7 +922,7 @@ complexNumber.imag # 3.0 type(complexNumber) == complex #True ``` -### Python中数字的算术运算 +### Python 中数字的算术运算 您可以使用算术运算符对数字执行算术运算:`+`、`-`、`*`、`/`(除法)、`%`(取余)、`**`(求幂)和`//`(向下取整除法): @@ -952,9 +952,9 @@ age = 8 age += 1 # age: 9 ``` -### Python内置函数 +### Python 内置函数 -有2个内置函数可以帮助处理数字: +有 2 个内置函数可以帮助处理数字: `abs()`返回一个数字的绝对值。 @@ -970,7 +970,7 @@ round(0.12) # 0 round(0.12, 1) # 0.1 ``` -Python标准库提供了其它几个数学实用函数和常量: +Python 标准库提供了其它几个数学实用函数和常量: - `math`包提供通用的数学函数和常量 - `cmath`包提供了处理复数的方法 @@ -981,7 +981,7 @@ Python标准库提供了其它几个数学实用函数和常量: <h2 id="constants-in-python">Python常量</h2> -Python中无法强制改变的值是常量。 +Python 中无法强制改变的值是常量。 比较常用的是枚举: @@ -1001,9 +1001,9 @@ class Constants(Enum): WIDTH = 1024 ``` -没有人会阻止您覆盖这个值,Python也不会阻止。(译者:全大写的变量表示不应改变的常量,这只是一种约定) +没有人会阻止您覆盖这个值,Python 也不会阻止。(译者:全大写的变量表示不应改变的常量,这只是一种约定) -正如您将来会看到的,大多数Python代码都采用这种命名约定的写法。 +正如您将来会看到的,大多数 Python 代码都采用这种命名约定的写法。 <h2 id="enums-in-python">Python枚举</h2> @@ -1051,7 +1051,7 @@ len(State) # 2 <h2 id="user-input-in-python">Python用户输入</h2> -在Python命令行程序中,您可以使用`print()`函数向用户显示信息: +在 Python 命令行程序中,您可以使用`print()`函数向用户显示信息: ```python name = "Roger" @@ -1076,7 +1076,7 @@ print('Your age is ' + age) 当您处理布尔值和返回布尔值的表达式时,您可以根据它们的值为`True`或`False`来采取不同的方式。 -在Python中,我们使用`if`语句来做到这一点: +在 Python 中,我们使用`if`语句来做到这一点: ```python condition = True @@ -1085,9 +1085,9 @@ if condition == True: # do something ``` -当条件解析为`True`时,就像上面的情况一样,if下面的代码块被执行。 +当条件解析为`True`时,就像上面的情况一样,if 下面的代码块被执行。 -什么是代码块?代码块是向右侧缩进一级(通常为4个空格)的部分: +什么是代码块?代码块是向右侧缩进一级(通常为 4 个空格)的部分: ```python condition = True @@ -1172,7 +1172,7 @@ print(result) # 3 <h2 id="lists-in-python">Python列表</h2> -列表是Python中一种基本的数据结构。 +列表是 Python 中一种基本的数据结构。 使用列表,您可以将多个值组合在一起,并使用一个名称引用它们。 @@ -1240,7 +1240,7 @@ items[2:] # ["Syd", True] len(items) # 4 ``` -您可以使用list的`append()`方法将新元素添加到列表中: +您可以使用 list 的`append()`方法将新元素添加到列表中: ```python items.append("Test") @@ -1260,7 +1260,7 @@ items += ["Test"] # items is ['Roger', 1, 'Syd', True, 'Test'] ``` -> 注意:使用`extend()`或`+=`不要忘记方括号。不要执行`items += "Test"`或`items.extend("Test")`,否则Python会在列表中添加4个单独的字符,即`['Roger', 1, 'Syd', True, 'T'、'e'、's'、't']` +> 注意:使用`extend()`或`+=`不要忘记方括号。不要执行`items += "Test"`或`items.extend("Test")`,否则 Python 会在列表中添加 4 个单独的字符,即`['Roger', 1, 'Syd', True, 'T'、'e'、's'、't']` 使用`remove()`方法删除元素: @@ -1308,7 +1308,7 @@ items.sort() items.sort(key=str.lower) ``` -(使用sort方法)排序会修改原始列表内容。为避免这种情况,您可以先复制列表 +(使用 sort 方法)排序会修改原始列表内容。为避免这种情况,您可以先复制列表 ```python itemscopy = items[:] @@ -1324,7 +1324,7 @@ print(sorted(items, key=str.lower)) <h2 id="tuples-in-python">Python元组</h2> -元组是Python中另一种基本的数据结构。 +元组是 Python 中另一种基本的数据结构。 它允许您创建不可变的对象组。这意味着一旦创建了元组,就无法修改它。您不能添加或删除元组中的元素。 @@ -1374,7 +1374,7 @@ names[1:] # ('Syd',) ``` 您可以使用全局函数`sorted()`创建元组排好序的版本: -(译者:请注意,元组没有sort方法,因为元组是不可改变的) +(译者:请注意,元组没有 sort 方法,因为元组是不可改变的) ```python sorted(names) @@ -1388,7 +1388,7 @@ newTuple = names + ("Vanille", "Tina") <h2 id="dictionaries-in-python">Python字典</h2> -字典是Python中非常重要的一种数据结构。 +字典是 Python 中非常重要的一种数据结构。 列表允许您创建值的集合,而字典允许您创建**键/值对**的集合。 @@ -1478,7 +1478,7 @@ dog['favorite food'] = 'Meat' del dog['favorite food'] ``` -要复制字典,请使用copy()方法: +要复制字典,请使用 copy()方法: (译者:这种方式是浅拷贝) ```python @@ -1487,7 +1487,7 @@ dogCopy = dog.copy() <h2 id="sets-in-python">Python集合</h2> -集合是Python另一个重要的数据结构。 +集合是 Python 另一个重要的数据结构。 可以说它像元组一样工作,但集合不是有序的,而且是**可变的**。 @@ -1564,7 +1564,7 @@ print("Roger" in names) # True 函数可以创建一组指令,我们在需要时运行这些指令。 -函数在Python和其它许多编程语言中是必不可少的。它帮助我们创建有意义的程序,因为我们可以使用函数将程序分解为可管理的部分,并且促进了代码的可读性和重用性。 +函数在 Python 和其它许多编程语言中是必不可少的。它帮助我们创建有意义的程序,因为我们可以使用函数将程序分解为可管理的部分,并且促进了代码的可读性和重用性。 这是一个名为`hello`的函数示例,它打印"Hello!": @@ -1623,7 +1623,7 @@ def hello(name, age): hello('Roger', 8) ``` -形参通过引用传递。Python中的所有内容都是对象,但其中一些是不可变的,包括整数、布尔值、浮点数、字符串和元组。这意味着如果您将它们作为参数传递给函数,并在函数内部修改它们的值,则新值不会反映在函数外部: +形参通过引用传递。Python 中的所有内容都是对象,但其中一些是不可变的,包括整数、布尔值、浮点数、字符串和元组。这意味着如果您将它们作为参数传递给函数,并在函数内部修改它们的值,则新值不会反映在函数外部: ```python def change(value): @@ -1655,7 +1655,7 @@ def hello(name): return ``` -我们可以在条件中包含return语句,这是在不满足起始条件时结束函数的常用方法: +我们可以在条件中包含 return 语句,这是在不满足起始条件时结束函数的常用方法: ```python def hello(name): @@ -1678,7 +1678,7 @@ def hello(name): <h2 id="objects-in-python">Python对象</h2> -Python中的一切都是对象。 +Python 中的一切都是对象。 原始类型(整数、字符串、浮点数……)的值也是对象。 同时列表、元组、字典和一切也都是对象。 @@ -1713,13 +1713,13 @@ items.pop() 这些(可使用的)方法取决于变量的数据类型。 -Python提供的全局函数`id()`可让您检查特定对象在内存中的位置。 +Python 提供的全局函数`id()`可让您检查特定对象在内存中的位置。 ```python id(age) # 140170065725376 ``` -> 您(电脑上查看的age)的内存地址值会不一样——这里只是作为一个例子来展示。 +> 您(电脑上查看的 age)的内存地址值会不一样——这里只是作为一个例子来展示。 如果给变量赋不同的值,它的地址会改变,因为变量已经指向存储在内存中另一个位置的另一个值: @@ -1752,7 +1752,7 @@ print(id(items)) # 140093713593920 如果对象提供改变其内容的方法,那么它是可变的。否则它是不可变的。 -Python定义的大多数类型都是不可变的, 例如`int` ,没有任何方法可以改变它的值。 如果您增加它的值 +Python 定义的大多数类型都是不可变的, 例如`int` ,没有任何方法可以改变它的值。 如果您增加它的值 ```python age = 8 @@ -1763,15 +1763,15 @@ age = age + 1 age += 1 ``` -然后您使用`id(age)`检查,您会发现`age`前后指向不同的内存位置。 原来的值并没有发生变异,age只是指向另一个值。 +然后您使用`id(age)`检查,您会发现`age`前后指向不同的内存位置。 原来的值并没有发生变异,age 只是指向另一个值。 <h2 id="loops-in-python">Python循环</h2> 循环是编程的重要组成部分。 -在 Python 中,我们有2种循环:**while循环**和**for循环**。 +在 Python 中,我们有 2 种循环:**while 循环**和**for 循环**。 -### Python中的`while`循环 +### Python 中的`while`循环 `while`循环是使用`while`关键字定义的,它重复执行自己的块,直到判断条件为`False`: @@ -1807,9 +1807,9 @@ while count < 10: print("After the loop") ``` -### Python中的`for`循环 +### Python 中的`for`循环 -使用`for`循环,我们可以让Python执行一个预先确定循环次数的代码块,并且不需要单独的变量和条件来检查它的值。 +使用`for`循环,我们可以让 Python 执行一个预先确定循环次数的代码块,并且不需要单独的变量和条件来检查它的值。 例如,我们可以迭代列表中的元素: @@ -1826,7 +1826,7 @@ for item in range(04): print(item) ``` -`range(4)`创建一个从0开始并包含4个元素的序列:`[0, 1, 2, 3]`。 +`range(4)`创建一个从 0 开始并包含 4 个元素的序列:`[0, 1, 2, 3]`。 如果要获取索引,您应该将序列包装到`enumerate()`函数中: @@ -1836,11 +1836,11 @@ for index, item in enumerate(items): print(index, item) ``` -### Python中的`Break`和`continue` +### Python 中的`Break`和`continue` `while`和 `for`循环都可以在代码块内被中断,这需要使用两个特殊关键字:`break`和`continue`。 -`continue`停止当前迭代并告诉Python执行下一个迭代。 +`continue`停止当前迭代并告诉 Python 执行下一个迭代。 `break`完全停止循环,并继续执行循环外的下一条指令。 @@ -1864,7 +1864,7 @@ for item in items: <h2 id="classes-in-python">Python类</h2> -除了使用Python提供的数据类型之外,我们还可以声明自定义的类,并使用类实例化对象。 +除了使用 Python 提供的数据类型之外,我们还可以声明自定义的类,并使用类实例化对象。 对象是类的实例。类是对象的类型。 @@ -1875,7 +1875,7 @@ class <class_name>: # 自定义的类 ``` -例如,让我们定义一个Dog类 +例如,让我们定义一个 Dog 类 ```python class Dog: @@ -1899,7 +1899,7 @@ class Dog: roger = Dog() ``` -现在`roger`是Dog类型的对象。 +现在`roger`是 Dog 类型的对象。 如果运行 @@ -1934,7 +1934,7 @@ roger.bark() # 'WOF!' 类的一个重要特性是继承。 -我们创建一个可以使用`walk()`方法的Animal 类: +我们创建一个可以使用`walk()`方法的 Animal 类: ```python class Animal: @@ -1942,7 +1942,7 @@ class Animal: print('Walking..') ``` -然后Dog类继承Animal类: +然后 Dog 类继承 Animal 类: ```python class Dog(Animal): @@ -1960,11 +1960,11 @@ roger.bark() # 'WOF!' <h2 id="modules-in-python">Python模块</h2> -每个Python文件都是一个模块。 +每个 Python 文件都是一个模块。 您可以从其它文件导入模块,这是任何具有一定复杂性的程序的基础,因为它促进了合理的组织结构和代码重用。 -在典型的Python程序中,一个文件作为入口点,那么其它文件是模块,并公开可以从模块中调用的函数。(译者:不只是可以公开函数,类、常量等等都行) +在典型的 Python 程序中,一个文件作为入口点,那么其它文件是模块,并公开可以从模块中调用的函数。(译者:不只是可以公开函数,类、常量等等都行) 文件`dog.py`包含以下代码: @@ -1997,7 +1997,7 @@ bark() 假设您将`dog.py`文件放在`lib`文件夹中。 -在该文件夹中,您需要创建一个名为`__init__.py`的空文件来告诉Python该文件夹包含模块。 +在该文件夹中,您需要创建一个名为`__init__.py`的空文件来告诉 Python 该文件夹包含模块。 现在您可以选择从`lib`中导入`dog`: @@ -2017,7 +2017,7 @@ bark() <h2 id="the-python-standard-library">Python标准库</h2> -Python通过其**标准库**公开了许多内置功能。 +Python 通过其**标准库**公开了许多内置功能。 标准库是各种应用程序的集合,包括数学应用,代码调试,以及图形用户界面创建等等。 @@ -2027,15 +2027,15 @@ Python通过其**标准库**公开了许多内置功能。 - `math` 数学计算相关应用程序 - `re` 正则表达式的使用 -- `json` 处理json数据 +- `json` 处理 json 数据 - `datetime` 处理日期 -- `sqlite3` 使用SQLite +- `sqlite3` 使用 SQLite - `os` 操作系统实用程序 - `random` 生成随机数 - `statistics` 数学统计相关应用程序 -- `requests` 执行HTTP网络请求 -- `http` 创建HTTP服务器 -- `urllib` 管理URL +- `requests` 执行 HTTP 网络请求 +- `http` 创建 HTTP 服务器 +- `urllib` 管理 URL 接下来介绍如何_使用_标准库的一个模块。您已经知道如何使用自己创建的模块,即从程序文件夹中的其它文件导入。 @@ -2063,21 +2063,21 @@ sqrt(4) # 2.0 如果您从一开始就学习正确的命名和格式约定,那么将更容易阅读其他人编写的代码,同样其他人也会发现您的代码易于阅读。 -Python在PEP8样式指南中定义了其代码风格约定。PEP即_Python Enhancement Proposals_,它描述了Python语言所有增强和讨论的地方。 +Python 在 PEP8 样式指南中定义了其代码风格约定。PEP 即_Python Enhancement Proposals_,它描述了 Python 语言所有增强和讨论的地方。 -有很多PEP提案,都可以在[https://www.python.org/dev/peps/](https://www.python.org/dev/peps/) 上找到。 +有很多 PEP 提案,都可以在[https://www.python.org/dev/peps/](https://www.python.org/dev/peps/) 上找到。 -PEP8是最早和最重要的提案之一,它定义了格式和以"pythonic"方式编写Python代码的一些规则。 +PEP8 是最早和最重要的提案之一,它定义了格式和以"pythonic"方式编写 Python 代码的一些规则。 您可以在此处阅读其完整内容:[https://www.python.org/dev/peps/pep-0008/](https://www.python.org/dev/peps/pep-0008/) ,下面是几点总结,您可以从这里快速开始: - 使用空格而不是制表符缩进 -- 使用4个空格缩进 -- Python文件用UTF-8编码 -- 一行代码最多80列 +- 使用 4 个空格缩进 +- Python 文件用 UTF-8 编码 +- 一行代码最多 80 列 - 每个语句写在自己所在的一行上 -- 函数、变量名和文件名使用小写,单词之间用下划线分隔(例如snake\_case) -- 类名单词首字母大写(例如CamelCase) +- 函数、变量名和文件名使用小写,单词之间用下划线分隔(例如 snake_case) +- 类名单词首字母大写(例如 CamelCase) - 包名是小写的,单词之间没有下划线 - 不应该改变的常量全用大写字母 - 变量名应该有意义 @@ -2092,7 +2092,7 @@ PEP8是最早和最重要的提案之一,它定义了格式和以"pythonic"方 调试代码是您应该学习的最佳技能之一,因为在许多困难的情况下,它将为您提供帮助。 -每种编程语言都有其调试器。Python使用`pdb`调试代码,可通过标准库获得。 +每种编程语言都有其调试器。Python 使用`pdb`调试代码,可通过标准库获得。 您可以通过在代码中添加一个断点来进行调试: @@ -2102,7 +2102,7 @@ breakpoint() > 如果需要,您可以添加更多断点。 -当Python解释器在您的代码中遇到断点时,它会停止执行代码,并会告诉您下一条将运行的指令是什么。 +当 Python 解释器在您的代码中遇到断点时,它会停止执行代码,并会告诉您下一条将运行的指令是什么。 接下来您可以做一些事情。 @@ -2151,9 +2151,9 @@ print(age) <h2 id="how-to-accept-arguments-from-the-command-line-in-python">Python接收从命令行传入的参数</h2> -当我们从命令行调用程序时,Python提供了几种方法来处理传递的参数。 +当我们从命令行调用程序时,Python 提供了几种方法来处理传递的参数。 -到目前为止,您已经使用过REPL来执行程序,或使用如下方法执行Python代码 +到目前为止,您已经使用过 REPL 来执行程序,或使用如下方法执行 Python 代码 ```sh python <文件名>.py @@ -2180,7 +2180,7 @@ print(sys.argv) 这是一种简单的方法,但您必须自己做很多工作。您需要验证参数,确保它们的类型是正确的,如果用户没有正确使用程序,您需要向他们打印反馈信息。 -Python在标准库中提供了另一个包来帮助您:`argparse`。 +Python 在标准库中提供了另一个包来帮助您:`argparse`。 您首先导入`argparse`并调用`argparse.ArgumentParser()`,传递程序的描述: @@ -2241,9 +2241,9 @@ program.py: error: argument -c/--color: 无效的选择: 'blue' (该选项只能 <h2 id="lambda-functions-in-python">Python的Lambda函数</h2> -Lambda函数(也称为匿名函数)是没有名称且只有一个表达式作为其主体的小型函数。 +Lambda 函数(也称为匿名函数)是没有名称且只有一个表达式作为其主体的小型函数。 -在Python中,它们是使用`lambda`关键字定义的: +在 Python 中,它们是使用`lambda`关键字定义的: ```python lambda <参数> : <表达式> @@ -2253,19 +2253,19 @@ lambda <参数> : <表达式> > 这很重要:表达式返回值,语句不返回。 -最简单的lambda函数示例是将数字的值加倍: +最简单的 lambda 函数示例是将数字的值加倍: ```python lambda num : num * 2 ``` -Lambda函数可以接受更多参数: +Lambda 函数可以接受更多参数: ```python lambda a, b : a * b ``` -无法直接调用Lambda函数,但您可以将它们分配给变量: +无法直接调用 Lambda 函数,但您可以将它们分配给变量: ```python multiply = lambda a, b : a * b @@ -2273,11 +2273,11 @@ multiply = lambda a, b : a * b print(multiply(2, 2)) # 4 ``` -Lambda函数的实用性在于与其它Python功能结合使用,例如结合`map()`、`filter()`和`reduce()`。 +Lambda 函数的实用性在于与其它 Python 功能结合使用,例如结合`map()`、`filter()`和`reduce()`。 <h2 id="recursion-in-python">Python递归</h2> -Python中的函数可以调用自身,这就是递归。递归在许多情况下都非常有用。 +Python 中的函数可以调用自身,这就是递归。递归在许多情况下都非常有用。 解释递归的常用方法是实现阶乘计算。 @@ -2301,13 +2301,13 @@ print(factorial(4)) # 24 print(factorial(5)) # 120 ``` -如果在 `factorial()` 函数中调用`factorial(n)`而不是`factorial(n-1)`,则会导致无限递归。 默认情况下,Python将在1000次调用时停止递归,此时您将收到`RecursionError`错误。 +如果在 `factorial()` 函数中调用`factorial(n)`而不是`factorial(n-1)`,则会导致无限递归。 默认情况下,Python 将在 1000 次调用时停止递归,此时您将收到`RecursionError`错误。 递归在很多地方都有用,它可以帮助我们在没有其它更好方法的情况下简化代码,所以了解这种技术是件好事。 <h2 id="nested-functions-in-python">Python嵌套函数</h2> -Python中函数可以嵌套在其它函数中。 +Python 中函数可以嵌套在其它函数中。 在函数内部定义的函数仅在该函数内可见。 @@ -2412,7 +2412,7 @@ def logtime(func): 文档非常重要,不仅可以用于告知其他人(自己写的)函数/类/方法/模块的目标是什么,还可以帮助您(在较长时间后)理解自己的代码。 -当您在6或12个月后会看您的的代码时,可能不记得写代码时脑海中的所有想法。这个时候阅读您的代码并理解它在做什么将变得非常困难。 +当您在 6 或 12 个月后会看您的的代码时,可能不记得写代码时脑海中的所有想法。这个时候阅读您的代码并理解它在做什么将变得非常困难。 注释是帮助自己(和他人)摆脱这种困境的一种方式: @@ -2482,7 +2482,7 @@ def increment(n): return n + 1 ``` -Python将处理这些(文档字符串),您可以使用全局函数`help()`来获取类/方法/函数/模块的文档。 +Python 将处理这些(文档字符串),您可以使用全局函数`help()`来获取类/方法/函数/模块的文档。 例如调用`help(increment)`会给返回这个: @@ -2509,7 +2509,7 @@ increment(n) 首先,使用全局函数`help()`(如果以文档字符串的形式提供)我们可以获得文档。 -然后,您可以使用print()获取有关函数的信息: +然后,您可以使用 print()获取有关函数的信息: ```python def increment(n): @@ -2572,7 +2572,7 @@ print(id(1)) # 140227521172384 <h2 id="annotations-in-python">Python注解</h2> -Python是动态类型的,我们不必指定变量、函数参数或函数返回值的类型。 +Python 是动态类型的,我们不必指定变量、函数参数或函数返回值的类型。 注解允许我们(可选地)这样做。 @@ -2596,7 +2596,7 @@ def increment(n: int) -> int: count: int = 0 ``` -Python将忽略这些注解。 一个名为[`mypy`](http://mypy-lang.org/)的工具可以独立运行,也可以集成到VS Code或PyCharm等IDE中,以便在您编码时自动检查静态类型错误。它还将帮助您在运行代码之前捕获类型不匹配的错误。 +Python 将忽略这些注解。 一个名为[`mypy`](http://mypy-lang.org/)的工具可以独立运行,也可以集成到 VS Code 或 PyCharm 等 IDE 中,以便在您编码时自动检查静态类型错误。它还将帮助您在运行代码之前捕获类型不匹配的错误。 这是一个很大的帮助,尤其是当您的软件规模变得很大并且需要重构代码时。 @@ -2604,7 +2604,7 @@ Python将忽略这些注解。 一个名为[`mypy`](http://mypy-lang.org/)的工 <h2 id="exceptions-in-python">Python异常</h2> -处理错误很重要,Python为我们提供了异常处理来做到这一点。 +处理错误很重要,Python 为我们提供了异常处理来做到这一点。 如果将代码行包装到`try:`块中: @@ -2613,7 +2613,7 @@ try: # 一些代码 ``` -如果发生错误,Python会提醒您,您可以使用`except`块确认发生了哪种错误: +如果发生错误,Python 会提醒您,您可以使用`except`块确认发生了哪种错误: ```python try: @@ -2713,7 +2713,7 @@ except Exception as error: print(error) ``` -您还可以扩展Exception来定义自己的异常类: +您还可以扩展 Exception 来定义自己的异常类: ```python class DogNotFoundException(Exception): @@ -2760,21 +2760,21 @@ with open(filename, 'r') as file: print(content) ``` -换句话说,Python有内置的隐式异常处理,其会自动为我们调用`close()`。 +换句话说,Python 有内置的隐式异常处理,其会自动为我们调用`close()`。 上面的例子只是为了介绍`with`的功能,而不是说它仅在处理文件方面对我们有帮助。 <h2 id="how-to-install-3rd-party-packages-in-python-using-pip">Python如何使用pip安装第三方包</h2> -Python标准库包含大量实用的程序,可以简化我们的开发需求,但是没有什么能满足_一切_。 +Python 标准库包含大量实用的程序,可以简化我们的开发需求,但是没有什么能满足_一切_。 这就是个人和公司创建第三方包,并将它们作为开源软件提供给整个社区的原因。 -这些模块都收集在一个地方,可在[https://pypi.org](https://pypi.org) 获得**Python包索引**,并且可以使用`pip`将它们(第三方模块)安装在您的系统上。 +这些模块都收集在一个地方,可在[https://pypi.org](https://pypi.org) 获得**Python 包索引**,并且可以使用`pip`将它们(第三方模块)安装在您的系统上。 -在撰写本文时,有超过270,000个免费第三方包可供我们使用。 +在撰写本文时,有超过 270,000 个免费第三方包可供我们使用。 -> 如果您按照Python安装说明进行操作,您应该已经安装了 `pip`。 +> 如果您按照 Python 安装说明进行操作,您应该已经安装了 `pip`。 使用命令`pip install`可以安装任何第三方包: @@ -2794,11 +2794,11 @@ python -m pip install <package> pip install requests ``` -一旦这样做,它就可以用于您所有的Python脚本,因为包是全局安装的。 +一旦这样做,它就可以用于您所有的 Python 脚本,因为包是全局安装的。 (第三方包安装的)具体位置取决于您的操作系统。 -在运行Python 3.9的macOS上,位置是`/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages`。 +在运行 Python 3.9 的 macOS 上,位置是`/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages`。 使用以下命令将第三方包升级到最新版本: @@ -2834,7 +2834,7 @@ pip show <package> numbers = [1, 2, 3, 4, 5] ``` -您可以使用列表推导式创建一个新列表,该列表由`numbers`列表元素的2次幂组成: +您可以使用列表推导式创建一个新列表,该列表由`numbers`列表元素的 2 次幂组成: ```python numbers_power_2 = [n**2 for n in numbers] @@ -2917,11 +2917,11 @@ In [9]: c.eat() cat eating ... ``` -译者:多态实际上是看运行时对象具体的类型,在Java中,是可以这样写的`Animal dog = new Dog()`,即创建一个`Animal`对象`dog`,这是**编译**时,但是在**运行**时`dog.eat()`打印`dog eating ...` +译者:多态实际上是看运行时对象具体的类型,在 Java 中,是可以这样写的`Animal dog = new Dog()`,即创建一个`Animal`对象`dog`,这是**编译**时,但是在**运行**时`dog.eat()`打印`dog eating ...` <h2 id="operator-overloading-in-python">Python运算符重载</h2> -运算符重载是一种先进的技术,我们可以用来使类具有可比性,并使它们与Python运算符一起工作。 +运算符重载是一种先进的技术,我们可以用来使类具有可比性,并使它们与 Python 运算符一起工作。 让我们来创建一个类`Dog`: @@ -2981,7 +2981,7 @@ class Dog: <h2 id="virtual-environments-in-python">Python虚拟环境</h2> -在您的系统上运行多个Python应用程序是很常见的。 +在您的系统上运行多个 Python 应用程序是很常见的。 当应用程序需要相同的模块时,有时您会遇到一个棘手的情况,即一个应用程序需要一个版本模块,而另一个应用程序需要该模块的不同版本。 @@ -2997,7 +2997,7 @@ python -m venv .venv (该命令)在您要开始的项目的文件夹或者现有项目的文件夹(的根目录下运行)。 -译者:项目的根目录即其本身,例如一个项目FCC在`/Users/abc/projects/FCC`,那么该项目的根目录就是`/Users/abc/projects/FCC/` +译者:项目的根目录即其本身,例如一个项目 FCC 在`/Users/abc/projects/FCC`,那么该项目的根目录就是`/Users/abc/projects/FCC/` 然后运行 @@ -3005,9 +3005,9 @@ python -m venv .venv source .venv/bin/activate ``` -> (如果是)在Fish shell上,使用 `source .venv/bin/activate.fish` +> (如果是)在 Fish shell 上,使用 `source .venv/bin/activate.fish` -执行这个命令将激活Python虚拟环境。根据您的配置,可能还会看到终端提示发生变化。 +执行这个命令将激活 Python 虚拟环境。根据您的配置,可能还会看到终端提示发生变化。 我的从 @@ -3025,8 +3025,8 @@ source .venv/bin/activate 我希望它能鼓励您更多去地了解 Python。 -有关Python和一般编程教程的更多信息,请查看我的博客 [flaviocopes.com](https://flaviocopes.com)。 +有关 Python 和一般编程教程的更多信息,请查看我的博客 [flaviocopes.com](https://flaviocopes.com)。 -在[mailto:flavio@flaviocopes.com](mailto:flavio@flaviocopes.com) 发送任何反馈、勘误或意见,您可以在Twitter[@flaviocopes](https://twitter.com/flaviocopes) 上与我联系 . +在[mailto:flavio@flaviocopes.com](mailto:flavio@flaviocopes.com) 发送任何反馈、勘误或意见,您可以在 Twitter[@flaviocopes](https://twitter.com/flaviocopes) 上与我联系 . -> 请注意:您可以[获取此Python手册的PDF、ePub和Mobi版本](https://flaviocopes.com/page/python-handbook/) +> 请注意:您可以[获取此 Python 手册的 PDF、ePub 和 Mobi 版本](https://flaviocopes.com/page/python-handbook/) diff --git a/chinese/articles/the-top-stack-overflowed-typescript-questions-explained.md b/chinese/articles/the-top-stack-overflowed-typescript-questions-explained.md index fee4ce13f..dd5490e24 100644 --- a/chinese/articles/the-top-stack-overflowed-typescript-questions-explained.md +++ b/chinese/articles/the-top-stack-overflowed-typescript-questions-explained.md @@ -91,7 +91,7 @@ interface Human { 以下是 type alias 和 interface 的主要区别: -### 关键区别:接口(Interfaces)只能描述 object shapes。类型别名(Type aliases)可用于其他类型(other types),如 primitives, unions 和 tuples。 +### 关键区别:接口(Interfaces)只能描述 object shapes。类型别名(Type aliases)可用于其他类型(other types),如 primitives, unions 和 tuples type alias 可以表示的数据类型中是相当灵活的。从 basic primitives(基本的基元)到 复杂的 unions(联合)和 tuples(元组), 如下所示: diff --git a/chinese/articles/the-ultimate-guide-to-python-from-beginner-to-intermediate-to-pro.md b/chinese/articles/the-ultimate-guide-to-python-from-beginner-to-intermediate-to-pro.md index fe237d163..d9e30cb71 100644 --- a/chinese/articles/the-ultimate-guide-to-python-from-beginner-to-intermediate-to-pro.md +++ b/chinese/articles/the-ultimate-guide-to-python-from-beginner-to-intermediate-to-pro.md @@ -19,7 +19,7 @@ print("Hello folks") > **注意:** 任何高于 3.5.2 的版本都支持绝大多数库和框架。 -## 索引: +## 索引 1. [绪论](#绪论) 2. [安装](#安装) @@ -67,7 +67,7 @@ print("Hello folks") **注意:** 这篇指南的开头部分是为初学者准备的。如果你拥有中级 Python 经验,随时可以使用上面的链接向前跳转。 -## 绪论: +## 绪论 根据 Github 的 [octoverse][44],Python 是 2019 年被开发者使用第二多的语言。 @@ -92,7 +92,7 @@ Python 背后的哲学就是代码必须可读,这是通过缩进实现的。P > **注意:** 我并不是特别支持上面提到的任何库或框架,它们在各自的领域中都非常流行,也得到了广泛使用。 -## 安装: +## 安装 学习任何编程语言的第一步都是安装它。如今,大多数操作系统都自带 Python。你可以在终端执行以下命令,检查 Python 是否可用: @@ -114,7 +114,7 @@ Python 3.7.0 - [Mac 用户][48] - [Linux 用户][49] -### Windows 用户: +### Windows 用户 - 打开 [Python 官网](50)。 - 点击下载按钮(下载 Python 3.8.2)[**注意:** 在你阅读本文时,版本可能会有所不同]。 @@ -197,7 +197,7 @@ python3 testing.py 22 ``` -## 注释: +## 注释 注释(comment)帮助我们(和其他人)理解为什么要写某段代码,让代码的编写更加容易。注释的另一大作用就是帮助我们提高代码的可读性。 @@ -211,7 +211,7 @@ python3 testing.py 有比这更好的解决办法吗?哈哈,有,就是注释。注释帮助我们理解为什么要写某段代码,它返回啥或者它干了啥。注释看作是这段代码的文档。 -## 打印: +## 打印 除了编辑器的调试工具,最常帮助开发者解决问题的东西就是 print 语句了。print 语句是所有编程中最容易被低估的语法之一。 @@ -225,7 +225,7 @@ Python 提供了一个内置的 print 方法,语法如下: print("Stay safe...") ``` -## 缩进: +## 缩进 Python 中另一个有趣的部分就是缩进(indentation)。为什么呢?答案很简单:缩进让代码易读、结构良好。Python 强制使用者遵守缩进规则,如果缩进不合适,你就会得到下面这个错误: @@ -235,7 +235,7 @@ IndentationError: unexpected indent 看到了吧,即使是 Python 中的错误也这么易读和理解。你可能会在刚开始的时候因强制缩进而感到心烦,但是你会慢慢发现缩进是开发者的好朋友。 -## 变量: +## 变量 顾名思义,变量(variable)就是能够变化的东西。在计算机程序中,变量则是引用内存位置的一种方式。 @@ -308,7 +308,7 @@ None <class 'NoneType'> ``` -## 运算符: +## 运算符 下图展示了 Python 中所有的运算符: @@ -394,7 +394,7 @@ False True ``` -## 条件语句: +## 条件语句 顾名思义,条件语句用于计算条件的真假。 @@ -775,7 +775,7 @@ number 5 number 6 ``` -## 用户输入: +## 用户输入 假设你正在构建一个命令行应用,现在你需要根据用户的输入执行不同的操作。为了达到这个目的,你可以使用 Python 内置的 `input` 方法。 @@ -817,7 +817,7 @@ Today's date: 12 又困惑了吗?我们输入了一个整数 12,但它还是告诉我们类型为字符串。这并不是一个 bug,它正是输入的工作机制。要将字符串转成整数,我们需要使用类型转换。 -## 类型转换: +## 类型转换 我们看到 `input` 方法为整数返回了字符串。如果我们现在想把这个输出和另一个整数进行比较,就需要一种将其转换回整数的方式。 @@ -1052,7 +1052,7 @@ dictA = dictB ``` -## 列表: +## 列表 假如你有一堆没有标签的数据,换句话说,每条数据都没有定义它的键。你会怎么保存它呢?这个时候就该列表出场了,数据的定义如下: @@ -1189,7 +1189,7 @@ Traceback (most recent call last): IndexError: list index out of range ``` -## 元组: +## 元组 元组(tuple)是一种有序但不可变(即数据不能被改变)的数据类型。 @@ -1407,7 +1407,7 @@ Python 会告诉我们:我们传递了三个参数,但是只有两个位置 所以当我们不知道函数的参数数量时该怎么做呢?我们可以使用 args 和 kwargs 解决这个问题。 -## Args: +## Args 当你不知道要给函数传递多少个参数时,可以使用 args 和 kwargs(kwargs 会在下面进行讨论)。 @@ -1447,7 +1447,7 @@ Python 会告诉我们:我们传递了三个参数,但是只有两个位置 10 ``` -## 关键字参数: +## 关键字参数 有时候,我们并不知道传递给函数的参数顺序。这时就可以使用关键词参数,不管我们以何种顺序传递参数,函数都会知道对应的参数值。来看这个示例: @@ -1498,7 +1498,7 @@ Username is Sharvin Age is 100 ``` -## 默认参数: +## 默认参数 有时,我们并不确定某个参数是否会在函数被调用时得到值。这种情况下可以使用默认参数,例如: @@ -1550,7 +1550,7 @@ SyntaxError: non-default argument follows default argument > **记住:** 所有的必备参数必须先于默认参数声明。 -## Kwargs: +## Kwargs 有时你并不知道会有多少个关键字参数会被传递给函数,这种情况可以使用 Kwargs。 @@ -1778,7 +1778,7 @@ Python 是一门多范式的编程语言,即它可以使用不同的方式解 首先,我们会理解类、对象和构造器。之后,我们会再次查看上述属性。如果你已经对类、对象和构造器有所了解,可以自由跳过。 -## 类: +## 类 Python 中一些可以直接使用的基本数据结构,比如数字、字符串和列表。这些都可以用来简单地表示名字、地方、价值,等等。 @@ -1811,7 +1811,7 @@ class Car: pass ``` -## 方法: +## 方法 方法(Method)看起来和函数一样,它们之间唯一的区别就是:方法依赖于对象。函数可以通过函数名调用,而方法必须通过它们的类引用调用。方法在类中定义。 @@ -1833,7 +1833,7 @@ Engine 总而言之,类提供定义的蓝图,但并不提供任何真实内容。`Car` 类定义了引擎(engine),但它并不会声明一辆特定汽车的引擎是什么。引擎是通过对象声明的。 -## 对象: +## 对象 对象(object)是类的实例。考虑上述汽车的例子,汽车就是我们的类,而 `toyota` 就是汽车的对象。我们可以创建多个对象的副本。每个对象都必须使用类进行定义。 @@ -1901,7 +1901,7 @@ Engine Wheel ``` -## 构造器: +## 构造器 `__init__` 方法是 Python 中的构造器方法(constructor method),构造器方法用于初始化数据。 @@ -1924,7 +1924,7 @@ Hello I am the constructor method. **注意:** 你永远都不必调用 `init()` 方法——它会在创建类实例时被自动调用。 -## 实例属性: +## 实例属性 所有的类都有对象,所有的对象都有属性(attributes)。属性就是对象具有的性质。我们使用 `__init__()`方法声明一个对象的初始属性。 @@ -1938,7 +1938,7 @@ class Car(): 在我们的示例中,每个 `Car()` 都有一个特定的型号(model),因此实例属性对每个实例来说都是唯一的。 -## 类属性: +## 类属性 我们看到实例属性是针对每个对象来说的,但是类属性对所有的实例来说都是一样的。我们借助类属性看一下汽车的示例: @@ -1950,7 +1950,7 @@ class Car(): 所以每辆汽车都可以有不同的型号,但是所有的汽车都只会有四个轮子。 -## Self: +## Self 现在我们来理解下 `self` 的含义,以及如何在面向对象编程中使用它。`self` 表示类的实例,我们可以通过这个关键字访问由构造器和类方法初始化的数据。 @@ -1988,7 +1988,7 @@ The brand is Bmw **注意:** `self` 是只是一个习惯,它并不是一个 Python 的关键词。方法中的 `self` 只是一个参数,我们可以使用另一个名字替换它。但是推荐使用 `self`,因为它会提高代码的可读性。 -## 继承: +## 继承 继承指一个类继承了另一个类的属性。 @@ -2119,7 +2119,7 @@ The name of our car is Audi r8 and color is black ``` -## Super: +## Super `super()` 返回一个父类的临时对象,允许我们调用父类的方法。 @@ -2158,7 +2158,7 @@ Car description: Car Name: Audi r8 Color: Red Brand name: Audi ``` -## 多重继承: +## 多重继承 多重继承(multiple inheritance)是指一个类从多个父类继承方法和属性。它允许我们在派生类或子类中使用多个基类或父类的性质。 @@ -2223,7 +2223,7 @@ Car description: Audi r8 is the car and it's cost is 2 cr 尽管多重继承可以被高效使用,但是也需要保持谨慎,避免程序变得模棱两可,防止程序对于其它程序员难于理解。 -## 多态: +## 多态 多态一词表示具有很多种形式。在编程中,多态表示同一个函数名(但是不同的函数签名)被用于不同的类型。 @@ -2275,7 +2275,7 @@ The Model is dominar. The color is black. ``` -## 封装: +## 封装 在大多数面向对象编程中,我们都可以限制对方法和变量的访问。这能防止数据被意外修改,也被称为封装。 @@ -2371,7 +2371,7 @@ Accessing Private variable: 5.2 L V10 封装使你可以更好地控制代码中的耦合程度,允许你在不影响其它部分的情况下修改类的实现。 -## 装饰器: +## 装饰器 假设你需要扩展多个函数的功能,你会怎么做? @@ -2457,7 +2457,7 @@ Line Number 3 - 错误捕获 - 为所有的函数和类扩展通用功能 -## 异常: +## 异常 在学习各种语法的过程中,我们也遇到了各种各样的错误。那些错误因为语法问题而出现。但是在真实应用中,错误(通常也称为 bug)并不只是由语法问题引起的,也可能是网络或其他原因。 @@ -2525,7 +2525,7 @@ Exception: Number below 6 are not allowed 异常有很多子类型,我推荐你阅读 [Python 文档][54],进一步理解它们。 -## 包的导入: +## 包的导入 你已经学完了 Python 的基础知识,现在已经做好构建出色应用程序的准备了。但是,等一等,我们还漏掉了一些重要的主题。 @@ -2618,7 +2618,7 @@ import json 这个库提供了两个处理 JSON 的方法,我们逐一进行理解: -### JSON loads: +### JSON loads 如果你想把 JSON 字符串转换回字典,就需要使用 `load` 方法。打开 Python shell,复制-粘贴以下代码: @@ -2634,7 +2634,7 @@ import json {'user_name': 'Sharvin', 'age': 1000} ``` -### JSON dumps: +### JSON dumps 现在,让我们用 `dumps` 方法将数据转换回 JSON 字符串格式吧。 diff --git a/chinese/articles/tips-to-boost-your-seo.md b/chinese/articles/tips-to-boost-your-seo.md index 60270fc99..9b08839e7 100644 --- a/chinese/articles/tips-to-boost-your-seo.md +++ b/chinese/articles/tips-to-boost-your-seo.md @@ -9,7 +9,7 @@ SEO (搜索引擎优化)对你的在线营销策略至关重要。它是优化 让你的网站在搜索引擎结果页面(SERPs)中获得顶级位置是将流量引导至你的网站并提高转化率的最快方法之一。 -本文将介绍提升SEO并发展你的网站的前10个技巧。 +本文将介绍提升 SEO 并发展你的网站的前 10 个技巧。 ## 如何创建内容销售策略 @@ -63,7 +63,7 @@ SEO (搜索引擎优化)对你的在线营销策略至关重要。它是优化 当每一个人进入你的网站,你需要想办法留住他们,低的跳出率对于谷歌来说表明你的内容是有效的,同时你会在搜索结果中的排名会高。 -记住,这对于你的网站不仅仅是关于带来更多的流量,而且保持流量来源于好的SEO策略。 +记住,这对于你的网站不仅仅是关于带来更多的流量,而且保持流量来源于好的 SEO 策略。 我知道你在想什么:怎么做可以减低我的跳出率。 @@ -111,10 +111,10 @@ GTmetrix 报告来自于 [Limey](https://limey.io/) ## 如何优化页面的 SEO -现在你已经掌握了一些基础知识,让我们来看看如何优化页面SEO。 -- 内容相关性:确保你的内容相关且最新。页面SEO的一个关键元素是拥有一个涵盖相关查询和用户可能提出的其他相关问题的页面。 +现在你已经掌握了一些基础知识,让我们来看看如何优化页面 SEO。 +- 内容相关性:确保你的内容相关且最新。页面 SEO 的一个关键元素是拥有一个涵盖相关查询和用户可能提出的其他相关问题的页面。 - 标题标签:确保你的标题标签相关且准确。你的标题标签应该让用户在访问你的网站之前了解他们将要点击的内容,所以确保它准确地反映了页面的主题,并对搜索者有意义(它不需要与你搜索的内容完全相同)。 -- Meta 描述:不要忘记 meta 描述!虽然它不会直接影响排名,但meta描述仍然很重要,因为它们会出现在搜索结果页面( SERPs ),如果正确优化,可以吸引用户点击。 +- Meta 描述:不要忘记 meta 描述!虽然它不会直接影响排名,但 meta 描述仍然很重要,因为它们会出现在搜索结果页面( SERPs ),如果正确优化,可以吸引用户点击。 - 标题:通过将内容分成具有清晰标题的部分来适当使用标题,尽可能包含关键词。 - 链接:链接到其他相关网站,并确保在可能的情况下进行内部链接,以帮助用户浏览你的网站(并包含带有上下文关键词的锚文本)。这也可以帮助提高页面权威。 - 图片和视频:有策略地使用图片和视频,使你的内容对不喜欢阅读长篇文字或更喜欢视觉效果的访问者更具吸引力。你还可以使用图片 alt 标签或视频字幕/文字记录(非自动化)来优化内容,因为谷歌也会抓取这些类型的媒体。 diff --git a/chinese/articles/top-statistics-concepts-to-know-before-getting-into-data-science.md b/chinese/articles/top-statistics-concepts-to-know-before-getting-into-data-science.md index 2f6a01b7e..7eac1998e 100644 --- a/chinese/articles/top-statistics-concepts-to-know-before-getting-into-data-science.md +++ b/chinese/articles/top-statistics-concepts-to-know-before-getting-into-data-science.md @@ -67,7 +67,7 @@ ## 什么是参数? -参数是描述总体特征的度量。比如,你正在观测一个国家的人口,你发现90%的居民为男性,10%为女性。数值90%和10%是整个人口的性别度量(描述统计)。它就是总体特征的一个参数。 +参数是描述总体特征的度量。比如,你正在观测一个国家的人口,你发现 90%的居民为男性,10%为女性。数值 90%和 10%是整个人口的性别度量(描述统计)。它就是总体特征的一个参数。 ## 什么是统计量 @@ -94,11 +94,11 @@ 常见的例子包括人名,狗的品种等等。然而,有一些数据看起来像数值数据,但也被归为类别数据。 -例如,假设你想根据年龄对某一群人进行分组,发现最低和最高年龄分别是10岁和60岁。然后你把年龄分成5个类别(10-20岁,21-30岁,31-40岁,41-50岁,51-60岁),并给每个类别分配数值,其中1代表10-20岁,2代表21-30岁,以此类推。 +例如,假设你想根据年龄对某一群人进行分组,发现最低和最高年龄分别是 10 岁和 60 岁。然后你把年龄分成 5 个类别(10-20 岁,21-30 岁,31-40 岁,41-50 岁,51-60 岁),并给每个类别分配数值,其中 1 代表 10-20 岁,2 代表 21-30 岁,以此类推。 在这种情况下,数值将被作为定性数据而不是定量数据来处理。随着不断深入数据科学,你将学会如何处理类别数据。 -了解了数据分类,现在我们来看看定量和定性数据在统计学中是如何计量的。统计学依据数据的计量尺度将数据划分为4类,它们分别是: +了解了数据分类,现在我们来看看定量和定性数据在统计学中是如何计量的。统计学依据数据的计量尺度将数据划分为 4 类,它们分别是: 1. 名义数据 2. 有序数据 @@ -117,14 +117,14 @@ **间隔数据**。间隔数据是指有排序的数值数据,并且可以进行测量(比如,数据之间可以相减)。温度计的读数就是一个间隔数据例子。 -例如,你可以测量摄氏4度和10度之间的差值,10度比4度高6度。间隔刻度数据有两个特点: +例如,你可以测量摄氏 4 度和 10 度之间的差值,10 度比 4 度高 6 度。间隔刻度数据有两个特点: 1. 它没有一个起点(也就是说,它不从零开始,可以有一个低于零的温度值) -2. 无法计算出它们的比例。比如,80摄氏度比20摄氏度高4倍,这不符合逻辑,因为它们没有一个起点。 +2. 无法计算出它们的比例。比如,80 摄氏度比 20 摄氏度高 4 倍,这不符合逻辑,因为它们没有一个起点。 **比率数据**。比率数据具有间隔数据可以被排序和计量的特点,但比率数据有一个共同的起点,所以能计算它们之间的比率。 -比如考试成绩分别为20、68、90或80分。我们可以给它排序,计算差值,并找到数值之间的比率,比如80分比20分高4倍。 +比如考试成绩分别为 20、68、90 或 80 分。我们可以给它排序,计算差值,并找到数值之间的比率,比如 80 分比 20 分高 4 倍。 现在我们已经介绍了数据的基本概念,接下来我们看看第一大类统计方法(描述统计)如何处理数据。 @@ -138,11 +138,11 @@ ### 什么是平均数 -当我们有一组像4,5,6,7,10这样的数值数据时,这组数据中的每个值都被称为一个数据点。我们可能想找到这组数据的平均值。 +当我们有一组像 4,5,6,7,10 这样的数值数据时,这组数据中的每个值都被称为一个数据点。我们可能想找到这组数据的平均值。 平均值本质上就是一组数据的平均数,计算方法是所有数据点的总和除以数据点的总个数。 -上面的数据集的总和是32,数据点的总个数是5,所以平均数,也就是平均值,是6.4。 +上面的数据集的总和是 32,数据点的总个数是 5,所以平均数,也就是平均值,是 6.4。 平均数只存在于定量数据上,定性数据没有平均数。 @@ -158,9 +158,9 @@ 离群值是指与其他数据点显著不同的异常数据点。离群值会导致我们得出错误的结论。下面就是一个典型的例子。 -假设你有一台机器能计算每天进入超市的顾客数。它某一周值为20、23、26、27、302。我们可以判断302就是一个离群值,因为它与其他数值有很大的差别。 +假设你有一台机器能计算每天进入超市的顾客数。它某一周值为 20、23、26、27、302。我们可以判断 302 就是一个离群值,因为它与其他数值有很大的差别。 -离群值可能是由突然的变化、机器故障或其他情况造成的。它们的出现会导致错误结论。例如,如果想知道平均超市顾客访问量,数值302就可能具有误导性,让我们认为平均访问量是75。 +离群值可能是由突然的变化、机器故障或其他情况造成的。它们的出现会导致错误结论。例如,如果想知道平均超市顾客访问量,数值 302 就可能具有误导性,让我们认为平均访问量是 75。 ## 什么是标准差? @@ -168,15 +168,15 @@ 标准差越接近于零,数据点就越趋近于平均值。 -标准差是一种非常重要的描述性统计。它能告诉我们数据集的离散程度。下图是一张数据正态分布图,X轴以标准差为刻度。 +标准差是一种非常重要的描述性统计。它能告诉我们数据集的离散程度。下图是一张数据正态分布图,X 轴以标准差为刻度。 ![1920px-Standard_deviation_diagram.svg](https://www.freecodecamp.org/news/content/images/2022/06/1920px-Standard_deviation_diagram.svg.png) -从上图可以看出,34.1% + 34.1% = 68.2% 的所有观测值都在一个标准差之内,或1σ(读作一个西格玛)。 +从上图可以看出,34.1% + 34.1% = 68.2% 的所有观测值都在一个标准差之内,或 1σ(读作一个西格玛)。 -加上13.6%+13.6%=27.2%的观测值在两个标准差之内,或2σ,以此类推。 +加上 13.6%+13.6%=27.2%的观测值在两个标准差之内,或 2σ,以此类推。 -不知道你是否听说过六西格玛这个工程学概念。它表示在质量保证过程中要考虑到六个标准差的可能性。这意味着除了最最极端的异常值之外,你要考虑到所有的情况。准确地说,是所有可能性的99.99966%。 +不知道你是否听说过六西格玛这个工程学概念。它表示在质量保证过程中要考虑到六个标准差的可能性。这意味着除了最最极端的异常值之外,你要考虑到所有的情况。准确地说,是所有可能性的 99.99966%。 现在我们已经掌握了一些以数字进行描述统计的方法,接下来让我们来看看一些常见的图形描述方法。 @@ -202,33 +202,33 @@ ![OutliersAnomalies--1-](https://www.freecodecamp.org/news/content/images/2022/06/OutliersAnomalies--1-.png) -图片由Ibrahim Ogunbiyi提供 +图片由 Ibrahim Ogunbiyi 提供 让我们来来看看上图都代表了什么意思。 **最小区间值**:最小区间值并不是最小值。它等于 ( Q1 -1.5*IQR) 。 -- Q1是第一四分位数。 -- IQR是第三四分位数和第一四分位数的差值。 +- Q1 是第一四分位数。 +- IQR 是第三四分位数和第一四分位数的差值。 最小区间界定了正常数据点的范围,它可以帮助我们发现那些小于正常范围的离散点。 -举个例子,假设我们的数据点是像这样分布[345, 402, 295, 386, 10]。我们可以判断数据点10是一个离群点,因为它远低于其他观察值。 +举个例子,假设我们的数据点是像这样分布[345, 402, 295, 386, 10]。我们可以判断数据点 10 是一个离群点,因为它远低于其他观察值。 -**第一四分位数**告诉我们25%的数据点低于这个数值,75%的数据点高于这个数值。它也被称为第25百分位数。 +**第一四分位数**告诉我们 25%的数据点低于这个数值,75%的数据点高于这个数值。它也被称为第 25 百分位数。 -**第二四分位数**告诉我们50%的数据点低于该数值,其余50%高于该数值。它也被称为第50百分位数。 +**第二四分位数**告诉我们 50%的数据点低于该数值,其余 50%高于该数值。它也被称为第 50 百分位数。 -**第三四分位数**告诉我们75%的数据点低于该数值,其余25%高于该数值。它也被称为第75百分位数。 +**第三四分位数**告诉我们 75%的数据点低于该数值,其余 25%高于该数值。它也被称为第 75 百分位数。 **最大区间值**,和最小区间值一样,也不是数据集中的最高值。它的计算公式是(Q3+1.5*IQR)。 - Q3 是第三四分位数。 -- IQR是第三四分位数和第一四分位数的差值。 +- IQR 是第三四分位数和第一四分位数的差值。 最大区间值可以帮助我们发现那些远高于其他观察值的离散点。 -比如,假设我们的数据点是像这样分布[645, 40, 25, 38, 42]。我们可以确定645是一个离散点,因为它是远远高于其他观察值。 +比如,假设我们的数据点是像这样分布[645, 40, 25, 38, 42]。我们可以确定 645 是一个离散点,因为它是远远高于其他观察值。 总结完不同类型的数据图形展示后,让我们进入最后一个话题: @@ -238,9 +238,9 @@ 如果一个变量中的数值变化引起另一个变量中的数值变化,我们就称这两个变量具有相关性。 -为了度量两个定量变量之间的相关性,我们常常用卡尔-皮尔逊公式来计算,其结果在-1和+1之间。 +为了度量两个定量变量之间的相关性,我们常常用卡尔-皮尔逊公式来计算,其结果在-1 和+1 之间。 -如果相关值接近1,表明这两个变量正相关(也就是说,当一个变量数值增加时,另一个变量数值也会增加)。如果数值接近-1,表明这两个变量负相关(即随着一个变量数值增加,另一个变量数值减少)。最后,如果相关值为0,则两个变量之间没有相关性。 +如果相关值接近 1,表明这两个变量正相关(也就是说,当一个变量数值增加时,另一个变量数值也会增加)。如果数值接近-1,表明这两个变量负相关(即随着一个变量数值增加,另一个变量数值减少)。最后,如果相关值为 0,则两个变量之间没有相关性。 你可以在[这里]((https://www.statisticshowto.com/probability-and-statistics/correlation-coefficient-formula/) )阅读更多关于相关性和卡尔-皮尔逊公式的知识。 diff --git a/chinese/articles/types-of-software-testing.md b/chinese/articles/types-of-software-testing.md index 085bbbd79..ba7938268 100644 --- a/chinese/articles/types-of-software-testing.md +++ b/chinese/articles/types-of-software-testing.md @@ -85,9 +85,9 @@ describe('Test to check add function', function(){ 你应该在软件开发生命周期早期使用静态应用程序安全性测试(SAST)。它是单元测试的一个例子。 -SAST反映了开发人员的能力,包括应用程序的通用设计和实现,因此它是白盒测试,或者叫由内而外的测试。 +SAST 反映了开发人员的能力,包括应用程序的通用设计和实现,因此它是白盒测试,或者叫由内而外的测试。 -SAST分析代码本身而不是最终的应用程序,你不需要执行代码就可以运行起来。 +SAST 分析代码本身而不是最终的应用程序,你不需要执行代码就可以运行起来。 ![](https://lh4.googleusercontent.com/R4aFSAcHZcrpNNzFnLlYk-vtXFq7QnjIJKzx_jvqmt-ycGE8CcMozgirFIxfXVXKkjYs1dV_nIQrhCFRC809_Kzp3FLvMqRw519XnDQHX8VEV0065Scw-SzxQlJg44xWeggZx2-e) @@ -95,29 +95,29 @@ SAST分析代码本身而不是最终的应用程序,你不需要执行代码 [云防御](https://www.clouddefense.ai/sast-static-application-security-testing)的安全分析师说, -> “SAST检查你的代码是否违反安全性规则,同时在源分支和目标分支之间比较已发现的漏洞……一旦项目新发现的漏洞会影响项目依赖性,你就会被通知到。” +> “SAST 检查你的代码是否违反安全性规则,同时在源分支和目标分支之间比较已发现的漏洞……一旦项目新发现的漏洞会影响项目依赖性,你就会被通知到。” 一旦发现漏洞,你就可以在最终应用程序构建之前把它们解决掉。 -你应该在软件项目的开发阶段就将SAST应用进去。在设计和编写应用程序时就将SAST扫描包含到开发流程中,不失为一个好方法。 +你应该在软件项目的开发阶段就将 SAST 应用进去。在设计和编写应用程序时就将 SAST 扫描包含到开发流程中,不失为一个好方法。 ### 动态应用程序安全性测试(DAST)定义 处于另一端的是动态应用程序安全性测试(DAST),它测试完整编译好的应用程序。你设计和运行这些测试时,不需要知道潜在的结构或代码。 -因为DAST采用黑客视角,它被称为黑盒测试,或由外向内的测试。 +因为 DAST 采用黑客视角,它被称为黑盒测试,或由外向内的测试。 -DAST通过攻击运行中的代码以及寻找可利用的潜在漏洞来进行测试。DAST可能采用跨站点脚本和SQL注入等常见攻击技术。 +DAST 通过攻击运行中的代码以及寻找可利用的潜在漏洞来进行测试。DAST 可能采用跨站点脚本和 SQL 注入等常见攻击技术。 -DAST在软件开发生命周期后面才进行,它是集成安全性测试的一个例子。由于很慢(一整个完整的应用程序的DAST测试平均可能需要花5到7天),它会为你揭示应用程序中黑客最有可能攻击的漏洞。 +DAST 在软件开发生命周期后面才进行,它是集成安全性测试的一个例子。由于很慢(一整个完整的应用程序的 DAST 测试平均可能需要花 5 到 7 天),它会为你揭示应用程序中黑客最有可能攻击的漏洞。 ### 交互式应用程序安全性测试定义 -交互式应用程序安全性测试 (IAST)是一种比较新的测试方法,它[结合了SAST和DAST的高效性](https://developer.ibm.com/recipes/tutorials/what-is-interactive-application-security-testing/),同时克服了与这些确立的测试相关联的问题。 +交互式应用程序安全性测试 (IAST)是一种比较新的测试方法,它[结合了 SAST 和 DAST 的高效性](https://developer.ibm.com/recipes/tutorials/what-is-interactive-application-security-testing/),同时克服了与这些确立的测试相关联的问题。 -IAST使用一种插入式的监控代理,来对应用程序进行持续实时扫描,从而发现错误和漏洞。尽管IAST是在应用程序运行时进行的, 它仍然被当作是一个SDLC早期的测试过程。 +IAST 使用一种插入式的监控代理,来对应用程序进行持续实时扫描,从而发现错误和漏洞。尽管 IAST 是在应用程序运行时进行的, 它仍然被当作是一个 SDLC 早期的测试过程。 -不管你在寻找什么样的软件进行测试,IAST最适合在QA(质量保证)环境中使用,同样,也很适合专门设计出来用于复制客户或者顾客真实使用产品的场景。 +不管你在寻找什么样的软件进行测试,IAST 最适合在 QA(质量保证)环境中使用,同样,也很适合专门设计出来用于复制客户或者顾客真实使用产品的场景。 ### 兼容性测试定义 @@ -132,7 +132,7 @@ IAST使用一种插入式的监控代理,来对应用程序进行持续实时 兼容性测试的例子包括: - 浏览器测试 (检查以确保你的网站或移动网址与不同的浏览器完全兼容) -- 移动测试 (确保您的应用程序与iOS和Android兼容) +- 移动测试 (确保您的应用程序与 iOS 和 Android 兼容) - 或软件测试 (如果你要创建多个需要彼此交互的软件应用程序,那么需要进行兼容性测试以确保它们正常运行)。 ## 软件测试金字塔之外 @@ -161,6 +161,6 @@ IAST使用一种插入式的监控代理,来对应用程序进行持续实时 ## 结论 -测试不仅仅是应用程序开发结束后QA部门应该做的事情。它也是[软件开发过程](/news/software-quality-assurance-guide/)的重要组成部分。 +测试不仅仅是应用程序开发结束后 QA 部门应该做的事情。它也是[软件开发过程](/news/software-quality-assurance-guide/)的重要组成部分。 了解你可以使用哪些测试以及它们如何工作,将帮助你保证应用程序运行良好、安全并且为最终用户所接受。 diff --git a/chinese/articles/ui-ux-design-guide.md b/chinese/articles/ui-ux-design-guide.md index 7c80e29e7..c478938dc 100644 --- a/chinese/articles/ui-ux-design-guide.md +++ b/chinese/articles/ui-ux-design-guide.md @@ -9,54 +9,54 @@ 不少人都有这种疑问。 -当下设计潮流和设计工具日新月异、推陈出新。前端设计越来越受到重视,[前端开发者](https://2020.stateofcss.com/en-US/)也开始涉足UI/UX设计。UI设计和UX设计常常被他们混为一谈。两者有本质上的区别,初学者应该充分了解两者区别。 +当下设计潮流和设计工具日新月异、推陈出新。前端设计越来越受到重视,[前端开发者](https://2020.stateofcss.com/en-US/)也开始涉足 UI/UX 设计。UI 设计和 UX 设计常常被他们混为一谈。两者有本质上的区别,初学者应该充分了解两者区别。 鉴于此,我和来自[Creative Tim](https://creative-tim.com)的团队伙伴做了很多研究,为大家提供了这份综合指南,展示了这两个大领域的核心内容与区别: -1. [**UI设计——界面外观如何**](#uidesignhowthingslook) -2. [**UX设计—— 产品效果如何**](#uxdesignhowthingswork) -3. [**UI设计和UX设计有何主要区别?**](#whatarethekeydifferencesbetweenuianduxdesign) -4. [**UI设计师和UX设计师分别做什么?**](#whatdouiuxdesignersdo) -5. [**将UI和UX工作混淆有什么弊端?**](#whatarethedisadvantagesofacombineduiuxrole) -6. [**UI/UX设计师如何协作?**](#howdouiuxdesignersworktogether) -7. [**UI/UX设计师收入水平如何?**](#whatarethesalariesofuianduxdesigners) -8. [**如何成为一名UI/UX专家?哪里找到相应的设计课程或教程?**](#howdoyoubecomeauioruxexpertwhatkindofdesigncoursestutorialsshouldyoutake) -9. [**你应该专攻哪一个领域?UI还是UX设计?**](#whichshouldyouspecializeinuioruxdesign) +1. [**UI 设计——界面外观如何**](#uidesignhowthingslook) +2. [**UX 设计—— 产品效果如何**](#uxdesignhowthingswork) +3. [**UI 设计和 UX 设计有何主要区别?**](#whatarethekeydifferencesbetweenuianduxdesign) +4. [**UI 设计师和 UX 设计师分别做什么?**](#whatdouiuxdesignersdo) +5. [**将 UI 和 UX 工作混淆有什么弊端?**](#whatarethedisadvantagesofacombineduiuxrole) +6. [**UI/UX 设计师如何协作?**](#howdouiuxdesignersworktogether) +7. [**UI/UX 设计师收入水平如何?**](#whatarethesalariesofuianduxdesigners) +8. [**如何成为一名 UI/UX 专家?哪里找到相应的设计课程或教程?**](#howdoyoubecomeauioruxexpertwhatkindofdesigncoursestutorialsshouldyoutake) +9. [**你应该专攻哪一个领域?UI 还是 UX 设计?**](#whichshouldyouspecializeinuioruxdesign) ## 1.UI 设计---界面外观如何? ![Image with the quote "A User interface is like a joke. If you have to explain it, it's not that good." — Martin LeBlac](https://www.freecodecamp.org/news/content/images/2021/07/quote-martin-leblac-1.jpg) -UI关注软件图形界面,包括按钮、布局、动画、转场、微交互等。简而言之,UI关于界面外观。 +UI 关注软件图形界面,包括按钮、布局、动画、转场、微交互等。简而言之,UI 关于界面外观。 -UI设计包括以下界面: +UI 设计包括以下界面: -- **[用户图形界面 (GUI)](https://en.wikipedia.org/wiki/Graphical_user_interface)**: GUI设计用户与系统控制工具之间的视觉交互。电脑桌面就是一种GUI。 -- **[语音控制界面 (VUI)](https://en.wikipedia.org/wiki/Voice_user_interface)**: VUI设计用户与系统的语音交互。三星的Bixby和苹果的Siri智能助手就是VUI例子。 -- **[手势识别界面](https://en.wikipedia.org/wiki/Gesture_recognition)**: 这种界面主要面向与虚拟现实(VR)以及其他基于手势的系统交互。用户面对的是3D空间。这是我们创建的可编辑[Soft UI VR控制面板](https://www.creative-tim.com/product/soft-ui-dashboard-pro)。 +- **[用户图形界面 (GUI)](https://en.wikipedia.org/wiki/Graphical_user_interface)**: GUI 设计用户与系统控制工具之间的视觉交互。电脑桌面就是一种 GUI。 +- **[语音控制界面 (VUI)](https://en.wikipedia.org/wiki/Voice_user_interface)**: VUI 设计用户与系统的语音交互。三星的 Bixby 和苹果的 Siri 智能助手就是 VUI 例子。 +- **[手势识别界面](https://en.wikipedia.org/wiki/Gesture_recognition)**: 这种界面主要面向与虚拟现实(VR)以及其他基于手势的系统交互。用户面对的是 3D 空间。这是我们创建的可编辑[Soft UI VR 控制面板](https://www.creative-tim.com/product/soft-ui-dashboard-pro)。 ![Example of a gesture-based interface.](https://www.freecodecamp.org/news/content/images/2021/07/soft-vr-2-1.gif) [来源](https://demos.creative-tim.com/soft-ui-dashboard-pro/pages/dashboards/default.html) -- 优秀的UI设计,需要考虑以下几点: +- 优秀的 UI 设计,需要考虑以下几点: - 设计致力于帮助用户以最小的努力完成任务。 - 设计赏心悦目,令人愉悦。 - 设计传达企业或组织品牌价值。 ![A list of principles for good design.](https://www.freecodecamp.org/news/content/images/2021/07/10-design-pinciples-1.jpg) -## **2. UX设计---产品效果如何?** +## **2. UX 设计---产品效果如何?** -UX设计关注用户与系统全生态的交互。符合逻辑的导航和自然丝滑的体验都属于UX范畴。简而言之,UX设计使用户拥有积极的体验。 +UX 设计关注用户与系统全生态的交互。符合逻辑的导航和自然丝滑的体验都属于 UX 范畴。简而言之,UX 设计使用户拥有积极的体验。 -UX设计流程主要包含以下步骤: +UX 设计流程主要包含以下步骤: - **交互设计**涉及用户如何使用系统交互组件(包括页面转场、动画、按钮等),以便完成任务。 - **用户研究**涉及广泛的调研,包括从新老用户收集意见反馈, 理解用户需求,并根据调研结果进行设计。 -- **信息架构**涉及信息内容的组织安排。为了便于用户完成任务,UX设计师需要厘清不同内容的类别关系,合理安排内容层级。 +- **信息架构**涉及信息内容的组织安排。为了便于用户完成任务,UX 设计师需要厘清不同内容的类别关系,合理安排内容层级。 -优秀的UX设计,需要达到以下标准: +优秀的 UX 设计,需要达到以下标准: - 产品易上手,易于理解。 - 产品能解决用户问题。 @@ -65,52 +65,52 @@ UX设计流程主要包含以下步骤: ### **认知偏差** -优秀的UX设计师需要时刻注意人类与生俱来的认知偏差。这些偏差会影响用户的使用体验。许多营销策略就是利用了这些认知偏差:: +优秀的 UX 设计师需要时刻注意人类与生俱来的认知偏差。这些偏差会影响用户的使用体验。许多营销策略就是利用了这些认知偏差:: ![A codex of cognitive biases](https://www.freecodecamp.org/news/content/images/2021/07/Cognitive_bias_codex_en.svg) [来源](https://upload.wikimedia.org/wikipedia/commons/6/65/Cognitive_bias_codex_en.svg) -## **3. UI设计和UX设计有何主要区别?** +## **3. UI 设计和 UX 设计有何主要区别?** ![Image of a branching walking path and dirt path.](https://www.freecodecamp.org/news/content/images/2021/07/ui-vs-ux-1.jpg) -UI设计和UX设计这两个词常常被混淆使用。 +UI 设计和 UX 设计这两个词常常被混淆使用。 尽管终端产品同时获益于这两种设计方法,但它们两者的设计过程却大相径庭。 -UX设计关注用户解决问题的全旅程,而UI设计则注重产品的外观。 +UX 设计关注用户解决问题的全旅程,而 UI 设计则注重产品的外观。 -以下是UX和UI设计的主要区别: +以下是 UX 和 UI 设计的主要区别: -- **关注点不同**:UI设计师的主要关注点是终端产品的视觉呈现,所以他们制作的是高保真的产品原型。UX设计师则更关注终端产品的目的和功能,以及产品的内在逻辑。此外,UI设计师处理产品的微观设计细节,UX设计师则更关注宏观的项目管理和分析。 -- **原型细节程度不同**:UI设计师设计彩色的产品原型,而UX设计师只绘制黑白的产品草图。 -- **使用工具不同**:UI设计师使用 [Sketch](https://www.sketch.com/)、[Flinto](https://www.flinto.com/)、 [Principle](https://principleformac.com/)等绘图工具,以及[InVision](https://www.invisionapp.com/)等设计协作工具。UX设计师使用线框图工具,比如 [Mockplus](https://www.mockplus.com/)。 -- **艺术成分不同**:UI设计师需要在设计中加入艺术成分,因为产品关系到终端用户的视听体验。而UX设计师需要更多的社会属性,因为他们需要理解用户想要获得何种体验。 +- **关注点不同**:UI 设计师的主要关注点是终端产品的视觉呈现,所以他们制作的是高保真的产品原型。UX 设计师则更关注终端产品的目的和功能,以及产品的内在逻辑。此外,UI 设计师处理产品的微观设计细节,UX 设计师则更关注宏观的项目管理和分析。 +- **原型细节程度不同**:UI 设计师设计彩色的产品原型,而 UX 设计师只绘制黑白的产品草图。 +- **使用工具不同**:UI 设计师使用 [Sketch](https://www.sketch.com/)、[Flinto](https://www.flinto.com/)、 [Principle](https://principleformac.com/)等绘图工具,以及[InVision](https://www.invisionapp.com/)等设计协作工具。UX 设计师使用线框图工具,比如 [Mockplus](https://www.mockplus.com/)。 +- **艺术成分不同**:UI 设计师需要在设计中加入艺术成分,因为产品关系到终端用户的视听体验。而 UX 设计师需要更多的社会属性,因为他们需要理解用户想要获得何种体验。 -这是同款产品UX线框图(左)和UI设计图(右): +这是同款产品 UX 线框图(左)和 UI 设计图(右): ![A simple UX design wireframe and a hi-fi UI prototype for the same web page.](https://www.freecodecamp.org/news/content/images/2021/07/soft-ui-design-system-1.jpg) [来源](https://www.creative-tim.com/product/soft-ui-design-system-pro) -## **UI设计师和UX设计师分别做什么?** +## **UI 设计师和 UX 设计师分别做什么?** ![A table showing the differences between UI and UX designers.](https://www.freecodecamp.org/news/content/images/2021/07/table-1.jpg) -想成为**UI设计师**需具备以下技能: +想成为**UI 设计师**需具备以下技能: - 适应敏捷工作模式。 - 能够绘制交互流程图、线框图、原型图等。 - 熟悉 [InVision](https://www.invisionapp.com/)、 [Sketch](https://www.sketch.com/) 、[Photoshop](https://www.photoshop.com/) 、[Figma](https://www.figma.com/) 等绘图工具。关于选择哪款绘图工具,可参看我们的这篇文章: -- 能够使用HTML5, CSS3和JavaScript三种前端语言。 +- 能够使用 HTML5, CSS3 和 JavaScript 三种前端语言。 - 能够完成从构思到交付视觉设计整套流程。 -- 与UX设计师、开发、测试人员和产品经理合作,为产品从设计到生产提供视觉创意。 +- 与 UX 设计师、开发、测试人员和产品经理合作,为产品从设计到生产提供视觉创意。 - 通过设计将公司的品牌和风格传达给用户。 - 通过用户分析和研究,改进产品外观效果。 - 根据不同设备尺寸,进行产品响应式设计,保证产品交互动画的一致性。 -想成为优秀的**UX设计师**需具备以下技能: +想成为优秀的**UX 设计师**需具备以下技能: - 适应敏捷工作模式。 - 对用户体验过程有深刻的理解。 @@ -118,90 +118,90 @@ UX设计关注用户解决问题的全旅程,而UI设计则注重产品的外 - 解决问题的能力,以及保持对设计、人、生活和技术的好奇心。 - 对相关利益方的管理和保持与客户的互动。 - 创建流程图、线框图、网站地图、原型图及其他用户体验交付产品。 -- 与开发人员、产品经理、UI设计师、外部利益相关方和测试工程师紧密合作,根据市场动态、用户反馈和技术限制来迭代设计。 +- 与开发人员、产品经理、UI 设计师、外部利益相关方和测试工程师紧密合作,根据市场动态、用户反馈和技术限制来迭代设计。 - 分析客户需求、竞争对手、产品结构,制定产品体验策略。 - 为移动端、网络端、桌面端和其他硬件终端创造无缝式的设计和交互策略。 -## **5. 将UI和UX工作混淆有什么弊端?** +## **5. 将 UI 和 UX 工作混淆有什么弊端?** ![A concept seat belt design from the 1960s which went around people's necks.](https://www.freecodecamp.org/news/content/images/2021/07/Artboard--4--1.jpg) -将UI和UX角色混淆就像同时带两顶帽子一样可笑。 +将 UI 和 UX 角色混淆就像同时带两顶帽子一样可笑。 -尽管许多组织都将UI/UX当做相同工种,但其实两者需要不同的技能。两者的关注点、思维方式和产品原型设计方法都大相径庭。 +尽管许多组织都将 UI/UX 当做相同工种,但其实两者需要不同的技能。两者的关注点、思维方式和产品原型设计方法都大相径庭。 将两个角色混淆就要求设计师不断地在概念和视觉之间不断切换。这不仅十分困难,还消耗了向两方面各自投入的时间精力。 -## **6. UI/UX设计师如何协作?** +## **6. UI/UX 设计师如何协作?** ![Designers working together.](https://www.freecodecamp.org/news/content/images/2021/07/Artboard-Copy-4--1--1.jpg) -尽管UI/UX设计师技能要求不同,但他们需要通力合作,为用户提供最佳体验。 +尽管 UI/UX 设计师技能要求不同,但他们需要通力合作,为用户提供最佳体验。 -UI设计需要具有美感,但如果没有一个好的UX设计,它的导航可能笨拙混乱。另一方面,一个产品的用户体验就算完美无缺,但如果没有一个好看的用户界面,就吸引不了用户。 +UI 设计需要具有美感,但如果没有一个好的 UX 设计,它的导航可能笨拙混乱。另一方面,一个产品的用户体验就算完美无缺,但如果没有一个好看的用户界面,就吸引不了用户。 -任何前端开发和设计过程都应该从了解用户需求开始。UX和UI设计师应该与开发者、产品经理和老板一道,共同确定最终产品的功能、外观和效果。 +任何前端开发和设计过程都应该从了解用户需求开始。UX 和 UI 设计师应该与开发者、产品经理和老板一道,共同确定最终产品的功能、外观和效果。 ![A quote from Steve Jobs that reads, "Design is not just what it looks like and feels like. Design is how it works."](https://www.freecodecamp.org/news/content/images/2021/07/quote-steve-jobs-1.jpg) -UX设计师通常参与产品设计的早期阶段,因为他们需要设计活动,帮助用户解决问题。这些活动包括用户分析和项目管理等。 +UX 设计师通常参与产品设计的早期阶段,因为他们需要设计活动,帮助用户解决问题。这些活动包括用户分析和项目管理等。 -之后,UI设计师根据UX设计师提供的草图进行美学和交互设计。 +之后,UI 设计师根据 UX 设计师提供的草图进行美学和交互设计。 -可以这样说,UI设计师和UX设计师携手并进,共同创造产品。即使有人身兼两职,也必须遵循不同的设计准则。 +可以这样说,UI 设计师和 UX 设计师携手并进,共同创造产品。即使有人身兼两职,也必须遵循不同的设计准则。 -## **7. UI/UX设计师收入水平如何?** +## **7. UI/UX 设计师收入水平如何?** ![A map showing different UI/UX designer salaries in different parts of the world.](https://www.freecodecamp.org/news/content/images/2021/07/ux-salary-1.jpg) -UI设计师在全世界的平均收入为5万元,美国的平均工资为9.1万,德国为5.7万,法国为4.7万,英国为6.7万。 +UI 设计师在全世界的平均收入为 5 万元,美国的平均工资为 9.1 万,德国为 5.7 万,法国为 4.7 万,英国为 6.7 万。 -UX设计师的收入略高,全球平均为5.2万。美国的UX设计师平均收入为10.2万,德国为5.3万,法国为4.9万,英国为6.8万。(来源: [Glassdoor](http://www.glassdoor.com/)) +UX 设计师的收入略高,全球平均为 5.2 万。美国的 UX 设计师平均收入为 10.2 万,德国为 5.3 万,法国为 4.9 万,英国为 6.8 万。(来源: [Glassdoor](http://www.glassdoor.com/)) 注:上面列出的所有工资都是该地区的平均水平,并且以美元为单位。 -## **8. 如何成为一名UI/UX专家?哪里找到相应的设计课程或教程?** +## **8. 如何成为一名 UI/UX 专家?哪里找到相应的设计课程或教程?** ![Animated people using laptops.](https://www.freecodecamp.org/news/content/images/2021/07/tutorials--1--1.jpg) -要想成为一名UI/UX专家,首先要对UI/UX设计非常感兴趣。确认了自己的兴趣,就应该找到相应资源,并参加设计课程,为就业市场做好准备。 +要想成为一名 UI/UX 专家,首先要对 UI/UX 设计非常感兴趣。确认了自己的兴趣,就应该找到相应资源,并参加设计课程,为就业市场做好准备。 -有一些在线课程可以帮助你入门UI设计, 如 [UI设计课程](https://careerfoundry.com/en/courses/become-a-ui-designer/) 和[学习UI设计](<https://learnui.design/>)。 +有一些在线课程可以帮助你入门 UI 设计, 如 [UI 设计课程](https://careerfoundry.com/en/courses/become-a-ui-designer/) 和[学习 UI 设计](<https://learnui.design/>)。 -对于UX设计,可以选择网上自学,报名网络课程。您可以参考这里介绍的[学习UI/UX的最佳在线课程](https://uxplanet.org/30-best-online-course-websites-to-learn-ui-ux-updated-6b104762731a),里面介绍了哪些网站提供免费课程,以及课程评价。 +对于 UX 设计,可以选择网上自学,报名网络课程。您可以参考这里介绍的[学习 UI/UX 的最佳在线课程](https://uxplanet.org/30-best-online-course-websites-to-learn-ui-ux-updated-6b104762731a),里面介绍了哪些网站提供免费课程,以及课程评价。 -还有许多大学公开的UX设计[付费课程](https://www.interaction-design.org/courses),或者来自[在线大学](https://online.rmit.edu.au/course/sc-user-experience-design-dtr105)的课程。同样可以参考这里介绍的[学习UI/UX的最佳在线课程](https://uxplanet.org/30-best-online-course-websites-to-learn-ui-ux-updated-6b104762731a),里面介绍了哪些网站提供免费课程,以及课程评价。 +还有许多大学公开的 UX 设计[付费课程](https://www.interaction-design.org/courses),或者来自[在线大学](https://online.rmit.edu.au/course/sc-user-experience-design-dtr105)的课程。同样可以参考这里介绍的[学习 UI/UX 的最佳在线课程](https://uxplanet.org/30-best-online-course-websites-to-learn-ui-ux-updated-6b104762731a),里面介绍了哪些网站提供免费课程,以及课程评价。 -假设你已经具备了成为UI/UX设计师所需的一些技能。在这种情况下,你也可以看看诸如[UX设计行为模式](ttps://uxdesign.cc/10-essential-cognitive-behavior-patterns-for-ux-design-7f0cc2e00d31)、[常用用户体验方法](https://uxdesign.cc/65-ux-methods-and-when-to-use-them-73e70c742d12)、[世界上最好的设计师的启发式图形设计](https://dribbble.com/tags/article)、以及[设计师应该阅读的学术研究论文](https://uxdesign.cc/5-academic-research-papers-every-designer-should-read-f24b170db295)等文章。 +假设你已经具备了成为 UI/UX 设计师所需的一些技能。在这种情况下,你也可以看看诸如[UX 设计行为模式](ttps://uxdesign.cc/10-essential-cognitive-behavior-patterns-for-ux-design-7f0cc2e00d31)、[常用用户体验方法](https://uxdesign.cc/65-ux-methods-and-when-to-use-them-73e70c742d12)、[世界上最好的设计师的启发式图形设计](https://dribbble.com/tags/article)、以及[设计师应该阅读的学术研究论文](https://uxdesign.cc/5-academic-research-papers-every-designer-should-read-f24b170db295)等文章。 -### **9. 你应该专攻哪一个领域?UI还是UX设计?** +### **9. 你应该专攻哪一个领域?UI 还是 UX 设计?** -专攻UI还是UX设计既取决于个人偏好,也要看自身特定技能组合。 +专攻 UI 还是 UX 设计既取决于个人偏好,也要看自身特定技能组合。 -然而,有大量的公司招聘身兼两职的UI/UX设计师。 +然而,有大量的公司招聘身兼两职的 UI/UX 设计师。 -虽然这两者的角色和职责各不相同,但UI/UX设计仍是一片蓝海,因此最好对两者都有足够的了解。 +虽然这两者的角色和职责各不相同,但 UI/UX 设计仍是一片蓝海,因此最好对两者都有足够的了解。 虽然两者很难平衡,但身兼两职当然报酬也更高,也利于提升自己在公司的重要性。 尽管如此,在两个角色之间来回切换十分困难,最好还是专攻一个领域。 -比如,你有艺术细胞,了解色彩组合搭配,知道如何提升产品视觉美感,那你更适合UI设计。 +比如,你有艺术细胞,了解色彩组合搭配,知道如何提升产品视觉美感,那你更适合 UI 设计。 -而如果你善于管理用户和相关利益方,具备出色的分析能力,并且了解如何改善用户体验,那么UX设计会更适合你。 +而如果你善于管理用户和相关利益方,具备出色的分析能力,并且了解如何改善用户体验,那么 UX 设计会更适合你。 ![A quote from Joe Sparano that reads, "Good design is obvious. Great design is transparent."](https://www.freecodecamp.org/news/content/images/2021/07/quote-joe-sparano-1.jpg) ## **10. 结语** -UI和UX设计都需要理解用户。 +UI 和 UX 设计都需要理解用户。 只要同用户有同理心,前端开发和设计并非十分复杂--有很多现成的组件、工具和套件可以使设计更高效。 -[Creative Tim](https://www.creative-tim.com/)为你提供了大量的免费、优质、可编辑的UI工具,如模板、仪表板和设计体系。 +[Creative Tim](https://www.creative-tim.com/)为你提供了大量的免费、优质、可编辑的 UI 工具,如模板、仪表板和设计体系。 -如果想用手绘组件的方式绘制插图,请关注Creative Tim的产品:[IRA Design](https://iradesign.io/)。 +如果想用手绘组件的方式绘制插图,请关注 Creative Tim 的产品:[IRA Design](https://iradesign.io/)。 欢迎在[Twitter](https://twitter.com/CreativeTim)上联系我们。 diff --git a/chinese/articles/ultimate-owners-guide-to-open-source.md b/chinese/articles/ultimate-owners-guide-to-open-source.md index 72ba27b1e..65a1ec945 100644 --- a/chinese/articles/ultimate-owners-guide-to-open-source.md +++ b/chinese/articles/ultimate-owners-guide-to-open-source.md @@ -279,7 +279,7 @@ 这一部分,以及即将到来的部分,也会与那些已经在维护开源项目并希望有所改进的人们有关。 -### 这一部分的基线: +### 这一部分的基线 > 你已经有了一个开源项目,我们可以在 Github 上访问它,并且可以很容易地通过某个程序包注册中心使用它。 @@ -380,7 +380,7 @@ API 参考很棒,但是没有什么比使用你的公共 API 的真实代码 现在,我们就来聊聊如何将公众的注意力吸引到你的项目上来,以及如何在吸引与正确管理贡献者上面对项目进行优化。 -### 这一部分的基线是: +### 这一部分的基线是 > *你已经有了一个开源项目,人们可以在 Github 上访问它,它有着良好的文档,并且可以很容易地通过某个程序包注册中心使用它。* diff --git a/chinese/articles/understand-call-apply-and-bind-in-javascript-with-examples.md b/chinese/articles/understand-call-apply-and-bind-in-javascript-with-examples.md index 812366b7c..d3134d1d1 100644 --- a/chinese/articles/understand-call-apply-and-bind-in-javascript-with-examples.md +++ b/chinese/articles/understand-call-apply-and-bind-in-javascript-with-examples.md @@ -5,9 +5,9 @@ ![How to Use the Call, Apply, and Bind Functions in JavaScript – with Code Examples](https://www.freecodecamp.org/news/content/images/size/w2000/2022/06/Screenshot-2022-06-14-at-8.53.33-PM-1.png) -在本文中,我将通过简单的示例来解释如何在JavaScript中使用call、apply和bind。 +在本文中,我将通过简单的示例来解释如何在 JavaScript 中使用 call、apply 和 bind。 -我们还将使用apply函数创建自己的map函数。 +我们还将使用 apply 函数创建自己的 map 函数。 话不多说,让我们开始吧! @@ -15,10 +15,10 @@ - [前提](#prerequisites) - [定义](#definitions) -- [如何在JavaScript中使用call函数](#how-to-use-the-call-function-in-javascript) -- [如何在JavaScript中使用apply函数](#how-to-use-the-apply-function-in-javascript) -- [如何在JavaScript中使用bind函数](#how-to-use-the-bind-function-in-javascript) -- [如何自定义map函数](#how-to-create-your-own-map-function) +- [如何在 JavaScript 中使用 call 函数](#how-to-use-the-call-function-in-javascript) +- [如何在 JavaScript 中使用 apply 函数](#how-to-use-the-apply-function-in-javascript) +- [如何在 JavaScript 中使用 bind 函数](#how-to-use-the-bind-function-in-javascript) +- [如何自定义 map 函数](#how-to-create-your-own-map-function) - [总结](#summary) <h2 id="prerequisites">前提</h2> @@ -27,7 +27,7 @@ - [函数](https://www.freecodecamp.org/news/what-is-a-function-javascript-function-examples/) - [函数原型](https://www.freecodecamp.org/news/all-you-need-to-know-to-understand-javascripts-prototype-a2bff2d28f03/) -- [this关键字](https://chinese.freecodecamp.org/news/what-is-this-in-javascript/) +- [this 关键字](https://chinese.freecodecamp.org/news/what-is-this-in-javascript/) <h2 id="definitions">定义</h2> @@ -39,7 +39,7 @@ **Bind** 函数创建一个稍后执行的函数,这个新函数的执行上下文由 `this` 提供。 -让我们先来看看call、apply和bind函数的例子,然后我们将来创建一个类似map的函数。 +让我们先来看看 call、apply 和 bind 函数的例子,然后我们将来创建一个类似 map 的函数。 <h2 id="how-to-use-the-call-function-in-javascript">如何在JavaScript中使用call函数</h2> @@ -57,13 +57,13 @@ func.call(thisObj, args1, args2, ...) - **func** 是通过不同`this`对象调用的函数 - **thisObj** 是用来替换函数`func`内部 `this`关键字的对象或者值 -- **args1, args2** args1, args2是参数,与改变后的`this`对象一起传递给调用的函数。 +- **args1, args2** args1, args2 是参数,与改变后的`this`对象一起传递给调用的函数。 -注意如果在不传入`thisObj`参数的情况下调用函数,JavaScript默认this值为全局对象。 +注意如果在不传入`thisObj`参数的情况下调用函数,JavaScript 默认 this 值为全局对象。 现在我们已经了解了`call`函数的背景,让我们通过一些示例来进一步了解它。 -### JS中如何在不同的上下文调用函数 +### JS 中如何在不同的上下文调用函数 考虑下面的例子。这个例子中有三个类 – `Car`、`Brand1`和`Brand2`: @@ -95,7 +95,7 @@ const newCarPrice = new definePrice(100000); 在这两个函数中, 我们都在`this`对象内调用`Car`函数,`this`对象分别代表了这两个函数。 例如在`setBrand`函数中,我们在代表函数上下文的`this`对象调用了`Car`函数,`definePrice`一样。 -### 在JS中如何在不传入参数的情况下调用call函数 +### 在 JS 中如何在不传入参数的情况下调用 call 函数 考虑下面的例子: @@ -110,7 +110,7 @@ function mountEntity(){ mountEntity.call(); //输出: Entity (obj) => console.log(obj) is mounted on [object Window] ``` -在这个例子中,调用`mountEntity`时,`thisObj`参数为空。 这时,JavaScript会指向全局对象。 +在这个例子中,调用`mountEntity`时,`thisObj`参数为空。 这时,JavaScript 会指向全局对象。 <h2 id="how-to-use-the-apply-function-in-javascript">如何在JavaScript中使用apply函数</h2> @@ -144,7 +144,7 @@ func.apply(thisObj, [args1, args2, ...]); func.apply(thisObj, new Array(args1, args2)); ``` -第三种语法可以传入arguments关键字: +第三种语法可以传入 arguments 关键字: ```Javascript func.apply(thisObj, arguments); @@ -212,7 +212,7 @@ func.bind(thisObj, arg1, arg2, ..., argN); - **func** 是通过不同`this`对象调用的函数 - **thisObj** 是用来替换函数`func`内部`this`关键字的对象或者值 -- **arg1, arg2…argN** – 和`call`函数类似,你可以传入一个或多个参数 +- **arg1, arg2……argN** – 和`call`函数类似,你可以传入一个或多个参数 `bind`函数返回一个一个新的函数,在这个函数中包含新的被调用函数内部`this`的值: @@ -222,7 +222,7 @@ func(arg1, arg2); 然后函数`func`根据参数被执行 -让我们一起来看一看如何在React类组件中使用`bind`函数: +让我们一起来看一看如何在 React 类组件中使用`bind`函数: ```JSX class App extends React.Component { @@ -241,10 +241,10 @@ class App extends React.Component { } ``` -考虑上述App组件,有以下几个组成部分: +考虑上述 App 组件,有以下几个组成部分: - `constructor` 调用类的函数,通过`new`关键字实例化 -- `render` 是执行或渲染JSX的函数 +- `render` 是执行或渲染 JSX 的函数 - `handleCode` 是打印组件的类方法 如果我们点击`Click Me`按钮,会得到报错`Cannot read properties of undefined (reading 'state')`。 @@ -257,7 +257,7 @@ class App extends React.Component { - 在类中`this`是一个普通的对象,并且有非静态类方法作为属性, 但是`handleCode`中的`this`指代另一个上下文 - 在这里`this`的值取决于函数被调用的位置,`handleCode`是在`onClick`事件中被调用 - 调用时`handleCode`函数内部的`this`被设置为`undefined` -- 我们尝试调用undefined的`state`属性,就导致了上文的报错 +- 我们尝试调用 undefined 的`state`属性,就导致了上文的报错 我们可以通过给`handleCode`方法的`this`指定上下文来解决这个问题,`bind`方法就派上用场了: @@ -283,7 +283,7 @@ class App extends React.Component { <h2 id="how-to-create-your-own-map-function">如何自定义map函数</h2> -在了解所有必要知识之后,让我们来自己创建一个map函数,我们先来看看自定义map函数需要了解什么: +在了解所有必要知识之后,让我们来自己创建一个 map 函数,我们先来看看自定义 map 函数需要了解什么: `map`函数的语法如下: @@ -293,7 +293,7 @@ arr.map(func) 其中: -- **arr**是map调用的数组 +- **arr**是 map 调用的数组 - **func** 是数组上每一个元素需要执行的函数 `map`函数的基本功能很简单: @@ -344,13 +344,13 @@ console.log(newArr); //输出:[2, 3, 4] <h2 id="summary">总结</h2> -本文通过示例展示了call、apply和bind的用法。 +本文通过示例展示了 call、apply 和 bind 的用法。 简单概括一下: -- Call、 apply和bind可以改变调用函数内部`this`关键字的上下文 +- Call、 apply 和 bind 可以改变调用函数内部`this`关键字的上下文 - 每个例子的调用方式不同 – `apply`通过一组数组执行,`call`执行结果类似但是参数由逗号隔开 -- 在react的类组件中,这些方法十分管用 +- 在 react 的类组件中,这些方法十分管用 感谢阅读! diff --git a/chinese/articles/understanding-redux-the-worlds-easiest-guide-to-beginning-redux.md b/chinese/articles/understanding-redux-the-worlds-easiest-guide-to-beginning-redux.md index 7989d93a1..c81393d6c 100644 --- a/chinese/articles/understanding-redux-the-worlds-easiest-guide-to-beginning-redux.md +++ b/chinese/articles/understanding-redux-the-worlds-easiest-guide-to-beginning-redux.md @@ -438,7 +438,7 @@ You’ll learn to flex your Redux muscles from a “known” concept such as Rea The React app we’ll be working with has been bootstrapped with `create-react-app`. Thus, the structure of the app is one you’re already used to. -You may grab the repo from [Github][13] if you want to follow along — which I recommend. +You may grab the repo from [Github][13] if you want to follow along—which I recommend. There’s an `index.js` entry file that renders an `<App />` component to the `DOM`. @@ -1813,7 +1813,7 @@ Having the rendering of the `<App/>` wrapped within a function means we can now store.subscribe(render); ``` -Where `render` is the entire render logic for the `<App />` — the one we just refactored. +Where `render` is the entire render logic for the `<App />`—the one we just refactored. You understand what’s happening here, right? @@ -1959,7 +1959,7 @@ There are two things you need to do. (ii) Handle the increase and decrease actions to actually affect the displayed time on the counter. -### Chapter 4: Building Skypey: A More Advanced Example. +### Chapter 4: Building Skypey: A More Advanced Example ![](https://cdn-media-1.freecodecamp.org/images/1*itX4GQXZ8hrq5Fr7t3zQyg.png) @@ -2500,7 +2500,7 @@ However, if `users` were stored in the state as an object, when retrieved and pa So, how do we resolve this? -#### **Solution #1a**: +#### **Solution #1a** Use `Lodash` for iterating over objects. @@ -2515,7 +2515,7 @@ import _ from 'lodash'; You call the `map` method on the `Lodash` object, `_.map()`. You pass in the object to be iterated over, and then pass in a callback function like you would with the default JavaScript `map` function. -#### **Solution #1b:** +#### **Solution #1b** Consider the usual way you’d map over an array to create a rendered list of users: @@ -2608,7 +2608,7 @@ You don’t want a message sent yesterday, showing like it was sent today. Order So, how would you solve this? -#### **Solution #2**: +#### **Solution #2** Keep a separate array of IDs to denote order. @@ -3197,7 +3197,7 @@ This is essentially the same as `{contacts: contacts}`. Hence, the `return state` statement within the reducer will return this value, `{contacts: contacts}` as the initial state of the application. -At this point, the app now works — just like before. The only difference here is that the initial state of the application is now managed by the Reducer. +At this point, the app now works—just like before. The only difference here is that the initial state of the application is now managed by the Reducer. Let’s keep refactoring. @@ -3209,7 +3209,7 @@ What’s the implication of this? It is like having just one Cashier in the entire bank hall. How scalable is that? -Even if the Cashier can do all the work effectively, it may be more manageable — and perhaps a better customer experience — to have more than one Cashier in the bank hall. +Even if the Cashier can do all the work effectively, it may be more manageable—and perhaps a better customer experience—to have more than one Cashier in the bank hall. Someone’s got to attend to everybody, and it’s a lot of work for just one person! @@ -3237,7 +3237,7 @@ However, consider the state object of the more complex Skypey application: ![](https://cdn-media-1.freecodecamp.org/images/1*FWFzkdKwxIVln7PQFsLKxQ.png) -Having a single reducer manage the entire state object is doable — but not the best approach. +Having a single reducer manage the entire state object is doable—but not the best approach. ![](https://cdn-media-1.freecodecamp.org/images/1*aD_wEZMqWfAOBtZpcWmE3g.png) @@ -4195,7 +4195,7 @@ Remember that the messages passed into `Chats` are specifically the messages for Whereas `state.messages` holds all the messages for every user contact, `state.messages[activeUserId]` will fetch the messages for the active user. -This is why every conversation is mapped to the user id of the user — for easy retrieval as we have done. +This is why every conversation is mapped to the user id of the user—for easy retrieval as we have done. Grab the active user’s messages and pass them as props in `Chats`. @@ -5092,7 +5092,7 @@ The following section is an excerpt from, [Understanding Redux 2][32]_._ > > _Setting up the Reducer, subscribing to the Store, listening and re-rendering upon state changes …… we can reduce some of the hassles._ > -> _Like Internet banking brings a breath of fresh air to the process of withdrawing money from your account, ‘bindings’ such as React-redux also make it slightly easier to use Redux with React — without performance concerns._ +> _Like Internet banking brings a breath of fresh air to the process of withdrawing money from your account, ‘bindings’ such as React-redux also make it slightly easier to use Redux with React—without performance concerns._ How sweet. diff --git a/chinese/articles/use-css-selectors-to-style-webpage.md b/chinese/articles/use-css-selectors-to-style-webpage.md index 957e506e1..1711da2cd 100644 --- a/chinese/articles/use-css-selectors-to-style-webpage.md +++ b/chinese/articles/use-css-selectors-to-style-webpage.md @@ -5,23 +5,23 @@ ![如何使用CSS选择器设计网页样式](https://www.freecodecamp.org/news/content/images/size/w2000/2021/05/CSS-Selectors.png) -CSS选择器是是CSS最重要的部分之一。它们能够使你在网页上针对你想要的HTML元素进行设置样式的能力。 +CSS 选择器是是 CSS 最重要的部分之一。它们能够使你在网页上针对你想要的 HTML 元素进行设置样式的能力。 -如果没有CSS选择器,你就无法将页面设置成你想要的样子。 +如果没有 CSS 选择器,你就无法将页面设置成你想要的样子。 -值得庆幸的是CSS选择器已经存在一段时间了,你可以随心所欲地对你的元素进行样式设计。 +值得庆幸的是 CSS 选择器已经存在一段时间了,你可以随心所欲地对你的元素进行样式设计。 -但如果你真的想要释放CSS的力量并创建令人惊叹的元素,那么你需要了解你能用CSS选择器做什么。也就是说,你需要先了解基本的CSS选择器,然后再学习高级的CSS选择器。 +但如果你真的想要释放 CSS 的力量并创建令人惊叹的元素,那么你需要了解你能用 CSS 选择器做什么。也就是说,你需要先了解基本的 CSS 选择器,然后再学习高级的 CSS 选择器。 -这篇文章将对这两方面进行研究。到最后,你会发现,你已经开始释放CSS选择器的力量并创造自己的不可思议的元素。所以,让我们从什么是CSS选择器开始吧。 +这篇文章将对这两方面进行研究。到最后,你会发现,你已经开始释放 CSS 选择器的力量并创造自己的不可思议的元素。所以,让我们从什么是 CSS 选择器开始吧。 -## 什么是CSS选择器? +## 什么是 CSS 选择器? -如果你之前写过任何CSS,那么你可能已经见过CSS选择器了。它们是CSS规则的第一部分,你可以使用CSS选择器来选择你要设置样式的HTML元素。 +如果你之前写过任何 CSS,那么你可能已经见过 CSS 选择器了。它们是 CSS 规则的第一部分,你可以使用 CSS 选择器来选择你要设置样式的 HTML 元素。 -在CSS里,选择器由CSS选择器规范来定义。这意味着每个选择器必须得到浏览器的[支持](https://www.w3.org/TR/selectors-3/#selectors)才能发挥作用。 +在 CSS 里,选择器由 CSS 选择器规范来定义。这意味着每个选择器必须得到浏览器的[支持](https://www.w3.org/TR/selectors-3/#selectors)才能发挥作用。 -CSS选择器通常分为五个不同的种类。这篇文章将从基础和高级两个关键类别来研究它们。以下是这五个类别。 +CSS 选择器通常分为五个不同的种类。这篇文章将从基础和高级两个关键类别来研究它们。以下是这五个类别。 1. 简单选择器 2. 组合选择器 @@ -33,11 +33,11 @@ CSS选择器通常分为五个不同的种类。这篇文章将从基础和高 # 基础选择器 -你可能见过许多类型的选择器 - 这些基本的CSS选择器足以让你构建时尚的网页。让我们来看看每个基本的CSS选择器,以确保我们了解它们的作用。 +你可能见过许多类型的选择器 - 这些基本的 CSS 选择器足以让你构建时尚的网页。让我们来看看每个基本的 CSS 选择器,以确保我们了解它们的作用。 ## CSS 元素(标签)选择器 -CSS元素选择器根据元素名来选择HTML元素。在HTML中,元素名就是类似于`h1`、 `p`的东西,或者类似于`article`或`footer`之类有意义的名字。因此,元素选择器选择所有具有你指定的名称的HTML元素。 +CSS 元素选择器根据元素名来选择 HTML 元素。在 HTML 中,元素名就是类似于`h1`、 `p`的东西,或者类似于`article`或`footer`之类有意义的名字。因此,元素选择器选择所有具有你指定的名称的 HTML 元素。 让我们来看看元素选择器的例子吧: @@ -61,11 +61,11 @@ article { ## CSS ID 选择器 -ID选择器选择具有匹配的ID属性的HTML元素。由于在一个HTML文档中不能有一个以上具有相同ID的元素,这个选择器允许你选择一个单独的元素。这意味着所选元素将是唯一的。 +ID 选择器选择具有匹配的 ID 属性的 HTML 元素。由于在一个 HTML 文档中不能有一个以上具有相同 ID 的元素,这个选择器允许你选择一个单独的元素。这意味着所选元素将是唯一的。 -要选择一个具有特定ID的元素,你可以使用`#`(哈希)字符,后面跟着是HTML元素的ID。在这种情况下它看起来就像这样`#id-name`。 +要选择一个具有特定 ID 的元素,你可以使用`#`(哈希)字符,后面跟着是 HTML 元素的 ID。在这种情况下它看起来就像这样`#id-name`。 -让我们来看看ID选择器的例子吧。 +让我们来看看 ID 选择器的例子吧。 ```css #projects-flex-container { @@ -74,17 +74,17 @@ ID选择器选择具有匹配的ID属性的HTML元素。由于在一个HTML文 } ``` -在上面这个例子里,我们选择了ID为`#projects-flex-container`的单个元素,并对其应用了样式。这个样式只适用于那些单独的元素。 +在上面这个例子里,我们选择了 ID 为`#projects-flex-container`的单个元素,并对其应用了样式。这个样式只适用于那些单独的元素。 -不过,有一点需要注意的是,在使用ID选择器时,你应该小心。由于ID选择器不能在其他元素上重复使用,你应该问你自己是否需要使用ID选择器来选择该元素。 +不过,有一点需要注意的是,在使用 ID 选择器时,你应该小心。由于 ID 选择器不能在其他元素上重复使用,你应该问你自己是否需要使用 ID 选择器来选择该元素。 ## CSS 类选择器 -类选择器选择具有相同的class属性的HTML元素。类选择器对于定位多个元素很有用,比如你想要匹配样式的卡片或图像。 +类选择器选择具有相同的 class 属性的 HTML 元素。类选择器对于定位多个元素很有用,比如你想要匹配样式的卡片或图像。 要选择具有特定类别的元素,你可以使用一个`.`字符(句号),然后在它后面加上类别的名称。 -让我们来看看CSS类选择器的例子。 +让我们来看看 CSS 类选择器的例子。 ```css .project-card { @@ -94,11 +94,11 @@ ID选择器选择具有匹配的ID属性的HTML元素。由于在一个HTML文 } ``` -上述的例子中,我们使用CSS类选择器选择了类名为`project-card`的所有元素。所有具有`project-card`类的元素将被应用列出的样式。 +上述的例子中,我们使用 CSS 类选择器选择了类名为`project-card`的所有元素。所有具有`project-card`类的元素将被应用列出的样式。 ## 通用选择器 -通用选择器用来选择所有的HTML元素,这意味者你页面上的每一个元素,从标题到页脚。你会经常使用它来页面的边距和填充保持一致,或者重置样式。 +通用选择器用来选择所有的 HTML 元素,这意味者你页面上的每一个元素,从标题到页脚。你会经常使用它来页面的边距和填充保持一致,或者重置样式。 通用选择器的语法是`*`字符(星号)。 @@ -109,13 +109,13 @@ ID选择器选择具有匹配的ID属性的HTML元素。由于在一个HTML文 } ``` -上述的例子中,通过使用通用选择器,它将整个页面的margin和padding清零。 +上述的例子中,通过使用通用选择器,它将整个页面的 margin 和 padding 清零。 -# 什么是CSS分組选择器 +# 什么是 CSS 分組选择器 -在我们讨论高级CSS选择器之前,我们需要快速了解一下分組选择器。这是一种常见的做法,你会在外面经常看到,它有助于使你的代码干净和可读。 +在我们讨论高级 CSS 选择器之前,我们需要快速了解一下分組选择器。这是一种常见的做法,你会在外面经常看到,它有助于使你的代码干净和可读。 -分组允许你一次选择多个HTML元素,并且只声明一次它们的样式。 +分组允许你一次选择多个 HTML 元素,并且只声明一次它们的样式。 让我们来看看组合选择器的例子吧。 @@ -139,7 +139,7 @@ h3 { } ``` -上面的CSS代码中,我们有三个元素`h1`、`h2`和`h3`,这三个元素都有相同的样式定义。因此,我们可以通过选择器分组来清理我们的代码。 +上面的 CSS 代码中,我们有三个元素`h1`、`h2`和`h3`,这三个元素都有相同的样式定义。因此,我们可以通过选择器分组来清理我们的代码。 为了给选择器分组,我们用`,`字符(逗号)将每个选择器分开。 @@ -155,7 +155,7 @@ h1, h2, h3 { 请注意,分組选择器可用于本文中所提及的所有选择器,也就是说,选择器不一定是要和上述代码一样的,也可以是其他的。 -如果我们想让它们共享样式定义,我们可以把一个类选择器和一个ID选择器分组。而我们可以把匹配的样式属性和值分组,然后在每个元素上定义不同的定义。 +如果我们想让它们共享样式定义,我们可以把一个类选择器和一个 ID 选择器分组。而我们可以把匹配的样式属性和值分组,然后在每个元素上定义不同的定义。 让我们拓展我们的例子来理解这个概念。 @@ -183,27 +183,27 @@ h3 { } ``` -这就是所有的基本选择器,如果你想学好CSS,你需要了解每个选择器的作用。有了这些知识,你现在应该可以做到这一点了。 +这就是所有的基本选择器,如果你想学好 CSS,你需要了解每个选择器的作用。有了这些知识,你现在应该可以做到这一点了。 -如果你想提高你的CSS水平,那么你将要了解高级的CSS选择器。 +如果你想提高你的 CSS 水平,那么你将要了解高级的 CSS 选择器。 -# 高级CSS选择器 +# 高级 CSS 选择器 -高级CSS选择器允许你突破CSS的界限,它们可以高度具体的确定你要选择哪些元素,以及选择该元素时它处于什么状态。 +高级 CSS 选择器允许你突破 CSS 的界限,它们可以高度具体的确定你要选择哪些元素,以及选择该元素时它处于什么状态。 让我们直接进入一些高级选择器,看看属性选择器。 -## CSS属性选择器 +## CSS 属性选择器 -属性选择器允许你根据某个属性是否存在来选择元素。换句话来说,这个CSS选择器将匹配页面上的任何元素,如果它有某个属性的话。 +属性选择器允许你根据某个属性是否存在来选择元素。换句话来说,这个 CSS 选择器将匹配页面上的任何元素,如果它有某个属性的话。 -属性是添加到HTML元素开始标签中的内容,它可以是`id`、`name`或`value`等内容。 +属性是添加到 HTML 元素开始标签中的内容,它可以是`id`、`name`或`value`等内容。 ```HTML <a title="Learn to code for free!" href="https://www.freecodecamp.org/">Learn to code</a> ``` -title是a元素中的一个属性。 +title 是 a 元素中的一个属性。 有七个属性选择器,它们分别允许你根据一个属性是否存在以及值可能包含的内容来查找元素。 @@ -236,7 +236,7 @@ a[title] { } ``` -在上述的例子中,我们的当前属性选择器查找了任何包含`title`属性的`a`元素,并对它们应用样式。其他所有没有包含title属性的a元素则不会应用这些样式。 +在上述的例子中,我们的当前属性选择器查找了任何包含`title`属性的`a`元素,并对它们应用样式。其他所有没有包含 title 属性的 a 元素则不会应用这些样式。 ### 等于属性选择器 @@ -304,14 +304,14 @@ a[href*="peterlunch"] { ## 组合选择器 -下一个高级选择器是组合选择器。这个选择器可以结合一个或以上的CSS选择器。在CSS中,有四种类型的组合选择器。 +下一个高级选择器是组合选择器。这个选择器可以结合一个或以上的 CSS 选择器。在 CSS 中,有四种类型的组合选择器。 1. 后代选择器 2. 直接子代选择器 3. 相邻兄弟选择器 4. 通用兄弟选择器 -要理解这些选择器如何工作,你必须首先理解HTML遵循一个家族树的层级结构。这意味着有一个父元素,它可以包含子元素,而子元素可以有子元素。以此类推,就像一颗家庭树。 +要理解这些选择器如何工作,你必须首先理解 HTML 遵循一个家族树的层级结构。这意味着有一个父元素,它可以包含子元素,而子元素可以有子元素。以此类推,就像一颗家庭树。 ```html <div> <!--parent--> @@ -362,7 +362,7 @@ div > p { } ``` -参照上面的HTML层级结构的例子,这个选择器将将只找到第一个`p`标签,而不是`article`标签中的`p`标签,因为它们不是父元素`div`的直接子元素。 +参照上面的 HTML 层级结构的例子,这个选择器将将只找到第一个`p`标签,而不是`article`标签中的`p`标签,因为它们不是父元素`div`的直接子元素。 ### 相邻兄弟选择器 @@ -412,7 +412,7 @@ button:hover { 在上述的例子中,当用户在一个按钮上悬停时,背景颜色将会变为红色。 -如果你想要更好的理解伪类选择器,我推荐你阅读Nash Vail的[这篇文章](/news/explained-css-pseudo-classes-cef3c3177361/),他对伪类的解释非常好。 +如果你想要更好的理解伪类选择器,我推荐你阅读 Nash Vail 的[这篇文章](/news/explained-css-pseudo-classes-cef3c3177361/),他对伪类的解释非常好。 接下来是我在[这篇文章](https://peterlunch.com/css-pseudo-elements/)中写过的伪元素选择器。这些选择器选择元素中的一部分,一个元素中的一部分可能是元素的第一个字母或者是该元素前后的内容。 @@ -439,6 +439,6 @@ p::first-letter { ## 总结 -现在你应该对CSS选择器有了很好的理解,以及如何使用它们来查找网页上的HTML元素。 +现在你应该对 CSS 选择器有了很好的理解,以及如何使用它们来查找网页上的 HTML 元素。 希望你喜欢阅读这篇文章。如果你在这篇文章中学到了什么,那么请查看我的[其他文章](https://bit.ly/2Re6Vdf)或订阅[我的频道](https://mailchi.mp/bfaa8a288d7c/7o1pve3bv9)以获得非常好的新手教程。 diff --git a/chinese/articles/use-google-search-tips.md b/chinese/articles/use-google-search-tips.md index 38c783229..a464140d5 100644 --- a/chinese/articles/use-google-search-tips.md +++ b/chinese/articles/use-google-search-tips.md @@ -15,17 +15,17 @@ ![Google search exact match example](https://www.freecodecamp.org/news/content/images/2021/07/google-exact.PNG) -## 2\. 使用AND操作符 +## 2. 使用 AND 操作符 -AND运算符将只返回与两个术语相关的结果。在下面的例子中,你可以看到这个运算符的完美使用情况。 +AND 运算符将只返回与两个术语相关的结果。在下面的例子中,你可以看到这个运算符的完美使用情况。 `html AND css` ![Google search with AND keyword](https://www.freecodecamp.org/news/content/images/2021/07/google-and.PNG) -## 3\. 使用OR操作符 +## 3. 使用 OR 操作符 -你可以使用OR运算符来获得与你输入的一个搜索词相关的结果。 +你可以使用 OR 运算符来获得与你输入的一个搜索词相关的结果。 `(javascript OR python) free course` @@ -33,7 +33,7 @@ AND运算符将只返回与两个术语相关的结果。在下面的例子中 ## 4\. 使用-(减)运算符 -操作符 `-` 将排除包含某个术语或短语的结果。因此,在我们的案例中,我们希望得到与JavaScript相关的结果,但排除其中的任何CSS结果。 +操作符 `-` 将排除包含某个术语或短语的结果。因此,在我们的案例中,我们希望得到与 JavaScript 相关的结果,但排除其中的任何 CSS 结果。 `javascript -css` @@ -59,7 +59,7 @@ AND运算符将只返回与两个术语相关的结果。在下面的例子中 ## 7\. 如何找到一个特定的文件类型 -你也可以使用这个非常有用的功能来帮助你找到一个特定的文件类型。如果你在寻找一些材料,如PDF,它就很方便。如果你以前没有使用过它,从现在开始你会使用。 +你也可以使用这个非常有用的功能来帮助你找到一个特定的文件类型。如果你在寻找一些材料,如 PDF,它就很方便。如果你以前没有使用过它,从现在开始你会使用。 `filetype:pdf learn css` diff --git a/chinese/articles/use-svg-images-in-css-html.md b/chinese/articles/use-svg-images-in-css-html.md index faf966edd..4d005071d 100644 --- a/chinese/articles/use-svg-images-in-css-html.md +++ b/chinese/articles/use-svg-images-in-css-html.md @@ -5,34 +5,34 @@ ![How to Use SVG Images in CSS and HTML – A Tutorial for Beginners](https://www.freecodecamp.org/news/content/images/size/w2000/2020/11/Screen-Shot-2020-11-15-at-3.59.07-PM.png) -SVG指可缩放矢量图形,是一种可以使用可扩展标记语言(XML)进行编写的矢量图形。 +SVG 指可缩放矢量图形,是一种可以使用可扩展标记语言(XML)进行编写的矢量图形。 -本教程将解释为什么需要使用SVG图像,以及如何在CSS和HTML中使用它们。 +本教程将解释为什么需要使用 SVG 图像,以及如何在 CSS 和 HTML 中使用它们。 -# 为什么需要使用SVG图像? +# 为什么需要使用 SVG 图像? -使用SVG图像的原因有很多,其中一些原因如下所示: +使用 SVG 图像的原因有很多,其中一些原因如下所示: -- SVG图像在放大或缩小的时候不会失真。 +- SVG 图像在放大或缩小的时候不会失真。 -- SVG图像可以使用IDE和文本编辑器进行创建和编辑。 +- SVG 图像可以使用 IDE 和文本编辑器进行创建和编辑。 -- SVG图像支持动画和交互。 +- SVG 图像支持动画和交互。 -- SVG图像文件小并且具有高扩展性。 +- SVG 图像文件小并且具有高扩展性。 -- SVG图像可以被搜索、索引、脚本化以及压缩。 +- SVG 图像可以被搜索、索引、脚本化以及压缩。 -现在来看如何使用SVG图像。 +现在来看如何使用 SVG 图像。 -# 如何下载本教程中使用的SVG图像 +# 如何下载本教程中使用的 SVG 图像 -如果你想使用本教程中的SVG图像,可以通过以下步骤下载。 +如果你想使用本教程中的 SVG 图像,可以通过以下步骤下载。 - 访问 [unDraw](https://undraw.co)。 @@ -51,49 +51,49 @@ SVG指可缩放矢量图形,是一种可以使用可扩展标记语言(XML) ![Download the SVG file](https://i.imgur.com/qGrT73n.png) -如果你正确执行了以上步骤,本教程中的SVG图像应该已经在你的电脑中了。 +如果你正确执行了以上步骤,本教程中的 SVG 图像应该已经在你的电脑中了。 ![](https://i.imgur.com/3uCGy6B.png) -现在,使用你喜爱的IDE或者文本编辑器打开这个SVG图像,并将它重命名为**happy.svg**或者任何你喜欢的名字。 +现在,使用你喜爱的 IDE 或者文本编辑器打开这个 SVG 图像,并将它重命名为**happy.svg**或者任何你喜欢的名字。 -# 如何在CSS和HTML中使用SVG图像 +# 如何在 CSS 和 HTML 中使用 SVG 图像 There are several different ways to use SVG images in CSS and HTML. We will explore six different methods in this tutorial. -在CSS和HTML中使用SVG图像有许多不同的方式。接下来,本教程将介绍其中6种方式。 +在 CSS 和 HTML 中使用 SVG 图像有许多不同的方式。接下来,本教程将介绍其中 6 种方式。 -## 1\. 如何利用`<img>`元素使用SVG图像 +## 1\. 如何利用`<img>`元素使用 SVG 图像 -利用`<img>`元素将SVG图像添加到网页中是最简单的方式。使用这种方法,需要在HTML文档中添加一个`<img>`元素,并且在该元素的`src`属性中指定该SVG图像的引用,代码如下所示: +利用`<img>`元素将 SVG 图像添加到网页中是最简单的方式。使用这种方法,需要在 HTML 文档中添加一个`<img>`元素,并且在该元素的`src`属性中指定该 SVG 图像的引用,代码如下所示: ```html <img src = "happy.svg" alt="My Happy SVG"/> ``` -如果你已经从unDraw网站上下载了SVG图像并将它重命名为**happy.svg**,你可以继续向下进行并将上面的代码片段添加到你的HTML文档中。 +如果你已经从 unDraw 网站上下载了 SVG 图像并将它重命名为**happy.svg**,你可以继续向下进行并将上面的代码片段添加到你的 HTML 文档中。 -如果每一步都操作正确,你的页面应该同下面demo中所示内容类似。👀 +如果每一步都操作正确,你的页面应该同下面 demo 中所示内容类似。👀 -当使用`<img>`标签添加SVG图像时没有指定大小,浏览器将默认显示原始SVG图像的大小。 +当使用`<img>`标签添加 SVG 图像时没有指定大小,浏览器将默认显示原始 SVG 图像的大小。 -例如,上面的demo中,没有修改SVG图像的大小,所以它保持了原始的大小(宽为`600.53015px`,高为`915.11162px`)。 +例如,上面的 demo 中,没有修改 SVG 图像的大小,所以它保持了原始的大小(宽为`600.53015px`,高为`915.11162px`)。 -**注意:** 为了改变原始大小,你需要像以下demo中一样使用CSS指定`img`的`width`和`height`,也可以直接更改原始图像的宽和高。 +**注意:** 为了改变原始大小,你需要像以下 demo 中一样使用 CSS 指定`img`的`width`和`height`,也可以直接更改原始图像的宽和高。 -虽然可以通过`<img>`标签来改变SVG图像的大小,但是在对SVG图像的主要样式进行修改时,这种方式仍有一些限制。 +虽然可以通过`<img>`标签来改变 SVG 图像的大小,但是在对 SVG 图像的主要样式进行修改时,这种方式仍有一些限制。 -## 2\. 如何在CSS的 `background-image`属性中使用SVG图像 +## 2. 如何在 CSS 的 `background-image`属性中使用 SVG 图像 This is similar to adding SVG to an HTML document using the `<img>` tag. But this time we do it with CSS instead of HTML as you can see in the code snippet below. -这种方式类似于使用`<img>`标签将SVG图像添加到HTML文档的方式,但是这种方法使用CSS操作代替了对HTML的操作,具体方式如下所示。 +这种方式类似于使用`<img>`标签将 SVG 图像添加到 HTML 文档的方式,但是这种方法使用 CSS 操作代替了对 HTML 的操作,具体方式如下所示。 ```css= body { background-image: url(happy.svg); @@ -101,17 +101,17 @@ body { ``` -使用SVG图像作为CSS背景图像与使用`<img>`标签的方式具有类似的限制。不过,这种方式支持更多自定义操作。 +使用 SVG 图像作为 CSS 背景图像与使用`<img>`标签的方式具有类似的限制。不过,这种方式支持更多自定义操作。 -可以查看下面的demo并随意使用CSS对其进行修改。 +可以查看下面的 demo 并随意使用 CSS 对其进行修改。 -## 3\. 如何使用内联SVG图像 +## 3. 如何使用内联 SVG 图像 -使用`<svg> </svg>`标签可以将SVG图像直接写入HTML文档中。 +使用`<svg> </svg>`标签可以将 SVG 图像直接写入 HTML 文档中。 -为了实现这种方式,需要使用VS code或者其他IDE打开SVG图像文件并复制其中的代码,然后将其粘贴到你的HTML文档中的`<body>`元素中,如下所示。 +为了实现这种方式,需要使用 VS code 或者其他 IDE 打开 SVG 图像文件并复制其中的代码,然后将其粘贴到你的 HTML 文档中的`<body>`元素中,如下所示。 ```htmlembedded= <body> @@ -120,57 +120,57 @@ body { ``` -如果每一步操作都正确,你的页面应该同下面demo中所示内容类似。 +如果每一步操作都正确,你的页面应该同下面 demo 中所示内容类似。 -在HTML文档中内联使用SVG图像的方式可以减少加载时间,因为只需要进行一次HTTP请求。 +在 HTML 文档中内联使用 SVG 图像的方式可以减少加载时间,因为只需要进行一次 HTTP 请求。 并且这种方式相较于使用`<img>` 或 `background-image`这两种方式具有更高的自定义性。 -## 4\. 如何利用`<object>`元素使用SVG图像 +## 4\. 如何利用`<object>`元素使用 SVG 图像 -还可以使用HTML中的`<object>`标签来将SVG图像添加到网页中,具体方式如以下代码所示: +还可以使用 HTML 中的`<object>`标签来将 SVG 图像添加到网页中,具体方式如以下代码所示: ``` <object data="happy.svg" width="300" height="300"> </object> ``` -使用`data`属性指定`<object>`所展示资源的URL,在本例中就是指SVG图像。 +使用`data`属性指定`<object>`所展示资源的 URL,在本例中就是指 SVG 图像。 -然后,使用`width` 和 `height`两个属性指定SVG图像的大小。 +然后,使用`width` 和 `height`两个属性指定 SVG 图像的大小。 同样,如果一切正常,你的网页现在应该长这个样子。😃 -所有支持SVG的浏览器都支持使用 `<object>`。 +所有支持 SVG 的浏览器都支持使用 `<object>`。 -## 5\. 如何利用`<iframe>`元素使用SVG图像 +## 5\. 如何利用`<iframe>`元素使用 SVG 图像 -虽然不建议使用这种方式,但是你的确能够使用`<iframe>`标签来添加一个SVG图像,正如下例所示。 +虽然不建议使用这种方式,但是你的确能够使用`<iframe>`标签来添加一个 SVG 图像,正如下例所示。 -但是要记住,`<iframe>`元素难以维护而且将不利于你的网站进行SEO优化。 +但是要记住,`<iframe>`元素难以维护而且将不利于你的网站进行 SEO 优化。 -而且,使用`<iframe>`元素也影响了可缩放矢量图形的可缩放性,因为使用这种方式添加的SVG图像不可以缩放。 +而且,使用`<iframe>`元素也影响了可缩放矢量图形的可缩放性,因为使用这种方式添加的 SVG 图像不可以缩放。 -## 6\. 如何利用`<embed>`元素使用SVG图像 +## 6\. 如何利用`<embed>`元素使用 SVG 图像 -利用`<embed>`元素是在HTML和CSS中使用SVG图像的另一种方式,其用法为`<embed src="happy.svg" />`。 +利用`<embed>`元素是在 HTML 和 CSS 中使用 SVG 图像的另一种方式,其用法为`<embed src="happy.svg" />`。 -然后,需要注意的是,这种方式也有其局限性。根据MDN的解释,大多数现代浏览器已经废弃和移除了对浏览器插件的支持。这意味着如果想要支持所有用户的浏览器,使用`<embed>`方式是不明智的。 +然后,需要注意的是,这种方式也有其局限性。根据 MDN 的解释,大多数现代浏览器已经废弃和移除了对浏览器插件的支持。这意味着如果想要支持所有用户的浏览器,使用`<embed>`方式是不明智的。 -下面是一个使用利用`<embed>`元素使用SVG图像的例子。 +下面是一个使用利用`<embed>`元素使用 SVG 图像的例子。 ## Conclusion ## 总结 -希望本文可以让你了解在CSS和HTML中使用SVG图像的不同方式,并引导你选择一种合适的方式将SVG图像添加到网站中。 +希望本文可以让你了解在 CSS 和 HTML 中使用 SVG 图像的不同方式,并引导你选择一种合适的方式将 SVG 图像添加到网站中。 如果有任何疑问,可以通过[Twitter](https://twitter.com/Didicodes)给我发送信息,我很乐意回复每一个问题。 diff --git a/chinese/articles/var-let-and-const-whats-the-difference.md b/chinese/articles/var-let-and-const-whats-the-difference.md index 26be3abaa..067f6fcca 100644 --- a/chinese/articles/var-let-and-const-whats-the-difference.md +++ b/chinese/articles/var-let-and-const-whats-the-difference.md @@ -122,7 +122,7 @@ So a variable declared in a block with `let` is only available for use within We see that using `hello` outside its block (the curly braces where it was defined) returns an error. This is because `let` variables are block scoped . -### let can be updated but not re-declared. +### let can be updated but not re-declared Just like `var`, a variable declared with `let` can be updated within its scope. Unlike `var`, a `let` variable cannot be re-declared within its scope. So while this will work: diff --git a/chinese/articles/web-scraping-in-javascript-with-puppeteer.md b/chinese/articles/web-scraping-in-javascript-with-puppeteer.md index cf6d25c6f..4714ffddd 100644 --- a/chinese/articles/web-scraping-in-javascript-with-puppeteer.md +++ b/chinese/articles/web-scraping-in-javascript-with-puppeteer.md @@ -415,7 +415,7 @@ After this, if you run our scraper again, after processing all instructions, you Quotes to Scrape second page loaded after clicking the "Next" button -## It’s Your Time! Here’s What You Can Do Next: +## It’s Your Time! Here’s What You Can Do Next Congrats on reaching the end of this introduction to scraping with Puppeteer! 👏 diff --git a/chinese/articles/what-are-functions-in-javascript-a-beginners-guide.md b/chinese/articles/what-are-functions-in-javascript-a-beginners-guide.md index eeb37cfb0..ca63e0cbc 100644 --- a/chinese/articles/what-are-functions-in-javascript-a-beginners-guide.md +++ b/chinese/articles/what-are-functions-in-javascript-a-beginners-guide.md @@ -6,7 +6,7 @@ ![什么是 JavaScript 中的函数(function)初学者的指南](https://www.freecodecamp.org/news/content/images/size/w2000/2022/06/banner-image-for-functions-1.png) -函数(function)是编程的基本概念之一。函数能让我们编写简洁的、模块化的、可多次使用的和可维护的代码,它还能帮助我们在编写代码时遵守DRY原则。 +函数(function)是编程的基本概念之一。函数能让我们编写简洁的、模块化的、可多次使用的和可维护的代码,它还能帮助我们在编写代码时遵守 DRY 原则。 在本文中,你将了解 JavaScript 中有哪些函数,如何编写自己的自定义函数以及如何执行它。 @@ -145,9 +145,9 @@ function greeting(){ 如果函数被定义为一个函数表达式,这就不灵了,因为函数表达式遵循的是从上到下的控制流序列。 -## **如何在JavaScript中使用箭头(arrow)函数?** +## **如何在 JavaScript 中使用箭头(arrow)函数?** -箭头函数是函数表达式的另一种记法,但它们的语法更短。它是在ES6中引入的,可以帮助我们编写更简洁的代码。 +箭头函数是函数表达式的另一种记法,但它们的语法更短。它是在 ES6 中引入的,可以帮助我们编写更简洁的代码。 在这里,函数关键字被排除在外,我们使用一个箭头符号(=>)来代替。语法看起来像这样: @@ -161,7 +161,7 @@ let nameOfFunction = (parameters) => { ## **什么是立即调用的函数表达式 (IIFEs)?** -IIFE是另一个函数表达式记法(显式的匿名函数),它在隔离状态下工作,独立于任何其他代码。它在被定义的地方被立即调用。 +IIFE 是另一个函数表达式记法(显式的匿名函数),它在隔离状态下工作,独立于任何其他代码。它在被定义的地方被立即调用。 其语法如下: @@ -172,7 +172,7 @@ IIFE是另一个函数表达式记法(显式的匿名函数),它在隔离 })(); ``` -IIFE的一个用例是包围一个你可能不会在代码中再次使用的变量。因此,一旦函数被执行,这个变量就不存在了。 +IIFE 的一个用例是包围一个你可能不会在代码中再次使用的变量。因此,一旦函数被执行,这个变量就不存在了。 ## **如何在一个函数中使用关键字 `return`?** @@ -196,7 +196,7 @@ sum(10, 20); 在一个函数中使用 `return` 可以很容易地处理函数返回的数据,可以把它作为一个值传递给另一个函数,或者对它进行额外的操作。 -## **JavaScript中的函数作用域(Scope)和闭包(Closures)是如何工作的?** +## **JavaScript 中的函数作用域(Scope)和闭包(Closures)是如何工作的?** 一个作用域是一个嵌套的名字空间,它将在其中创建的名字本地化,这样这些名字就不会与在该作用域之外创建的类似名字发生冲突。在一个函数中,有一些作用域规则是适用的。 diff --git a/chinese/articles/what-can-you-do-with-python-the-3-main-applications.md b/chinese/articles/what-can-you-do-with-python-the-3-main-applications.md index 50e4f2545..29c7be47e 100644 --- a/chinese/articles/what-can-you-do-with-python-the-3-main-applications.md +++ b/chinese/articles/what-can-you-do-with-python-the-3-main-applications.md @@ -6,13 +6,13 @@ 但是随着时间的迁移,我发现 Python 有三个主要的流行应用程序: -- Web开发 +- Web 开发 - 数据科学 - 包括机器学习,数据分析和数据可视化 - 脚本编写 让我们依次讨论它们。 -### **Web开发** +### **Web 开发** 基于 Python 的 web 框架(比如 **Django** 和 **Flask** )最近变得非常流行。 @@ -64,23 +64,23 @@ Django 和 Flask 是两个最受欢迎的 Python web 框架。如果你刚刚入 ### **数据科学 - 包括机器学习,数据分析和数据可视化** -#### **首先,回顾下机器学习 _是什么_ 。** +#### **首先,回顾下机器学习 _是什么_ 我认为最好的方式解释什么是机器学习,就是给你一个简单的样例。 假如你想要开发一个程序去自动检测数图片里面的内容。 -所以,放置一个下图(图1),你想要你的程序去识别这是一只狗。 +所以,放置一个下图(图 1),你想要你的程序去识别这是一只狗。 ![](https://cdn-media-1.freecodecamp.org/images/0*Mbj3L2cl0zzT2A0L) -图1 +图 1 -再放置另一个图片(图2),你想要你的程序去识别这是一张桌子。 +再放置另一个图片(图 2),你想要你的程序去识别这是一张桌子。 ![](https://cdn-media-1.freecodecamp.org/images/0*gxTn_CbMyCcQ1NFV) -图2 +图 2 你或许会说,好的,我可以写一些代码去完成这个功能。比如,如果图片中有很多浅棕色像素,那么我们可以说这里是一只狗。 @@ -92,7 +92,7 @@ Django 和 Flask 是两个最受欢迎的 Python web 框架。如果你刚刚入 机器学习通常实现一种算法,可以自动检测给定的输入模型。 -你可以传入1000张狗的图片和1000张桌子的图片到机器学习算法中。之后算法将会去学习狗和桌子之间的差异。之后你传入一张新的狗或者桌子的图片,算法将能够识别出它属于哪一个。 +你可以传入 1000 张狗的图片和 1000 张桌子的图片到机器学习算法中。之后算法将会去学习狗和桌子之间的差异。之后你传入一张新的狗或者桌子的图片,算法将能够识别出它属于哪一个。 我认为这个机器学习算法就是某种与婴儿学习新鲜事物具有相似性。婴儿是如何学习到一个事物是狗还是桌子呢?可能是从一堆样例中学习到的。 @@ -152,9 +152,9 @@ Django 和 Flask 是两个最受欢迎的 Python web 框架。如果你刚刚入 ![](https://cdn-media-1.freecodecamp.org/images/1*62T-rtheKPehgZdPTEpKww.png) -条形图1 - Python生成的 +条形图 1 - Python 生成的 -从这幅图我们可以得知,在这个特定的星期日,男性购买了400多种产品,女性购买了350多种产品。 +从这幅图我们可以得知,在这个特定的星期日,男性购买了 400 多种产品,女性购买了 350 多种产品。 作为数据分析师,你可能会针对这种差异提出一些可能的解释。 @@ -164,7 +164,7 @@ Django 和 Flask 是两个最受欢迎的 Python web 框架。如果你刚刚入 ![](https://cdn-media-1.freecodecamp.org/images/1*VgNfqK5XxNfHxx6S4VFCjQ.png) -线形图1 - Python生成的 +线形图 1 - Python 生成的 我们将查看整整一周的数据,而不是仅显示周日的数据。正如你说看到的,从这个线形图,我们可以看到这种差异在不同的日子是相当一致的。 @@ -174,7 +174,7 @@ Django 和 Flask 是两个最受欢迎的 Python web 框架。如果你刚刚入 ![](https://cdn-media-1.freecodecamp.org/images/1*dMpu_fd-THNXRJhHIq2O3g.png) -线形图2 - 也是由Python生成的 +线形图 2 - 也是由 Python 生成的 那么,你怎么解释周日的差异? @@ -184,9 +184,9 @@ Django 和 Flask 是两个最受欢迎的 Python web 框架。如果你刚刚入 我在 Google 和 Microsoft 工作时所做的数据分析工作与该示例非常相似,只是更为复杂。我实际上在 Google 公司使用 Python 去处理这种分析问题,然而在 Microsoft 公司我使用 JavaScript 技术。 -我在两家公司都是用SQL从数据库中抽取数据。之后,我将会使用 Python 和 Matplotlib (在 Google 公司)或者 JavaScript和D3.js(在 Microsoft 公司)去可视化和分析数据。 +我在两家公司都是用 SQL 从数据库中抽取数据。之后,我将会使用 Python 和 Matplotlib (在 Google 公司)或者 JavaScript 和 D3.js(在 Microsoft 公司)去可视化和分析数据。 -#### **使用Python做数据分析和可视化** +#### **使用 Python 做数据分析和可视化** 最流行的数据可视化类库是 [Matplotlib][8]。 @@ -202,7 +202,7 @@ Django 和 Flask 是两个最受欢迎的 Python web 框架。如果你刚刚入 你首先应该学习数据分析和可视化的基础知识。当我在网上寻找好的资源时,找不到任何资源。因此,我最终制作了有关此话题的 YouTube 视频: -我还最终完成了[关于Pluralsight的主题的完整课程] [10],你可以通过注册10天的免费试用期来免费获取。 +我还最终完成了[关于 Pluralsight 的主题的完整课程] [10],你可以通过注册 10 天的免费试用期来免费获取。 我建议这两种方式都采取。 @@ -240,13 +240,13 @@ Django 和 Flask 是两个最受欢迎的 Python web 框架。如果你刚刚入 你可以使用功能 Python 的 Tkinter 模块,但是它并不是最受欢迎的选择。 -取而代之的是,似乎[Java,C#和C ++] [11]之类的语言更受欢迎。 +取而代之的是,似乎[Java,C#和 C ++] [11]之类的语言更受欢迎。 最近一些公司也开始使用 JavaScript 来创建桌面应用。 [例如,Slack 公司的桌面应用是由 Electron 构建的][12]。Electron 允许你使用 JavaScript 构建桌面应用。 -对于我个人而言,如果我正在构建一个桌面应用,我将会选择使用 JavaScript 语言。如果你使用 JavaScript 的话,它可以让你重用Web版本中的某些代码。 +对于我个人而言,如果我正在构建一个桌面应用,我将会选择使用 JavaScript 语言。如果你使用 JavaScript 的话,它可以让你重用 Web 版本中的某些代码。 然后,我并不是一个桌面应用的专家,所以请给我留言,告诉我你支持还是反对我的观点。 @@ -255,7 +255,7 @@ Django 和 Flask 是两个最受欢迎的 Python web 框架。如果你刚刚入 我建议使用 Python 3,因为它是较新版本,并且是目前最受欢迎的选择。 -### **脚注:关于后端代码与前端代码的注释(以防你不熟悉这些术语):** +### **脚注:关于后端代码与前端代码的注释(以防你不熟悉这些术语)** 假设你想制作类似 Instagram 的产品。 @@ -285,7 +285,7 @@ Django 和 Flask 是两个最受欢迎的 Python web 框架。如果你刚刚入 ###喜欢这篇文章吗? 然后,你可能也喜欢我的 YouTube 频道。 -我有一个名为[CS Dojo] [13]的编程教育 YouTube 频道,它有着440,000多名订阅者,在这里我可以创作更多类似本文的内容。 +我有一个名为[CS Dojo] [13]的编程教育 YouTube 频道,它有着 440,000 多名订阅者,在这里我可以创作更多类似本文的内容。 比如,你可能会喜欢这些视频: diff --git a/chinese/articles/what-does-d-mean-in-regex.md b/chinese/articles/what-does-d-mean-in-regex.md index 98e325bb9..d1b9a6e56 100644 --- a/chinese/articles/what-does-d-mean-in-regex.md +++ b/chinese/articles/what-does-d-mean-in-regex.md @@ -26,7 +26,7 @@ 除了`\d`,正则表达式中还有很多元字符,比如下面的: -- `\w` 匹配所有单词字符(小写字母a到z、大写字母A到Z、数字0到9和下划线_) +- `\w` 匹配所有单词字符(小写字母 a 到 z、大写字母 A 到 Z、数字 0 到 9 和下划线_) - `\D` 匹配所有非数字字符。它是 `\d` 的补集 - `\W` 匹配所有非单词字符 - `\s` 匹配所有空格字符,包括空格、制表符和回车符等。 @@ -35,7 +35,7 @@ 下面我们一起看看怎样用`\d`元字符匹配数字。 -第一个例子是匹配`7253289593`这个数字ID。 +第一个例子是匹配`7253289593`这个数字 ID。 这是一个十位数,要想匹配它,你可以重复写十次`\d`元字符: @@ -53,7 +53,7 @@ <figure class="kg-card kg-card-image kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/03/Screenshot-2023-03-02-at-12.17.33.png" alt="Screenshot-2023-03-02-at-12.17.33" class="kg-image"><figcaption></figcaption></figure> -`\d`元字符同样可以在Javascript中使用: +`\d`元字符同样可以在 Javascript 中使用: ```js // 测试一个数字ID @@ -83,7 +83,7 @@ console.log(regex3.test(naijaPhoneNum)); // true 所以如果你在正则表达式中使用 `[0-9]` 代替 `\d`,你仍然能够进行字符的匹配。 -例如,我们可以使用 `[0-9]` 而不是 `\d` 来匹配前面例子中的数字ID: +例如,我们可以使用 `[0-9]` 而不是 `\d` 来匹配前面例子中的数字 ID: <figure class="kg-card kg-card-image kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/03/Screenshot-2023-03-02-at-12.28.17.png" alt="Screenshot-2023-03-02-at-12.28.17" class="kg-image"><figcaption></figcaption></figure> diff --git a/chinese/articles/what-does-mvc-mean-in-computer-science.md b/chinese/articles/what-does-mvc-mean-in-computer-science.md index aeaee3854..36d2a6aa2 100644 --- a/chinese/articles/what-does-mvc-mean-in-computer-science.md +++ b/chinese/articles/what-does-mvc-mean-in-computer-science.md @@ -5,24 +5,24 @@ ![MVC in Computer Science – The MVC Model](https://www.freecodecamp.org/news/content/images/size/w2000/2022/06/mvc-cover.png) -MVC是模型(Model)、视图(View)和控制器(Controller)的缩写。这一软件架构模式诞生于20世纪70年代后期,被用于创建桌面应用。当然现在这一模式也在web应用中被广泛使用。 +MVC 是模型(Model)、视图(View)和控制器(Controller)的缩写。这一软件架构模式诞生于 20 世纪 70 年代后期,被用于创建桌面应用。当然现在这一模式也在 web 应用中被广泛使用。 -我将在这篇文章中深入讲解什么是MVC以及它代表的三个组件。 +我将在这篇文章中深入讲解什么是 MVC 以及它代表的三个组件。 -我也准备了图片辅助你理解MVC,不过咱还是先读文章,:) +我也准备了图片辅助你理解 MVC,不过咱还是先读文章,:) ## 目录 -- [什么是MVC,为什么使用它?](#whatismvcandwhyisitused) -- [哪些语言和框架使用MVC?](#whichlanguagesandframeworksusemvc) -- [MVC中的模型是什么?](#whatisthemodelinmvc) -- [MVC中的视图是什么?](#whatistheviewinmvc) -- [MVC中的控制器是什么?](#whatisthecontrollerinmvc) +- [什么是 MVC,为什么使用它?](#whatismvcandwhyisitused) +- [哪些语言和框架使用 MVC?](#whichlanguagesandframeworksusemvc) +- [MVC 中的模型是什么?](#whatisthemodelinmvc) +- [MVC 中的视图是什么?](#whatistheviewinmvc) +- [MVC 中的控制器是什么?](#whatisthecontrollerinmvc) - [总结](#conclusion) <h2 id="whatismvcandwhyisitused"> 什么是MVC,为什么使用它?</h2> -在计算机科学中,MVC是一种软件设计模式,这种模式将应用代码组织成三个相互交织的部分——模型、视图和控制器。 +在计算机科学中,MVC 是一种软件设计模式,这种模式将应用代码组织成三个相互交织的部分——模型、视图和控制器。 模型是与数据库交互的逻辑;视图是用户接口和交互,控制器是是视图和数据库之间的中介。 @@ -33,28 +33,28 @@ MVC是模型(Model)、视图(View)和控制器(Controller)的缩写 在其他一些框架中,模型与视图直接交互。 ![Copy-of-mvc2](https://www.freecodecamp.org/news/content/images/2022/06/Copy-of-mvc2.png) -MVC设计模式旨在将应用代码分成各自的单位,来简化维护和优化。这种方式被称为“关注点分离”。 +MVC 设计模式旨在将应用代码分成各自的单位,来简化维护和优化。这种方式被称为“关注点分离”。 <h2 id="whichlanguagesandframeworksusemvc"> 哪些语言和框架使用MVC?</h2> -过去,MVC仅被用于桌面(GUI)[https://zh.wikipedia.org/wiki/%E5%9B%BE%E5%BD%A2%E7%94%A8%E6%88%B7%E7%95%8C%E9%9D%A2],现在许多语言和框架也使用MVC来设计web应用。 +过去,MVC 仅被用于桌面(GUI)[https://zh.wikipedia.org/wiki/%E5%9B%BE%E5%BD%A2%E7%94%A8%E6%88%B7%E7%95%8C%E9%9D%A2],现在许多语言和框架也使用 MVC 来设计 web 应用。 -一些框架甚至强制使用MVC,所以你可能没意识到你已经使用过MVC。 +一些框架甚至强制使用 MVC,所以你可能没意识到你已经使用过 MVC。 -例如在一个全栈Express应用中,开发者往往把代码打包到模型、控制器和客户(视图)三个文件夹。 +例如在一个全栈 Express 应用中,开发者往往把代码打包到模型、控制器和客户(视图)三个文件夹。 ![Annotation-2022-06-20-103520](https://www.freecodecamp.org/news/content/images/2022/06/Annotation-2022-06-20-103520.png) 截图是我为我最喜欢的足球运动员编写的[玩笑生成器](https://blooming-reef-46396.herokuapp.com/)的文件结构。 -使用MVC的编程语言包括:C、 C++、C#、 Java、 Ruby、 Smalltalk等。 +使用 MVC 的编程语言包括:C、 C++、C#、 Java、 Ruby、 Smalltalk 等。 -使用MVC的框架包括:Angular、 Express、 Django、 Flask、 Laravel、 Ruby on rails等。 +使用 MVC 的框架包括:Angular、 Express、 Django、 Flask、 Laravel、 Ruby on rails 等。 <h2 id="whatisthemodelinmvc">MVC中的模型是什么?</h2> -模型组件负责从数据库获取数据的逻辑。同样,你也可以使用JSON文件来提供数据。 +模型组件负责从数据库获取数据的逻辑。同样,你也可以使用 JSON 文件来提供数据。 -例如,如果有一个电子商务应用的SQL数据库,模型的代码可能是`product-data = db.get(SELECT * FROM products;`。 +例如,如果有一个电子商务应用的 SQL 数据库,模型的代码可能是`product-data = db.get(SELECT * FROM products;`。 在大多数情况下,模型和控制器沟通,然后给视图(UI)发送数据;另一些情况下,模型直接给视图发送数据。 @@ -62,9 +62,9 @@ MVC设计模式旨在将应用代码分成各自的单位,来简化维护和 视图组件是直接和用户交互的组件,视图与控制器沟通用户通过鼠标或者键盘发出的请求。 -如HTML、CSS和JavaScript这类语言经常用于编写视图组件。你也可以使用React、Vue和Svelte这类框架。 +如 HTML、CSS 和 JavaScript 这类语言经常用于编写视图组件。你也可以使用 React、Vue 和 Svelte 这类框架。 -一些开发者也会使用如:Handlebars、ejs和liquidjs这类模板引擎来执行视图。 +一些开发者也会使用如:Handlebars、ejs 和 liquidjs 这类模板引擎来执行视图。 在电商应用中,视图组件的代码如下: @@ -95,11 +95,11 @@ if (success) { <h2 id="conclusion">总结</h2> -在web应用和其他软件产品中MVC模式被广发使用。 +在 web 应用和其他软件产品中 MVC 模式被广发使用。 -可能刚接触MVC时你会觉得困惑,但是持续学习一段时间,之后你一定会豁然开朗。 +可能刚接触 MVC 时你会觉得困惑,但是持续学习一段时间,之后你一定会豁然开朗。 -如果你仍对MVC感到困惑,你可以这样类比: +如果你仍对 MVC 感到困惑,你可以这样类比: - **你**打电话给餐厅点了一份披萨 – 你是`view` - 你向**服务员**点单 – 服务员是`controller` diff --git a/chinese/articles/what-google-taught-me-about-technical-writting.md b/chinese/articles/what-google-taught-me-about-technical-writting.md index bbb0e1401..a2ecd8a06 100644 --- a/chinese/articles/what-google-taught-me-about-technical-writting.md +++ b/chinese/articles/what-google-taught-me-about-technical-writting.md @@ -5,7 +5,7 @@ ![How Google's Technical Writing Course Helped Me Become a Better Writer](https://www.freecodecamp.org/news/content/images/size/w2000/2021/02/Technical-Writting-One.png) -谷歌有一门技术写作课程,我最近刚学完这个课程并且非常喜欢。课程大约需要4个小时,并且配套有一些实操练习,可以检测自己的学习效果。 +谷歌有一门技术写作课程,我最近刚学完这个课程并且非常喜欢。课程大约需要 4 个小时,并且配套有一些实操练习,可以检测自己的学习效果。 我将简要说明我学完这门课程后学到了什么,同时也会总结出其中最好的部分,这样你就可以了解这门课所涉及的大概内容。 @@ -31,13 +31,13 @@ 使用代词时,一定要确保代指事物是清晰明确的,不然很容易让读者混淆。例如这样: -> C++是一门相当古老的语言,但JavaScript也有些年代了。不过,我还是喜欢它。 +> C++是一门相当古老的语言,但 JavaScript 也有些年代了。不过,我还是喜欢它。 -啊?你到底喜欢什么?C++还是JavaScript?这里的**代词**并不明晰。 +啊?你到底喜欢什么?C++还是 JavaScript?这里的**代词**并不明晰。 要明晰代词的用法,可以像这样: -> C++是一门相当古老的语言,但JavaScript也有些年代了。我真的喜欢C++。 +> C++是一门相当古老的语言,但 JavaScript 也有些年代了。我真的喜欢 C++。 一般来说,在校对时,如果不清楚代词指的是什么,那就用名词而不要用代词。**这个**或者**那个**特别容易引起歧义。无论什么时候用这类代词,确保指代明确。 @@ -67,15 +67,15 @@ 在写作过程中,“**在(某地)存在一个**”和“**在(某地)存在多个**”几乎都可以删掉,让你的观点更简洁。 -这2个说法通常很泛在并且让读者感到厌烦。改写一下句子吧。以下是一些示例: +这 2 个说法通常很泛在并且让读者感到厌烦。改写一下句子吧。以下是一些示例: - 在软件和硬件之间有很多重叠的地方。 -- 在JavaScript中不存在多线程。 +- 在 JavaScript 中不存在多线程。 我希望你也认同改后的句子读起来更好: - 软件和硬件有很多重叠的地方。 -- JavaScript没有多线程。 +- JavaScript 没有多线程。 ### 尽量少用形容词和副词 @@ -89,13 +89,13 @@ 对比: -> 在生产模式中,代码运行效率将提升225%。 +> 在生产模式中,代码运行效率将提升 225%。 希望你也认同改写后的句子更精准且可量化。 ### 使用列表 -一个长句里包含很多元素时,你可以把长句拆成列表形式。例如,如果你要列举某项技术的好处,你可以说,X是一个不错的选择,因为: +一个长句里包含很多元素时,你可以把长句拆成列表形式。例如,如果你要列举某项技术的好处,你可以说,X 是一个不错的选择,因为: - 它是轻量级的 - 它速度很快 @@ -226,7 +226,7 @@ ### 根据读者不同而调整所写内容 -试着让你的文档适合对应的读者。在 [dev.to](https://dev.to/) 上,你可能用一种写作方式;在freeCodeCamp文章专栏上,你可能会用另一种写作方式。 +试着让你的文档适合对应的读者。在 [dev.to](https://dev.to/) 上,你可能用一种写作方式;在 freeCodeCamp 文章专栏上,你可能会用另一种写作方式。 根据读者不同而调整所写内容。例如,如果你要向更多读者解释你公司的架构,因为你的读者不像你同事知道那么多,你必须把事情解释得更透彻。 diff --git a/chinese/articles/what-is-a-block-cipher.md b/chinese/articles/what-is-a-block-cipher.md index 27aa7e40c..7c1d4edef 100644 --- a/chinese/articles/what-is-a-block-cipher.md +++ b/chinese/articles/what-is-a-block-cipher.md @@ -19,7 +19,7 @@ ## 当你开发一个密码时,哪些原则时重要的? -克尔克霍夫原则指出,一个加密系统应该是安全的,即使所有的细节(key除外)都是公开的。克劳德-香农后来将这一说成 `敌人知道系统`。 +克尔克霍夫原则指出,一个加密系统应该是安全的,即使所有的细节(key 除外)都是公开的。克劳德-香农后来将这一说成 `敌人知道系统`。 从本质上讲,一个设计得非常好得系统应该能够发送加密信息,即使攻击者能使用相同得算法(用不同得密钥)加密和解密自己的消息。加密信息的安全性应该完全取决于密钥。 @@ -37,9 +37,9 @@ ## 什么是块和流加密(Stream Ciphers)? -块和流加密都是对称密钥密码(如DES、RCx、Blowfish和Rijndael AES)。块加密将明文逐块转换为密文,而流加密则每次转换为一个字节。 +块和流加密都是对称密钥密码(如 DES、RCx、Blowfish 和 Rijndael AES)。块加密将明文逐块转换为密文,而流加密则每次转换为一个字节。 -大多数现代对称算法都是`block ciphers`(块加密),尽管块大小不同(如EDS(64位),AES(128,192和256位),等等)。 +大多数现代对称算法都是`block ciphers`(块加密),尽管块大小不同(如 EDS(64 位),AES(128,192 和 256 位),等等)。 ### 流加密的优势是什么? @@ -49,7 +49,7 @@ 流加密的工作原理是由密钥生成一系列的伪随机字节(对于给定的密钥,加密和解密过程中的使用相同的伪随机字节)。不同的密钥会产生不同的字节串。 -为了加密数据,明文字节与伪随机字节串进行XOR(异或操作)。为了解密,将加密后产生的文本与相同的伪随机字节串进行XOR(异或操作),就可以看到明文。 +为了加密数据,明文字节与伪随机字节串进行 XOR(异或操作)。为了解密,将加密后产生的文本与相同的伪随机字节串进行 XOR(异或操作),就可以看到明文。 ### 块加密的优势是什么? @@ -63,21 +63,21 @@ 为了对多块的数据进行加密,有几种`模式`已经被开发出来。这些模块将单块原则用于更长的消息。 -块加密有5种保密模式。其中一些模式需要一个初始化向量(IV) 才能发挥作用。 +块加密有 5 种保密模式。其中一些模式需要一个初始化向量(IV) 才能发挥作用。 -### 什么是Initialization Vector (IV 初始化变量)? +### 什么是 Initialization Vector (IV 初始化变量)? -IV的本质只是用于创建密文的另一个输入(除了明文和密钥)。它是一个数据块,被几种模式的块加密用来随机化加密,这样即使相同的明文被重复加密,也会产生不同的密文。 +IV 的本质只是用于创建密文的另一个输入(除了明文和密钥)。它是一个数据块,被几种模式的块加密用来随机化加密,这样即使相同的明文被重复加密,也会产生不同的密文。 它通常不是秘密,尽管它不会被重复使用。在理想情况下,它应该是随机的,不可预测和一次性使用。 -两个相同的信息用相同的密钥加密,但不同的IV,将导致不同的密文。 这使进行攻击更加困难。 +两个相同的信息用相同的密钥加密,但不同的 IV,将导致不同的密文。 这使进行攻击更加困难。 ### Electronic Code Book Mode (ECB 电子代码本模式) 在明文的输入块和密文输出块有一个固定的映射(基本上就像一个实际的密码本,密文的字与明文直接相关)。 -ECD对每个明文块应用加密函数进行加密(对每个密文块应用逆向函数进行解密)。这意味着CBC可以并行地加密和解密多个块(因为它们相互不依赖),加快了处理进程。 +ECD 对每个明文块应用加密函数进行加密(对每个密文块应用逆向函数进行解密)。这意味着 CBC 可以并行地加密和解密多个块(因为它们相互不依赖),加快了处理进程。 ![](https://megankaczanowski.com/content/images/2020/12/Screen-Shot-2020-12-31-at-8.22.20-PM.png) @@ -88,85 +88,85 @@ https://en.wikipedia.org/wiki/Block\_cipher\_mode\_of\_operation 填充实质上为了满足块大小而添加的额外数据。在这种模式下,给定相同的密钥,相同的明文块将总是导致相同的密文块。这是它很容易受到攻击,所以这种模式很少使用(应该避免使用)。 ### Cipher Block Chaining Mode (CBC 加密块链模式) -这种加密模式将新的明文块与前一个加密区`关联(chains)`或者结合起来,这需要为第一个块提供一个IV。IV不需要加密,但它要不可预测。 +这种加密模式将新的明文块与前一个加密区`关联(chains)`或者结合起来,这需要为第一个块提供一个 IV。IV 不需要加密,但它要不可预测。 -CBC将第一个明文块与IV密文块进行XOR(异或),以创建第一个密文块。使用ECB模式将IV作为一个短消息单独发送。 +CBC 将第一个明文块与 IV 密文块进行 XOR(异或),以创建第一个密文块。使用 ECB 模式将 IV 作为一个短消息单独发送。 -然后,CBD对该块使用加密算法,产生第一个密文块。然后,CBC将此密文块与第一个文明块进行XOR(异或),并应用加密算法产生第二个密文块,如果反复,直到消息的最后。 +然后,CBD 对该块使用加密算法,产生第一个密文块。然后,CBC 将此密文块与第一个文明块进行 XOR(异或),并应用加密算法产生第二个密文块,如果反复,直到消息的最后。 -为了解密,CBC做了想法的工作--第一个密文块使用反向的加密算法,然后将该块与IV进行XOR(异或),得到第一个明文块。 +为了解密,CBC 做了想法的工作--第一个密文块使用反向的加密算法,然后将该块与 IV 进行 XOR(异或),得到第一个明文块。 -然后,CBC将加密算法的逆运算应用于第二个密文块,并将该块与第一密文块进行XOR,得到第二个明文块。这个过程将一直持续到消息被解密。 +然后,CBC 将加密算法的逆运算应用于第二个密文块,并将该块与第一密文块进行 XOR,得到第二个明文块。这个过程将一直持续到消息被解密。 ![](https://megankaczanowski.com/content/images/2020/12/Screen-Shot-2020-12-31-at-8.22.37-PM.png) https://en.wikipedia.org/wiki/Block\_cipher\_mode\_of\_operation -因为每个输入块(除了第一个)都依赖于前一个块被加密,CBC不能并行地进行加密。然而,由于解密需要(立即可用)密文块进行XOR,所以它可以并行进行。CBC是最常用的模式之一。 +因为每个输入块(除了第一个)都依赖于前一个块被加密,CBC 不能并行地进行加密。然而,由于解密需要(立即可用)密文块进行 XOR,所以它可以并行进行。CBC 是最常用的模式之一。 -于ECB相似,要使用这种模式正常工作,消息的长度需要是块大小的两倍,不足要进行填充来满足长度。 +于 ECB 相似,要使用这种模式正常工作,消息的长度需要是块大小的两倍,不足要进行填充来满足长度。 ### Cipher Feedback Mode (CFB 加密反馈模式) -CFB与CBC相似,但CFB不是使用整个前一个密文块计算下一个块,而是前一个块的一部分。 +CFB 与 CBC 相似,但 CFB 不是使用整个前一个密文块计算下一个块,而是前一个块的一部分。 -1 CFB从一个与加密块预期大小相同的IV开始,使用加密算法对其进行加密。 +1 CFB 从一个与加密块预期大小相同的 IV 开始,使用加密算法对其进行加密。 -2 CFB保留`s`(重要)字节,并将其与要传输的明文`s`字节进行XOR(异或)。 +2 CFB 保留`s`(重要)字节,并将其与要传输的明文`s`字节进行 XOR(异或)。 -3 然后,CFB将IV的`s`字节向左移动,将步骤2 产生的密文字节插入右边的字节中(IV保持相同的长度)。 +3 然后,CFB 将 IV 的`s`字节向左移动,将步骤 2 产生的密文字节插入右边的字节中(IV 保持相同的长度)。 4 然后重复这些步骤。 -为了解密消息,CFB使用IV作为第一个块,并通过执行上述步骤3和应用加密算法形成块形成每个后续块。然后,CFB与 `s`字节相应的密文进行XOR(异或),以显示明文。 +为了解密消息,CFB 使用 IV 作为第一个块,并通过执行上述步骤 3 和应用加密算法形成块形成每个后续块。然后,CFB 与 `s`字节相应的密文进行 XOR(异或),以显示明文。 -在CFB内部,加密系统一次处理 `s < b`明文位,即使算法本身进行 `b-bits`到 `b-bits`的转换。这意味着s 可以是任何数字,包含一个1个字节,CFP可以在流加密中作为一个功能运行。 +在 CFB 内部,加密系统一次处理 `s < b`明文位,即使算法本身进行 `b-bits`到 `b-bits`的转换。这意味着 s 可以是任何数字,包含一个 1 个字节,CFP 可以在流加密中作为一个功能运行。 ![](https://megankaczanowski.com/content/images/2020/12/Screen-Shot-2020-12-31-at-8.24.31-PM.png) https://en.wikipedia.org/wiki/Block\_cipher\_mode\_of\_operation -不幸的是,这意味着CFB可以向下游传播错误。如果收到一个错误的字节,当CFB用它来解密第一个字节时,它将产生一个错误的解密,导致后面的解密错误。 +不幸的是,这意味着 CFB 可以向下游传播错误。如果收到一个错误的字节,当 CFB 用它来解密第一个字节时,它将产生一个错误的解密,导致后面的解密错误。 -像CBC一样,当CFB加密时,每轮的输入都依赖于前一轮的结果,这意味着加密不能并行进行,尽管如果输入块首先由IV和密文创建,则可以并行进行解密。 +像 CBC 一样,当 CFB 加密时,每轮的输入都依赖于前一轮的结果,这意味着加密不能并行进行,尽管如果输入块首先由 IV 和密文创建,则可以并行进行解密。 ### Output Feedback (OFB 输出反馈) -OFB与CFB类似,但它不是将 `s<b` 位处理转换成 ` b-bits to b-bits`,而是直接`s`位处理。与CFB类似,OFB可以在流加密中使用。 +OFB 与 CFB 类似,但它不是将 `s<b` 位处理转换成 `b-bits to b-bits`,而是直接`s`位处理。与 CFB 类似,OFB 可以在流加密中使用。 -OFB要求IV是唯一的nonce(一次性使用),用于给定的密钥每次执行。 +OFB 要求 IV 是唯一的 nonce(一次性使用),用于给定的密钥每次执行。 -首先,OFB加密算法对IV进行加密,产生一个块。然后,OFB将此块与一个明文块进行XOR,产生第一密文块。 +首先,OFB 加密算法对 IV 进行加密,产生一个块。然后,OFB 将此块与一个明文块进行 XOR,产生第一密文块。 -OFB用加密算法对第一个输出块进行加密,输出第二个输出块。然后,它将此块与第二个明文块进行XOR(异或),产生第二个密文块。OFB在信息的长度上重复这一过程。 +OFB 用加密算法对第一个输出块进行加密,输出第二个输出块。然后,它将此块与第二个明文块进行 XOR(异或),产生第二个密文块。OFB 在信息的长度上重复这一过程。 ![](https://megankaczanowski.com/content/images/2020/12/Screen-Shot-2020-12-31-at-8.22.54-PM.png) https://en.wikipedia.org/wiki/Block\_cipher\_mode\_of\_operation -解密时,OFB用加密算法对IV进行加密,产生一个输出块。然后OFB将此块与第一个密文块进行XOR(异或),恢复第一个明文块。 +解密时,OFB 用加密算法对 IV 进行加密,产生一个输出块。然后 OFB 将此块与第一个密文块进行 XOR(异或),恢复第一个明文块。 -OFB用加密算法对第一个输出块进行加密,产生第二个输出块。然后OFB将其与第二个密文块进行XOR,恢复第二个明文块。OFB为信息的长度重复这一过程。 +OFB 用加密算法对第一个输出块进行加密,产生第二个输出块。然后 OFB 将其与第二个密文块进行 XOR,恢复第二个明文块。OFB 为信息的长度重复这一过程。 -由于用于解密的输出块是本地生成的,OFB比CFB更能抵抗传输错误。 +由于用于解密的输出块是本地生成的,OFB 比 CFB 更能抵抗传输错误。 ### Counter (CTR 计算器) -CTR将加密算法应用于一组独特的输入块(计数器),以产生输出,这些输出与明文进行XOR(异或)以产生密码文本。 +CTR 将加密算法应用于一组独特的输入块(计数器),以产生输出,这些输出与明文进行 XOR(异或)以产生密码文本。 -CTR用加密算法对第一个计数器进行加密,然后将产生的输出与第一个明文块进行XOR,产生第一个密文块。CTR对每个区块重复这一过程(有一个新的计数器--计数器在使用单一密钥加密的所有信息中必须是唯一的)。 +CTR 用加密算法对第一个计数器进行加密,然后将产生的输出与第一个明文块进行 XOR,产生第一个密文块。CTR 对每个区块重复这一过程(有一个新的计数器--计数器在使用单一密钥加密的所有信息中必须是唯一的)。 -如果最终的块是一个`s`字节的部分块(输出块的最重要的位),`s`被用于XOR,而输出块的`b-s`字节被丢弃。 +如果最终的块是一个`s`字节的部分块(输出块的最重要的位),`s`被用于 XOR,而输出块的`b-s`字节被丢弃。 ![](https://megankaczanowski.com/content/images/2020/12/Screen-Shot-2020-12-31-at-8.23.02-PM.png) https://en.wikipedia.org/wiki/Block\_cipher\_mode\_of\_operation -解密也遵循同样的模式。CTR用加密算法对计数器进行加密,然后将输出与相应的密文块进行XOR,产生明文块。 +解密也遵循同样的模式。CTR 用加密算法对计数器进行加密,然后将输出与相应的密文块进行 XOR,产生明文块。 -如果最终的块是一个`s`字节的部分块(那么输出块的最重要的比特)。s,被用于XOR,而输出块的`b-s`字节被丢弃。 +如果最终的块是一个`s`字节的部分块(那么输出块的最重要的比特)。s,被用于 XOR,而输出块的`b-s`字节被丢弃。 -CTR已被证明至少与其他四种模式一样安全,同时还能够并行执行(包括加密和解密),这意味着它非常快。 +CTR 已被证明至少与其他四种模式一样安全,同时还能够并行执行(包括加密和解密),这意味着它非常快。 如果可以确定每个区块的计数器区块,并且可以在收到明文或密文之前对计数器进行加密(如果内存不受限制),那么每个区块都可以独立恢复。 @@ -198,7 +198,7 @@ _例如,大卫发现了一个敌方间谍的藏身之处,并在间谍发送 这进一步细分为批量选择的明文(攻击者可以提交一组明文并收到密文,但不能再次这样做)和调整选择的明文(攻击者可以提交明文,并根据以前的密码文本提交额外的明文,获得新的密文)。 -_例如:民族国家A正在窃听另一个民族国家B的加密通信,并且知道A和B所有的加密都使用同一个密钥。A和B向另一个民族国家C发送了一份敏感的外交通讯,知道它将通过加密通道传输,从而给他们提供了一个选定的明文--密文对。_ +_例如:民族国家 A 正在窃听另一个民族国家 B 的加密通信,并且知道 A 和 B 所有的加密都使用同一个密钥。A 和 B 向另一个民族国家 C 发送了一份敏感的外交通讯,知道它将通过加密通道传输,从而给他们提供了一个选定的明文--密文对。_ ### 选定密文攻击 @@ -206,7 +206,7 @@ _例如:民族国家A正在窃听另一个民族国家B的加密通信,并 _比如说:David 知道敌方间谍明天要发送一份加密信息,所以他用自己选择的密文替换原密文,然后监视收件人,听他们读出信息的明文。 _ -### 资料来源/更深入阅读: +### 资料来源/更深入阅读 - [NIST Recommendations for Block Cipher Modes of Operation](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf) - [Diffusion and Confusion](https://www.nku.edu/~christensen/diffusionandconfusion) diff --git a/chinese/articles/what-is-a-callback-function-in-javascript-js-callbacks-example-tutorial.md b/chinese/articles/what-is-a-callback-function-in-javascript-js-callbacks-example-tutorial.md index ef9112b80..41c8d36cf 100644 --- a/chinese/articles/what-is-a-callback-function-in-javascript-js-callbacks-example-tutorial.md +++ b/chinese/articles/what-is-a-callback-function-in-javascript-js-callbacks-example-tutorial.md @@ -5,17 +5,17 @@ ![What is a Callback Function in JavaScript? JS Callbacks Example Tutorial](https://www.freecodecamp.org/news/content/images/size/w2000/2022/08/pexels-pixabay-39656.jpg) -JavaScript高阶函数可以接受另一个函数作为参数。这些使用其他函数作为参数的函数被称为回调函数。 +JavaScript 高阶函数可以接受另一个函数作为参数。这些使用其他函数作为参数的函数被称为回调函数。 -## 什么是JavaScript中的回调函数? +## 什么是 JavaScript 中的回调函数? 回调是将另一个函数作为参数传递的函数。 这意味着父函数可以是任意类型的函数,但是回调函数仅在父函数限定的场景中使用(或者是几个有限的情况)。 -## 如何在JavaScript中创建回调函数? +## 如何在 JavaScript 中创建回调函数? -你可以像创建其他函数那样在JavaScript中创建回调函数: +你可以像创建其他函数那样在 JavaScript 中创建回调函数: ```javascript function callbackFunction () { @@ -43,7 +43,7 @@ anyFunction(callbackFunction); 现在让我们一起来编写回调函数,你会在编程生涯中频繁碰到它,让我们开始吧! -JavaScript已经内置了一个高阶函数——`every`方法。 +JavaScript 已经内置了一个高阶函数——`every`方法。 `every`方法是一个数组方法,该方法使用回调函数来检查数组中的每一个元素是不是都通过指定的测试。 @@ -59,7 +59,7 @@ function callbackFunction(element, index, array) { 回调函数可以很简单,也可以非常复杂。我们需要一些上下文来创建一个示例。 -### 如何在JavaScript中编写回调函数 +### 如何在 JavaScript 中编写回调函数 假设我们要处理一个元素为字符串的数组。你需要检查这个数组是否有且仅有一个字符串,这个字符串的长度为三个字母;全部是大写字母,并且三个字母各不相同。 @@ -242,9 +242,9 @@ function callbackFunction(element, index, array) { 另外,字符串必须全部为大写字母,并且都英文字母。 -我们使用`charCodeAt`来实现。这个方法返回字母的ASCII 编码,大写字母的ASCII编码是65到90,可以通过这个方法来检查是否仅为大写英文字母。 +我们使用`charCodeAt`来实现。这个方法返回字母的 ASCII 编码,大写字母的 ASCII 编码是 65 到 90,可以通过这个方法来检查是否仅为大写英文字母。 -有三个元素需要被检查: `element.charCodeAt(0)`、 `element.charCodeAt(1)`和 `element.charCodeAt(2)`,它们的值必须在65到90之间。虽然只有三个字母,但是我们可以使用循环。 +有三个元素需要被检查: `element.charCodeAt(0)`、 `element.charCodeAt(1)`和 `element.charCodeAt(2)`,它们的值必须在 65 到 90 之间。虽然只有三个字母,但是我们可以使用循环。 如下代码: @@ -302,9 +302,9 @@ anArray.every(callbackFunction); 程序变得更复杂,就越有必要使用回调函数。 -## 为什么在JavaScript中使用回调函数? +## 为什么在 JavaScript 中使用回调函数? -回调函数是JavaScript的一个简洁特性。 这意味着我们可以使用一个普通函数处理一些事情(如`every`检查数组中的每一个元素是否通过某个测试, `filter`筛选掉一些不符合标准的元素 `replace`是一个字符串回调函数,可以在特定条件下替换掉字符串的一部分) 以及一个回调函数在特定情况下处理特定的行为。 +回调函数是 JavaScript 的一个简洁特性。 这意味着我们可以使用一个普通函数处理一些事情(如`every`检查数组中的每一个元素是否通过某个测试, `filter`筛选掉一些不符合标准的元素 `replace`是一个字符串回调函数,可以在特定条件下替换掉字符串的一部分) 以及一个回调函数在特定情况下处理特定的行为。 - `filter` 可以通过回调函数设定的条件筛掉一些元素 - `every` 可以通过回调函数设定的条件来检查数组上的每一个元素 diff --git a/chinese/articles/what-is-a-compiler-in-c.md b/chinese/articles/what-is-a-compiler-in-c.md index 032db0ffe..d392e5073 100644 --- a/chinese/articles/what-is-a-compiler-in-c.md +++ b/chinese/articles/what-is-a-compiler-in-c.md @@ -14,7 +14,7 @@ 这篇文章将教你。 1. 用一个比喻说明什么是编译器。 -2. C语言编译器的基本历史。 +2. C 语言编译器的基本历史。 别担心,你不需要编程经验就能理解什么是编译器。 @@ -43,11 +43,11 @@ 同样的事情也发生在编程中。 -在这种情况下,我们使用的是C语言。 +在这种情况下,我们使用的是 C 语言。 -你必须采取知道你将在.c文件中输入什么,以及它的输入是否正确。 +你必须采取知道你将在.c 文件中输入什么,以及它的输入是否正确。 -在这个例子中,该文件被称为main.c +在这个例子中,该文件被称为 main.c ```C #include <stdio.h> @@ -72,7 +72,7 @@ gcc -o main main.c -Wall ![Bash command that compiles code](https://miro.medium.com/max/1400/1*cqSo_NpYOYKdSjlBs8_EiQ.png) -**第二步: 编译c代码的命令** +**第二步: 编译 c 代码的命令** 第三步,也是最后一步,就是简单地了解程序的输出情况--确保它是按照我们的要求运行。 @@ -88,7 +88,7 @@ gcc -o main main.c -Wall 比较 C 文件的编译和单词的翻译。 -## C语言编译器如何工作 +## C 语言编译器如何工作 ![1*OH7zqS0KyPMj5qCfjM-lTA](https://miro.medium.com/max/1400/1*OH7zqS0KyPMj5qCfjM-lTA.jpeg) @@ -96,11 +96,11 @@ gcc -o main main.c -Wall 多年来,技术已经以令人难以置信的速度发展起来。这同样适用于编译器。 -随着时间的推移,C语言编译器已经演变成了许多版本。 +随着时间的推移,C 语言编译器已经演变成了许多版本。 -就像PlayStation--有PlayStation 2、PlayStation 3、PlayStation 4,等等。 +就像 PlayStation--有 PlayStation 2、PlayStation 3、PlayStation 4,等等。 -C语言编译器的情况也是如此。一旦它被_标准化,_就会产生许多版本。: +C 语言编译器的情况也是如此。一旦它被_标准化,_就会产生许多版本。: - C89/90, a version of C once _standardized,_ - C99 代替 C89 和 C90 在 1999. @@ -108,11 +108,11 @@ C语言编译器的情况也是如此。一旦它被_标准化,_就会产生 - C17 代替 C11 在 2018. - C2X 将代替 C17 在 2023. -就像PlayStation一样,每个新版本都有新的功能。 +就像 PlayStation 一样,每个新版本都有新的功能。 -有些人喜欢只在他们的PlayStation2上玩。 +有些人喜欢只在他们的 PlayStation2 上玩。 -这对程序员来说也是一样的。由于各种原因,程序员可能更喜欢用C99或C11编写和调试C代码。 +这对程序员来说也是一样的。由于各种原因,程序员可能更喜欢用 C99 或 C11 编写和调试 C 代码。 ## 关于编译器的更多信息 @@ -126,7 +126,7 @@ C语言编译器的情况也是如此。一旦它被_标准化,_就会产生 `-Wall` 选项启用编译器警告。编译器警告让你知道你的代码中有些地方不太对。 -这类似于Grammarly。如果Grammarly建议改变一个句子,在大多数情况下,你应该改变它,使其更清晰、更正确。 +这类似于 Grammarly。如果 Grammarly 建议改变一个句子,在大多数情况下,你应该改变它,使其更清晰、更正确。 否则,如果你试图改变一个已经正确的短语中的东西,它可能会变得难以辨认。 @@ -150,28 +150,28 @@ C语言编译器的情况也是如此。一旦它被_标准化,_就会产生 这套规则可以是一条法律,一个证书,或者只是某个领域的工人使用的基本惯例。 -这同样适用于C语言编译器。 +这同样适用于 C 语言编译器。 -正是标准化帮助人们就事情应该如何做达成一致,无论是C语言编译器、汽车部件,还是其他任何东西。 +正是标准化帮助人们就事情应该如何做达成一致,无论是 C 语言编译器、汽车部件,还是其他任何东西。 -标准化还可以帮助人们就使用哪个版本的C语言达成一致。C语言编译器就是一个例子。 +标准化还可以帮助人们就使用哪个版本的 C 语言达成一致。C 语言编译器就是一个例子。 -长期以来,C语言编译器一直被认为是软件开发的一个基本组成部分。 +长期以来,C 语言编译器一直被认为是软件开发的一个基本组成部分。 -由于有了C语言编译器标准,开发者可以编译和运行其他人的代码,而不必担心他们的编译器无法工作。 +由于有了 C 语言编译器标准,开发者可以编译和运行其他人的代码,而不必担心他们的编译器无法工作。 为了创造如此重要的行业基石,必须有一个负责建立标准的组织 -许多组织创建和管理标准。就C语言编译器而言,ISO(国际标准化组织)管理着这些标准。 +许多组织创建和管理标准。就 C 语言编译器而言,ISO(国际标准化组织)管理着这些标准。 -只要ISO管理未来的C编译器标准,程序员和公司就可以开发可靠的软件。 +只要 ISO 管理未来的 C 编译器标准,程序员和公司就可以开发可靠的软件。 ## 结束语 谢你的阅读! 现在你明白: - 什么是编译器 -- C语言编译器的基本历史 +- C 语言编译器的基本历史 - 标准化意味着什么 -[这是GitHub仓库](https://github.com/tiagomonteiro0715/freecodecamp-my-articles-source-code),里面有我创建的代码和图像文件。 +[这是 GitHub 仓库](https://github.com/tiagomonteiro0715/freecodecamp-my-articles-source-code),里面有我创建的代码和图像文件。 diff --git a/chinese/articles/what-is-a-dns-server.md b/chinese/articles/what-is-a-dns-server.md index baf2ec896..ae8003438 100644 --- a/chinese/articles/what-is-a-dns-server.md +++ b/chinese/articles/what-is-a-dns-server.md @@ -5,48 +5,48 @@ ![What is a DNS Server? DNS Servers Explained](https://www.freecodecamp.org/news/content/images/size/w2000/2022/04/christina-wocintechchat-com-glRqyWJgUeY-unsplash--1-.jpg) -如果没有DNS服务器,网络根本无法工作。它们负责将域名翻译成IP地址。然后计算机使用这些IP地址来定位和连接到网络服务器,并将用户发送到正确的网站。 +如果没有 DNS 服务器,网络根本无法工作。它们负责将域名翻译成 IP 地址。然后计算机使用这些 IP 地址来定位和连接到网络服务器,并将用户发送到正确的网站。 -许多人第一次了解DNS系统是在2021年10月,当时由于灾难性的DNS配置错误,Facebook的所有应用程序和网站同时瘫痪。 +许多人第一次了解 DNS 系统是在 2021 年 10 月,当时由于灾难性的 DNS 配置错误,Facebook 的所有应用程序和网站同时瘫痪。 -### 什么是IP地址? +### 什么是 IP 地址? -IP地址是网络上一个设备的唯一标识符。它们被用来将流量导向网络上的正确设备。 +IP 地址是网络上一个设备的唯一标识符。它们被用来将流量导向网络上的正确设备。 -例如,google.com的主IP地址是172.217.165.14。 +例如,google.com 的主 IP 地址是 172.217.165.14。 -IP地址可能很难记住。特别是如果它们是长而复杂的。相反,人类对可读的名字更容易记住。 +IP 地址可能很难记住。特别是如果它们是长而复杂的。相反,人类对可读的名字更容易记住。 -## DNS服务器的主要类型有哪些? +## DNS 服务器的主要类型有哪些? -有许多不同类型的DNS服务器,每个都有其独特的功能。 +有许多不同类型的 DNS 服务器,每个都有其独特的功能。 -最常见的DNS服务器类型是递归DNS服务器。它负责代表其客户进行DNS查询。 +最常见的 DNS 服务器类型是递归 DNS 服务器。它负责代表其客户进行 DNS 查询。 -### 递归DNS服务器如何工作 +### 递归 DNS 服务器如何工作 -客户端--通常是一个网络浏览器--向一个递归的DNS服务器发送一个DNS查询(这个域名的IP地址是什么)。该服务器对查询进行解析,然后将答案返回给客户。 +客户端--通常是一个网络浏览器--向一个递归的 DNS 服务器发送一个 DNS 查询(这个域名的 IP 地址是什么)。该服务器对查询进行解析,然后将答案返回给客户。 -递归DNS服务器通常由互联网服务提供商(ISP)运行。这些向你收取每月的互联网接入费用的公司。 +递归 DNS 服务器通常由互联网服务提供商(ISP)运行。这些向你收取每月的互联网接入费用的公司。 -### 权威性DNS服务器如何工作 +### 权威性 DNS 服务器如何工作 -另一种类型的DNS服务器是权威性DNS服务器。这些服务器负责存储一个域的DNS记录。它们包含一个公共IP地址和相应主机名的数据库。 +另一种类型的 DNS 服务器是权威性 DNS 服务器。这些服务器负责存储一个域的 DNS 记录。它们包含一个公共 IP 地址和相应主机名的数据库。 -权威DNS服务器负责将域名翻译成IP地址。这使用户能够使用域名而不是IP地址访问网站。 +权威 DNS 服务器负责将域名翻译成 IP 地址。这使用户能够使用域名而不是 IP 地址访问网站。 -权威的DNS服务器通常由域名注册商提供。 +权威的 DNS 服务器通常由域名注册商提供。 -### 配置DNS服务器的方式有哪些? +### 配置 DNS 服务器的方式有哪些? -你可以使用这些方法之一来配置DNS服务器: +你可以使用这些方法之一来配置 DNS 服务器: -- **静态IP地址服务器** - 已分配给特定计算机的永久性IP地址。静态IP地址是需要随时访问的计算机的理想选择,如服务器。 -- **动态IP地址服务器** - 当设备没有一直连接到网络时(如公共Wi-Fi网络),这些是有用的。你也可以用它们来平衡网络流量,或者给那些不经常连接到网络的设备分配临时IP地址。 +- **静态 IP 地址服务器** - 已分配给特定计算机的永久性 IP 地址。静态 IP 地址是需要随时访问的计算机的理想选择,如服务器。 +- **动态 IP 地址服务器** - 当设备没有一直连接到网络时(如公共 Wi-Fi 网络),这些是有用的。你也可以用它们来平衡网络流量,或者给那些不经常连接到网络的设备分配临时 IP 地址。 - **Round Robin 服务器** – 这些通过返回一个 IP 地址列表来解析域名——每个地址都对应一个能够提供所请求信息的服务器。 Round Robin 服务器可以在一组服务器之间平均分配流量。 这样可以确保没有单个服务器因请求而过载,并且其他服务器也可以接收到它们公平份额的流量。 -- **负载均衡器** – FreeCodeCamp.org使用负载均衡服务器,我想大多数大型网站也是如此。 +- **负载均衡器** – FreeCodeCamp.org 使用负载均衡服务器,我想大多数大型网站也是如此。 -你还可以配置DNS服务器使用不同类型的缓存,这可以提高性能。 +你还可以配置 DNS 服务器使用不同类型的缓存,这可以提高性能。 ### 什么是缓存? @@ -56,27 +56,27 @@ IP地址可能很难记住。特别是如果它们是长而复杂的。相反, 这就是内容交付网络(CDN)的工作方式。缓存可以极大地加快你的网站或服务的性能。 -## DNS会改变你的IP地址吗? +## DNS 会改变你的 IP 地址吗? -不,切换DNS服务器不会改变你的IP地址。 +不,切换 DNS 服务器不会改变你的 IP 地址。 -DNS服务器将域名翻译成IP地址。默认情况下,所有的网络浏览器都有自动检测其当前网络的DNS设置的选项。 +DNS 服务器将域名翻译成 IP 地址。默认情况下,所有的网络浏览器都有自动检测其当前网络的 DNS 设置的选项。 -因此,当你连接到虚拟专用网络(VPN)时,你的VPN的DNS服务器将取代你的ISP的DNS服务器。 +因此,当你连接到虚拟专用网络(VPN)时,你的 VPN 的 DNS 服务器将取代你的 ISP 的 DNS 服务器。 -## 我如何设置一个DNS服务器? +## 我如何设置一个 DNS 服务器? -如果你想为你的公司或组织建立自己的DNS服务器,这里有一些开始的步骤: +如果你想为你的公司或组织建立自己的 DNS 服务器,这里有一些开始的步骤: -1. 选择正确的DNS服务器软件。一些流行的选择包括BIND、ISC DHCP和PowerDNS。 -2. 将DNS服务器软件安装在一个专用服务器上。这将帮助你确保你的服务器拥有可靠运行所需的资源。如果你使用云计算,你就不必担心停电或网络中断会使你的DNS瘫痪。 -3. 配置DNS服务器软件。这包括设置DNS区域和记录。 -4. 测试DNS服务器。一旦启动并运行,你可以通过模拟流量对其进行压力测试,以确保它不会出问题。 +1. 选择正确的 DNS 服务器软件。一些流行的选择包括 BIND、ISC DHCP 和 PowerDNS。 +2. 将 DNS 服务器软件安装在一个专用服务器上。这将帮助你确保你的服务器拥有可靠运行所需的资源。如果你使用云计算,你就不必担心停电或网络中断会使你的 DNS 瘫痪。 +3. 配置 DNS 服务器软件。这包括设置 DNS 区域和记录。 +4. 测试 DNS 服务器。一旦启动并运行,你可以通过模拟流量对其进行压力测试,以确保它不会出问题。 -也有很多托管的DNS服务器工具,你可以使用,这应该是开箱即用,为你节省一些时间。这些工具每个月要花一点钱,但需要较少的专业知识来维护。 +也有很多托管的 DNS 服务器工具,你可以使用,这应该是开箱即用,为你节省一些时间。这些工具每个月要花一点钱,但需要较少的专业知识来维护。 -## 我希望你能学到很多关于DNS服务器的知识 +## 我希望你能学到很多关于 DNS 服务器的知识 -我希望你觉得这对你有帮助。如果你想学习更多关于编程和技术的知识,请尝试 [freeCodeCamp的核心编码课程](<https://www.freecodecamp.org/learn>),它是免费的。 +我希望你觉得这对你有帮助。如果你想学习更多关于编程和技术的知识,请尝试 [freeCodeCamp 的核心编码课程](<https://www.freecodecamp.org/learn>),它是免费的。 -> 译者注: 一个好用的的IP地理信息和CDN服务提供商查询工具 [nali](https://github.com/zu1k/nali) \ No newline at end of file +> 译者注: 一个好用的的 IP 地理信息和 CDN 服务提供商查询工具 [nali](https://github.com/zu1k/nali) \ No newline at end of file diff --git a/chinese/articles/what-is-a-domain-name-example.md b/chinese/articles/what-is-a-domain-name-example.md index f10b06554..a06c9ba94 100644 --- a/chinese/articles/what-is-a-domain-name-example.md +++ b/chinese/articles/what-is-a-domain-name-example.md @@ -9,7 +9,7 @@ 域名很重要,因为它使人们容易记住你的网站地址。 -想象一下,如果你不得不记住每个你想访问的网站的IP地址,会发生什么?这将是一个非常痛苦的网络之旅。 +想象一下,如果你不得不记住每个你想访问的网站的 IP 地址,会发生什么?这将是一个非常痛苦的网络之旅。 ## 域名中可以使用哪些字符? @@ -33,9 +33,9 @@ TLD 是顶级域名的意思。当域名系统(DNS)在 20 世纪 80 年代 其他受欢迎的顶级域名有.co(与 .com 类似,但适用于初创企业)。 -请注意,.org 是最初的顶级域名之一,是为非营利组织设计的。但实际上,任何人都可以注册一个.org 域名。例如,Craigslist.org是一个营利性的。 +请注意,.org 是最初的顶级域名之一,是为非营利组织设计的。但实际上,任何人都可以注册一个.org 域名。例如,Craigslist.org 是一个营利性的。 -所以要小心:一个公司使用.org 域名并不意味着他们是免税的非营利机构。(顺便说一下,如果你想知道,freeCodeCamp是一个免税的非营利组织)。 +所以要小心:一个公司使用.org 域名并不意味着他们是免税的非营利机构。(顺便说一下,如果你想知道,freeCodeCamp 是一个免税的非营利组织)。 ## 如何注册域名? @@ -49,4 +49,4 @@ TLD 是顶级域名的意思。当域名系统(DNS)在 20 世纪 80 年代 ## 现在你知道了域名工作的基本原理 -我希望你觉得这对你有帮助。如果你想学习更多关于编程和技术的知识,请尝试 [freeCodeCamp的核心编码课程](https://www.freecodecamp.org/learn)。它是免费的。 +我希望你觉得这对你有帮助。如果你想学习更多关于编程和技术的知识,请尝试 [freeCodeCamp 的核心编码课程](https://www.freecodecamp.org/learn)。它是免费的。 diff --git a/chinese/articles/what-is-a-virtual-machine-and-how-to-setup-a-vm-on-windows-linux-and-mac.md b/chinese/articles/what-is-a-virtual-machine-and-how-to-setup-a-vm-on-windows-linux-and-mac.md index b567e2e9d..01b0b15a0 100644 --- a/chinese/articles/what-is-a-virtual-machine-and-how-to-setup-a-vm-on-windows-linux-and-mac.md +++ b/chinese/articles/what-is-a-virtual-machine-and-how-to-setup-a-vm-on-windows-linux-and-mac.md @@ -10,37 +10,37 @@ 虚拟机在主机上的一个窗口中运行,为用户提供与他们使用完全不同的计算机相同的体验。虚拟机从主机上被沙盒化。这意味着,在虚拟机上运行的任何东西都不会影响主机。 -虚拟机通常用于在操作系统上运行软件,而这些软件最初并不是为其设计的。例如,如果你使用的是一台Mac电脑,你可以在Mac电脑上的Windows虚拟机内运行Windows程序。虚拟机也被用来快速设置软件的镜像,访问被病毒感染的数据,以及测试其他操作系统。 +虚拟机通常用于在操作系统上运行软件,而这些软件最初并不是为其设计的。例如,如果你使用的是一台 Mac 电脑,你可以在 Mac 电脑上的 Windows 虚拟机内运行 Windows 程序。虚拟机也被用来快速设置软件的镜像,访问被病毒感染的数据,以及测试其他操作系统。 -一台物理计算机可以同时运行多个虚拟机。通常情况下,一台服务器会使用一个叫做管理程序的程序来管理同时运行的多个虚拟机。虚拟机有虚拟硬件,包括CPU、内存、硬盘等。每一块虚拟硬件都被映射到主机上的真实硬件。 +一台物理计算机可以同时运行多个虚拟机。通常情况下,一台服务器会使用一个叫做管理程序的程序来管理同时运行的多个虚拟机。虚拟机有虚拟硬件,包括 CPU、内存、硬盘等。每一块虚拟硬件都被映射到主机上的真实硬件。 虚拟机有一些缺点。由于硬件资源是间接的,它们不像物理计算机那样高效。此外,当许多虚拟机在一台计算机上同时运行时,性能可能变得不稳定。 ## 虚拟机软件 -你可以使用许多不同的虚拟机程序。一些选择是VirtualBox(Windows、Linux、Mac OS X),VMware Player(Windows、Linux),VMware Fusion(Mac OS X)和Parallels Desktop(Mac OS X)。 +你可以使用许多不同的虚拟机程序。一些选择是 VirtualBox(Windows、Linux、Mac OS X),VMware Player(Windows、Linux),VMware Fusion(Mac OS X)和 Parallels Desktop(Mac OS X)。 -VirtualBox是最受欢迎的虚拟机程序之一,因为它是免费的、开源的,并且可以在所有流行的操作系统上使用。我们将告诉你如何使用VirtualBox设置虚拟机。 +VirtualBox 是最受欢迎的虚拟机程序之一,因为它是免费的、开源的,并且可以在所有流行的操作系统上使用。我们将告诉你如何使用 VirtualBox 设置虚拟机。 ## 设置一个虚拟机(VirtualBox) ![Virtualbox_logo](https://upload.wikimedia.org/wikipedia/commons/d/d5/Virtualbox_logo.png) -VirtualBox是甲骨文公司的一个开源的虚拟机程序。它允许用户在虚拟驱动器上虚拟安装许多操作系统,包括Windows、BSD、Linux、Solaris,等等。 +VirtualBox 是甲骨文公司的一个开源的虚拟机程序。它允许用户在虚拟驱动器上虚拟安装许多操作系统,包括 Windows、BSD、Linux、Solaris,等等。 -由于VirtualBox可以在Windows、Linux和Mac上运行,因此在每个操作系统中设置虚拟机的过程都基本相同。 +由于 VirtualBox 可以在 Windows、Linux 和 Mac 上运行,因此在每个操作系统中设置虚拟机的过程都基本相同。 -首先是下载和安装VirtualBox。你可以在这个链接上下载它: [VirtualBox 下载地址](https://www.virtualbox.org/wiki/Downloads) +首先是下载和安装 VirtualBox。你可以在这个链接上下载它: [VirtualBox 下载地址](https://www.virtualbox.org/wiki/Downloads) -你还需要为你想在虚拟机中运行的操作系统下载一个.iso文件。例如,你可以在这里下载一个Windows 10.iso文件: [https://www.microsoft.com/en-us/software-download/windows10ISO](https://www.microsoft.com/en-us/software-download/windows10ISO) +你还需要为你想在虚拟机中运行的操作系统下载一个.iso 文件。例如,你可以在这里下载一个 Windows 10.iso 文件: [https://www.microsoft.com/en-us/software-download/windows10ISO](https://www.microsoft.com/en-us/software-download/windows10ISO) -当你运行了VirtualBox,点击 "new" 按钮 +当你运行了 VirtualBox,点击 "new" 按钮 ![image-68](https://www.freecodecamp.org/news/content/images/2019/10/image-68.png) 创建一个新的虚拟机。 -接下来,你将选择你打算安装的操作系统。在 "Name" 框中,输入你要安装的操作系统的名称。VirtualBox将根据你输入的名称猜测类型和版本,但如果你需要,可以改变这些设置。 +接下来,你将选择你打算安装的操作系统。在 "Name" 框中,输入你要安装的操作系统的名称。VirtualBox 将根据你输入的名称猜测类型和版本,但如果你需要,可以改变这些设置。 ![image-69](https://www.freecodecamp.org/news/content/images/2019/10/image-69.png) @@ -54,7 +54,7 @@ VirtualBox是甲骨文公司的一个开源的虚拟机程序。它允许用户 启动虚拟机。 -一旦虚拟机启动,选择你想使用的.iso镜像文件。 +一旦虚拟机启动,选择你想使用的.iso 镜像文件。 ![image-72](https://www.freecodecamp.org/news/content/images/2019/10/image-72.png) @@ -64,6 +64,6 @@ VirtualBox是甲骨文公司的一个开源的虚拟机程序。它允许用户 ![image-73](https://www.freecodecamp.org/news/content/images/2019/10/image-73.png) -Windows 10已成功在虚拟机内运行。 +Windows 10 已成功在虚拟机内运行。 -恭喜你!你已经在VirtualBox中运行了你的第一个虚拟机。 +恭喜你!你已经在 VirtualBox 中运行了你的第一个虚拟机。 diff --git a/chinese/articles/what-is-an-svg-file.md b/chinese/articles/what-is-an-svg-file.md index 55b184e58..4bfa5cc09 100644 --- a/chinese/articles/what-is-an-svg-file.md +++ b/chinese/articles/what-is-an-svg-file.md @@ -5,7 +5,7 @@ ![什么是 SVG 文件?](https://www.freecodecamp.org/news/content/images/size/w2000/2022/06/svg.png) -SVG 意思是可缩放矢量图形。它是一种web友好的基于矢量的文件格式,用于在 Internet 上呈现二维图像。 +SVG 意思是可缩放矢量图形。它是一种 web 友好的基于矢量的文件格式,用于在 Internet 上呈现二维图像。 你可以通过扩展名识别 SVG 文件 – `.svg`. @@ -13,15 +13,15 @@ SVG 意思是可缩放矢量图形。它是一种web友好的基于矢量的文 这意味着无论 SVG 文件如何重新加工、缩放或调整大小,它们都不会像 PNG、JPG 和其他光栅图像那样变得模糊和像素化。 -本文将向你展示 SVG 图像文件的可能性,以及如何通过编码为自己制作一个SVG。 +本文将向你展示 SVG 图像文件的可能性,以及如何通过编码为自己制作一个 SVG。 ## 文章目录 -- [如何制作SVG文件](#howtomakeansvgfile) +- [如何制作 SVG 文件](#howtomakeansvgfile) - [如何使用图像编辑程序制作 SVG](#howtomakeansvgwithimageeditingprograms) - [如何使用 XML 制作 SVG](#howtomakeansvgwithxml) - [SVG 文件有什么用途?](#whatisansvgfileusedfor) -- [如何打开SVG文件](#howtoopenansvgfile) +- [如何打开 SVG 文件](#howtoopenansvgfile) - [如何将 SVG 文件转换为图像?](#howdoiconvertansvgfiletoanimage) - [结论](#conclusion) diff --git a/chinese/articles/what-is-coding-used-for.md b/chinese/articles/what-is-coding-used-for.md index 5bb4229ff..9ac15af63 100644 --- a/chinese/articles/what-is-coding-used-for.md +++ b/chinese/articles/what-is-coding-used-for.md @@ -87,10 +87,10 @@ 一些流行的高级编程语言和他们的用例包括: - **Python** 是一种用于处理、操作和分析大型复杂数据的语言。它也是执行网页抓取的首选语言。网页抓取是用来收集原始用户数据的一种技术。 -- **Ruby** 是用来创建自动化工具和脚本的语言。借助Rails框架(一种构建在Ruby之上的web框架),你可以创建各种动态web应用。 -- **JavaScript** 是万维网的三种语言之一(其他两种是HTML和CSS标记语言),JavaScript是创建交互式web应用的首选语言。 -- **Java** 不能和JavaScript混淆——它们完全不同。Java经常被用于编写手机应用、桌面软件和嵌入式系统。 -- **Swift** 是用于创建iOS手机应用的语言。 +- **Ruby** 是用来创建自动化工具和脚本的语言。借助 Rails 框架(一种构建在 Ruby 之上的 web 框架),你可以创建各种动态 web 应用。 +- **JavaScript** 是万维网的三种语言之一(其他两种是 HTML 和 CSS 标记语言),JavaScript 是创建交互式 web 应用的首选语言。 +- **Java** 不能和 JavaScript 混淆——它们完全不同。Java 经常被用于编写手机应用、桌面软件和嵌入式系统。 +- **Swift** 是用于创建 iOS 手机应用的语言。 - **C#** 用于创建桌面应用、企业软件和游戏开发。 同时也有必要提一下高级编程语言通常分为两类: @@ -110,7 +110,7 @@ <h3 id="tool">代码教你如何思考</h3> -苹果曾经的联合创始人、主席以及CEO乔布斯曾经说过: +苹果曾经的联合创始人、主席以及 CEO 乔布斯曾经说过: > “我认为这个国家(美国)的每一个人都应高学习如何编写计算机程序,学习一种计算机语言,因为它指导你如何思考。这就像去读法学院一样。我不认为每一个人都应该成为一名律师,但是去读法学院十分有帮助,因为它能够帮助你建立思考方法。所以我认为计算机科学实际上隶属于文科,每一个人都应该学习。 @@ -150,13 +150,13 @@ 不论身处在哪里,越来越多的人可以通过学习必要的技能,来获得薪酬更好的技术工作,提升生活质量。 -freeCodeCamp的使命就是帮助人们完全免费学习代码。 +freeCodeCamp 的使命就是帮助人们完全免费学习代码。 -freeCodeCamp搭建了经过深思熟虑、结构完善的[互动教程](https://www.freecodecamp.org/),包含响应式网页设计、关系数据库、SQL、Python等课程。 +freeCodeCamp 搭建了经过深思熟虑、结构完善的[互动教程](https://www.freecodecamp.org/),包含响应式网页设计、关系数据库、SQL、Python 等课程。 -[freeCodeCamp YouTube频道](https://www.youtube.com/channel/UC8butISFwT-Wl7EV0hUK0BQ)也包含数千小时的内容,你可以浏览并且找到各种技术话题的完整教学视频。 +[freeCodeCamp YouTube 频道](https://www.youtube.com/channel/UC8butISFwT-Wl7EV0hUK0BQ)也包含数千小时的内容,你可以浏览并且找到各种技术话题的完整教学视频。 -如果你想更深入了解某个话题,可以浏览[freeCodeCamp专栏](https://www.freecodecamp.org/news/)。这里有超过8000篇文章,包括代码技术话题的完全手册。 +如果你想更深入了解某个话题,可以浏览[freeCodeCamp 专栏](https://www.freecodecamp.org/news/)。这里有超过 8000 篇文章,包括代码技术话题的完全手册。 <h3 id="digital-literacy">代码将提高你的数字素养</h3> @@ -193,8 +193,8 @@ freeCodeCamp搭建了经过深思熟虑、结构完善的[互动教程](https:// - 笔记本电脑 - 台式电脑 - 智能电视 -- MP3播放器或者iPods -- 用来记录和检测心率和跑步时间的智能手环和计步器。你也可以通过它们连接手机来获取通知,甚至可以连MP3播放器来听音乐。 +- MP3 播放器或者 iPods +- 用来记录和检测心率和跑步时间的智能手环和计步器。你也可以通过它们连接手机来获取通知,甚至可以连 MP3 播放器来听音乐。 - 计算机 - 数码相机 - 逐渐取代纸质签名方式的数字签字板 @@ -224,8 +224,8 @@ freeCodeCamp搭建了经过深思熟虑、结构完善的[互动教程](https:// - 交通信号灯内置了微处理器,能够根据道路交通流量来变化信号灯。 - 电子停车咪表和停车场售票机。 - 地铁或者火车的售票机以及条形码扫码机器。它们读取车票上的条形码,就能够得知你从哪里来,你在车上待了多久,还可以计算你的票还剩多少时间,车票什么时候过期。 -- GPS定位系统确保你在不熟悉的地方也不会迷路。 -- 公共场所、主干道和购物中心的CCTV(闭路电视)摄像头确保监管和安全。 +- GPS 定位系统确保你在不熟悉的地方也不会迷路。 +- 公共场所、主干道和购物中心的 CCTV(闭路电视)摄像头确保监管和安全。 - 垂直电梯里微处理器,确保停靠在你通过按钮选择的楼层,传感器来检查是否有物品挡住了电梯门。 @@ -242,13 +242,13 @@ freeCodeCamp搭建了经过深思熟虑、结构完善的[互动教程](https:// - 空调 - 冰箱和冰柜 - 你可能还拥有虚拟数字助理或通过语音激活软件和语音识别设备。例如你提出问题或告诉他们向联系人列表中的某人发送电子邮件。他们执行您通过语音设置的任务 -- 将你所有设备连接到网络的WiFi路由 +- 将你所有设备连接到网络的 WiFi 路由 ## 总结 希望现在你已经了解什么是编码,以及为什么你的日常生活中代码无处不在。 -迈出学习代码的第一步,可以使用[freeCodeCamp教程](https://www.freecodecamp.org/learn/)。 +迈出学习代码的第一步,可以使用[freeCodeCamp 教程](https://www.freecodecamp.org/learn/)。 从第一个证书——[响应式网页设计](https://www.freecodecamp.org/learn/2022/responsive-web-design/)开始,然后按照你自己的节奏来。 diff --git a/chinese/articles/what-is-cross-site-request-forgery.md b/chinese/articles/what-is-cross-site-request-forgery.md index 4118eb8ce..a099af1df 100644 --- a/chinese/articles/what-is-cross-site-request-forgery.md +++ b/chinese/articles/what-is-cross-site-request-forgery.md @@ -5,27 +5,27 @@ ![Cross Site Request Forgery – What is a CSRF Attack and How to Prevent It](https://www.freecodecamp.org/news/content/images/size/w2000/2021/04/megan-article-image.jpg) -跨站点请求伪造(Cross Site Request Forgery)又被称作CSRF,是恶意站点或程序通过已认证用户的浏览器在受信任站点上执行操作非正常操作。可进行的恶意操作局限于已在网站通过身份验证的用户的功能。 +跨站点请求伪造(Cross Site Request Forgery)又被称作 CSRF,是恶意站点或程序通过已认证用户的浏览器在受信任站点上执行操作非正常操作。可进行的恶意操作局限于已在网站通过身份验证的用户的功能。 -例如,Jane 可能会在查看电子邮件的同时登录了她的网上银行,然后可能会点进钓鱼邮件中的自带转账请求的链接(比如迷惑性的短链接),要求Jane的银行转账至被攻击者控制的账户里。 +例如,Jane 可能会在查看电子邮件的同时登录了她的网上银行,然后可能会点进钓鱼邮件中的自带转账请求的链接(比如迷惑性的短链接),要求 Jane 的银行转账至被攻击者控制的账户里。 -由于Jane已经登陆了银行,该转账请求会被自动执行,因为是通过已被Jane授权了的浏览器发出的请求。 +由于 Jane 已经登陆了银行,该转账请求会被自动执行,因为是通过已被 Jane 授权了的浏览器发出的请求。 -## 什么是HTTP请求(Requests)和 Cookie? +## 什么是 HTTP 请求(Requests)和 Cookie? ### HTTP GET 请求 -这个请求用于向 Web 服务器请求数据(比如输入URL(请求网站)加载)。 +这个请求用于向 Web 服务器请求数据(比如输入 URL(请求网站)加载)。 ### HTTP POST 请求 -这个请求用于向web服务器发送数据(比如提交Web表单)。 +这个请求用于向 web 服务器发送数据(比如提交 Web 表单)。 某些 GET 和 POST 请求会自动触发,无需用户交互(例如获取搜索建议或使用 `img src` 属性加载图像内容)。 -### 会话Cookies +### 会话 Cookies 会话 cookie 是 HTTP 处理状态的一种方式(因为它本身不处理状态)。 网站使用会话 cookie(包含唯一 ID)来识别用户并保留他们的会话。 -设置会话cookie后,用户浏览器会在每次请求时将 cookie 发送到服务器,以供站点识别用户。 +设置会话 cookie 后,用户浏览器会在每次请求时将 cookie 发送到服务器,以供站点识别用户。 攻击者可以通过强制用户浏览器发送请求来利用 cookie 来冒充用户,如果用户已经登录到站点,cookie 将随请求自动发送。 @@ -35,41 +35,41 @@ - 攻击者想在网站应用中执行一种操作 — 例如更改密码、转账等。 - 不包含不可猜测的请求参数 — 攻击者可以猜测(或知道)网站应用的此类请求中需要的所有参数。 -- 该操作仅依赖 cookie 来验证请求是否来自用户,并可以通过 HTTP请求执行。 +- 该操作仅依赖 cookie 来验证请求是否来自用户,并可以通过 HTTP 请求执行。 -CSRF 会影响网站使用 cookie,浏览器身份验证或客户端证书对用户进行身份验证的 Web 应用程序。基本上CSRF可能发生在应用程序自动将用户证书或身份添加到请求的任何情况下。 +CSRF 会影响网站使用 cookie,浏览器身份验证或客户端证书对用户进行身份验证的 Web 应用程序。基本上 CSRF 可能发生在应用程序自动将用户证书或身份添加到请求的任何情况下。 -CSRF 攻击可以利用 GET 请求或 POST 请求(由于POST请求更复杂,因此不常见)。 +CSRF 攻击可以利用 GET 请求或 POST 请求(由于 POST 请求更复杂,因此不常见)。 两者都需要攻击者先诱骗受害者加载或将信息提交到 Web 应用程序。这可以通过多种方式发生 - 例如钓鱼链接。 -另外,类似于 XSS(跨站点脚本),CSRF 可以是一个可以被存储型漏洞。 比如攻击者将攻击代码存储在被接受的HTML代码中就会导致存储型CSRF(例如 `IMG` 或 `IFRAME` 标签)时。 这意味着浏览该页面的任何人都可能受到影响。 该漏洞可以伪装成普通链接或隐藏在图像标签中。 +另外,类似于 XSS(跨站点脚本),CSRF 可以是一个可以被存储型漏洞。 比如攻击者将攻击代码存储在被接受的 HTML 代码中就会导致存储型 CSRF(例如 `IMG` 或 `IFRAME` 标签)时。 这意味着浏览该页面的任何人都可能受到影响。 该漏洞可以伪装成普通链接或隐藏在图像标签中。 例如,作为网页上的普通链接<a href=“malicious link”>Unsubscribe here</a>` Or, as an image tag 或者伪装成图片标签: `<img src=“malicious link” width=“0” height=“0” border=“0”>` -## CSRF示例 +## CSRF 示例 想象一下,您的银行 (bank.com) 使用 GET 请求处理转账,其中包括几个参数(收款人身份以及转账的金额)。 -例如,如果Jim想给他的朋友Bob10 美元,请求可能如下所示: +例如,如果 Jim 想给他的朋友 Bob10 美元,请求可能如下所示: `http://bank.com/transfer?recipient=Bob&amount=10` 该请求还包括一个会话 cookie,用于标识帐户所持有人,以便银行知道从哪里取款。 -现在,攻击者可能成功使Jim 单击如下所示的链接(但已被巧妙的用缩短器或超链接缩短): +现在,攻击者可能成功使 Jim 单击如下所示的链接(但已被巧妙的用缩短器或超链接缩短): `http://bank.com/transfer?recipient=Hacker&amount=100000` -因为 Jim 已经登录,他的浏览器会将他的cookie 和请求一并发送 — 所以他的银行相信他本人正在请求转账于是执行了转账请求。 +因为 Jim 已经登录,他的浏览器会将他的 cookie 和请求一并发送 — 所以他的银行相信他本人正在请求转账于是执行了转账请求。 -## 如何防止CSRF攻击 +## 如何防止 CSRF 攻击 ### 谨慎选择网站框架 -使用内置了防护CSRF的框架,例如 `.NET`。 正确的配置很重要。如果您使用的框架没有保护,可以使用 Anti-CSRF 令牌添加保护。 +使用内置了防护 CSRF 的框架,例如 `.NET`。 正确的配置很重要。如果您使用的框架没有保护,可以使用 Anti-CSRF 令牌添加保护。 ### 使用 Anti-CSRF 令牌 @@ -77,13 +77,13 @@ Or, as an image tag 或者伪装成图片标签: `<img src=“malicious link” 这个令牌通过隐藏字段发送,应该是一个会在很短的时间内失效的不可预测的随机数,且不能重复使用。 -根据不同页面的信息敏感程度,可以针对每个请求使用不同的令牌,或者仅针对不同的表单。令牌应该以安全的方式进行对比验证(例如比较哈希值),并且不应在 HTTP GET请求中发送,防止作为 URL 的一部分或者 Referrer泄漏。 +根据不同页面的信息敏感程度,可以针对每个请求使用不同的令牌,或者仅针对不同的表单。令牌应该以安全的方式进行对比验证(例如比较哈希值),并且不应在 HTTP GET 请求中发送,防止作为 URL 的一部分或者 Referrer 泄漏。 ### 在 Cookie 中使用 SameSite 标记 SameSite 标记的 cookie,只能发送来自同域名的请求。 -基本上,www.bank.com 可以被允许向`www.bank.com/updatepassword` 提交request请求。 但`www.maliciousdomain.com`向 www.bank.com/updatepassword 发出请求时不能发送会话cookie,因此无法进行攻击。 +基本上,www.bank.com 可以被允许向`www.bank.com/updatepassword` 提交 request 请求。 但`www.maliciousdomain.com`向 www.bank.com/updatepassword 发出请求时不能发送会话 cookie,因此无法进行攻击。 现在大多数浏览器都支持这个标志,但不是全部。 它应该是综合防御战略的一部分。 @@ -94,24 +94,24 @@ SameSite 标记的 cookie,只能发送来自同域名的请求。 例如,您可以采取以下一些其他保护措施 - 使用标准头文件验证来源(确定请求的来源和目标位置是否匹配) - 使用自定义的请求头文件(站点将不接受没有相应的头文件的请求) -- 双重提交 cookie(尤其是一秒钟随机生成且未知的cookie;对于攻击者来说,必须提交该参数才能正常请求)。 +- 双重提交 cookie(尤其是一秒钟随机生成且未知的 cookie;对于攻击者来说,必须提交该参数才能正常请求)。 ### 让用户参与交易流程 对于转账或密码更改等敏感操作,要求用户行为(如 CAPTCHA、一次性令牌或重新身份验证)。 -## 无效的措施示例: +## 无效的措施示例 - **多步骤交易** 只要攻击者可以预测/确定每个步骤,有多少步骤并不重要。 - **HTTPS:** 总是一个好的安全措施,但对于 CSRF 无效 - **URL 改写** 这可以防止攻击者在 CSRF 攻击期间猜测受害者的会话 ID,但随后攻击者可以在 URL 中看到它。用一个缺陷替换另一个缺陷并不是一个好主意。 - **使用 私密(Secret)Cookie:** 私密 cookie 同样是作为请求的一部分提交,也就是仍然可以被攻击者利用。 - **只接受 POST 请求/避免 GET 请求:** 伪造的 POST 请求仍可用于执行 CSRF 攻击。 -### CSRF的其他称呼 +### CSRF 的其他称呼 -CSRF 也被称作 XSRF, Sea Surf (CSRF发音), 会话叠置(Session Riding), 跨站点引用伪造(Cross-Site Reference Forgery),恶意连接(Hostile Linking), 一键攻击(One-Click Attack). +CSRF 也被称作 XSRF, Sea Surf (CSRF 发音), 会话叠置(Session Riding), 跨站点引用伪造(Cross-Site Reference Forgery),恶意连接(Hostile Linking), 一键攻击(One-Click Attack). -### Sources/Further Reading: +### Sources/Further Reading ### 资料来源/扩展阅读 - [OWASP CSRF](https://owasp.org/www-community/attacks/csrf) - [OWASP CSRF Prevention](https://owasp.org/www-community/attacks/csrf) diff --git a/chinese/articles/what-is-data-analysis.md b/chinese/articles/what-is-data-analysis.md index df873db42..de7e61f34 100644 --- a/chinese/articles/what-is-data-analysis.md +++ b/chinese/articles/what-is-data-analysis.md @@ -35,10 +35,10 @@ 5. [步骤五:分享结果](#step-5) 5. [数据分析需要什么技能?](#skills) 1. [熟练掌握数学和统计学知识](#maths) - 2. [了解SQL和关系型数据库](#sql) + 2. [了解 SQL 和关系型数据库](#sql) 3. [掌握一门编程语言](#programming-language) 4. [熟悉数据可视化工具](#data-viz) - 5. [了解Excel](#excel) + 5. [了解 Excel](#excel) ## 什么是数据?数据的含义和定义 @@ -259,15 +259,15 @@ 这里有一些数学课程可以让你起步: -- [大学代数——免费大学数学前置课程(7小时)](https://www.freecodecamp.org/news/learn-algebra-to-improve-your-programming-skills/) -- [微积分先修课——免费大学数学前置课程(7小时)](https://www.freecodecamp.org/news/precalculus-learn-college-math-prerequisites-with-this-free-5-hour-course/) +- [大学代数——免费大学数学前置课程(7 小时)](https://www.freecodecamp.org/news/learn-algebra-to-improve-your-programming-skills/) +- [微积分先修课——免费大学数学前置课程(7 小时)](https://www.freecodecamp.org/news/precalculus-learn-college-math-prerequisites-with-this-free-5-hour-course/) - [程序员的数学课](https://www.freecodecamp.org/news/maths-for-programmers/) 数据分析师需要掌握统计学和概率知识,以收集和分析数据,找出其中的模式,并从数据中得出结论。 要想开始学习,可以先从统计学入门课程入手,然后继续学习进阶课题: -- [学习大学统计学(8小时免费课程)](https://www.freecodecamp.org/news/free-statistics-course/) +- [学习大学统计学(8 小时免费课程)](https://www.freecodecamp.org/news/free-statistics-course/) - [如果想学习数据科学,请上这几堂统计课](https://www.freecodecamp.org/news/if-you-want-to-learn-data-science-take-a-few-of-these-statistics-classes-9bbabab098b9#.esdiw8wnk) ### 了解 SQL 和关系型数据库 @@ -284,7 +284,7 @@ SQL 是 **S**tructured **Q**uery **L**anguage (结构化查询语言)的缩 要想学习 SQL ,可以参考以下资源: -- [SQL 命令小抄——如何在10分钟内学会 SQL](https://www.freecodecamp.org/news/learn-sql-in-10-minutes/) +- [SQL 命令小抄——如何在 10 分钟内学会 SQL](https://www.freecodecamp.org/news/learn-sql-in-10-minutes/) - [学习 SQL ——初学者的免费关系数据库课程](https://www.freecodecamp.org/news/learn-sql-free-relational-database-courses-for-beginners/) - [关系型数据库认证](https://www.freecodecamp.org/learn/relational-database/) @@ -298,7 +298,7 @@ Python 是一种通用的编程语言,因其语法类似英语,所以对初 Python 提供了丰富的用于数据处理的包和库,如 Pandas 和 NumPy ,以及用于数据可视化的 Matplotlib 。 -要想开始学习,可以先看下[零基础如何学习 Python ](https://www.freecodecamp.org/news/how-to-learn-python/)。 +要想开始学习,可以先看下[零基础如何学习 Python](https://www.freecodecamp.org/news/how-to-learn-python/)。 一旦你理解了基础知识,你就可以继续学习 Pandas 、 NumPy 和 Matplotlib 。 @@ -308,16 +308,16 @@ Python 提供了丰富的用于数据处理的包和库,如 Pandas 和 NumPy - [Python 中用于数据科学的 Pandas 终极指南](https://www.freecodecamp.org/news/the-ultimate-guide-to-the-pandas-library-for-data-science-in-python/) - [Python 中用于科学计算的 NumPy 终极指南](https://www.freecodecamp.org/news/the-ultimate-guide-to-the-numpy-scientific-computing-library-for-python/) - [学习 NumPy 并开始用 Python 进行科学计算](https://www.freecodecamp.org/news/numpy-python-tutorial/) -- [如何用 Python 、 Pandas 和 Numpy 分析数据——10小时课程](https://www.freecodecamp.org/news/how-to-analyze-data-with-python-pandas/) +- [如何用 Python 、 Pandas 和 Numpy 分析数据——10 小时课程](https://www.freecodecamp.org/news/how-to-analyze-data-with-python-pandas/) - [Matplotlib 课程——学习 Python 数据可视化](https://www.freecodecamp.org/news/matplotlib-course-learn-python-data-visualization/) -- [Python 数据科学--面向初学者的12小时免费课程,学习 Pandas 、 NumPy 、 Matplotlib 及更多](https://www.freecodecamp.org/news/python-data-science-course-matplotlib-pandas-numpy/) +- [Python 数据科学--面向初学者的 12 小时免费课程,学习 Pandas 、 NumPy 、 Matplotlib 及更多](https://www.freecodecamp.org/news/python-data-science-course-matplotlib-pandas-numpy/) R 是一种用于统计分析和数据分析的语言。换言之,它不像 Python 那样适合初学者。 要开始学习 R 语言,可以从以下课程入手: - [R 语言解读](https://www.freecodecamp.org/news/r-programming-language-explained/) -- [2小时掌握 R 语言基础(免费统计编程课程)](https://www.freecodecamp.org/news/r-programming-course/) +- [2 小时掌握 R 语言基础(免费统计编程课程)](https://www.freecodecamp.org/news/r-programming-course/) ### 熟悉数据可视化工具 @@ -344,7 +344,7 @@ Excel 是数据分析中最必不可少的工具之一。 要学习如何使用 Excel ,请查看以下课程: - [学习 Excel ——完整视频课程](https://www.freecodecamp.org/news/learn-microsoft-excel/) -- [Excel 在线课程——11门免费 Excel 培训课程](https://www.freecodecamp.org/news/excel-classes-online-free-excel-training-courses/) +- [Excel 在线课程——11 门免费 Excel 培训课程](https://www.freecodecamp.org/news/excel-classes-online-free-excel-training-courses/) - [针对 Excel 用户的 Python 数据分析课程](https://www.freecodecamp.org/news/data-analysis-with-python-for-excel-users-course/) ## 结论 diff --git a/chinese/articles/what-is-docker-learn-how-to-use-containers-with-examples.md b/chinese/articles/what-is-docker-learn-how-to-use-containers-with-examples.md index 8cc787e55..17cd9c30f 100644 --- a/chinese/articles/what-is-docker-learn-how-to-use-containers-with-examples.md +++ b/chinese/articles/what-is-docker-learn-how-to-use-containers-with-examples.md @@ -32,7 +32,7 @@ Docker 的超能力的核心是利用所谓的 [cgroups](https://en.wikipedia.or ## Database 容器 -使用Docker,你可以在几秒钟内启动许多类型的数据库。这很容易,而且不会因为你运行数据库所需的其他要求而污染你的本地系统。一切都与Docker容器打包在一起。 +使用 Docker,你可以在几秒钟内启动许多类型的数据库。这很容易,而且不会因为你运行数据库所需的其他要求而污染你的本地系统。一切都与 Docker 容器打包在一起。 通过在 [hub.docker.com](https://hub.docker.com/) 搜索,你可以为许多数据库找到现成的容器。 @@ -42,19 +42,19 @@ Docker 的超能力的核心是利用所谓的 [cgroups](https://en.wikipedia.or docker run --rm -v "$PWD/data":/var/lib/mysql --name mysql -e MYSQL_ROOT_PASSWORD=admin-password -e MYSQL_DATABASE=my-database -p 3306:3306 mysql:8.0.28-debian ``` -该命令使用了运行Docker容器的高级功能: +该命令使用了运行 Docker 容器的高级功能: -- `-v "$PWD/data"` 映射了你的本地目录到容器的 `./data`目录 , 这使你能够启动Docker容器而不丢失你的数据, +- `-v "$PWD/data"` 映射了你的本地目录到容器的 `./data`目录 , 这使你能够启动 Docker 容器而不丢失你的数据, - `-p 3306:3306` 映射容器的 `3306` 端口到我们的机器的 `3306` 端口上,以便其他应用程序可以使用它, - `-e MYSQL_DATABASE=my-database` 设置一个环境变量,自动创建一个名为`my-database`的新数据库, - `-e MYSQL_ROOT_PASSWORD=admin-password` 设置一个环境变量来设置管理密码, - `--rm` 停止时移除容器。 -这些环境变量和更多的环境变量都记录在 [Docker镜像的页面](https://hub.docker.com/_/mysql/?tab=description)。 +这些环境变量和更多的环境变量都记录在 [Docker 镜像的页面](https://hub.docker.com/_/mysql/?tab=description)。 ### 如何使用数据库容器进行开发 -你将使用一个流行的技术栈来构建,一个基于[Java](https://www.w3schools.com/java/java_intro.asp) 和[Spring Boot](https://spring.io/projects/spring-boot) 的 Web 应用程序。为了专注于 Docker 部分,你可以从官方的 [用Rest指南访问JPA数据](https://spring.io/guides/gs/accessing-data-rest/) 克隆一个简单的演示应用程序。 +你将使用一个流行的技术栈来构建,一个基于[Java](https://www.w3schools.com/java/java_intro.asp) 和[Spring Boot](https://spring.io/projects/spring-boot) 的 Web 应用程序。为了专注于 Docker 部分,你可以从官方的 [用 Rest 指南访问 JPA 数据](https://spring.io/guides/gs/accessing-data-rest/) 克隆一个简单的演示应用程序。 ```sh # Download the sample application @@ -66,7 +66,7 @@ cd complete 该应用程序自带一个内存数据库,这对生产来说没有价值,因为它不允许多个服务访问和修改(mutate)一个数据库。一个[MySQL](https://www.mysql.com/)数据库更适合于将你的应用程序扩展到更多的读和写。 -因此,将MySQL驱动添加到你的 `pom.xml` 文件: +因此,将 MySQL 驱动添加到你的 `pom.xml` 文件: ```xml <!-- Disable in memory database --> @@ -190,11 +190,11 @@ public class AccessingDataRestApplicationTests { 使用简单的 Docker 工具对你的应用程序进行 Docker 化是可能的,但不推荐。 -你可以建立你的应用程序,使用一个包含Java的基础容器,然后复制并运行你的应用程序。但是有很多陷阱,每种语言和框架都是如此。所以一定要寻找能让你的生活更轻松的工具。 +你可以建立你的应用程序,使用一个包含 Java 的基础容器,然后复制并运行你的应用程序。但是有很多陷阱,每种语言和框架都是如此。所以一定要寻找能让你的生活更轻松的工具。 在这个例子中,你将使用 [Jib](https://github.com/GoogleContainerTools/jib) 和 [distroless containers](https://github.com/GoogleContainerTools/distroless) 来轻松构建一个 Docker 容器。将两者结合使用,可以得到一个最小的、安全的、可复制的容器,它在本地和生产中的工作方式是一样的。 -要使用 Jib,你需要把它作为一个maven插件添加到你的 `pom.xml` 文件: +要使用 Jib,你需要把它作为一个 maven 插件添加到你的 `pom.xml` 文件: ```xml <build> @@ -253,11 +253,11 @@ docker run --net=host my-docker-image 一切都在你独立的 Docker 环境中,并在本地工作,像持续集成系统和生产系统中,你可能启动数百个你的应用程序。 -你可以在 [我的GitHub Docker For Development应用实例库](https://github.com/sesigl/docker-for-development-example-application) 中找到可以使用的例子。 +你可以在 [我的 GitHub Docker For Development 应用实例库](https://github.com/sesigl/docker-for-development-example-application) 中找到可以使用的例子。 我希望你喜欢这篇文章。 -如果你喜欢它,觉得有必要给我点赞,或者只是想联系我,[在Twitter上关注我](https://twitter.com/sesigl)。 +如果你喜欢它,觉得有必要给我点赞,或者只是想联系我,[在 Twitter 上关注我](https://twitter.com/sesigl)。 我在 eBay Kleinanzeigen 工作,这是全球最大的电子商务公司之一。顺便说一下,[我们正在招聘](https://www.ebay-kleinanzeigen.de/careers)! diff --git a/chinese/articles/what-is-dom-in-javascript.md b/chinese/articles/what-is-dom-in-javascript.md index 210119495..fc76d27b5 100644 --- a/chinese/articles/what-is-dom-in-javascript.md +++ b/chinese/articles/what-is-dom-in-javascript.md @@ -5,33 +5,33 @@ ![What is the DOM? A Behind-the-Scenes Guide](https://www.freecodecamp.org/news/content/images/size/w2000/2022/09/What-is-DOM-and-Events--in-JavaScipt--2-.png) -对于一名高效工作的前端开发者来说,了解JavaScript中的DOM和事件是如何运作非常重要。 +对于一名高效工作的前端开发者来说,了解 JavaScript 中的 DOM 和事件是如何运作非常重要。 -本文将讲解DOM是什么以及如何运作的。 +本文将讲解 DOM 是什么以及如何运作的。 -## DOM是什么? +## DOM 是什么? -DOM的全称是文档对象模型,是连接JavaScript和web浏览器之间的接口。 +DOM 的全称是文档对象模型,是连接 JavaScript 和 web 浏览器之间的接口。 -通过DOM,你可以编写JavaScript来创建、修改、删除HTML元素,设置样式、类别和属性,监听以及和响应事件。 +通过 DOM,你可以编写 JavaScript 来创建、修改、删除 HTML 元素,设置样式、类别和属性,监听以及和响应事件。 -HTML文档生成DOM树之后,你就可以与之交互。DOM是一个复杂的API,包含与DOM树交互的方法和属性。 +HTML 文档生成 DOM 树之后,你就可以与之交互。DOM 是一个复杂的 API,包含与 DOM 树交互的方法和属性。 ![Frame-70-1](https://www.freecodecamp.org/news/content/images/2022/09/Frame-70-1.png) -DOM图解 +DOM 图解 -你可以在[这个网站](https://fritscher.ch/dom-css/)视觉化DOM。 +你可以在[这个网站](https://fritscher.ch/dom-css/)视觉化 DOM。 -## DOM是如何运作的 – 幕后揭秘 +## DOM 是如何运作的 – 幕后揭秘 -DOM的组织形式非常巧妙。顶层元素被称作`EventTarget`。你可以借助下图更好地了解工作原理。 +DOM 的组织形式非常巧妙。顶层元素被称作`EventTarget`。你可以借助下图更好地了解工作原理。 ![DOM-behind-the-scene-1](https://www.freecodecamp.org/news/content/images/2022/09/DOM-behind-the-scene-1.png) `EventTarget`接口由可以接收事件、并且可以创建监听器的对象实现。换句话说,任何事件目标都会实现与该接口有关的这三个方法。虽然**Element**及其子项、**Document** 和 **Window** 是最常见的事件目标(EventTarget),但其他对象也可以是。 -Window代表了浏览器窗口。所有全局的JavaScript对象、函数和变量都自动归属于window对象。全局变量是window对象的属性,全局函数是window对象的方法。即便是文档对象(HTML DOM)也是window对象的属性。 +Window 代表了浏览器窗口。所有全局的 JavaScript 对象、函数和变量都自动归属于 window 对象。全局变量是 window 对象的属性,全局函数是 window 对象的方法。即便是文档对象(HTML DOM)也是 window 对象的属性。 ```js window.document.getElementById("header"); @@ -41,19 +41,19 @@ window.document.getElementById("header"); document.getElementById("header"); ``` -Nodes归属于DOM(即文档对象模型)。在DOM中,如元素、属性、文本等都被组织在一个分层级的树状结构中,相互为子或父元素。这些个体就被称为node(节点)。 +Nodes 归属于 DOM(即文档对象模型)。在 DOM 中,如元素、属性、文本等都被组织在一个分层级的树状结构中,相互为子或父元素。这些个体就被称为 node(节点)。 -上图中的Node可以被JavaScript对象表示。我们通常使用`document.querySelector()`, `document.getElementById()`等方法来调用这些节点。 +上图中的 Node 可以被 JavaScript 对象表示。我们通常使用`document.querySelector()`, `document.getElementById()`等方法来调用这些节点。 现在让我们看看文档。 -## 如何使用DOM来选择、创建和删除元素 +## 如何使用 DOM 来选择、创建和删除元素 -因为DOM,我们可以使用JavaScript来选择、删除和创建元素。 +因为 DOM,我们可以使用 JavaScript 来选择、删除和创建元素。 ### 如何选择元素 -JavaScript中有各种各样的方式来选择HTML元素: +JavaScript 中有各种各样的方式来选择 HTML 元素: - document.getElementById(); - document.getElementByClassName(); @@ -63,7 +63,7 @@ JavaScript中有各种各样的方式来选择HTML元素: #### 如何使用 `document.getElementById()` 方法 - `getElementById()`方法返回的元素id与传入的字符串匹配。因为HTML元素的id是唯一值,所以这种方式的速度最快。 + `getElementById()`方法返回的元素 id 与传入的字符串匹配。因为 HTML 元素的 id 是唯一值,所以这种方式的速度最快。 例子: @@ -74,9 +74,9 @@ console.log(ele); // 打印对应id名称的元素 #### 如何使用`document.getElementByClassName()`方法 -`document.getElementByClassName()`返回一个`HTMLCollection`,这个集合中的元素类名与传入的字符串匹配。我们可以同时搜索多个类别名称,传入的时候用空格隔开,它会实时返回的HTMLCollection。 +`document.getElementByClassName()`返回一个`HTMLCollection`,这个集合中的元素类名与传入的字符串匹配。我们可以同时搜索多个类别名称,传入的时候用空格隔开,它会实时返回的 HTMLCollection。 -“实时”是什么意思?就是说,一旦我们给传入的类名添加一个元素,HTMLCollection会自动获取这个新的元素。 +“实时”是什么意思?就是说,一旦我们给传入的类名添加一个元素,HTMLCollection 会自动获取这个新的元素。 例子: @@ -87,7 +87,7 @@ console.log(ele); // 打印实时HTMLCollection #### 如何使用`document.getElementByTagName();`方法 -`document.getElementByTagName()`返回一个`HTMLCollection`,这个集合中的元素标签与传入的字符串匹配。可以对任何HTML元素调用这个方法,它会返回实时的HTMLCollection。 +`document.getElementByTagName()`返回一个`HTMLCollection`,这个集合中的元素标签与传入的字符串匹配。可以对任何 HTML 元素调用这个方法,它会返回实时的 HTMLCollection。 例子: @@ -99,9 +99,9 @@ console.log(paragraph); // p 元素 HTMLCollection console.log(heading); // h1 元素 HTMLCollection ``` -#### 如何使用document.querySelector()方法 +#### 如何使用 document.querySelector()方法 -`document.querySelector()`返回满足传入条件的第一个HTML元素。在这个方法中我们可以传入类名、id和标签名。参见例子: +`document.querySelector()`返回满足传入条件的第一个 HTML 元素。在这个方法中我们可以传入类名、id 和标签名。参见例子: ```js const id = document.querySelector("#idname"); // 使用id @@ -112,23 +112,23 @@ const tag = document.querySelector("p"); // 使用标签名 选择规则: - 若使用类别名,在开头要添加(.), 如 (".classname") -- 若使用id, 在开头要添加(#),如("#id") +- 若使用 id, 在开头要添加(#),如("#id") - 若使用标签,可以直接使用标签名,如 ("p") -#### 如何使用document.querySelectorAll()方法 +#### 如何使用 document.querySelectorAll()方法 -`document.querySelectorAll()`是`querySelector`方法的延伸。这个方法返回**所有**匹配传入值的元素。该方法返回一个不会实时更新的NodeList。 +`document.querySelectorAll()`是`querySelector`方法的延伸。这个方法返回**所有**匹配传入值的元素。该方法返回一个不会实时更新的 NodeList。 ```js const ele = document.querySelectorAll("p"); console.log(ele); // 返回p标签的NodeList ``` -**注意**: HTMLCollection是实时更新的,但是NodeList并不是。 +**注意**: HTMLCollection 是实时更新的,但是 NodeList 并不是。 ### 如何创建元素 -你可以在JavaScript中创建HTML元素,并且动态地添加到HTML中。可以使用`document.createElement()`来添加元素,在括号内传入元素的标签名。 +你可以在 JavaScript 中创建 HTML 元素,并且动态地添加到 HTML 中。可以使用`document.createElement()`来添加元素,在括号内传入元素的标签名。 元素创建成功之后,可以添加类名,属性以及该元素的文本内容。 @@ -151,7 +151,7 @@ document.querySelector(".links").after(ele); ``` -在上述例子中,我们在JavaScript中创建了一个anchor标签,并且添加了类名和属性。在HTML中更新这个元素有以下四种方法: +在上述例子中,我们在 JavaScript 中创建了一个 anchor 标签,并且添加了类名和属性。在 HTML 中更新这个元素有以下四种方法: - `prepend()`: 在父节点的第一个子节点之前插入数据 - `append()`: 对位于最后一个索引的元素之后插入数据或内容 @@ -160,7 +160,7 @@ document.querySelector(".links").after(ele); ### 如何删除元素 -我们已经了解了如何使用JavaScript创建元素以及如何将它添加到HTML中。但是如果我们想要删除一个HTML元素呢?很简单!只需要对这个元素调用`remove()`方法。 +我们已经了解了如何使用 JavaScript 创建元素以及如何将它添加到 HTML 中。但是如果我们想要删除一个 HTML 元素呢?很简单!只需要对这个元素调用`remove()`方法。 **例子:** @@ -173,11 +173,11 @@ ele.addEventListener('click', function(){ }) ``` -## 如何通过JavaScript来控制CSS +## 如何通过 JavaScript 来控制 CSS -我们已经学习了如何通过JavaScript来控制HTML,现在我们来学习如何通过JavaScript来控制CSS,帮助你动态地更改页面。 +我们已经学习了如何通过 JavaScript 来控制 HTML,现在我们来学习如何通过 JavaScript 来控制 CSS,帮助你动态地更改页面。 -如果想要通过点击一个元素,就更改这个元素的背景颜色的话,可以通过JavaScript来实现。 +如果想要通过点击一个元素,就更改这个元素的背景颜色的话,可以通过 JavaScript 来实现。 **语法如下:** @@ -189,7 +189,7 @@ ele.style.propertyName = value; //ele.style.color = red; ``` -当使用JavaScript来修改CSS属性的时候,需要注意在CSS中 `-` 用来连接单词,如 `background-color`但在JavaScript中我们使用 `backgroundColor` (驼峰样式)。 +当使用 JavaScript 来修改 CSS 属性的时候,需要注意在 CSS 中 `-` 用来连接单词,如 `background-color`但在 JavaScript 中我们使用 `backgroundColor` (驼峰样式)。 **例子:** @@ -198,7 +198,7 @@ const ele = document.querySelector("div"); ele.style.backgroundColor = "red"; ``` -假设你已经给项目编写好了CSS,你希望通过JavaScript来添加类名,可以使用`classList`。 +假设你已经给项目编写好了 CSS,你希望通过 JavaScript 来添加类名,可以使用`classList`。 **例子:** @@ -229,7 +229,7 @@ ele.className = "p-10"; // 删除所有现有类别,添加p-10 当用户点击鼠标、让鼠标在元素上滑过、按键等时,事件就发生了。所以当你想在事件发生时做一些事情的话,就可以使用事件处理器来引发这个事件。 -我们使用事件处理器在事件发生的时候来执行一些JavaScript代码。在JavaScript中有许多不同的事件处理器 (这里是一份简单的[列表](https://way2tutorial.com/html/html5_events_handler_list.php)),但将事件处理器添加到元素的方法相同。 +我们使用事件处理器在事件发生的时候来执行一些 JavaScript 代码。在 JavaScript 中有许多不同的事件处理器 (这里是一份简单的[列表](https://way2tutorial.com/html/html5_events_handler_list.php)),但将事件处理器添加到元素的方法相同。 **语法如下:** @@ -261,7 +261,7 @@ ele.addEventListener("click", function(){ ## 事件传播:冒泡和捕获 -事件传播决定了元素接收事件的顺序。有两种处理DOM中事件传播顺序的方式:事件冒泡和事件捕获。 +事件传播决定了元素接收事件的顺序。有两种处理 DOM 中事件传播顺序的方式:事件冒泡和事件捕获。 ![Frame-71-1](https://www.freecodecamp.org/news/content/images/2022/09/Frame-71-1.png) @@ -336,21 +336,21 @@ ele.addEventListener("click", function(){ 仔细看上面的代码示例,我分别在`nav`标签和`anchor`标签添加了事件处理器:当你点击`nav`的时候背景色会变成绿色,而当点击`anchor`标签的时候,背景色会看成粉色。 -但是当你点击`anchor`标签的时候,anchor和nav的背景色都发生了变化。这是因为**事件冒泡**。 +但是当你点击`anchor`标签的时候,anchor 和 nav 的背景色都发生了变化。这是因为**事件冒泡**。 -**当你只点击nav元素的时候发生的事情:** +**当你只点击 nav 元素的时候发生的事情:** ![Frame-72--1-](https://www.freecodecamp.org/news/content/images/2022/09/Frame-72--1-.png) -**点击nav元素的时候发生的事情** +**点击 nav 元素的时候发生的事情** -**当你只点击anchor元素的时候发生的事情:** +**当你只点击 anchor 元素的时候发生的事情:** ![Frame-73--1-](https://www.freecodecamp.org/news/content/images/2022/09/Frame-73--1-.png) -**当你点击anchor元素的时候发生的事情** +**当你点击 anchor 元素的时候发生的事情** -停止事件传播的方式是在事件发起的元素的事件处理器上使用 `stopPropagation()`。这样就可以阻止nav元素因为上面的原因被激活。 +停止事件传播的方式是在事件发起的元素的事件处理器上使用 `stopPropagation()`。这样就可以阻止 nav 元素因为上面的原因被激活。 ```html <!DOCTYPE html> @@ -404,21 +404,21 @@ ele.addEventListener("click", function(){ </html> ``` -## 如何遍历DOM +## 如何遍历 DOM -"优秀的JavaScript开发者应该知道如何遍历DOM — 即 **从一个元素选择到另一个元素的行为** " – [Zell Liew](https://zellwk.com/blog/dom-traversals/) +"优秀的 JavaScript 开发者应该知道如何遍历 DOM — 即 **从一个元素选择到另一个元素的行为** " – [Zell Liew](https://zellwk.com/blog/dom-traversals/) 现在我们来看看为什么遍历会比 `document.querySelector()`方法好用,以及如何像专家一样遍历。 -有三种遍历DOM的方式: +有三种遍历 DOM 的方式: - 向上 - 向下 - 相临 -### 如何向上遍历DOM +### 如何向上遍历 DOM -有两种向上遍历DOM的方式: +有两种向上遍历 DOM 的方式: - parentElement - closest @@ -452,7 +452,7 @@ console.log(ele.closest(".demo")); // This is heading 在上述代码中我们选在离`.heading`最近并且类名为 `.demo`的元素。 -### 如何向下遍历DOM +### 如何向下遍历 DOM 我们可以在选择器中使用`children`方法来向下遍历,就可以选择到直属子元素。 @@ -475,14 +475,14 @@ console.log(child); // 返回HTMLCollection // div中的4个元素 ``` -### 如何遍历相邻的DOM +### 如何遍历相邻的 DOM -遍历相邻的DOM很有趣,有两种主要的方法: +遍历相邻的 DOM 很有趣,有两种主要的方法: - previousElementSibling - nextElementSibling -使用`previousElementSibling`方法,我们可以选择HTML中的前一个元素: +使用`previousElementSibling`方法,我们可以选择 HTML 中的前一个元素: ```html <div> @@ -496,7 +496,7 @@ const ele = document.querySelector("h1"); console.log(ele.previousElementSibling); // <a href="#">Link-1</a> ``` -使用`nextElementSibling`,我们可以选择HTML中的后一个元素: +使用`nextElementSibling`,我们可以选择 HTML 中的后一个元素: ```html <div> @@ -512,7 +512,7 @@ console.log(ele.nextElementSibling); // <h1>Heading</h1> ## **总结** -希望你已经了解JavaScript中的DOM是如何工作的,感谢阅读! +希望你已经了解 JavaScript 中的 DOM 是如何工作的,感谢阅读! 你可以在以下地方关注我: diff --git a/chinese/articles/what-is-git-learn-git-version-control.md b/chinese/articles/what-is-git-learn-git-version-control.md index 4b9dbe878..4b09af3fa 100644 --- a/chinese/articles/what-is-git-learn-git-version-control.md +++ b/chinese/articles/what-is-git-learn-git-version-control.md @@ -119,7 +119,7 @@ GitHub 和 Git 之间的不同之处。 ![](https://www.freecodecamp.org/news/content/images/2021/03/1_r3tL0x5-6x60uycPkIQxbg.png) 我们的文件在工作目录。 -### 步骤二:增加文件到暂存区。 +### 步骤二:增加文件到暂存区 我们可以使用`git add`命令去增加或更新文件到暂存区。如果我们决定不想在下一次提交中包括一些改动了的文件,那么简单地确保不把这些特殊的文件加到暂存区就行了。 ![](https://www.freecodecamp.org/news/content/images/2021/03/updated.png) diff --git a/chinese/articles/what-is-huffman-coding.md b/chinese/articles/what-is-huffman-coding.md index eaa9c6496..662da3dda 100644 --- a/chinese/articles/what-is-huffman-coding.md +++ b/chinese/articles/what-is-huffman-coding.md @@ -51,7 +51,7 @@ ## 解码字符串 -要解码文本,只需根据 ` 0`(左分支)或 `1`(右分支)前进,直到获取到一个字符。 写下该字符,然后从顶部重新开始: +要解码文本,只需根据 `0`(左分支)或 `1`(右分支)前进,直到获取到一个字符。 写下该字符,然后从顶部重新开始: ![Decoding using the tree'](https://www.baseclass.io/huffman-coding/4.png) diff --git a/chinese/articles/what-is-mlops-machine-learning-operations-explained.md b/chinese/articles/what-is-mlops-machine-learning-operations-explained.md index 8176b4e17..5a3795bcc 100644 --- a/chinese/articles/what-is-mlops-machine-learning-operations-explained.md +++ b/chinese/articles/what-is-mlops-machine-learning-operations-explained.md @@ -29,7 +29,7 @@ If you look MLOps up on Google trends, you'll see that it is a relatively new di ![](https://www.freecodecamp.org/news/content/images/2021/03/Screenshot-2021-03-23-at-4.39.30-AM.png) -### Here’s how I’d define MLOps: +### Here’s how I’d define MLOps MLOps is an engineering discipline that aims to unify ML systems development (dev) and ML systems deployment (ops) in order to standardize and streamline the continuous delivery of high-performing models in production. @@ -43,10 +43,10 @@ In order to understand MLOps, we must first understand the ML systems lifecycle. From start to bottom, the following teams chip in: -- **Business development or Product team** — defining business objective(s) with KPIs -- **Data Engineering** — data acquisition and preparation. -- **Data Science** — architecting ML solutions and developing models. -- **IT or DevOps** — complete deployment setup, monitoring alongside scientists. +- **Business development or Product team**—defining business objective(s) with KPIs +- **Data Engineering** —data acquisition and preparation. +- **Data Science**—architecting ML solutions and developing models. +- **IT or DevOps**—complete deployment setup, monitoring alongside scientists. Here is a very simplified representation of the ML lifecycle. @@ -65,7 +65,7 @@ Managing such systems at scale is not an easy task, and there are numerous bottl - There is a **shortage of Data Scientists** who are good at developing and deploying scalable web applications. There is a new profile of ML Engineers on the market these days that aims to serve this need. It is a sweet spot at the intersection of Data Science and DevOps. - **Changing business objectives in the model** —There are many dependencies with the data continuously changing, maintaining performance standards of the model, and ensuring AI governance. It’s hard to keep up with the continuous model training and evolving business objectives. - **Communication gaps** between technical and business teams with a hard-to-find common language to collaborate. Most often, this gap becomes the reason that big projects fail. -- **Risk assessment —** there is a lot of debate going on around the black-box nature of such ML/DL systems. Often models tend to drift away from what they were initially intended to do. Assessing the risk/cost of such failures is a very important and meticulous step. +- **Risk assessment—** there is a lot of debate going on around the black-box nature of such ML/DL systems. Often models tend to drift away from what they were initially intended to do. Assessing the risk/cost of such failures is a very important and meticulous step. For example, the cost of an inaccurate video recommendation on YouTube would be much lower compared to flagging an innocent person for fraud and blocking their account, and declining their loan applications. ## What Skills Do You Need for MLOps? @@ -90,12 +90,12 @@ Searching for data is one of the most strenuous tasks. It is a process with seve - Check the credibility of the data and its source. - Is the data source compliant with regulations like GDPR? - How to make the dataset accessible? -- What is the type of source — static (files) or real-time streaming (sensors)? +- What is the type of source—static (files) or real-time streaming (sensors)? - How many sources are to be used? - How to build a data pipeline that can drive both training and optimization once the model is deployed in the production environment? - What cloud services will you use? -### 3\. Data preparation and processing — part of data engineering. +### 3. Data preparation and processing—part of data engineering Data preparation includes tasks like feature engineering, cleaning (formatting, checking for outliers, imputations, rebalancing, and so on), and then selecting the set of features that contribute to the output of the underlying problem. @@ -105,7 +105,7 @@ An important part of deploying such pipelines is to choose the right combination You might want to practice building a few different kinds of pipelines (Batch vs Streaming) and try to deploy those pipelines on the cloud. -### 4\. Model training and experimentation — data science +### 4\. Model training and experimentation—data science As soon as your data is prepared, you move on to the next step of training your ML model. @@ -143,8 +143,8 @@ Other tasks include: You should build your ML pipelines keeping in mind the following tasks: -- Identify system requirements — parameters, compute needs, triggers. -- Choose an appropriate cloud architecture — hybrid or multi-cloud. +- Identify system requirements—parameters, compute needs, triggers. +- Choose an appropriate cloud architecture—hybrid or multi-cloud. - Construct training and testing pipelines. - Track and audit the pipeline runs. - Perform data validation. @@ -153,15 +153,15 @@ You should build your ML pipelines keeping in mind the following tasks: There are mainly two ways of deploying an ML model: -- Static deployment or embedded model — where the model is packaged into installable application software and is then deployed. For example, an application that offers batch-scoring of requests. -- Dynamic deployment — where the model is deployed using a web framework like FastAPI or Flask and is offered as an API endpoint that responds to user requests. +- Static deployment or embedded model—where the model is packaged into installable application software and is then deployed. For example, an application that offers batch-scoring of requests. +- Dynamic deployment—where the model is deployed using a web framework like FastAPI or Flask and is offered as an API endpoint that responds to user requests. Within dynamic deployment, you can use different methods: - deploying on a server (a virtual machine) - deploying in a container - serverless deployment -- model streaming — instead of REST APIs, all of the models and application code are registered on a stream processing engine like Apache Spark, Apache Storm, and Apache Flink. +- model streaming—instead of REST APIs, all of the models and application code are registered on a stream processing engine like Apache Spark, Apache Storm, and Apache Flink. Following are the considerations: @@ -170,7 +170,7 @@ Following are the considerations: - Performing explainability checks. - Ensuring that all governance requirements have been met. - Checking the quality of any data artifacts -- Load testing — compute resource usage. +- Load testing—compute resource usage. ### 7\. Monitor, optimize and maintain models diff --git a/chinese/articles/what-is-promise-in-javascript-for-beginners.md b/chinese/articles/what-is-promise-in-javascript-for-beginners.md index dab219d1b..037fd9a88 100644 --- a/chinese/articles/what-is-promise-in-javascript-for-beginners.md +++ b/chinese/articles/what-is-promise-in-javascript-for-beginners.md @@ -5,15 +5,15 @@ ![What is a Promise? JavaScript Promises for Beginners](https://www.freecodecamp.org/news/content/images/size/w2000/2021/08/JS-Promises-Expl.png) -如果你还是一个Javascript小白,你可能要纠结一下Promise到底是什么。 +如果你还是一个 Javascript 小白,你可能要纠结一下 Promise 到底是什么。 -最近我发了一条推特,描述了这个问题,我被大家的反馈震惊到了。所以我决定写一篇关于Promise的教程。 +最近我发了一条推特,描述了这个问题,我被大家的反馈震惊到了。所以我决定写一篇关于 Promise 的教程。 -我看过很多关于Promise的文章,但大部分教程都没有通过类比的方式解释清楚Promise到底是什么。初学者搞不懂Promise的根本原因是他们不知道Promise是做什么的,以及如何在简单和复杂的场景中使用它。 +我看过很多关于 Promise 的文章,但大部分教程都没有通过类比的方式解释清楚 Promise 到底是什么。初学者搞不懂 Promise 的根本原因是他们不知道 Promise 是做什么的,以及如何在简单和复杂的场景中使用它。 -因此在这篇教程中,我将通过一个小故事来解释什么是Promise,Promise是如何工作的。同时我也会通过一些代码示例来说明在JavaScript中如何使用Promise。 +因此在这篇教程中,我将通过一个小故事来解释什么是 Promise,Promise 是如何工作的。同时我也会通过一些代码示例来说明在 JavaScript 中如何使用 Promise。 -## 什么是Promise? +## 什么是 Promise? 想象一下,你准备面试某个公司的前端工程师。 @@ -41,34 +41,34 @@ 如果室友找到了简历,他会很高兴的告诉你他找到了,而你将继续面试,并**获得(FULFILL)**这份工作。 -## 如何把上述过程翻译成JS Code? +## 如何把上述过程翻译成 JS Code? -室友承诺找简历并回复信息的过程等同于我们在JavaScript中定义一个Promise。定义Promise并不能直接或立即获得返回值,而是返回一个Promise对象。这个Promise对象在一段时间后会接收返回值。 +室友承诺找简历并回复信息的过程等同于我们在 JavaScript 中定义一个 Promise。定义 Promise 并不能直接或立即获得返回值,而是返回一个 Promise 对象。这个 Promise 对象在一段时间后会接收返回值。 -Promise对象是异步的,这就意味着程序需要花点时间才能获得结果。这和找简历是一样的,都需要花点时间去找。 +Promise 对象是异步的,这就意味着程序需要花点时间才能获得结果。这和找简历是一样的,都需要花点时间去找。 基于这个原因,在找的这个时间里,面试官并不是什么都没做,而是基于**简历一会就找到**的承诺,他们依然开始面试候选人。在这个场景里,我们用**简历一会就找到**的承诺替换了**真实的简历**。 -同理,JS引擎也并不是等着什么也不做,而是继续执行后续代码,并将返回的Promise对象状态设置为**Pending**。 +同理,JS 引擎也并不是等着什么也不做,而是继续执行后续代码,并将返回的 Promise 对象状态设置为**Pending**。 -回复信息包含是否找到简历的状态信息。对于Promise对象来说,回复信息被称作返回值。 +回复信息包含是否找到简历的状态信息。对于 Promise 对象来说,回复信息被称作返回值。 如果回复信息是“success”,我们将录取候选人。如果是“failure”,我们不录取该候选人。 -在Promise中,我们通过[回调函数](/news/javascript-callback-functions-what-are-callbacks-in-js-and-how-to-use-them/) 处理Promise的返回值。这些处理函数定义在`then()`方法中。 +在 Promise 中,我们通过[回调函数](/news/javascript-callback-functions-what-are-callbacks-in-js-and-how-to-use-them/) 处理 Promise 的返回值。这些处理函数定义在`then()`方法中。 为了指定如何调用回调函数,需要使用以下两个方法: -- `resolve(value)`: 表明Promise任务成功,调用`then()`的成功回调函数。 -- `reject(error)`: 表明Promise任务失败,调用`then()`的错误回调函数。 +- `resolve(value)`: 表明 Promise 任务成功,调用`then()`的成功回调函数。 +- `reject(error)`: 表明 Promise 任务失败,调用`then()`的错误回调函数。 -如果Promise成功,则调用成功回调,如果失败,调用失败回调。 +如果 Promise 成功,则调用成功回调,如果失败,调用失败回调。 -在异步任务完成之前,Promise只是一个占位符。当你定义了一个Promise,你并不会**立即**获得返回值,而是获得一个Promise对象。 +在异步任务完成之前,Promise 只是一个占位符。当你定义了一个 Promise,你并不会**立即**获得返回值,而是获得一个 Promise 对象。 -## 如何在JavsScript中使用Promise +## 如何在 JavsScript 中使用 Promise -你可以通过`Promise`类定义一个Promise对象。 +你可以通过`Promise`类定义一个 Promise 对象。 ```js const myPromise = new Promise((resolve, reject) => { @@ -80,13 +80,13 @@ const myPromise = new Promise((resolve, reject) => { console.log(myPromise); ``` -示例1 +示例 1 在控制台运行将返回一个`Promise`对象: ![](https://www.freecodecamp.org/news/content/images/2021/08/promise-console-1.png) -除了通过构造函数声明一个Promise对象外,还可以使用Promise内置的API进行声明: +除了通过构造函数声明一个 Promise 对象外,还可以使用 Promise 内置的 API 进行声明: ```js const anotherPromise = Promise.resolve("this is the eventual value the promise will return") @@ -94,15 +94,15 @@ const anotherPromise = Promise.resolve("this is the eventual value the promise w console.log(anotherPromise); ``` -示例2 +示例 2 -示例1中的Promise等待3s后获取到成功返回的信息:`this is the eventual...`,而示例2中将立即获取到成功返回的信息。 +示例 1 中的 Promise 等待 3s 后获取到成功返回的信息:`this is the eventual...`,而示例 2 中将立即获取到成功返回的信息。 -## JavaScript Promise中的错误处理 +## JavaScript Promise 中的错误处理 -Promise对象也能被*rejected*。大多数时候,**rejected**的发生是因为执行异步任务的时候抛出了错误,此时就会调用`reject()`方法。 +Promise 对象也能被*rejected*。大多数时候,**rejected**的发生是因为执行异步任务的时候抛出了错误,此时就会调用`reject()`方法。 -下面的示例展示了一个Promise对象是如何执行reject方法的: +下面的示例展示了一个 Promise 对象是如何执行 reject 方法的: ```js const myPromise = new Promise((resolve, reject) => { @@ -114,19 +114,19 @@ const myPromise = new Promise((resolve, reject) => { ``` -示例3 +示例 3 -你能想到Promise被rejected的原因吗?如果你的答案是:`a`的值是false,那么恭喜你答对了。 +你能想到 Promise 被 rejected 的原因吗?如果你的答案是:`a`的值是 false,那么恭喜你答对了。 -在示例3中,代码执行3s后将调用reject方法,因为`(a)?`表达式的值是false,所以触发`reject`方法。 +在示例 3 中,代码执行 3s 后将调用 reject 方法,因为`(a)?`表达式的值是 false,所以触发`reject`方法。 -## Promise的链式调用 +## Promise 的链式调用 -当Promise返回了某个值,通常你都会对返回值进行处理。 +当 Promise 返回了某个值,通常你都会对返回值进行处理。 比如,你发送了一个网络请求,你期望获取数据并展示在页面上。 -你可以定义两个回调函数,当Promise返回成功或失败时进行回调。这两个回调函数定义在`then()`内: +你可以定义两个回调函数,当 Promise 返回成功或失败时进行回调。这两个回调函数定义在`then()`内: ```JS const anotherPromise = new Promise((resolve, reject) => { @@ -140,13 +140,13 @@ anotherPromise .then(value => { console.log(value) }) ``` -示例4 +示例 4 -示例4的代码在3s后返回成功信息: +示例 4 的代码在 3s 后返回成功信息: ![](https://www.freecodecamp.org/news/content/images/2021/08/EVENTAL-RETURN.png) -原则上你可以无限链式调用,调用链会依次执行,而且前一个then的返回值作为参数传入后一个then。 +原则上你可以无限链式调用,调用链会依次执行,而且前一个 then 的返回值作为参数传入后一个 then。 ```js const anotherPromise = new Promise((resolve, reject) => { @@ -161,19 +161,19 @@ anotherPromise .then(value => { console.log(value) }) ``` -示例5 +示例 5 但我们还是遗漏了重要的内容。 要时刻记住,`then()`方法必须有两个回调函数:第一个参数是成功回调,第二个参数是错误回调。 -在示例4和示例5中都没有传入第二个回调函数。因此,如果代码报错,就没有错误回调捕获错误信息。 +在示例 4 和示例 5 中都没有传入第二个回调函数。因此,如果代码报错,就没有错误回调捕获错误信息。 -如果你执意要在`then()`中只定义一个回调函数(即成功回调),那么你就需要在Promise调用链的末端调用`catch()`方法捕获任何可能的报错信息。 +如果你执意要在`then()`中只定义一个回调函数(即成功回调),那么你就需要在 Promise 调用链的末端调用`catch()`方法捕获任何可能的报错信息。 -### 如何在JS中使用`catch()`方法 +### 如何在 JS 中使用`catch()`方法 -在Promise调用链上,无论哪个环节报错,`catch()`方法都会被调用。 +在 Promise 调用链上,无论哪个环节报错,`catch()`方法都会被调用。 ```js const myPromise = new Promise((resolve, reject) => { @@ -190,17 +190,17 @@ myPromise ``` -示例6 +示例 6 -因为`myPromise`最终状态是rejected,`then()`方法中的成功回调被忽略。而`catch()`方法中的错误回调被执行,并在控制台打印如下错误信息: +因为`myPromise`最终状态是 rejected,`then()`方法中的成功回调被忽略。而`catch()`方法中的错误回调被执行,并在控制台打印如下错误信息: ![](https://www.freecodecamp.org/news/content/images/2021/08/Catch.png) ## 写在最后 -JavaScript中的Promise是一个运行异步任务的强大功能。大部分情况下,在前端面试时,面试官都会问一些关于promise的问题。 +JavaScript 中的 Promise 是一个运行异步任务的强大功能。大部分情况下,在前端面试时,面试官都会问一些关于 promise 的问题。 -在这片文章中,我已经解释了promise的简单应用场景,也通过示例的方式展示了基本用法。 +在这片文章中,我已经解释了 promise 的简单应用场景,也通过示例的方式展示了基本用法。 希望你能从文章中获取有用的知识。如果你喜欢编程教程,点击查看[我的博客](https://ubahthebuilder.tech/user-authentication-vs-user-authorization-what-do-they-mean-in-back-end-web-development)。我会经常发布一些有关软件开发的文章。 @@ -208,4 +208,4 @@ JavaScript中的Promise是一个运行异步任务的强大功能。大部分情 ********P/S********: -如果你也在学习JavaScript,我也创建了一个电子书,上面有50个关于js的主题,而且都是我亲自手绘的哦~~ [点此查看](https://ubahthebuilder.gumroad.com/l/js-50). +如果你也在学习 JavaScript,我也创建了一个电子书,上面有 50 个关于 js 的主题,而且都是我亲自手绘的哦~~ [点此查看](https://ubahthebuilder.gumroad.com/l/js-50). diff --git a/chinese/articles/what-is-rem-in-css.md b/chinese/articles/what-is-rem-in-css.md index 1da028d37..ddcadd40e 100644 --- a/chinese/articles/what-is-rem-in-css.md +++ b/chinese/articles/what-is-rem-in-css.md @@ -9,21 +9,21 @@ ![CSS REM – What is REM in CSS?](https://www.freecodecamp.org/news/content/images/size/w2000/2022/03/rem-in-css-cover-image.jpeg) -在这篇文章中,我会讨论一些CSS中REM的用例。 +在这篇文章中,我会讨论一些 CSS 中 REM 的用例。 -首先我会介绍关于CSS属性和值的一些必要的背景知识,然后我会对比绝对长度值和相对长度值。REM是相对长度值。 +首先我会介绍关于 CSS 属性和值的一些必要的背景知识,然后我会对比绝对长度值和相对长度值。REM 是相对长度值。 -最后两部分,我会介绍在字体大小方面REM的用处,如何通过REM实现响应式网页。让我们开始吧! +最后两部分,我会介绍在字体大小方面 REM 的用处,如何通过 REM 实现响应式网页。让我们开始吧! ## 基础知识 -这部分我会通过介绍一些CSS知识来引入。 +这部分我会通过介绍一些 CSS 知识来引入。 -### 什么是CSS? +### 什么是 CSS? CSS(层叠样式表)通过属性和值幻化出来网页上的美学魔法。 -假设你需要通过一个精雕细琢的边框来增强图片的美感,你希望这条边框是条黑色实线。这时`border`就是你需要选择的属性,而`solid`就是这个属性对应的值。在CSS中这两个关键字帮助你创建出理想边框。 +假设你需要通过一个精雕细琢的边框来增强图片的美感,你希望这条边框是条黑色实线。这时`border`就是你需要选择的属性,而`solid`就是这个属性对应的值。在 CSS 中这两个关键字帮助你创建出理想边框。 你或许会猜想一定还有更多各种各样的值,毕竟仅仅一条实心线做的边框是不足以装点你的页面的。 @@ -35,27 +35,27 @@ border: 2px solid #ffff00; 使用边框缩写属性 -在上面的代码片段中,`2px`是边框长度的值, `solid` 是边框样式的一个关键字, RGB16进制`#ffff00` 代表黄色。 于是我们就创造了一个还不错的边框。(好啦,我知道不太美观,但你懂我的意思!) +在上面的代码片段中,`2px`是边框长度的值, `solid` 是边框样式的一个关键字, RGB16 进制`#ffff00` 代表黄色。 于是我们就创造了一个还不错的边框。(好啦,我知道不太美观,但你懂我的意思!) -在正式介绍REM之前,还需要说明一下,不同的属性对应不同的与之匹配的值。这些值的合集被称作`值类型`或者`数据类型`。 +在正式介绍 REM 之前,还需要说明一下,不同的属性对应不同的与之匹配的值。这些值的合集被称作`值类型`或者`数据类型`。 -让我们来借助上个例子说明这个概念。`color`就是一种值类型,RGB16进制值`#fff00`(代表黄色)是这个值类型的一个具体值。为了帮助理解,你可以把值类型看作类型,具体的值看作这些类型的个例。 +让我们来借助上个例子说明这个概念。`color`就是一种值类型,RGB16 进制值`#fff00`(代表黄色)是这个值类型的一个具体值。为了帮助理解,你可以把值类型看作类型,具体的值看作这些类型的个例。 所以一旦你不知道某个属性接受什么类型的值,搜索`值类型`,就可以获得答案。 -### CSS中的REM是什么? +### CSS 中的 REM 是什么? -REM就是一种值类型,R代表的是Root(根)。REM是一种长度类型的值/数据。另一种长度类型就是我们的老朋友像素(`px`)。任何接受长度作为值的属性都可以使用REM:其中包括`margin`、`padding`等。 +REM 就是一种值类型,R 代表的是 Root(根)。REM 是一种长度类型的值/数据。另一种长度类型就是我们的老朋友像素(`px`)。任何接受长度作为值的属性都可以使用 REM:其中包括`margin`、`padding`等。 -你可能会好奇,为什么我们要使用REM,答案就在下一章。 +你可能会好奇,为什么我们要使用 REM,答案就在下一章。 -## CSS中的相对长度值和绝对长度值 +## CSS 中的相对长度值和绝对长度值 -在CSS中有两种长度值:绝对长度值和相对长度值。 +在 CSS 中有两种长度值:绝对长度值和相对长度值。 ### 绝对长度值 -绝对长度值包括: `px` (1英寸的九十六分之一), `in` (1英寸) 和 `cm` (相当于 37.8px 或 25.2/64英寸)。 可以在[MDN](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units)查看更多例子。 +绝对长度值包括: `px` (1 英寸的九十六分之一), `in` (1 英寸) 和 `cm` (相当于 37.8px 或 25.2/64 英寸)。 可以在[MDN](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units)查看更多例子。 当使用绝对长度值的时候,你可以确保任何情况他们的大小几乎保持不变。当你知道输出大小的准确尺寸时它就派上用场,如在整页模式下。但如果有多种屏幕样式需求的话,绝对长度值就不奏效了。 @@ -70,15 +70,15 @@ REM就是一种值类型,R代表的是Root(根)。REM是一种长度类型 - 在处理`font-size`属性时,与父元素的字体大小有关; - 在处理其他一些属性,如`height`时,与元素自身的字体大小有关。 -`vw` 代表窗口宽度的1%。 也就是说如果你把`width`设置为10vw,元素就会占窗口10%的宽度。 其他长度参见[这里](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units)。 +`vw` 代表窗口宽度的 1%。 也就是说如果你把`width`设置为 10vw,元素就会占窗口 10%的宽度。 其他长度参见[这里](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units)。 相对长度值的优势非常明显,可以用它们来构建响应式网站。即网站会根据你的设定,有规律地自适应屏幕大小。 -## 根EM(REM)和根字体大小 +## 根 EM(REM)和根字体大小 -REM的大小取决根元素的字体大小。根元素通过伪类`:root`或者`html`选择器选定。 +REM 的大小取决根元素的字体大小。根元素通过伪类`:root`或者`html`选择器选定。 -因此`1rem`继承了根元素`font-size`的大小。 也就是说在整个CSS代码中1REM的大小保持不变。 如果用户没有修改根元素的大小,字体大小默认为 `16px`。 +因此`1rem`继承了根元素`font-size`的大小。 也就是说在整个 CSS 代码中 1REM 的大小保持不变。 如果用户没有修改根元素的大小,字体大小默认为 `16px`。 请看下面的例子: @@ -94,7 +94,7 @@ h1 { 反推出 `2rem` 等于多少 `px`并不难, 但为了计算设置为 1.125rem (即 16 \* 1.125: `18px`)的副标题的像素大小,你真的要在手边准备一台计算器吗? -好在有一个简化方法。记住你可以用百分比来设定根元素的字体大小,所以开发者们发现根元素默认大小的62.5%等于 `10px`。这样计算就变得容易多了: +好在有一个简化方法。记住你可以用百分比来设定根元素的字体大小,所以开发者们发现根元素默认大小的 62.5%等于 `10px`。这样计算就变得容易多了: ``` html { @@ -106,21 +106,21 @@ h1 { } ``` -考虑到无障碍性,`font-size`必须设定为REM(或者其他相对长度值)。 因为在一些浏览器中,如果长度值为`px`,即便浏览器设置改变了,字体大小也不会改变。 +考虑到无障碍性,`font-size`必须设定为 REM(或者其他相对长度值)。 因为在一些浏览器中,如果长度值为`px`,即便浏览器设置改变了,字体大小也不会改变。 -对于视力障碍人群来说,或许需要放大4倍才能看清文本。使用REM可以确保文字按需呈现大小,因为这样的话,文字大小由用户选择的字体大小默认值来决定。 +对于视力障碍人群来说,或许需要放大 4 倍才能看清文本。使用 REM 可以确保文字按需呈现大小,因为这样的话,文字大小由用户选择的字体大小默认值来决定。 -## 响应式网页设计中的REM +## 响应式网页设计中的 REM -响应式网页设计是一个综合性话题,包含不同的方面的内容。freeCodeCamp有两个相关课程。(如果感兴趣,你可以查看[https://www.freecodecamp.org/learn](https://www.freecodecamp.org/learn)) +响应式网页设计是一个综合性话题,包含不同的方面的内容。freeCodeCamp 有两个相关课程。(如果感兴趣,你可以查看[https://www.freecodecamp.org/learn](https://www.freecodecamp.org/learn)) -接下来我会讲解REM是如何辅助构建响应式网站的。 +接下来我会讲解 REM 是如何辅助构建响应式网站的。 -在这篇[文章](https://web.dev/responsive-web-design-basics/#optimize-text-for-reading)中可以看到,谷歌鼓励将一行文字的词数控制在10个以内,这个规定符合经典的可读性理论。 +在这篇[文章](https://web.dev/responsive-web-design-basics/#optimize-text-for-reading)中可以看到,谷歌鼓励将一行文字的词数控制在 10 个以内,这个规定符合经典的可读性理论。 谷歌建议在确定的断点使用媒体查询,这样内容或文本行的宽度就不会太长,给用户提供最佳的阅读体验。 -以下例子,灵感来源Adrian Sandu的[文章](https://www.sitepoint.com/understanding-and-using-rem-units-in-css/) : +以下例子,灵感来源 Adrian Sandu 的[文章](https://www.sitepoint.com/understanding-and-using-rem-units-in-css/) : ```CSS html { @@ -165,23 +165,23 @@ html { 你可以在 [codePen](https://codepen.io/slimattcode/pen/jOaJrjZ?editors=0100) 查看效果。可以调整窗口大小观察布局的变化。 -你可能会注意到在上面的例子中,在媒体查询的定义中的 `1rem`始终为`16px`。但在媒体查询代码块中, `1rem`的大小随着根元素`font-size`的值变化,根元素为62.5%的`16px`,所以这里的`1rem`为`10px`。 +你可能会注意到在上面的例子中,在媒体查询的定义中的 `1rem`始终为`16px`。但在媒体查询代码块中, `1rem`的大小随着根元素`font-size`的值变化,根元素为 62.5%的`16px`,所以这里的`1rem`为`10px`。 -出现这种情况,是因为媒体查询代码块中的REM始终继承浏览器默认字体大小,通常情况下是`16px`。一旦用户更改了默认设定,REM就会随之变化,以适配用户指定的一些无障碍设定需求。 +出现这种情况,是因为媒体查询代码块中的 REM 始终继承浏览器默认字体大小,通常情况下是`16px`。一旦用户更改了默认设定,REM 就会随之变化,以适配用户指定的一些无障碍设定需求。 -上面的代码采取了手机优先的设计策略。我把第一个断点定义为`435px`。注意在这个断点内,文本的宽度始终不变化,但是文本周围的留白成比例改变,约为之前的1.5倍。下面是分步骤图解: +上面的代码采取了手机优先的设计策略。我把第一个断点定义为`435px`。注意在这个断点内,文本的宽度始终不变化,但是文本周围的留白成比例改变,约为之前的 1.5 倍。下面是分步骤图解: -当窗口宽度小于435px时的布局: +当窗口宽度小于 435px 时的布局: ![Capture-1](https://www.freecodecamp.org/news/content/images/2022/03/Capture-1.JPG) -容器占手机屏幕宽度的100% +容器占手机屏幕宽度的 100% 当窗口宽度介于`435px`与`652px`之间时的布局: ![Capture1](https://www.freecodecamp.org/news/content/images/2022/03/Capture1.JPG) -容器中的文本大约10个词一行 +容器中的文本大约 10 个词一行 当窗口宽度大于 `652px`时的布局: @@ -189,19 +189,19 @@ html { ## 总结 -在这篇文章中我们探索了CSS中的REM。我们可以利用相对长度值REM来有逻辑地规划网站中的字体大小。 +在这篇文章中我们探索了 CSS 中的 REM。我们可以利用相对长度值 REM 来有逻辑地规划网站中的字体大小。 -REM也可以适应无障碍需求,满足更改了默认`font-size`用户的需求。 +REM 也可以适应无障碍需求,满足更改了默认`font-size`用户的需求。 -最后我们也探索了如何使用REM构建响应式网页,同时适配用户改变浏览器默认设置后的变化。 +最后我们也探索了如何使用 REM 构建响应式网页,同时适配用户改变浏览器默认设置后的变化。 在写这篇文章的时候,我借鉴了以下内容: -- [通过例子学习CSS单位– Em, Rem, VH, and VW ](https://www.freecodecamp.org/news/learn-css-units-em-rem-vh-vw-with-code-examples/) 作者 —— Joy Shaheb -- [CSS单位指南: 解释CSS中的em、remvh、vw等](https://www.freecodecamp.org/news/css-unit-guide/)来自freeCodeCamp -- [CSS单位对抗: EM和REM之间的战斗](https://www.freecodecamp.org/news/em-units-versus-rem-units-fight-382c16af8a67/) 作者——ZAYDEK -- [CSS中的REM: 理解并使用REM单位](https://www.sitepoint.com/understanding-and-using-rem-units-in-css/) 作者——Adrian Sandu -- [CSS的值和单位](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units)、[长度](https://developer.mozilla.org/en-US/docs/Web/CSS/length)、[字体大小](https://developer.mozilla.org/en-US/docs/Web/CSS/font-size) 来自MDN +- [通过例子学习 CSS 单位– Em, Rem, VH, and VW](https://www.freecodecamp.org/news/learn-css-units-em-rem-vh-vw-with-code-examples/) 作者 —— Joy Shaheb +- [CSS 单位指南: 解释 CSS 中的 em、remvh、vw 等](https://www.freecodecamp.org/news/css-unit-guide/)来自 freeCodeCamp +- [CSS 单位对抗: EM 和 REM 之间的战斗](https://www.freecodecamp.org/news/em-units-versus-rem-units-fight-382c16af8a67/) 作者——ZAYDEK +- [CSS 中的 REM: 理解并使用 REM 单位](https://www.sitepoint.com/understanding-and-using-rem-units-in-css/) 作者——Adrian Sandu +- [CSS 的值和单位](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Values_and_units)、[长度](https://developer.mozilla.org/en-US/docs/Web/CSS/length)、[字体大小](https://developer.mozilla.org/en-US/docs/Web/CSS/font-size) 来自 MDN - [无障碍响应式设计](https://web.dev/accessible-responsive-design/) 作者—— Dave Gash、 Meggin Kearney、Rachel Andrew 和 Rob Dodson - [响应式布局基础](https://web.dev/responsive-web-design-basics/) 作者—— Pete LePage 和 Rachel Andrew diff --git a/chinese/articles/what-is-sql-injection-how-to-prevent-it.md b/chinese/articles/what-is-sql-injection-how-to-prevent-it.md index 7768415b2..a4d6ddf96 100644 --- a/chinese/articles/what-is-sql-injection-how-to-prevent-it.md +++ b/chinese/articles/what-is-sql-injection-how-to-prevent-it.md @@ -103,7 +103,8 @@ https://xkcd.com/327/ 此外,审查数据库管理系统(DBMS)运行所在的操作系统帐户的权限。默认情况下,许多帐户具有非常强大的权限 - 将其更改为更合适的权限。 -### 来源/进一步阅读: + +### 来源/进一步阅读 * [OWASP SQL Injection](https://owasp.org/www-community/attacks/SQL_Injection) * [PortSwigger SQL Injection](https://portswigger.net/web-security/sql-injection) diff --git a/chinese/articles/what-is-state-in-react-explained-with-examples.md b/chinese/articles/what-is-state-in-react-explained-with-examples.md index f7a7815ea..d30dbaeba 100644 --- a/chinese/articles/what-is-state-in-react-explained-with-examples.md +++ b/chinese/articles/what-is-state-in-react-explained-with-examples.md @@ -5,11 +5,11 @@ -状态state是React中最复杂的东西,初学者和有经验的开发人员都很难理解。因此,在本文中,我们将探索React中状态的所有基础知识。 +状态 state 是 React 中最复杂的东西,初学者和有经验的开发人员都很难理解。因此,在本文中,我们将探索 React 中状态的所有基础知识。 在理解状态之前,让我们先了解一些基础知识,以便稍后更容易理解状态。 -## 如何用React在用户界面渲染数据 +## 如何用 React 在用户界面渲染数据 在屏幕上渲染任何东西,我们要使用`ReactDOM.render` 方法。 @@ -18,8 +18,8 @@ ```plain ReactDOM.render(element, container[, callback]) ``` -* `element` 元素可以是任何HTML元素,JSX或返回JSX的组件 -* `container` 容器是UI上我们想要在其中呈现数据的元素 +* `element` 元素可以是任何 HTML 元素,JSX 或返回 JSX 的组件 +* `container` 容器是 UI 上我们想要在其中呈现数据的元素 * `callback` 回调是可选的函数,我们可以传递它,它在屏幕上渲染或重新渲染时被调用 看看下面的代码: @@ -32,7 +32,7 @@ const rootElement = document.getElementById("root"); ReactDOM.render(<h1>Welcome to React!</h1>, rootElement); ``` 这是一个 [代码示例](https://codesandbox.io/s/focused-shockley-oh4tn?file=/src/index.js). -在这里,我们只是向屏幕呈现一个h1元素。 +在这里,我们只是向屏幕呈现一个 h1 元素。 要渲染多个元素,我们可以如下所示: @@ -51,7 +51,7 @@ ReactDOM.render( ); ``` 这是一个[代码示例](https://codesandbox.io/s/white-hooks-dgru0?file=/src/index.js)。 -如果内容变多,我们可以将JSX取出,放在一个变量中,这是渲染内容的首选方式,就像这样: +如果内容变多,我们可以将 JSX 取出,放在一个变量中,这是渲染内容的首选方式,就像这样: ```plain import React from "react"; @@ -69,9 +69,9 @@ const content = ( ReactDOM.render(content, rootElement); ``` 这是一个 [代码示例](https://codesandbox.io/s/trusting-night-5g825?file=/src/index.js)。 -在这里,我们还添加了一对额外的圆括号,以正确对齐JSX并使其成为单个JSX表达式。 +在这里,我们还添加了一对额外的圆括号,以正确对齐 JSX 并使其成为单个 JSX 表达式。 -如果您想详细了解JSX及其各种重要特性,请[在这里查看我的文章](https://www.freecodecamp.org/news/jsx-in-react-introduction/)。 +如果您想详细了解 JSX 及其各种重要特性,请[在这里查看我的文章](https://www.freecodecamp.org/news/jsx-in-react-introduction/)。 现在,让我们在屏幕上显示一个按钮和一些文本: @@ -101,9 +101,9 @@ ReactDOM.render(content, rootElement); ![counter_initial](https://www.freecodecamp.org/news/content/images/2021/04/counter_initial.gif) -正如你所看到的,当我们单击按钮时,计数器`counter` 的值会增加,就像您在控制台中看到的那样。但在UI上它没有更新。 +正如你所看到的,当我们单击按钮时,计数器`counter` 的值会增加,就像您在控制台中看到的那样。但在 UI 上它没有更新。 -这是因为我们在加载页面时,只使用 `ReactDOM.render` 方法一次,来渲染`counter` JSX的内容。我们不会再次调用它,所以即使`counter`的值在更新,它也不会显示在UI上。让我们来解决这个问题。 +这是因为我们在加载页面时,只使用 `ReactDOM.render` 方法一次,来渲染`counter` JSX 的内容。我们不会再次调用它,所以即使`counter`的值在更新,它也不会显示在 UI 上。让我们来解决这个问题。 ```jsx import React from "react"; @@ -140,34 +140,34 @@ renderContent(); ![counter_updated](https://www.freecodecamp.org/news/content/images/2021/04/counter_updated.gif) -正如你所看到的,它正按照预期工作,计数器`counter` 的值正正确地显示在UI上。 +正如你所看到的,它正按照预期工作,计数器`counter` 的值正正确地显示在 UI 上。 -你可能认为在每次单击按钮时重新呈现整个DOM的成本很高——但事实并非如此。这是因为React使用了Virtual DOM算法来检查UI上发生了什么变化,并且只重新呈现发生了变化的元素。因此,整个DOM不会再次被重新渲染。 +你可能认为在每次单击按钮时重新呈现整个 DOM 的成本很高——但事实并非如此。这是因为 React 使用了 Virtual DOM 算法来检查 UI 上发生了什么变化,并且只重新呈现发生了变化的元素。因此,整个 DOM 不会再次被重新渲染。 ![counter_preview](https://www.freecodecamp.org/news/content/images/2021/04/counter_preview.gif) 这是一个你可以自己尝试代码的[效果预览](https://8gsgu.csb.app/)。 -正如您在HTML结构中看到的,只有计数器 `counter` 的值被重新渲染,因为它是HTML结构中唯一显示的东西。这就是React如此之快的原因,虚拟DOM使React更加有用。 +正如您在 HTML 结构中看到的,只有计数器 `counter` 的值被重新渲染,因为它是 HTML 结构中唯一显示的东西。这就是 React 如此之快的原因,虚拟 DOM 使 React 更加有用。 -但是,每次我们想要更新UI时调用`renderContent` 函数仍然是不可行的。所以React添加了“state”的概念。 +但是,每次我们想要更新 UI 时调用`renderContent` 函数仍然是不可行的。所以 React 添加了“state”的概念。 -## 介绍React中的State +## 介绍 React 中的 State 状态允许我们管理应用程序中不断变化的数据。它被定义为一个对象,我们在其中定义键-值对,指定我们希望在应用程序中追踪的各种数据。 -在React里,所有的代码在一个component组件里被定义。 +在 React 里,所有的代码在一个 component 组件里被定义。 -在React中创建组件的方法主要有两种: +在 React 中创建组件的方法主要有两种: * 类组件 * 函数组件 ->现在我们将从类组件开始。在本文后面,我们将看到一种创建函数组件的方法。 +> 现在我们将从类组件开始。在本文后面,我们将看到一种创建函数组件的方法。 你应该知道如何使用类组件以及函数组件,包括钩子。 -你不应该通过React钩子直接学习函数组件,而是应该首先理解类组件,这样才容易理解基础知识。 +你不应该通过 React 钩子直接学习函数组件,而是应该首先理解类组件,这样才容易理解基础知识。 -你可以通过使用ES6类关键字和扩展React提供的`Component`类来创建一个组件,如下所示: +你可以通过使用 ES6 类关键字和扩展 React 提供的`Component`类来创建一个组件,如下所示: ```jsx import React from "react"; @@ -206,21 +206,21 @@ const rootElement = document.getElementById("root"); ReactDOM.render(<Counter />, rootElement); ``` ->注意组件的名称首字母要大写 (`Counter`) 。 +> 注意组件的名称首字母要大写 (`Counter`) 。 这是一个 [代码示例](https://codesandbox.io/s/nostalgic-burnell-57fhd?file=/src/index.js)。 看看这里我们在做什么。 -* 在构造函数内,首先通过传递props来调用 `super` 。 +* 在构造函数内,首先通过传递 props 来调用 `super` 。 * 然后我们将状态定义为对象,将`counter` 定义为对象的属性。 * 我们还将`this`的环境绑定到`handleClick`函数因此在`handleClick`函数中我们得到了这个的正确上下文。 * 然后在`handleClick`函数中,我们更新`countor`并将其记录到控制台。 -* 在`render`方法中,我们返回我们想要在UI上渲染的JSX。 +* 在`render`方法中,我们返回我们想要在 UI 上渲染的 JSX。 ![counter_mutate_state](https://www.freecodecamp.org/news/content/images/2021/04/counter_mutate_state.gif) -正如您在控制台中所看到的, `counter`正在正确地更新——但它没有在UI上更新。 +正如您在控制台中所看到的, `counter`正在正确地更新——但它没有在 UI 上更新。 这是因为我们直接将 `handleClick` 函数中的状态更新为: @@ -228,13 +228,13 @@ ReactDOM.render(<Counter />, rootElement); this.state.counter = this.state.counter + 1 ``` -因此React不会重新渲染组件(**直接更新状态也是一个不好的做法**)。 +因此 React 不会重新渲染组件(**直接更新状态也是一个不好的做法**)。 ->永远不要在React中直接更新/改变状态,因为这是一个糟糕的做法,它会导致应用程序出现问题。另外,如果你直接更改状态,那么在状态更改时不会重新渲染组件。 +> 永远不要在 React 中直接更新/改变状态,因为这是一个糟糕的做法,它会导致应用程序出现问题。另外,如果你直接更改状态,那么在状态更改时不会重新渲染组件。 -## setState的语法 +## setState 的语法 -为了改变状态,React为我们提供了一个setState函数,允许我们更新状态的值。 +为了改变状态,React 为我们提供了一个 setState 函数,允许我们更新状态的值。 `setState`函数的语法如下: @@ -245,9 +245,9 @@ setState(updater, [callback]) * `updater` 被更新的可以是函数或对象。 * `callback`回调函数是一个可选函数,在状态成功更新后执行。 ->调用 `setState` 会自动重新渲染整个组件及其所有子组件。我们不需要像前面使用`renderContent` 函数那样手动重新渲染。 +> 调用 `setState` 会自动重新渲染整个组件及其所有子组件。我们不需要像前面使用`renderContent` 函数那样手动重新渲染。 -## 如何使用函数更新React中的状态 +## 如何使用函数更新 React 中的状态 让我们修改 [上面的代码示例](https://codesandbox.io/s/nostalgic-burnell-57fhd?file=/src/index.js) 来使用 `setState` 函数更新状态。 @@ -267,21 +267,21 @@ handleClick() { } ``` -在这里,我们将一个函数作为 `setState` 方法的第一个参数传递,并返回一个新的状态对象,其中`counter` 在 `counter`的上一个值的基础上增加1。 +在这里,我们将一个函数作为 `setState` 方法的第一个参数传递,并返回一个新的状态对象,其中`counter` 在 `counter`的上一个值的基础上增加 1。 我们在上面的代码中使用箭头函数,但是使用普通函数也可以。 ![counter_updated_async](https://www.freecodecamp.org/news/content/images/2021/04/counter_updated_async.gif) -如果您注意到,我们正在正确地获得UI上 `counter` 的更新值。 但在控制台中,我们获取的是以前的 `counter` 值,尽管我们在调用 `this.setState` 之后添加了console.log。 +如果您注意到,我们正在正确地获得 UI 上 `counter` 的更新值。 但在控制台中,我们获取的是以前的 `counter` 值,尽管我们在调用 `this.setState` 之后添加了 console.log。 ->这是因为 `setState` 方法本质上是异步的。 +> 这是因为 `setState` 方法本质上是异步的。 -这意味着,尽管我们调用了`setState` 来一个个地增加 `counter` ,但它没有立刻生效。这是因为我们调用 `setState` 方法时,整个组件被重新渲染 – 因此React需要使用虚拟DOM算法检查需要更改的内容,然后执行各种检查以高效更新UI。 +这意味着,尽管我们调用了`setState` 来一个个地增加 `counter` ,但它没有立刻生效。这是因为我们调用 `setState` 方法时,整个组件被重新渲染 – 因此 React 需要使用虚拟 DOM 算法检查需要更改的内容,然后执行各种检查以高效更新 UI。 这是你可能没办法在调用 `setState`后,马上获得 `counter` 更新的原因。 ->这在React中是非常重要的,因为你会在写代码时遇到调试困难的问题。请记住`setState` 在React里是异步的。 +> 这在 React 中是非常重要的,因为你会在写代码时遇到调试困难的问题。请记住`setState` 在 React 里是异步的。 如果您想在使用`setState` 后立即获得更新后的状态值, 你可以传递一个函数作为第二个参数,状态一更新就调用 `setState` 。 @@ -289,7 +289,7 @@ handleClick() { ![counter_updated_sync](https://www.freecodecamp.org/news/content/images/2021/04/counter_updated_sync.gif) -正如你所看到的,我们在控制台中和UI上同时获得了正确的 `counter` 值。 +正如你所看到的,我们在控制台中和 UI 上同时获得了正确的 `counter` 值。 在上面的演示中, `handleClick`方法是这样的: @@ -308,9 +308,9 @@ handleClick() { 这里对于 `setState` 函数调用,我们传递了两个参数。第一个是返回新状态的函数,第二个是在状态更新后调用的回调函数。我们只是将更新的计数器值记录到回调函数中的控制台。 ->尽管React提供了一个回调函数来立即获取更新后的状态值,但建议您只在快速测试或记录日志时使用它。 +> 尽管 React 提供了一个回调函数来立即获取更新后的状态值,但建议您只在快速测试或记录日志时使用它。 -相反,React建议你使用`componentDidUpdate` 方法,这是一个React生命周期方法,看起来像这样: +相反,React 建议你使用`componentDidUpdate` 方法,这是一个 React 生命周期方法,看起来像这样: ```js componentDidUpdate(prevProps, prevState) { @@ -378,12 +378,12 @@ handleClick = () => { 由于箭头函数没有自己的 `this` 上下文,所以它将使用上下文作为类,因此不需要使用 `.bind` 方法。 这使得代码更简单,更容易理解,因为我们不需要不停地绑定每个事件处理器。 ->[create-react-app](https://github.com/facebook/create-react-app) 已经内置了对它的支持,现在就可以开始使用这个语法了。 -从现在开始,我们将使用这种语法,因为它是编写React组件的更流行和首选的方法。 +> [create-react-app](https://github.com/facebook/create-react-app) 已经内置了对它的支持,现在就可以开始使用这个语法了。 +从现在开始,我们将使用这种语法,因为它是编写 React 组件的更流行和首选的方法。 如果您想了解更多关于类属性语法的知识,请查看 [我的文章](https://javascript.plainenglish.io/how-to-write-clean-and-easy-to-understand-react-code-using-class-properties-syntax-5b375b0618d3?source=friends_link&sk=c170992cab9025fddb7b34b8894ea993)。 -## 如何使用ES简化语法 +## 如何使用 ES 简化语法 如果你在上面的代码示例中查看 `setState` 方法的调用,它会像这样: ```js @@ -394,7 +394,7 @@ this.setState((prevState) => { }); ``` -这么多代码,只是为了从一个函数中返回一个对象,我们用了5行代码。 +这么多代码,只是为了从一个函数中返回一个对象,我们用了 5 行代码。 我们能如下简化成一行: @@ -402,7 +402,7 @@ this.setState((prevState) => { this.setState((prevState) => ({ counter: prevState.counter + 1 })); ``` -这里,我们将对象包装在圆括号中,使其隐式返回。这是可行的,因为如果我们在箭头函数中只有一条语句,我们可以跳过return关键字和花括号,像这样: +这里,我们将对象包装在圆括号中,使其隐式返回。这是可行的,因为如果我们在箭头函数中只有一条语句,我们可以跳过 return 关键字和花括号,像这样: ```js @@ -419,7 +419,7 @@ const add = (a, b) => a + b; 这是一个更新的 [代码示例](https://codesandbox.io/s/zen-galois-pew17?file=/src/index.js)。 -## 如何在React中使用对象作为状态更新器 +## 如何在 React 中使用对象作为状态更新器 在上面的代码中,我们使用一个函数作为`setState` 的第一个参数,但我们也可以传递一个对象作为参数。 @@ -460,7 +460,7 @@ class User extends React.Component { 在这里,我们添加了一个输入文本框,用户在其中输入自己的名字,当用户在文本框中输入时,它会显示在文本框的下方。 -在状态中,我们初始化了name属性为 `Mike` ,并在输入文本框中添加了一个 `onChange` 处理程序,如下所示: +在状态中,我们初始化了 name 属性为 `Mike` ,并在输入文本框中添加了一个 `onChange` 处理程序,如下所示: ```js state = { @@ -505,7 +505,7 @@ handleClick = () => { } ``` -我们取 `counter` 的当前值然后加1。它运行良好,如下图所示: +我们取 `counter` 的当前值然后加 1。它运行良好,如下图所示: ![object_setstate_correct](https://www.freecodecamp.org/news/content/images/2021/04/object_setstate_correct.gif) @@ -527,15 +527,15 @@ handleClick = () => { } ``` -在这里,我们首先将 `counter` 值设置为5,然后将其增加1。所以 `counter` 的预期值是6。我们来看看是不是这样。 +在这里,我们首先将 `counter` 值设置为 5,然后将其增加 1。所以 `counter` 的预期值是 6。我们来看看是不是这样。 ![object_setstate_wrong](https://www.freecodecamp.org/news/content/images/2021/04/object_setstate_wrong.gif) -正如您所看到的,当我们第一次单击按钮时,我们期望 `counter` 值变成5,但它变成了1,并且在随后的每一次单击它都增加1。 +正如您所看到的,当我们第一次单击按钮时,我们期望 `counter` 值变成 5,但它变成了 1,并且在随后的每一次单击它都增加 1。 -这是因为,正如我们前面所看到的, `setState` 方法本质上是异步的。当我们调用 `setState` 时, `counter` 的值不会立即变为5,因此在下一行我们得到的 `counter` 值为0,这是我们在开始时初始化的状态。 +这是因为,正如我们前面所看到的, `setState` 方法本质上是异步的。当我们调用 `setState` 时, `counter` 的值不会立即变为 5,因此在下一行我们得到的 `counter` 值为 0,这是我们在开始时初始化的状态。 -所以当我们再次调用 `setState` 将 `counter` 加1时,它变成1,并且它只继续加1。 +所以当我们再次调用 `setState` 将 `counter` 加 1 时,它变成 1,并且它只继续加 1。 要解决这个问题,我们需要使用 `setState` 的更新程序语法,其中传递一个函数作为第一个参数。 @@ -565,19 +565,19 @@ handleClick = () => { ![object_setstate_updater](https://www.freecodecamp.org/news/content/images/2021/04/object_setstate_updater.gif) -如您所见,当我们第一次单击按钮时, `counter` 的值变为7。这和预期的一样,因为首先我们把它设为5,然后把它加2次1,所以它就变成了7。即使我们多次点击按钮,它仍然保持在7,因为每次点击我们都将它重置为5并增加两次。 +如您所见,当我们第一次单击按钮时, `counter` 的值变为 7。这和预期的一样,因为首先我们把它设为 5,然后把它加 2 次 1,所以它就变成了 7。即使我们多次点击按钮,它仍然保持在 7,因为每次点击我们都将它重置为 5 并增加两次。 -这是因为在 `handleClick` 内部,我们调用 `setState` 将计数器值设置为5,方法是将一个对象作为 `setState` 函数的第一个参数传递给该函数。在那之后,我们调用了两个 `setState` 调用,其中我们使用函数作为第一个参数。 +这是因为在 `handleClick` 内部,我们调用 `setState` 将计数器值设置为 5,方法是将一个对象作为 `setState` 函数的第一个参数传递给该函数。在那之后,我们调用了两个 `setState` 调用,其中我们使用函数作为第一个参数。 那么这是如何正确工作的呢? -当React遇到 `setState` 调用时,它将调度更新以更改状态,因为它是异步的。但是在完成状态更改之前,React会看到有另一个 `setState` 调用。 +当 React 遇到 `setState` 调用时,它将调度更新以更改状态,因为它是异步的。但是在完成状态更改之前,React 会看到有另一个 `setState` 调用。 -因此,React不会立即使用新的 `counter` 值重新呈现。相反,它合并所有的 `setState` 调用,并基于前面 `counter` 的值更新 `counter` ,因为我们已经使用了 `prevState``.counter` 去计算 `counter`。 +因此,React 不会立即使用新的 `counter` 值重新呈现。相反,它合并所有的 `setState` 调用,并基于前面 `counter` 的值更新 `counter` ,因为我们已经使用了 `prevState``.counter` 去计算 `counter`。 -一旦所有 `setState` 调用都成功完成,React才重新渲染组件。因此,即使有三个 `setState` 调用,React也只会重新渲染组件一次,这可以通过在 `render` 方法中添加console.log语句来确认。 +一旦所有 `setState` 调用都成功完成,React 才重新渲染组件。因此,即使有三个 `setState` 调用,React 也只会重新渲染组件一次,这可以通过在 `render` 方法中添加 console.log 语句来确认。 ->因此,需要记住的一点是,在使用对象作为 `setState` 调用的第一个参数时应该小心,因为它可能会导致不可预知的结果。使用函数作为第一个参数,可以根据前面的结果获得正确的结果。 +> 因此,需要记住的一点是,在使用对象作为 `setState` 调用的第一个参数时应该小心,因为它可能会导致不可预知的结果。使用函数作为第一个参数,可以根据前面的结果获得正确的结果。 你可能不会像我们在上面的演示中所做的那样一遍又一遍地调用 `setState`,但你可以在另一个函数中调用它,如下所示: @@ -606,11 +606,11 @@ handleClick = () => { 在 `doSomethingElse` 中你可能认为你会得到 `isLoggedIn` 状态为`true if`条件中的代码会被执行。但是它不会被执行,因为 `setState` 是异步的,状态可能不会立即更新。 -这就是为什么React添加了 `componendDidUpdate` 这样的生命周期方法,以便在状态或道具更新时做一些事情。 +这就是为什么 React 添加了 `componendDidUpdate` 这样的生命周期方法,以便在状态或道具更新时做一些事情。 ->请注意检查您是否在下一行或下一个函数中再次使用相同的 `state` 变量,做些事来避免这些结果。 +> 请注意检查您是否在下一行或下一个函数中再次使用相同的 `state` 变量,做些事来避免这些结果。 -## 如何合并React中的setState调用 +## 如何合并 React 中的 setState 调用 看看 [这个代码示例](https://codesandbox.io/s/bold-cache-zcj4u?file=/src/index.js)。 @@ -650,27 +650,27 @@ this.setState((prevState) => ({ })); ``` -我们只能更新我们想要更新的那个。React会自动合并其他状态属性,所以我们不需要自己手动合并它们。 +我们只能更新我们想要更新的那个。React 会自动合并其他状态属性,所以我们不需要自己手动合并它们。 ![state_merged-1](https://www.freecodecamp.org/news/content/images/2021/04/state_merged-1.gif) 正如您所看到的,我们成功地分别独立地更改了 `counter` 和 `username`。 -## 如何在React的函数组件中使用状态 +## 如何在 React 的函数组件中使用状态 到目前为止,我们已经了解了如何在类组件中使用状态。现在让我们看看如何在函数组件中使用它。 函数组件与类组件类似,只是它们没有状态和生命周期方法。这就是为什么您可能听说过它们被称为无状态函数组件。 -这些组件只接受道具并返回一些JSX。 +这些组件只接受道具并返回一些 JSX。 函数组件使代码更短,更容易理解和测试。 -它们的执行速度也快一些,因为它们没有生命周期方法。在类组件中扩展的 `React.Component` 类 也没有为React带来的额外数据。 +它们的执行速度也快一些,因为它们没有生命周期方法。在类组件中扩展的 `React.Component` 类 也没有为 React 带来的额外数据。 看看这个[代码示例](https://codesandbox.io/s/sleepy-pascal-8ugh3?file=/src/index.js)。 -在这里,我们从[random user generator API](https://randomuser.me/)中加载一个包含20个随机用户的列表,当组件在 `componentDidMount` 方法中加载时,如下所示: +在这里,我们从[random user generator API](https://randomuser.me/)中加载一个包含 20 个随机用户的列表,当组件在 `componentDidMount` 方法中加载时,如下所示: ```js componentDidMount() { @@ -681,7 +681,7 @@ componentDidMount() { } ``` -一旦我们获取了那些用户,我们会设置它为 `users` 状态,并在UI上显示它。 +一旦我们获取了那些用户,我们会设置它为 `users` 状态,并在 UI 上显示它。 ```jsx @@ -713,11 +713,11 @@ const User = (props) => { **这个`User` 组件是一个函数组件。** -函数组件是以大写字母开头并返回JSX的函数。 +函数组件是以大写字母开头并返回 JSX 的函数。 -无论组件是类组件还是函数组件,都要记住以像 `User` 这样的大写字母开头。这就是React在使用 `<User />`等普通HTML元素时将其与普通HTML元素区别开来的原因。 +无论组件是类组件还是函数组件,都要记住以像 `User` 这样的大写字母开头。这就是 React 在使用 `<User />`等普通 HTML 元素时将其与普通 HTML 元素区别开来的原因。 -如果我们使用 `<user />` ,React将检查名称为user的HTML元素。因为没有这样的HTML元素,所以您不会得到想要的输出。 +如果我们使用 `<user />` ,React 将检查名称为 user 的 HTML 元素。因为没有这样的 HTML 元素,所以您不会得到想要的输出。 在上面的 `User` 函数组件中,我们将道具传递给函数的道具参数中的组件。 @@ -727,13 +727,13 @@ const User = (props) => { 因此,函数组件优先于类组件。 -一旦我们有了 `props` ,我们就会使用对象解构语法来获取其中的值并显示在UI上。 +一旦我们有了 `props` ,我们就会使用对象解构语法来获取其中的值并显示在 UI 上。 -## 如何在React Hooks中使用状态 +## 如何在 React Hooks 中使用状态 -从版本16.8.0开始,React引入了钩子。它们完全改变了我们在React中编写代码的方式。使用React hook,我们可以在函数组件中使用状态和生命周期方法。 +从版本 16.8.0 开始,React 引入了钩子。它们完全改变了我们在 React 中编写代码的方式。使用 React hook,我们可以在函数组件中使用状态和生命周期方法。 ->React钩子是添加了状态和生命周期方法的函数组件。 +> React 钩子是添加了状态和生命周期方法的函数组件。 所以现在,类组件和函数组件之间没有什么区别。 它们都可以有状态和生命周期方法。 @@ -742,11 +742,11 @@ const User = (props) => { 现在,您很少会发现使用类组件编写的反应组件。 -要使用React hook声明状态,我们需要使用 `useState` 钩子。 +要使用 React hook 声明状态,我们需要使用 `useState` 钩子。 `useState` 钩子接受一个参数,该参数是状态的初始值。 -在类组件中,状态总是一个对象。但是在使用 `useState` 时,可以提供任何值作为初始值,如number, string, boolean, object, array, null等。 +在类组件中,状态总是一个对象。但是在使用 `useState` 时,可以提供任何值作为初始值,如 number, string, boolean, object, array, null 等。 `useState` 钩子返回一个数组,它的第一个值是当前状态的值。第二个值是我们将用于更新状态的函数,类似于 `setState` 方法。 @@ -804,29 +804,29 @@ const App = () => { ReactDOM.render(<App />, document.getElementById("root")); ``` -这是使用React钩子写的[代码示例](https://codesandbox.io/s/elegant-heyrovsky-3qco5?file=/src/index.js)。 +这是使用 React 钩子写的[代码示例](https://codesandbox.io/s/elegant-heyrovsky-3qco5?file=/src/index.js)。 -如您所见,使用React钩子使代码更简短,更容易理解。 +如您所见,使用 React 钩子使代码更简短,更容易理解。 让我们理解一下上面的代码: * 为了使用 `useState` 钩子,我们需要像第一行那样导入它。 -* 在App组件内部,我们通过传递0作为初始值并使用解构语法来调用 `useState` 。我们将 `useState` 返回的数组值存储到 `counter`和 `setCounter` 变量中。 +* 在 App 组件内部,我们通过传递 0 作为初始值并使用解构语法来调用 `useState` 。我们将 `useState` 返回的数组值存储到 `counter`和 `setCounter` 变量中。 * 常用的约定是在用于更新状态的函数名前加上 `setCounter` 中的 `set` 关键字。 * 当单击递增按钮时,我们定义了一个内联函数,并通过传递更新的计数器值来调用 `setCounter` 函数。 * 注意,因为我们已经有了计数器的值,所以我们使用 `setCounter(counter + 1)` 来增加计数器的值。 -* 由于内联on click处理程序中只有一条语句,所以不需要将代码移动到单独的函数中。不过,如果处理程序内部的代码变得复杂,您可以这样做。 -如果您想了解关于useState和其他React hook的更多细节(以及示例),请查看我的React hook介绍文章。 +* 由于内联 on click 处理程序中只有一条语句,所以不需要将代码移动到单独的函数中。不过,如果处理程序内部的代码变得复杂,您可以这样做。 +如果您想了解关于 useState 和其他 React hook 的更多细节(以及示例),请查看我的 React hook 介绍文章。 ### Thanks for reading! -想要详细了解所有ES6+特性,包括let和const,承诺,各种承诺方法,数组和对象解构,箭头函数,异步/等待,导入和导出,以及从头开始的很多更多? +想要详细了解所有 ES6+特性,包括 let 和 const,承诺,各种承诺方法,数组和对象解构,箭头函数,异步/等待,导入和导出,以及从头开始的很多更多? -请查阅我的《掌握现代JavaScript([Mastering Modern JavaScript](https://modernjavascript.yogeshchavan.dev/))》一书。这本书涵盖了学习React的所有先决条件,并帮助您在JavaScript和React方面变得更好。 +请查阅我的《掌握现代 JavaScript([Mastering Modern JavaScript](https://modernjavascript.yogeshchavan.dev/))》一书。这本书涵盖了学习 React 的所有先决条件,并帮助您在 JavaScript 和 React 方面变得更好。 ->在 [这里](https://www.freecodecamp.org/news/learn-modern-javascript/) 查看这本书的免费预览内容。 -此外,您可以查看我的**免费**[介绍React Router](https://yogeshchavan.podia.com/react-router-introduction)课程,从零学习React Router。 +> 在 [这里](https://www.freecodecamp.org/news/learn-modern-javascript/) 查看这本书的免费预览内容。 +此外,您可以查看我的**免费**[介绍 React Router](https://yogeshchavan.podia.com/react-router-introduction)课程,从零学习 React Router。 -想订阅 JavaScript, React, Node.js相关内容的日常更新?[在领英关注我](https://www.linkedin.com/in/yogesh-chavan97/)。 +想订阅 JavaScript, React, Node.js 相关内容的日常更新?[在领英关注我](https://www.linkedin.com/in/yogesh-chavan97/)。 [![banner](https://gist.github.com/myogeshchavan97/98ae4f4ead57fde8d47fcf7641220b72/raw/c3e4265df4396d639a7938a83bffd570130483b1/banner.jpg)](https://bit.ly/3w0DGum) diff --git a/chinese/articles/what-is-systems-engineering.md b/chinese/articles/what-is-systems-engineering.md index 20ff46efd..f5a451a60 100644 --- a/chinese/articles/what-is-systems-engineering.md +++ b/chinese/articles/what-is-systems-engineering.md @@ -5,7 +5,7 @@ ![What is Systems Engineering? A Beginner's Guide](https://www.freecodecamp.org/news/content/images/size/w2000/2022/03/tt.jpeg) -我最近在读 J. Martin 的书《Systems Engineering Guidebook – A Process for Developing Systems and Products》(系统工程指南--开发系统和产品的过程)[1]。 从中我了解到,系统工程可以降低40%的制造成本——你知道吗? +我最近在读 J. Martin 的书《Systems Engineering Guidebook – A Process for Developing Systems and Products》(系统工程指南--开发系统和产品的过程)[1]。 从中我了解到,系统工程可以降低 40%的制造成本——你知道吗? 我以前也不知道。即使在上了一堂以系统工程为主题的课后,我也只有在了解其在现实生活中的应用时才明白其相关性。 @@ -17,7 +17,7 @@ 事实上,谷歌有自己的学科,叫做 [工程生产力](https://landing.google.com/engprod/). -Facebook也有自己的,叫做 [生产工程](https://engineering.fb.com/category/production-engineering/). +Facebook 也有自己的,叫做 [生产工程](https://engineering.fb.com/category/production-engineering/). 另外,亚马逊雇佣了一个 [团队](https://www.amazon.jobs/en/teams/aws-system-development-engineer) 的工程师来帮助建立其云计算基础设施。 @@ -64,7 +64,7 @@ Facebook也有自己的,叫做 [生产工程](https://engineering.fb.com/categ 图片显示了一个网站模板项目的目录(或文件夹)的结构。 -许多这些目录包含多个.html、.css和.js文件。 +许多这些目录包含多个.html、.css 和.js 文件。 这些文件都有组件和特性,与项目内的其他文件相互作用。从本质上讲,你在这里有一个系统。 diff --git a/chinese/articles/what-is-terraform-learn-infrastructure-as-code.md b/chinese/articles/what-is-terraform-learn-infrastructure-as-code.md index bd3bd192d..7d0afac51 100644 --- a/chinese/articles/what-is-terraform-learn-infrastructure-as-code.md +++ b/chinese/articles/what-is-terraform-learn-infrastructure-as-code.md @@ -5,7 +5,7 @@ ![什么是Terraform?学习Terraform和基础设施即代码](https://www.freecodecamp.org/news/content/images/size/w2000/2021/04/terraform-article.jpeg) -Terraform使用一个帮助你以代码形式管理各种云基础设施服务的工具。你把你的基础设施代码化,所以它也被称为基础设施即代码(IaC)。 +Terraform 使用一个帮助你以代码形式管理各种云基础设施服务的工具。你把你的基础设施代码化,所以它也被称为基础设施即代码(IaC)。 云对越来越多的公司来说已经非常重要。它不仅有助于减少花销和节省时间,还能让客户专注于他们的核心业务。 @@ -13,23 +13,23 @@ Terraform使用一个帮助你以代码形式管理各种云基础设施服务 随着云服务商数量的增加和他们的他们的服务更加灵活,能够管理你的云基础设施变的越来越重要。 -Terraform的出于基础设施即代码(IaC)的概念。简单来说,laC 是通过代码来管理你的基础设施。 +Terraform 的出于基础设施即代码(IaC)的概念。简单来说,laC 是通过代码来管理你的基础设施。 -让我们以某个云上的任何计算资源为例,比如AWS的EC2。向AWS申请一个EC2实例,只需要在AWS 注册,提供一堆数值,然后点击 `Launch`(启动)按键。该资源将会在几分钟内准备好。 +让我们以某个云上的任何计算资源为例,比如 AWS 的 EC2。向 AWS 申请一个 EC2 实例,只需要在 AWS 注册,提供一堆数值,然后点击 `Launch`(启动)按键。该资源将会在几分钟内准备好。 -只要我们向AWS提供这些值,它们就会在AWS上生成。当然,这样是传统的方式。 +只要我们向 AWS 提供这些值,它们就会在 AWS 上生成。当然,这样是传统的方式。 -Terraform提供了一种方法,使用这些`credentials`完成认证和以 _configurations_ 的输入信息,在目标云上创建一个资源。 +Terraform 提供了一种方法,使用这些`credentials`完成认证和以 _configurations_ 的输入信息,在目标云上创建一个资源。 -这些配置以Terraform的语法进行资源的描述。 配置化是你声明你的基础设施的理想状态的方法--基本上是声明式的语法。 +这些配置以 Terraform 的语法进行资源的描述。 配置化是你声明你的基础设施的理想状态的方法--基本上是声明式的语法。 -Terraform使用云服务商提供的API来创建资源。 +Terraform 使用云服务商提供的 API 来创建资源。 -## Terraform的好处 +## Terraform 的好处 Terraform 是[Hashicorp](https://www.hashicorp.com/)的一个产品, 使用 [Hashicorp 配置语法](https://github.com/hashicorp/hcl) (HCL)来表示配置 . -在下面的例子中,你可以看到EC2实例的最简单的形式的描述: +在下面的例子中,你可以看到 EC2 实例的最简单的形式的描述: ``` provider “aws” { @@ -42,56 +42,56 @@ resource “aws_instance” “myec2” { } ``` -这个简单的例子足以让我们了解Terraform的能力。 +这个简单的例子足以让我们了解 Terraform 的能力。 -这个代码包含两个部分`provider`和`resource`。`provider` 是Terraform告诉AWS,我们想在`us-west-1`区使用AWS的服务。 +这个代码包含两个部分`provider`和`resource`。`provider` 是 Terraform 告诉 AWS,我们想在`us-west-1`区使用 AWS 的服务。 - `resource` 让Terraform告诉AWS,在AWS提供的所有基础设施资源中,我们想创建一个实例(EC2)。 + `resource` 让 Terraform 告诉 AWS,在 AWS 提供的所有基础设施资源中,我们想创建一个实例(EC2)。 `resource`第一个参数为 `aws_instance`,第二个参数资源的命名,在本例中是`myec2`。 -`resource`有几个参数,说明AWS机器镜像和用于创建该资源的实例类型。 +`resource`有几个参数,说明 AWS 机器镜像和用于创建该资源的实例类型。 -在这里,我们已经用代码的形式来表示我们的基础设施,让我们看laC的一些优势。 +在这里,我们已经用代码的形式来表示我们的基础设施,让我们看 laC 的一些优势。 -1. 由于基础设施的创建现在被浓缩在配置/代码文件中, 它 **更容易维护**, 因为我们现在可以利用Git等版本控制软件来进行协作和维护。 -2. 基础设施的规划阶段所需的时间减少了,因为可以在 **短时间内** 编写配置。这些配置很容易被Terraform使用,几分钟就可以创建云资源。 +1. 由于基础设施的创建现在被浓缩在配置/代码文件中, 它 **更容易维护**, 因为我们现在可以利用 Git 等版本控制软件来进行协作和维护。 +2. 基础设施的规划阶段所需的时间减少了,因为可以在 **短时间内** 编写配置。这些配置很容易被 Terraform 使用,几分钟就可以创建云资源。 3. **改变** 基础设施 **是更容易了** 和 改代码一样了. 4. 软件开发中的应用管理生命周期的优势也适用于基础设施。这让它 **更有效率**. -## Terraform的功能 +## Terraform 的功能 ### 协调工作 -当部署各种端到端服务事,涉及到创建云资源时,Terraform作为协调过程的核心。 +当部署各种端到端服务事,涉及到创建云资源时,Terraform 作为协调过程的核心。 ### 不与特定云服务商绑定 -由于Terraform支持大多数云,包括AWS、MS Azure和GCP,所以你不必担心厂商的锁定问题。Terraform的`registry provides` 提供了所支持的云服务商的文档。 +由于 Terraform 支持大多数云,包括 AWS、MS Azure 和 GCP,所以你不必担心厂商的锁定问题。Terraform 的`registry provides` 提供了所支持的云服务商的文档。 -在各种云上的描述基础设施的语法是相同的,因此与云服务商的特定的API的学习曲线是一样的,但不会被遗忘。 +在各种云上的描述基础设施的语法是相同的,因此与云服务商的特定的 API 的学习曲线是一样的,但不会被遗忘。 ### 声明式语法 -Terraform文件中基础设施是声明性的--所以作为开发者,我们不需要担心让Terraform理解创建资源所需的步骤,相反,我们需要让Terraform知道所需的状态,Terraform会在内部处理这些步骤。 +Terraform 文件中基础设施是声明性的--所以作为开发者,我们不需要担心让 Terraform 理解创建资源所需的步骤,相反,我们需要让 Terraform 知道所需的状态,Terraform 会在内部处理这些步骤。 ### 模块化 -Terraform 提供的模块可以帮助我们重复使用Terraform的代码。一个复杂的基础设施被分解成多个模块,每个模块都可以在不同的项目中重复使用。 +Terraform 提供的模块可以帮助我们重复使用 Terraform 的代码。一个复杂的基础设施被分解成多个模块,每个模块都可以在不同的项目中重复使用。 -将给定的Terraform配置转换为模块是非常容易的,Terraform有它的预建模块的生态体系。 +将给定的 Terraform 配置转换为模块是非常容易的,Terraform 有它的预建模块的生态体系。 ### 状态管理 -在Terraform创建和规划基础设施的同时,维护状态。这可以与其他团队成员分享,以达到协作的目的。 +在 Terraform 创建和规划基础设施的同时,维护状态。这可以与其他团队成员分享,以达到协作的目的。 -Terraform让你可以远程管理状态,这有助于防止团队成员在尝试重新创建基础设施时出现混乱。 +Terraform 让你可以远程管理状态,这有助于防止团队成员在尝试重新创建基础设施时出现混乱。 ### 提供者 -Terraform不是一个完整的配置工具,但它有助于`provisioning`活动。Terraform的 _local-exec_ 和 _remote-exec_ 模块让你运行内联脚本。内联脚本有助于在成功创建资源后安装软件组件。 +Terraform 不是一个完整的配置工具,但它有助于`provisioning`活动。Terraform 的 _local-exec_ 和 _remote-exec_ 模块让你运行内联脚本。内联脚本有助于在成功创建资源后安装软件组件。 -这在Chef、Ansible和Salt Stack等配置管理工具安装它们各自的代理时特别有用。当它们安装成功,就直接发送一个`UP`信号。 +这在 Chef、Ansible 和 Salt Stack 等配置管理工具安装它们各自的代理时特别有用。当它们安装成功,就直接发送一个`UP`信号。 ### 开源 @@ -99,23 +99,23 @@ Terraform 是开源软件。 当然它也有一个企业版. ## Terraform 的工作流程 \[初始化 - 执行计划 - 投入使用 - 销毁\] -你需要采取一些简单的步骤来运行你的Terraform代码。这些步骤与云平台上的资源的生命周期密切相关。 +你需要采取一些简单的步骤来运行你的 Terraform 代码。这些步骤与云平台上的资源的生命周期密切相关。 同样,这些步骤跟云平台无关,这意味着同样的步骤/命令可以在任何给定的云平台上 **创建,更新,和销毁** 资源都是有效的。 -**注意:**,本文不涉及 Terraform的安装步骤,我假设你已经在系统中安装了Terraform CLI。 +**注意:**,本文不涉及 Terraform 的安装步骤,我假设你已经在系统中安装了 Terraform CLI。 ### 运行 `init` 命令 -当我们准备好了配置文件,我们需要运行的第一个命令是 `terraform init`。Terraform的安装二进制并不包含对所有云服务商的支持。 +当我们准备好了配置文件,我们需要运行的第一个命令是 `terraform init`。Terraform 的安装二进制并不包含对所有云服务商的支持。 -相反,根据云供应商,在Terraform运行代码前,会下载适当的插件。在我们的例子中,运行`terraform init`将下载`AWS`提供插件。这个命令帮助 _initialize_ 这个给定的 Terraform目录。 +相反,根据云供应商,在 Terraform 运行代码前,会下载适当的插件。在我们的例子中,运行`terraform init`将下载`AWS`提供插件。这个命令帮助 _initialize_ 这个给定的 Terraform 目录。 ### 生成一个执行计划 -`terraform plan`,命令 **生成一个执行计划**。根据你提供的配置,Terraform会生成一个执行计划。在这个阶段,Terraform会在语法错误、API认证、状态验证等方面进行可行性检查。 +`terraform plan`,命令 **生成一个执行计划**。根据你提供的配置,Terraform 会生成一个执行计划。在这个阶段,Terraform 会在语法错误、API 认证、状态验证等方面进行可行性检查。 -`plan` 在实际执行前高亮显示Terraform 脚本中的任何修改。如果它成功了,它会输出基础设施中潜在变化的摘要。你应该在 _apply_ 命令之前运行它。因为它能让你在修改基础设施之前意识到风险。 +`plan` 在实际执行前高亮显示 Terraform 脚本中的任何修改。如果它成功了,它会输出基础设施中潜在变化的摘要。你应该在 _apply_ 命令之前运行它。因为它能让你在修改基础设施之前意识到风险。 ### `Apply` 让改变生效 @@ -129,23 +129,23 @@ Terraform 是开源软件。 当然它也有一个企业版. ## Terraform 实战 -这篇文件理论已经讲足够了。让我们试着通过在AWS上实际创建一个EC2实例,来实践我们目前学到的东西。 +这篇文件理论已经讲足够了。让我们试着通过在 AWS 上实际创建一个 EC2 实例,来实践我们目前学到的东西。 -首先,如果你还没安装Terraform CLI。其实安装非常简单,你可以找到你的操作系统的[安装步骤](https://learn.hashicorp.com/tutorials/terraform/install-cli)。 +首先,如果你还没安装 Terraform CLI。其实安装非常简单,你可以找到你的操作系统的[安装步骤](https://learn.hashicorp.com/tutorials/terraform/install-cli)。 -在你的系统中创建一个目录/文件夹,并创建第一个Terraform文件类型,讲其命名为`main.tf`。默认情况下,只要它的扩展名为`.tf`,我们可以给它起任何名字。 +在你的系统中创建一个目录/文件夹,并创建第一个 Terraform 文件类型,讲其命名为`main.tf`。默认情况下,只要它的扩展名为`.tf`,我们可以给它起任何名字。 -Terraform CLI会识别存在特定的目录下的所有扩展名为`.tf`的文件,然后执行。 +Terraform CLI 会识别存在特定的目录下的所有扩展名为`.tf`的文件,然后执行。 -将上述代码粘贴在该文件中并保存。**请注意**,你需要根据你的区域(region) 使用正确的AMI。 +将上述代码粘贴在该文件中并保存。**请注意**,你需要根据你的区域(region) 使用正确的 AMI。 -由于这是第一次执行Terraform代码,我们需要在这个目录中 _初始化_ Terraform,运行`terraform init`,将安装`AWS`所需的插件。 +由于这是第一次执行 Terraform 代码,我们需要在这个目录中 _初始化_ Terraform,运行`terraform init`,将安装`AWS`所需的插件。 -启动一个终端,进入到我们的Terraform所在的目录,并运行以下命令。 +启动一个终端,进入到我们的 Terraform 所在的目录,并运行以下命令。 `terraform init` -这应该产生如下输出,非常清楚,我们可以看到安装的`AWS`插件的安装版本是V3.22.0 +这应该产生如下输出,非常清楚,我们可以看到安装的`AWS`插件的安装版本是 V3.22.0 ``` Initializing the backend... @@ -190,21 +190,21 @@ can't guarantee that exactly these actions will be performed if "terraform apply" is subsequently run. ``` -`plan`命令指出哪些资源将被创建。在我们的例子中,它计划用给定的配置创建一个`myec2`实例。它显示了用于创建的AMI ID +`plan`命令指出哪些资源将被创建。在我们的例子中,它计划用给定的配置创建一个`myec2`实例。它显示了用于创建的 AMI ID 此外,它还表明其他的属性,如`arn`和`associate_public_ip_addres`是已知的,当实例创建。 -上面的虚线上的表示最终的一组变化--增加1种资源,没有任何变化或者销毁。 +上面的虚线上的表示最终的一组变化--增加 1 种资源,没有任何变化或者销毁。 所以,到现在为止,一切看起来很好。让我们继续并配置应用。在终端运行下面的命令并观察输出。 `terraform apply` -一旦确认,需要几秒时间来完成AWS上的EC2实例的创建,这可以从`terraform apply`产生的输出看到。 +一旦确认,需要几秒时间来完成 AWS 上的 EC2 实例的创建,这可以从`terraform apply`产生的输出看到。 -正如下面的输出所示,在我的情况下,创建EC2实例需要51秒,而且实例的ID也是可用的。 +正如下面的输出所示,在我的情况下,创建 EC2 实例需要 51 秒,而且实例的 ID 也是可用的。 -通过登录你的AWS控制台并搜索具有以下的ID的EC2实例,来验证这一点。如果这一切运作良好,你应该能找到它。 +通过登录你的 AWS 控制台并搜索具有以下的 ID 的 EC2 实例,来验证这一点。如果这一切运作良好,你应该能找到它。 ``` Plan: 1 to add, 0 to change, 0 to destroy. @@ -226,19 +226,19 @@ aws_instance.myec2: Creation complete after 51s [id=i-04ef3120a0006a153] Apply complete! Resources: 1 added, 0 changed, 0 destroyed. ``` -因此,我们已经成功地使用IaC 来定义/声明和创建我们在AWS上的虚拟机的配置。 +因此,我们已经成功地使用 IaC 来定义/声明和创建我们在 AWS 上的虚拟机的配置。 如果我不需要这个虚拟机了,我们可以用相同的配置来销毁它。 请注意,如果我们对配置做了任何修改,但不打算应用这些修改,然后我们试图销毁以前的配置,我们可能会遇到错误。 -这是因为Terraform在状态文件中维护了配置和现实世界资源之间的关系。改变配置会影响它的应用关系。Terraform将把这视为要创建新的资源。 +这是因为 Terraform 在状态文件中维护了配置和现实世界资源之间的关系。改变配置会影响它的应用关系。Terraform 将把这视为要创建新的资源。 -Terraform 状态是一个值得单独讨论的话题,所以我们将在后面介绍。现在,要销毁EC2实例,在终端运行以下命令。 +Terraform 状态是一个值得单独讨论的话题,所以我们将在后面介绍。现在,要销毁 EC2 实例,在终端运行以下命令。 `terraform destroy` -在销毁资源之前,Terraform通过输出一个计划,要求我们进行确认。它表明,运行`destroy`命令将销毁1个资源,这是我们所期望的。 +在销毁资源之前,Terraform 通过输出一个计划,要求我们进行确认。它表明,运行`destroy`命令将销毁 1 个资源,这是我们所期望的。 ``` Terraform destroy @@ -262,12 +262,12 @@ aws_instance.myec2: Destruction complete after 1m5s Destroy complete! Resources: 1 destroyed. ``` -同样,销毁资源也需要几秒钟,Terraform不会让你一直悬着,因为它会每10秒更新状态。 +同样,销毁资源也需要几秒钟,Terraform 不会让你一直悬着,因为它会每 10 秒更新状态。 -一旦资源被销毁,它就会确认它已经完成。 请登录AWS控制台,验证资源是否被终止。 +一旦资源被销毁,它就会确认它已经完成。 请登录 AWS 控制台,验证资源是否被终止。 ### 多谢你的阅读! -我希望你能从这个基本介绍中理解Terraform的工作原理。我将在后面的文章中写更多的文章,涵盖更深的概念,如状态、语法、CLI,后端等等 +我希望你能从这个基本介绍中理解 Terraform 的工作原理。我将在后面的文章中写更多的文章,涵盖更深的概念,如状态、语法、CLI,后端等等 如果你喜欢这些内容,请考虑订阅,关注和分享这篇博文[Let'sDoTech](https://letsdotech.dev/), [Instagram](https://www.instagram.com/letsdotech/), [Twitter](https://twitter.com/letsdotech_dev), [LinkedIn](https://www.linkedin.com/company/letsdotech). \ No newline at end of file diff --git a/chinese/articles/what-is-web3.md b/chinese/articles/what-is-web3.md index 9deb8cccb..85d1c0323 100644 --- a/chinese/articles/what-is-web3.md +++ b/chinese/articles/what-is-web3.md @@ -5,13 +5,13 @@ ![什么是Web3?解读未来的去中心化网络](https://www.freecodecamp.org/news/content/images/size/w2000/2021/05/web3-future-of-web.jpg) -如果你读到这篇文章,那么你已经是当代互联网世界的一员了。我们现在使用的网络和10年前大不相同。所以,互联网是怎么演化的,更重要的是 ——— 接下来它会如何发展?为什么我们需要在意这些问题呢? +如果你读到这篇文章,那么你已经是当代互联网世界的一员了。我们现在使用的网络和 10 年前大不相同。所以,互联网是怎么演化的,更重要的是 ——— 接下来它会如何发展?为什么我们需要在意这些问题呢? 历史教会我们,这些变化都会是重要的。 我将在这篇文章里讲述互联网的演化、发展走向,以及为什么了解这些事情很重要。 -想象一下网络是如何与你的日常生活息息相关的。网络如何改变了社会,还有社交媒体平台、手机应用.....而现在,网络正在经历又一个模式的转换。 +想象一下网络是如何与你的日常生活息息相关的。网络如何改变了社会,还有社交媒体平台、手机应用……而现在,网络正在经历又一个模式的转换。 ## **互联网的演化** @@ -19,9 +19,9 @@ ### **什么是 Web 1.0?** -Web 1.0 是互联网的第一个迭代。参与者是消费互联网内容的人群,而创造者是那些典型的网站开发者。当时的网站内容基本只有文字和图片的形式。Web 1.0 大致从1991年延续至2004年。 +Web 1.0 是互联网的第一个迭代。参与者是消费互联网内容的人群,而创造者是那些典型的网站开发者。当时的网站内容基本只有文字和图片的形式。Web 1.0 大致从 1991 年延续至 2004 年。 -Web 1.0 时期的网站是由静态网页组成的,并没有动态的html。数据和内容由静态文件系统提供而非数据库,网站也几乎没有交互功能。 +Web 1.0 时期的网站是由静态网页组成的,并没有动态的 html。数据和内容由静态文件系统提供而非数据库,网站也几乎没有交互功能。 你可以把 Web 1.0 当作只读互联网。 @@ -127,7 +127,7 @@ Web3 用一些新特性增强了我们今天所知的互联网。 web3 是: 此外,如果公司真的成功了,任何参与其中的人都需要很长时间才能价值变现,这通常会导致工作多年而没有任何真正的投资回报。 -相反,想象一下,一个全新的、令人兴奋的项目被宣布,它解决了一个真正的问题。 从第一天开始,所有人都可以参与构建或投资。 公司宣布发行x个代币,并将其中10%的部分给那些早期建设者,将10%出售给公众,其余留作未来贡献者的支付金和项目资金。 +相反,想象一下,一个全新的、令人兴奋的项目被宣布,它解决了一个真正的问题。 从第一天开始,所有人都可以参与构建或投资。 公司宣布发行 x 个代币,并将其中 10%的部分给那些早期建设者,将 10%出售给公众,其余留作未来贡献者的支付金和项目资金。 股东们可以使用他们的代币对项目未来的走向进行投票,帮助构建项目的人可以在代币发行后出售他们的部分资产来赚钱。 diff --git a/chinese/articles/whats-new-in-react-18.md b/chinese/articles/whats-new-in-react-18.md index 8c5ed845a..d590f0d41 100644 --- a/chinese/articles/whats-new-in-react-18.md +++ b/chinese/articles/whats-new-in-react-18.md @@ -5,49 +5,49 @@ ![React 18 有什么新特性?? 并发、批处理、过渡API等](https://www.freecodecamp.org/news/content/images/size/w2000/2021/07/SUSPENSE-BATCHING-TRANSITION.png) -大家好!在这篇文章中,我将在8分钟内向你展示React的最新版本--React 18。在这篇文章中,我将在8分钟内向你展示React的最新版本--React 18的新内容。 +大家好!在这篇文章中,我将在 8 分钟内向你展示 React 的最新版本--React 18。在这篇文章中,我将在 8 分钟内向你展示 React 的最新版本--React 18 的新内容。 首先,你可能想知道最新的变化是否会破坏你目前的代码,或者你是否必须学习完全不相关的新概念。 -好吧,不用担心--你可以继续你现在的工作,或者继续学习你目前的React课程,因为React 18不会破坏任何东西。 +好吧,不用担心--你可以继续你现在的工作,或者继续学习你目前的 React 课程,因为 React 18 不会破坏任何东西。 如果你想看一个视频来补充你的阅读,请在这里查看。 对于那些真正想了解发生了什么的人,这里有详细的介绍。 -## 什么是React的并发性? +## 什么是 React 的并发性? 这个版本的主要主题是**并发**。首先,让我们看看什么是并发性。 -并发是指同时执行多个任务的能力。以一个标准的React应用为例,让我们考虑在一个组件中播放一个动画,同时用户能够在其他React组件中点击或输入。 +并发是指同时执行多个任务的能力。以一个标准的 React 应用为例,让我们考虑在一个组件中播放一个动画,同时用户能够在其他 React 组件中点击或输入。 ![https://s3-us-west-2.amazonaws.com/secure.notion-static.com/f5a65bda-b76b-466f-abe3-3d8fbb232655/firefox_PJSGkv5qWX.png](https://www.freecodecamp.org/news/content/images/2021/07/firefox_juLm49pOIQ.png) -当用户在打字和点击按钮的时候,在React的上下文中,一个动画也在那里渲染。 +当用户在打字和点击按钮的时候,在 React 的上下文中,一个动画也在那里渲染。 -React必须管理所有的函数调用、Hook调用和事件回调,其中几个甚至可能同时发生。如果React把所有时间都花在渲染动画帧上,用户就会觉得应用程序被 "卡住 "了,因为它不会对他们的输入做出反应。 +React 必须管理所有的函数调用、Hook 调用和事件回调,其中几个甚至可能同时发生。如果 React 把所有时间都花在渲染动画帧上,用户就会觉得应用程序被 "卡住 "了,因为它不会对他们的输入做出反应。 -现在,在单线程进程上运行的React必须对这些事件和功能进行组合、重新排序和优先处理,以便给用户一个最佳的、高性能的体验。 +现在,在单线程进程上运行的 React 必须对这些事件和功能进行组合、重新排序和优先处理,以便给用户一个最佳的、高性能的体验。 -为了做到这一点,React在内部使用了一个 "调度器",负责对这些回调进行优先排序和调用。 +为了做到这一点,React 在内部使用了一个 "调度器",负责对这些回调进行优先排序和调用。 -在React 18之前,用户没有办法控制这些函数的调用顺序。但现在,React通过Transition API将这个事件循环的一些控制权交给了用户。 +在 React 18 之前,用户没有办法控制这些函数的调用顺序。但现在,React 通过 Transition API 将这个事件循环的一些控制权交给了用户。 -你可以在Dan Abramov的这篇文章中读到更多关于这方面的内容。: [An ELI5 of concurrency](https://github.com/reactwg/react-18/discussions/46#discussioncomment-846786). +你可以在 Dan Abramov 的这篇文章中读到更多关于这方面的内容。: [An ELI5 of concurrency](https://github.com/reactwg/react-18/discussions/46#discussioncomment-846786). -## 过渡API +## 过渡 API -React的开发者已经公开了一些API,允许React用户对并发性进行一些控制。 +React 的开发者已经公开了一些 API,允许 React 用户对并发性进行一些控制。 -其中一个API是 `startTransition`,它允许开发者向React指出哪些动作可能会阻塞线程并导致屏幕上的滞后。 +其中一个 API 是 `startTransition`,它允许开发者向 React 指出哪些动作可能会阻塞线程并导致屏幕上的滞后。 -通常情况下,这些动作是你以前可能使用过的去抖(debounce),比如通过搜索API的网络调用,或者像搜索1000个字符串的数组这样的重渲染过程。 +通常情况下,这些动作是你以前可能使用过的去抖(debounce),比如通过搜索 API 的网络调用,或者像搜索 1000 个字符串的数组这样的重渲染过程。 被`startTransition`装饰器(wrapped)的更新被标记为非紧急的,如果有更紧急的更新,如点击或按键,则会被打断。 -如果一个过渡被用户打断(例如,在搜索字段中输入多个字母),React会扔掉未完成的陈旧的渲染工作,只渲染最新的更新。 +如果一个过渡被用户打断(例如,在搜索字段中输入多个字母),React 会扔掉未完成的陈旧的渲染工作,只渲染最新的更新。 -### 过渡API例子 +### 过渡 API 例子 为了更详细地理解这一点,让我们考虑一个带有搜索字段的组件。假设它有两个函数来控制状态: @@ -59,7 +59,7 @@ setInputValue(input) setSearchQuery(input); ``` -`setInputValue`负责响应更新输入字段,而`setSearchQuery`负责根据当前输入值进行搜索。现在,如果这些函数调用在用户每次开始输入时都同步发生,就会发生2种情况: +`setInputValue`负责响应更新输入字段,而`setSearchQuery`负责根据当前输入值进行搜索。现在,如果这些函数调用在用户每次开始输入时都同步发生,就会发生 2 种情况: 1. 几个搜索调用将被进行,这将延迟或减慢其他网络调用。 2. 或者,更有可能的是,搜索操作会变得非常繁重,并且会在每次按键时锁定屏幕。 @@ -81,9 +81,9 @@ startTransition(() => { }); ``` -过渡让你保持大多数互动的流畅性,即使它们引起了重大的UI变化。它们还可以让你避免浪费时间来渲染不再相关的内容。 +过渡让你保持大多数互动的流畅性,即使它们引起了重大的 UI 变化。它们还可以让你避免浪费时间来渲染不再相关的内容。 -React还提供了一个新的Hook,叫做`useTransition`,所以你可以在过渡期内显示一个加载器。这有助于向用户表明,应用程序正在处理他们的输入,并将很快显示结果。 +React 还提供了一个新的 Hook,叫做`useTransition`,所以你可以在过渡期内显示一个加载器。这有助于向用户表明,应用程序正在处理他们的输入,并将很快显示结果。 ```jsx import { useTransition } from'react'; @@ -104,21 +104,21 @@ const callback = () => { {isPending && <Spinner />} ``` -作为一条经验法则,只要有网络调用或渲染阻塞进程存在,你就可以使用过渡API。 +作为一条经验法则,只要有网络调用或渲染阻塞进程存在,你就可以使用过渡 API。 -你可以在这篇文章中阅读更多关于API的信息, [对startTransition的解释](https://github.com/reactwg/react-18/discussions/41),由React核心团队的Ricky负责维护。 +你可以在这篇文章中阅读更多关于 API 的信息, [对 startTransition 的解释](https://github.com/reactwg/react-18/discussions/41),由 React 核心团队的 Ricky 负责维护。 -### 过渡API的演示 +### 过渡 API 的演示 -在应用程序中使用 "useTransition "和Suspense: [https://codesandbox.io/s/sad-banach-tcnim?file=/src/App.js:664-676](https://codesandbox.io/s/sad-banach-tcnim?file=/src/App.js:664-676) +在应用程序中使用 "useTransition "和 Suspense: [https://codesandbox.io/s/sad-banach-tcnim?file=/src/App.js:664-676](https://codesandbox.io/s/sad-banach-tcnim?file=/src/App.js:664-676) 复杂渲染算法下的 "startTransition "演示: [https://react-fractals-git-react-18-swizec.vercel.app/](https://react-fractals-git-react-18-swizec.vercel.app/) -## 在React中进行批处理 +## 在 React 中进行批处理 接下来是批处理。批处理是开发者一般不需要关心的事情,但知道幕后发生的事情是很好的。 -每当你使用`setState`来改变任何函数中的变量时,React不是在每个`setState`上进行渲染,而是收集所有的`setStates`,然后一起执行它们。这就是所谓的批处理。 +每当你使用`setState`来改变任何函数中的变量时,React 不是在每个`setState`上进行渲染,而是收集所有的`setStates`,然后一起执行它们。这就是所谓的批处理。 ```jsx function App() { @@ -140,9 +140,9 @@ function App() { } ``` -这对性能来说是很好的,因为它避免了不必要的重新渲染。它还可以防止你的组件呈现 `半成品`状态,即只有一个状态变量被更新,这可能导致你的代码中出现UI故障和错误。 +这对性能来说是很好的,因为它避免了不必要的重新渲染。它还可以防止你的组件呈现 `半成品`状态,即只有一个状态变量被更新,这可能导致你的代码中出现 UI 故障和错误。 -然而,React过去在执行批处理的时间上并不一致。这是因为React过去只在浏览器事件中(如点击)进行批处理更新,但在这里,我们是在事件`处理后`(在一个获取回调中)更新状态。: +然而,React 过去在执行批处理的时间上并不一致。这是因为 React 过去只在浏览器事件中(如点击)进行批处理更新,但在这里,我们是在事件`处理后`(在一个获取回调中)更新状态。: ```jsx function App() { @@ -167,38 +167,38 @@ function App() { } ``` -在React 18中开始使用 `[createRoot](<https://github.com/reactwg/react-18/discussions/5>)`, 所有的状态更新将被自动批处理,无论它们来自哪里。 +在 React 18 中开始使用 `[createRoot](<https://github.com/reactwg/react-18/discussions/5>)`, 所有的状态更新将被自动批处理,无论它们来自哪里。 -这意味着在超时、promises、本地事件处理程序或任何其他事件中的更新将以与React事件中的更新相同的方式进行批处理。这将导致React的渲染工作减少,从而提高应用程序的性能。 +这意味着在超时、promises、本地事件处理程序或任何其他事件中的更新将以与 React 事件中的更新相同的方式进行批处理。这将导致 React 的渲染工作减少,从而提高应用程序的性能。 -你可以在Dan Abramov的 [批处理的解释](https://github.com/reactwg/react-18/discussions/21), +你可以在 Dan Abramov 的 [批处理的解释](https://github.com/reactwg/react-18/discussions/21), 阅读更多关于批处理的信息。 ### 批处理演示 -在React 18之前: [https://codesandbox.io/s/hopeful-fire-ge4t2?file=/src/App.tsx](https://codesandbox.io/s/hopeful-fire-ge4t2?file=/src/App.tsx) +在 React 18 之前: [https://codesandbox.io/s/hopeful-fire-ge4t2?file=/src/App.tsx](https://codesandbox.io/s/hopeful-fire-ge4t2?file=/src/App.tsx) -在React 18之后: [https://codesandbox.io/s/morning-sun-lgz88?file=/src/index.js](https://codesandbox.io/s/morning-sun-lgz88?file=/src/index.js) +在 React 18 之后: [https://codesandbox.io/s/morning-sun-lgz88?file=/src/index.js](https://codesandbox.io/s/morning-sun-lgz88?file=/src/index.js) ## The Suspense API -React 18包含了很多变化,以改善React在服务端渲染(SSR)下的性能[Server-Side Rendered](/news/server-side-rendering-your-react-app-in-three-simple-steps-7a82b95db82e/)。 服务器端渲染是一种在服务器上将JS数据渲染成HTML的方式,以节省前端的计算。在大多数情况下,获得更快的初始页面加载。 +React 18 包含了很多变化,以改善 React 在服务端渲染(SSR)下的性能[Server-Side Rendered](/news/server-side-rendering-your-react-app-in-three-simple-steps-7a82b95db82e/)。 服务器端渲染是一种在服务器上将 JS 数据渲染成 HTML 的方式,以节省前端的计算。在大多数情况下,获得更快的初始页面加载。 -React以4个连续的步骤执行服务器端渲染。 +React 以 4 个连续的步骤执行服务器端渲染。 - 在服务器上,为每个组件获取数据。 -- 在服务器上,整个应用程序被渲染成HTML并发送给客户端。 -- 在客户端,整个应用程序的JavaScript代码被提取出来 -- 在客户端,JavaScript将React连接到服务器生成的HTML,这就是所谓的Hydration。 +- 在服务器上,整个应用程序被渲染成 HTML 并发送给客户端。 +- 在客户端,整个应用程序的 JavaScript 代码被提取出来 +- 在客户端,JavaScript 将 React 连接到服务器生成的 HTML,这就是所谓的 Hydration。 -React 18引入了`Suspense`API,它允许你将你的应用程序分解成**小的独立单元**,这些单元将独立完成这些步骤,并且不会阻碍应用程序的其他部分。因此,你的应用程序的用户将更快地看到内容,并能更快地开始与它互动。 +React 18 引入了`Suspense`API,它允许你将你的应用程序分解成**小的独立单元**,这些单元将独立完成这些步骤,并且不会阻碍应用程序的其他部分。因此,你的应用程序的用户将更快地看到内容,并能更快地开始与它互动。 -### Suspense API是如何工作的 +### Suspense API 是如何工作的 -#### 流式HTML +#### 流式 HTML -在今天的SSR中,渲染HTML和hydration是 "全有或全无"。客户端必须一次性地获取和hydration所有的应用程序。 +在今天的 SSR 中,渲染 HTML 和 hydration 是 "全有或全无"。客户端必须一次性地获取和 hydration 所有的应用程序。 -但是React 18给了你一个新的可能性。你可以用`<Suspense>`来包装(wrap)页面的一部分。 +但是 React 18 给了你一个新的可能性。你可以用`<Suspense>`来包装(wrap)页面的一部分。 ```jsx <Suspense fallback={<Spinner />}> @@ -206,34 +206,34 @@ React 18引入了`Suspense`API,它允许你将你的应用程序分解成**小 </Suspense> ``` -通过用`<Suspense>`包装(warp)组件,我们告诉React,它不需要等待评论来开始为页面的其他部分传输HTML。相反,React将发送占位符(一个旋转器spinner)。 +通过用`<Suspense>`包装(warp)组件,我们告诉 React,它不需要等待评论来开始为页面的其他部分传输 HTML。相反,React 将发送占位符(一个旋转器 spinner)。 -当服务器上的评论数据准备好了,React将发送额外的HTML到同一个流中,以及一个最小的内联`<script>`标签来把HTML放在 "正确的地方"。 +当服务器上的评论数据准备好了,React 将发送额外的 HTML 到同一个流中,以及一个最小的内联`<script>`标签来把 HTML 放在 "正确的地方"。 -#### 选择Hydration +#### 选择 Hydration -在React 18之前,如果应用程序的完整JavaScript代码没有加载进来,hydration就无法启动。对于较大的应用程序,这个过程可能需要一段时间。 +在 React 18 之前,如果应用程序的完整 JavaScript 代码没有加载进来,hydration 就无法启动。对于较大的应用程序,这个过程可能需要一段时间。 -但在React 18中,`<Suspense>`可以让你在子组件加载之前就对应用进行hydration。 +但在 React 18 中,`<Suspense>`可以让你在子组件加载之前就对应用进行 hydration。 -通过用`<Suspense>`包装(warp)组件,你可以告诉React,它们不应该阻止页面的其他部分,甚至是hydration。这意味着你不再需要等待所有的代码加载,以便开始hydration。React可以在加载部分时进行hydration。 +通过用`<Suspense>`包装(warp)组件,你可以告诉 React,它们不应该阻止页面的其他部分,甚至是 hydration。这意味着你不再需要等待所有的代码加载,以便开始 hydration。React 可以在加载部分时进行 hydration。 -这2个`Suspense`的功能和React 18中引入的其他几个变化极大地加快了初始页面的加载。 +这 2 个`Suspense`的功能和 React 18 中引入的其他几个变化极大地加快了初始页面的加载。 -你可以在这篇文章中阅读更多内容 [Suspense SSR的解释](https://github.com/reactwg/react-18/discussions/37) 和Dan Abramov做的相关修改。 +你可以在这篇文章中阅读更多内容 [Suspense SSR 的解释](https://github.com/reactwg/react-18/discussions/37) 和 Dan Abramov 做的相关修改。 -### Suspense的演示 +### Suspense 的演示 [https://codesandbox.io/s/recursing-mclaren-1ireo?file=/src/index.js:458-466](https://codesandbox.io/s/recursing-mclaren-1ireo?file=/src/index.js:458-466) ## 总结 -因此,总结起来,React 18所带来的功能是: +因此,总结起来,React 18 所带来的功能是: -- 用Transition API进行并发控制, +- 用 Transition API 进行并发控制, - 函数调用和事件的自动批处理,以提高应用内的性能,以及 -- 用Suspense为SSR提供更快的页面加载。 +- 用 Suspense 为 SSR 提供更快的页面加载。 -虽然与上一版本的React没有很大的区别,但所有这些变化都使React成为所有框架的潮流引领者。 +虽然与上一版本的 React 没有很大的区别,但所有这些变化都使 React 成为所有框架的潮流引领者。 -谢谢你阅读这篇文章! 你可以在freeCodeCamp这里查看我以前关于React的帖子和教程。你也可以在Twitter上关注我[@thewritingdev](https://twitter.com/thewritingdev),在那里我每天发布关于React和Web开发的内容。 \ No newline at end of file +谢谢你阅读这篇文章! 你可以在 freeCodeCamp 这里查看我以前关于 React 的帖子和教程。你也可以在 Twitter 上关注我[@thewritingdev](https://twitter.com/thewritingdev),在那里我每天发布关于 React 和 Web 开发的内容。 \ No newline at end of file diff --git a/chinese/articles/whats-the-difference-between-authentication-and-authorisation.md b/chinese/articles/whats-the-difference-between-authentication-and-authorisation.md index 2e85970d3..97d2322e9 100644 --- a/chinese/articles/whats-the-difference-between-authentication-and-authorisation.md +++ b/chinese/articles/whats-the-difference-between-authentication-and-authorisation.md @@ -5,7 +5,7 @@ ![Authentication vs Authorization – What's the Difference?](https://www.freecodecamp.org/news/content/images/size/w2000/2022/09/Background1.jpg) -在开发的过程中,常常听说认证(authentication)和授权(authorization),他们的缩写都为auth,所以非常容易混淆。 +在开发的过程中,常常听说认证(authentication)和授权(authorization),他们的缩写都为 auth,所以非常容易混淆。 通过这篇文章,你将学习到: @@ -21,28 +21,28 @@ ### 认证的方法 -#### 单因素认证: +#### 单因素认证 这种认证方式常被应用在低风险的系统中。只需要提供一种因素来进行认证,通常是密码,所以特别容易受到钓鱼和键盘记录器攻击。 -除此之外,最近由DataProt发布的[文章](https://dataprot.net/statistics/password-statistics/)显示,有78%的Z时代青年在多个服务使用同一个密码。也就是说一旦攻击者得到一个账号的密码,就非常有可能通过同样的密码截获其他的服务账户。 +除此之外,最近由 DataProt 发布的[文章](https://dataprot.net/statistics/password-statistics/)显示,有 78%的 Z 时代青年在多个服务使用同一个密码。也就是说一旦攻击者得到一个账号的密码,就非常有可能通过同样的密码截获其他的服务账户。 -#### 双因素认证: +#### 双因素认证 这种方法更安全,因为它包含两个身份验证因素——通常是你知道的元素,例如用户名和密码,加上你拥有的东西,例如手机短信或安全令牌。 -双因素身份验证流程是,你输入发送到设备的一次性SMS密码,或者是链接一段身份验证器应用程序代码,并提供不断变化的访问代码。 +双因素身份验证流程是,你输入发送到设备的一次性 SMS 密码,或者是链接一段身份验证器应用程序代码,并提供不断变化的访问代码。 如你所想,这比简单地输入密码或单个身份验证凭据要安全得多。你需要知道登录凭据,并有权访问第二部分的物理设备(上文中的手机等)。 近年来,双因素身份验证在在线服务中变得非常普遍,并且是许多大公司默认的身份验证方法。许多应用要求你设置双因素身份验证才能使用该服务。 -#### 多因素身份认证: +#### 多因素身份认证 -进一步让身份验证更加安全的方式是使用3个或者更多因素。这种形式的身份验证通常需要以下前提: +进一步让身份验证更加安全的方式是使用 3 个或者更多因素。这种形式的身份验证通常需要以下前提: - 你知道的元素 (用户名 + 密码或者用户名 + 安全问题及答案) -- 你拥有的元素 (手机sms卡, 验证应用, USB密钥) +- 你拥有的元素 (手机 sms 卡, 验证应用, USB 密钥) - 属于你的元素 (指纹 / 人脸识别) 由于以上原因,多因素身份验证提供最强大的保护,你必须满足多个因素才能够通过验证,而这些因素通常非常难以被“黑掉”或者造假。 @@ -73,11 +73,11 @@ 展现认证过程的图片 -这些数据会发送到服务器,然后认证的过程就开始了。服务器数据库会检查验证你提供的信息,如果检验一致,你就通过认证。然后你得到一种身份识别数据,例如cookie或者JSON Web令牌(JWT令牌)。 +这些数据会发送到服务器,然后认证的过程就开始了。服务器数据库会检查验证你提供的信息,如果检验一致,你就通过认证。然后你得到一种身份识别数据,例如 cookie 或者 JSON Web 令牌(JWT 令牌)。 成功!你通过的认证,并且可以访问网站了。 -你可以在freeCodeCamp跟着Beau Carnes学习更多JWT令牌的内容。[点击这里](https://www.freecodecamp.org/news/what-are-json-web-tokens-jwt-auth-tutorial/)。 +你可以在 freeCodeCamp 跟着 Beau Carnes 学习更多 JWT 令牌的内容。[点击这里](https://www.freecodecamp.org/news/what-are-json-web-tokens-jwt-auth-tutorial/)。 接下来让我们看一看什么是授权。 @@ -101,7 +101,7 @@ 根据框架的不同,你可以选择不同的执行授权的方式。 -例如在.NET框架中,你可以使用基于角色的访问控制或者基于声明的访问控制。 +例如在.NET 框架中,你可以使用基于角色的访问控制或者基于声明的访问控制。 基于角色的访问控制的核心是,在你的系统中每个用户都被分配了一个角色。这些角色有与之相关的预定义的权限。被授予一个角色意味着用户将自动继承所有这些权限。角色是在用户创建和设置时分配。 @@ -123,9 +123,9 @@ 当你到达宴会门口的时候,一个安保会要求你提供昵称和密码,然后他们通过清单**认证** 你的凭证。如果信息匹配,就递给你一个信封,告知你可以进入宴会。 -一旦被宴会准入,你就可以自由访问宴会的任意公共领域和场馆,因为这些地区不需要**授权** (每一个都有权享受宴会)。 但是,你或许想要瞧一瞧VIP区域。 +一旦被宴会准入,你就可以自由访问宴会的任意公共领域和场馆,因为这些地区不需要**授权** (每一个都有权享受宴会)。 但是,你或许想要瞧一瞧 VIP 区域。 -当你靠近VIP区域的时候,另一个安保要求你打开信封(你的权限和角色)。他们查看之后,发现你不具备VIP资格。这样你就未被**授权** 访问。简言之,身份验证查验用户身份或者可以访问的服务,而授权确定他们进入后可以做什么。 +当你靠近 VIP 区域的时候,另一个安保要求你打开信封(你的权限和角色)。他们查看之后,发现你不具备 VIP 资格。这样你就未被**授权** 访问。简言之,身份验证查验用户身份或者可以访问的服务,而授权确定他们进入后可以做什么。 ## 为什么要同时实现身份验证和授权? @@ -144,4 +144,4 @@ - 认证 =  验证用户身份或进程。 - 授权 = 确定用户/系统是否有权使用资源或执行操作。 -如果你想要和我讨论更多本文相关内容,欢迎在Twiiter上找到我![@gweaths](http://twitter.com/gweaths) +如果你想要和我讨论更多本文相关内容,欢迎在 Twiiter 上找到我![@gweaths](http://twitter.com/gweaths) diff --git a/chinese/articles/why-learn-python-and-how-to-get-started.md b/chinese/articles/why-learn-python-and-how-to-get-started.md index 1ddc1edf6..11defd698 100644 --- a/chinese/articles/why-learn-python-and-how-to-get-started.md +++ b/chinese/articles/why-learn-python-and-how-to-get-started.md @@ -5,47 +5,47 @@ <figure class="kg-card kg-card-image kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/size/w2000/2023/02/OOP--2-.png" alt="为什么Python适合初学者 - 以及如何开始学习)" class="kg-image"><figcaption></figcaption></figure> -Python相比较其他编程来说有着较为简单的语法,所以学习Python对于很多刚进入编程领域的初学者来说是一个很好的选择。 +Python 相比较其他编程来说有着较为简单的语法,所以学习 Python 对于很多刚进入编程领域的初学者来说是一个很好的选择。 -Python还是一门应用领域很广的编程语言,这也就意味着你可以在各种各样的工作和领域中使用它。 +Python 还是一门应用领域很广的编程语言,这也就意味着你可以在各种各样的工作和领域中使用它。 跟很多刚进入技术领域的新手一样,我在寻找如何正确学习编程的道路上也遇到了很多困难。我只能来来回地调整学习方式,以找到一种正确且让自己感到舒服的学习方法。 -刚开始进入技术领域时,我学习的是一些Web开发的基础知识,比如HTML、CSS和一点Javascript的知识。后开我开始试着学习不同的编程语言,以期找到最适合我的编程语言。 +刚开始进入技术领域时,我学习的是一些 Web 开发的基础知识,比如 HTML、CSS 和一点 Javascript 的知识。后开我开始试着学习不同的编程语言,以期找到最适合我的编程语言。 -我学过一点C语言和Java,甚至还学过Pascal _(主要是学校在大一的时候进行了相关的课程)_。 +我学过一点 C 语言和 Java,甚至还学过 Pascal _(主要是学校在大一的时候进行了相关的课程)_。 -这些都发生在我学习Python之前,但是尝试了Python并且学习了一部分内容以后,我决定将它作为我的主力编程语言。 +这些都发生在我学习 Python 之前,但是尝试了 Python 并且学习了一部分内容以后,我决定将它作为我的主力编程语言。 -接着读下去,你就可以更好地理解为什么Python是初学者最容易学习的编程语言,并且还有它的好处,以及帮助你入门的资源和技巧。 +接着读下去,你就可以更好地理解为什么 Python 是初学者最容易学习的编程语言,并且还有它的好处,以及帮助你入门的资源和技巧。 -## 为什么学习Python? +## 为什么学习 Python? 不论你是一个初学者还是已经学过几门编程语言,我们都不得不承认每新学一门编程语言都是一项新的挑战。当你没有正确的资料和指导时更是如此。但是如果有了正确的帮助,你可以把这个挑战变成一个值得享受的经历。 -Guido van Rossum在1989年12月3日正式创造出了Python,他有着这样一句设计理念:**只有一种方法去做某事,这就是它能够工作的原因** +Guido van Rossum 在 1989 年 12 月 3 日正式创造出了 Python,他有着这样一句设计理念:**只有一种方法去做某事,这就是它能够工作的原因** -Python是一种解释型、高级、通用编程语言。作为一种编程语言,Python使用大部分英文关键字,因此与其他语言相比,它具有更少的例外和特殊情况。 +Python 是一种解释型、高级、通用编程语言。作为一种编程语言,Python 使用大部分英文关键字,因此与其他语言相比,它具有更少的例外和特殊情况。 -现在我们来一起看看你可能想要学习Python的一些原因。 +现在我们来一起看看你可能想要学习 Python 的一些原因。 -### Python简单的语法 +### Python 简单的语法 -Python是一门语法简单的编程语言,这也是为什么它被很多刚进入技术领域的初学者作为第一语言,但是相信我,经过透彻的讲解,你也会被说服的。 +Python 是一门语法简单的编程语言,这也是为什么它被很多刚进入技术领域的初学者作为第一语言,但是相信我,经过透彻的讲解,你也会被说服的。 -跟其他语言相比较,Python可以用极其简单的语法结构,所以它可以使用较少的代码来完成一些特定的工作。这就可以使得初学者在短时间内学习代码基础和理解编程语言。 +跟其他语言相比较,Python 可以用极其简单的语法结构,所以它可以使用较少的代码来完成一些特定的工作。这就可以使得初学者在短时间内学习代码基础和理解编程语言。 -除此以外,Python还是一门解释型的编程语言,这就意味着你不需要使用编译器去写代码和运行代码 +除此以外,Python 还是一门解释型的编程语言,这就意味着你不需要使用编译器去写代码和运行代码 -咱们来举一个例子,如何使用Python、Java、C++来分别输出“Hello World”语句: +咱们来举一个例子,如何使用 Python、Java、C++来分别输出“Hello World”语句: -这里是Python输出“Hello World”的方式 +这里是 Python 输出“Hello World”的方式 ```python print("Hello World) ``` -这是Java: +这是 Java: ```java public class HelloWorld { @@ -55,7 +55,7 @@ public class HelloWorld { } ``` -然后这是C++: +然后这是 C++: ```C++ #include <iostream> @@ -67,71 +67,71 @@ int main() { } ``` -看完上面的代码片段就可以发现,相比于其他语言,Python有着最简要和最简单的语法结构。 +看完上面的代码片段就可以发现,相比于其他语言,Python 有着最简要和最简单的语法结构。 -### Python的多功能性 +### Python 的多功能性 -这一条也是Python能成为一个非常流行的编程语言的原因。不论你处在什么领域,什么工作,你都可能在个人项目或工作中使用Python。 +这一条也是 Python 能成为一个非常流行的编程语言的原因。不论你处在什么领域,什么工作,你都可能在个人项目或工作中使用 Python。 例如: -- **工业应用** – Python被用于各种广泛领域,包括网络开发、数据科学、机器学习、人工智能、科学计算等等。最近,它的影响在人工智能和ML领域得到了真正的体现,使其成为一种高需求的技能。 -- **框架和代码库** – Python有着大量的框架和代码库,所有这些都使它在创建应用程序时更容易使用,并能帮助你在更短的时间内开发项目。 -- **兼容性** – Python是一种跨平台语言,这意味着在一个操作系统中编写的代码可以在另一个操作系统中执行而没有任何问题。这对于从事网络项目的开发人员来说非常有用,他们希望在多种设备上测试项目的功能。 -- **开源** – 我喜欢Python的其中一个原因是它有一个非常活跃的开源社区,这让那些想要学习和为语言做出贡献的人有很多资源可以使用,而且这也让Python成为了一个不断进化、越来越好的语言。 +- **工业应用** – Python 被用于各种广泛领域,包括网络开发、数据科学、机器学习、人工智能、科学计算等等。最近,它的影响在人工智能和 ML 领域得到了真正的体现,使其成为一种高需求的技能。 +- **框架和代码库** – Python 有着大量的框架和代码库,所有这些都使它在创建应用程序时更容易使用,并能帮助你在更短的时间内开发项目。 +- **兼容性** – Python 是一种跨平台语言,这意味着在一个操作系统中编写的代码可以在另一个操作系统中执行而没有任何问题。这对于从事网络项目的开发人员来说非常有用,他们希望在多种设备上测试项目的功能。 +- **开源** – 我喜欢 Python 的其中一个原因是它有一个非常活跃的开源社区,这让那些想要学习和为语言做出贡献的人有很多资源可以使用,而且这也让 Python 成为了一个不断进化、越来越好的语言。 -## 作为一个新的开发者,学习Python的好处 +## 作为一个新的开发者,学习 Python 的好处 -作为一个进入技术领域的初学者,学习Python会有很多好处。除了我们刚才讨论的内容外,这里还有一些: +作为一个进入技术领域的初学者,学习 Python 会有很多好处。除了我们刚才讨论的内容外,这里还有一些: ### 在各行业的受欢迎程度 -Python被用于许多行业,如网络开发、数据科学、人工智能、金融、教育、研究、安全等等。 +Python 被用于许多行业,如网络开发、数据科学、人工智能、金融、教育、研究、安全等等。 -拥有Python知识和技能可以为各种职业机会打开大门,如软件工程师、DevOps工程师、数据科学家、研究分析师等,使你在就业市场上获得优势。 +拥有 Python 知识和技能可以为各种职业机会打开大门,如软件工程师、DevOps 工程师、数据科学家、研究分析师等,使你在就业市场上获得优势。 ### 实用性 -如果你对Python得心应手,你将能够很好地利用这些技能,解决现实世界的问题。 +如果你对 Python 得心应手,你将能够很好地利用这些技能,解决现实世界的问题。 -凭借其广泛的库和框架,你将能够开发出能够帮助预测结果、可视化数据和理解不同趋势的应用程序和系统,Python在帮助你实现任务自动化方面也非常出色。 +凭借其广泛的库和框架,你将能够开发出能够帮助预测结果、可视化数据和理解不同趋势的应用程序和系统,Python 在帮助你实现任务自动化方面也非常出色。 ### 社区和支持 -Python社区提供了许多资源来帮助第一次学习的人。这些资源包括[在线教程](https://www.freecodecamp.org/news/the-python-handbook/)、论坛、编码挑战、[课程](https://www.freecodecamp.org/news/learn-python-free-python-courses-for-beginners/)等等。 +Python 社区提供了许多资源来帮助第一次学习的人。这些资源包括[在线教程](https://www.freecodecamp.org/news/the-python-handbook/)、论坛、编码挑战、[课程](https://www.freecodecamp.org/news/learn-python-free-python-courses-for-beginners/)等等。 -freeCodeCamp也提供了一个Python课程,[你可以在这里查看](https://www.freecodecamp.org/learn/scientific-computing-with-python/)。 +freeCodeCamp 也提供了一个 Python 课程,[你可以在这里查看](https://www.freecodecamp.org/learn/scientific-computing-with-python/)。 这类支持可以帮助新人在尝试建立自己的职业生涯时获得动力和参与感。 -## 学习Python的小贴士 +## 学习 Python 的小贴士 -尽管Python是一个很好的初学者编程语言,但这并不意味着它容易上手。你需要采取必要的措施并找到正确的工具来帮助你在学习道路上前行。 +尽管 Python 是一个很好的初学者编程语言,但这并不意味着它容易上手。你需要采取必要的措施并找到正确的工具来帮助你在学习道路上前行。 首先,一个正确的资源是很好的开始,一个路线图也会很有帮助。 -这一点不太需要担心,[roadmap.sh](https://roadmap.sh/python)是一个开源项目,它有一个专门为不同职业道路制作的路线图和指导,其中就包括Python。 +这一点不太需要担心,[roadmap.sh](https://roadmap.sh/python)是一个开源项目,它有一个专门为不同职业道路制作的路线图和指导,其中就包括 Python。 当我们了解了如何学习以后,我们现在就可以着手于帮助你实现学习目标的资源了。 -### 学习Python的资源 +### 学习 Python 的资源 -Python的学习资源有好多好多,其中有免费的也有付费的。 +Python 的学习资源有好多好多,其中有免费的也有付费的。 -- 循序渐进的课程平台有:Codecademy, W3Schools, Pythontutorial.net, Python.org, 谷歌的Python Class, 以及 Educative. -- 全程指导课程平台有:freeCodeCamp, Udemy, Coursera, edX, Programiz, 微软的Introduction to Python Course +- 循序渐进的课程平台有:Codecademy, W3Schools, Pythontutorial.net, Python.org, 谷歌的 Python Class, 以及 Educative. +- 全程指导课程平台有:freeCodeCamp, Udemy, Coursera, edX, Programiz, 微软的 Introduction to Python Course -还有一件需要注意的事情:写代码对于完全掌握Python是不可或缺的一步,熟能生巧。 +还有一件需要注意的事情:写代码对于完全掌握 Python 是不可或缺的一步,熟能生巧。 构建项目是一个提升理解代码能力的好方法,先从一些小项目开始,循序渐进,逐渐增加到更复杂的项目。 为了帮助你开始,这里有一个清单你可以看看:[list of Python projects](https://www.freecodecamp.org/news/python-projects-for-beginners/) -也可以尝试在与他人一起学习或分享的环境中学习。这一方面可以通过参加在线社区或者在社交媒体上对一些你知道的问题分享答案。例如,你可以在推特上参加#100DaysofCode活动,或参加其他挑战,同时你也会得到你提出的问题的答案。 +也可以尝试在与他人一起学习或分享的环境中学习。这一方面可以通过参加在线社区或者在社交媒体上对一些你知道的问题分享答案。例如,你可以在推特上参加#100DaysofCode 活动,或参加其他挑战,同时你也会得到你提出的问题的答案。 记住,在学习编程时,先理解基本概念,例如数据类型、函数和控制结构,然后再深入学习高级主题,这非常重要。 -作为一名有志成为Python开发者的学习者,学会如何调试你的代码是非常关键的,这对你成功成为开发者非常重要。所以,确保你在早期学习并掌握这些技巧。 +作为一名有志成为 Python 开发者的学习者,学会如何调试你的代码是非常关键的,这对你成功成为开发者非常重要。所以,确保你在早期学习并掌握这些技巧。 ### 如何保持积极的态度 @@ -148,6 +148,6 @@ Python的学习资源有好多好多,其中有免费的也有付费的。 ## 结论 -在这个指南中,我重点强调了一些原因,说明为什么你应该考虑把Python作为你的第一门编程语言。你也了解了Python的不同应用以及你可以用它做什么。 +在这个指南中,我重点强调了一些原因,说明为什么你应该考虑把 Python 作为你的第一门编程语言。你也了解了 Python 的不同应用以及你可以用它做什么。 为了帮助你开始学习,我强烈建议你充分利用所有可用资源,包括这里列出的资源以及其他在线资源(例如书籍),以更好地理解这门语言。 diff --git a/chinese/articles/working-from-home-tips-to-stay-productive.md b/chinese/articles/working-from-home-tips-to-stay-productive.md index eeb2416f6..4f6e66296 100644 --- a/chinese/articles/working-from-home-tips-to-stay-productive.md +++ b/chinese/articles/working-from-home-tips-to-stay-productive.md @@ -10,9 +10,9 @@ 的确,两年多以前,我开始了我的第一份全职工作,成为一名软件工程师。同时,我也开始了我的第一次远程体验。 -如今,我已经23岁了,住在欧洲,为一家位于新加坡的公司工作,并计划在亚洲开始数字游民生活。 +如今,我已经 23 岁了,住在欧洲,为一家位于新加坡的公司工作,并计划在亚洲开始数字游民生活。 -根据我的经验,我把15个在家工作的技巧放在一起,以帮助你保持生产力。我把这些技巧分为5类,你会在下面发现。 +根据我的经验,我把 15 个在家工作的技巧放在一起,以帮助你保持生产力。我把这些技巧分为 5 类,你会在下面发现。 请记住,这些技巧是基于我的个人经验、阅读和文件。每个人都是不同的,其中一些可能不符合你的需求和目标。 @@ -106,7 +106,7 @@ 远程工作并不意味着远离你的同事。即使有时在社交方面会显得很疏远,但有一点是必不可少的,那就是要保持社交。 -不要害羞--向你的同事约个15-30分钟的咖啡会,以便更好地了解他们。 +不要害羞--向你的同事约个 15-30 分钟的咖啡会,以便更好地了解他们。 如果你不是作为自由职业者单独从事你的项目,有许多在线活动和社区可以让你与志同道合的人交谈,与他们分享咖啡。 @@ -162,7 +162,7 @@ 与上一条提示有点关系,限制分心有助于你保持专注。审视你的环境,并尝试在工作时移除或推开所有让你烦心的物品。 -这里有一些例子:收起你的手机,工作时关闭YouTube视频,关掉电视,等等。 +这里有一些例子:收起你的手机,工作时关闭 YouTube 视频,关掉电视,等等。 ## Wrapping up diff --git a/chinese/articles/writing-tips-software-developers.md b/chinese/articles/writing-tips-software-developers.md index 4cb5dccf8..63f1f480b 100644 --- a/chinese/articles/writing-tips-software-developers.md +++ b/chinese/articles/writing-tips-software-developers.md @@ -87,7 +87,7 @@ Alpha Particle 公司的首席技术官 Keanan Koppenhaver 告诉我,过度在 另一种策略是让写作成为一种日常习惯。经营 Developer Avocados 通讯的 Alex Lakatos 在去年的部分时间里完成了一个每日写作挑战: -> 💡 平均下来,形成一个新的习惯需要 66 天。现在离 2021年还剩 65 天,我们何不早点开始进行我们的新年计划呢? +> 💡 平均下来,形成一个新的习惯需要 66 天。现在离 2021 年还剩 65 天,我们何不早点开始进行我们的新年计划呢? > > 以我的计划为例:我正在努力持续地发布内容,所以在今年剩下的时间里,我将尝试每天至少写 100 字。 [pic.twitter.com/M0dHrJ36ef](https://t.co/M0dHrJ36ef) > @@ -111,7 +111,7 @@ Adam DuVander 指出,对自己的这些期望保持诚实是至关重要的。 ## 结论 -> 你的前100篇博客、视频、帖子、推特、生活、播客、创作可能都是垃圾 +> 你的前 100 篇博客、视频、帖子、推特、生活、播客、创作可能都是垃圾 > > 克服心里的障碍,去做吧!先过了这 100 关再说 > diff --git a/chinese/articles/yield-yield-how-generators-work-in-javascript-3086742684fc.md b/chinese/articles/yield-yield-how-generators-work-in-javascript-3086742684fc.md index 025873fc7..7d69c9307 100644 --- a/chinese/articles/yield-yield-how-generators-work-in-javascript-3086742684fc.md +++ b/chinese/articles/yield-yield-how-generators-work-in-javascript-3086742684fc.md @@ -7,7 +7,7 @@ by Ashay Mandwarya ?️?? -# Yield! Yield! How Generators work in JavaScript. +# Yield! Yield! How Generators work in JavaScript ![js0jIJmWBpkS7FqofAi1h-JFSyq6FXE1cyC5](https://cdn-media-1.freecodecamp.org/images/js0jIJmWBpkS7FqofAi1h-JFSyq6FXE1cyC5) diff --git a/package.json b/package.json index 182dbed6f..91c772150 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,10 @@ "url": "https://github.com/freeCodeCamp/news-translation/issues" }, "devDependencies": { - "husky": "^4.3.0", - "lint-md-cli": "^0.1.2", - "lint-staged": "^10.5.4", - "prettier": "^2.2.1" + "@lint-md/cli": "^2.0.0", + "husky": "^8.0.3", + "lint-staged": "^13.2.3", + "prettier": "^3.0.0" }, "prettier": { "singleQuote": true, @@ -27,7 +27,8 @@ "scripts": { "lint": "lint-md chinese/**/*.md", "format": "lint-md chinese/**/*.md --fix", - "test": "lint-staged" + "test": "lint-staged", + "prepare": "husky install" }, "husky": { "hooks": {