<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ herosql - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ herosql - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 23 May 2026 08:28:37 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/author/herosql/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 编程训练营手册——沉浸工程项目详解 ]]>
                </title>
                <description>
                    <![CDATA[ 在你花费数千美元和几个月的时间参加编码训练营之前，请花 30 分钟读一读这本手册。 这本手册为谁而写  * 每个考虑参加训练营的人  * 每个考虑建立训练营或在训练营任教的开发人员  * 每个写关于训练营的媒体人 对于那些认为自己太忙而没有时间阅读本手册的人 我给你的建议可以归纳为：自己做研究。 先申请很多开发者工作，去获取一些工作的面试机会。你或许可以获取一份开发者的工作而不需要参加训练营。 不要盲目地相信训练营的推荐和就业统计数据，使用领英直接联系他们的校友。 自己做好准备，确保你有足够的钱。如果你准备好参加，确保你有足够的现金来支付学费。并且确保你有足够的钱在训练营里生活，以及你在六个月后才能申请工作时维持生计。 训练营不是魔法，训练营只能帮助你做好准备，你 需要学习一切，你 需要通过开发者工作的面试流程。你 需要投入工作中. 客观性的说明 我设计这本手册尽量做到客观的。为此，我没有提及任何的训练营或他们创始人的名字，我没有链接到任何一个网站。 我写手册不是为了帮助训练营。我写这本手册是为了帮助你。 作为老师和 freeCodeCamp 的创建者，我处于一个独特 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/coding-bootcamp-handbook/</link>
                <guid isPermaLink="false">6669705539176c03d7d0b252</guid>
                
                    <category>
                        <![CDATA[ 编程训练营 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ herosql ]]>
                </dc:creator>
                <pubDate>Wed, 12 Jun 2024 10:22:18 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2024/06/If-you-dream-it--you-can-do-it--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/coding-bootcamp-handbook/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">The Coding Bootcamp Handbook –  Immersive Engineering Programs Explained</a>
      </p><!--kg-card-begin: markdown--><p>在你花费数千美元和几个月的时间参加编码训练营之前，请花 30 分钟读一读这本手册。</p>
<h1 id="">这本手册为谁而写</h1>
<ul>
<li>每个考虑参加训练营的人</li>
<li>每个考虑建立训练营或在训练营任教的开发人员</li>
<li>每个写关于训练营的媒体人</li>
</ul>
<h1 id="">对于那些认为自己太忙而没有时间阅读本手册的人</h1>
<p>我给你的建议可以归纳为：<strong>自己做研究</strong>。</p>
<p><strong>先申请很多开发者工作</strong>，去获取一些工作的面试机会。你或许可以获取一份开发者的工作而不需要参加训练营。</p>
<p><strong>不要盲目地相信训练营的推荐和就业统计数据</strong>，使用领英直接联系他们的校友。</p>
<p><strong>自己做好准备</strong>，确保你有足够的钱。如果你准备好参加，确保你有足够的现金来支付学费。并且确保你有足够的钱在训练营里生活，以及你在六个月后才能申请工作时维持生计。</p>
<p><strong>训练营不是魔法</strong>，训练营只能帮助你做好准备，<em>你</em> 需要学习一切，<em>你</em> 需要通过开发者工作的面试流程。<em>你</em> 需要投入工作中.</p>
<h1 id="">客观性的说明</h1>
<p>我设计这本手册尽量做到客观的。为此，<strong>我没有提及任何的训练营或他们创始人的名字</strong>，我没有链接到任何一个网站。</p>
<p>我写手册不是为了帮助训练营。我写这本手册是为了帮助你。</p>
<p>作为老师和 freeCodeCamp 的创建者，我处于一个独特的位置来写训练营，有三个原因：</p>
<ol>
<li>许多的训练营在他们的课程和课程前期工作中用到 freeCodeCamp。我帮助很多训练营创始人在怎么训练人们进入开发者职业生涯做好准备。</li>
<li>2010 年初，我在旧金山学习了编程，当时训练营首次出现。我没有参加训练营，但我和许多训练营创始人一起出去玩，并参加了学生“演示日”。</li>
<li>过去三年，我进行了重要的初步研究，我发布了<a href="https://www.kaggle.com/freecodecamp/datasets">几个数据集</a> 其中包括数千名训练营校友的回复。</li>
</ol>
<p>freeCodeCamp 的使命是帮助尽可能多的人有学习编码。训练营帮助很多人实现这一目标。所以他们正在帮助 freeCodeCamp 完成我们的使命。</p>
<p>尽管如此，freeCodeCamp 从未从训练营获得过任何报酬。几家大型训练营就赞助事宜与我们接洽。我们一直拒绝。</p>
<h1 id="">为什么我要写这本手册</h1>
<p>当你在谷歌上搜索“编码训练营”或是“在 [城市名] 的编码训练营”，你会找到很多的训练营的评论网站。但这些评论网站存在根本性问题。</p>
<p>首先，这些评论网站都是训练营自己赞助的。训练营付钱给广告，他们买更高的搜索排名，他们支付给支持付费的博客文章，专栏文章和其他宣传。</p>
<p>这是利益冲突。</p>
<p>其次，这些网站上的许多评论都是学生被迫写的。我听过几个训练营毕业生的故事，他们被迫留下积极的评论。在一些案例中，训练营让学员写评论作为强制性的课堂活动。</p>
<p>还有很多营销部门写的虚假评论。</p>
<p>不可能知道哪些训练营是按规则进行的，哪些是作弊的。因此，这些评论网站所做的一切就是帮助作弊者淹没更道德的训练营。</p>
<p>你应该自己想想，而不是依赖充满可疑评论的网站。做出如此重要的决定没有捷径。</p>
<blockquote>
<p>“第 #1 很难得知市场上训练营的质量。在线评价完全是 100% 的游戏。职业统计也是残酷的游戏。 训练营唯一无法游戏的是进入训练营的难度。” - 我在研究本手册时采访过的一位前训练营经理。</p>
</blockquote>
<p>本手册会给你一个框架，让你明白训练营是如何运作的。它将帮助你研究你的选择并规划你的第一份开发人员工作的路线。</p>
<h1 id="">训练营到底是什么</h1>
<p><img src="https://images.unsplash.com/photo-1528901166007-3784c7dd3653?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="一起编码" width="1080" height="720" loading="lazy"></p>
<p>训练营是学校，是你全天学习编码的地方 —— 通常是面对面学习。</p>
<p>很多训练营为期 12 周，甚至有长达一年的。</p>
<p>很多训练营费用在$10,000 美元 到 $20,000 美元之间。</p>
<p>一些训练营发放贷款 —— 直接或间接通过金融公司。</p>
<p>一些训练营提供“收入共享协议”，你无需预先付款，而是在几年（通常是两年）内支付税前收入的一定百分比（通常是 17%）。这不是“免费的钱”——我将在下面详细解释。</p>
<p>训练营的目的是让一满教室没有参加过技术工作的人，帮助他们获取第一份开发工作。</p>
<p>这时一项困难的任务。而且这是一大笔钱的赌注。从而引导我们进入下一个问题。</p>
<h1 id="">训练营真的有效吗</h1>
<p>在许多案例中，是的。每年，数以千计的训练营毕业生获得了他们的第一份开发工作。</p>
<blockquote>
<p>“最好的训练营让人们能够变成有经验（轻度）的编程人员。他们在某些方面所做的就是选择，他们让人们在高压下学习”——一个训练营的经理和我说的</p>
</blockquote>
<p>但是也有训练营毕业生，并没有获得开发工作，最后回到他们之前的岗位。</p>
<p>成功归结为以下几个因素</p>
<ul>
<li>
<p>训练营的选择性如何</p>
</li>
<li>
<p>老师们的能力如何</p>
</li>
<li>
<p>且运营的训练营的人是否真正关心他们的就业统计数据，还是只关注短期收益。</p>
</li>
</ul>
<p>大多的训练营不公开分享他们的数据。训练营可能会使用非标准指标。这让你无法进行同类比较。</p>
<p>但训练营的透明度运动正在不断发展。他们互相施压，要求对方承担更多责任。</p>
<p>一些训练营希望在政府被迫介入监管之前对该行业进行自我监管。</p>
<p>训练营作为高中后教育的一种形式仅存在了几年。他们没有像学院和大学一样被监管。这就是说，通过认证。</p>
<h2 id="">训练营是否获得官方认证</h2>
<p>短期来说是没有，他们没有得到官方认证。</p>
<p>但首先，认证意味着什么？为什么这对学院和大学很重要。</p>
<p>在美国，很多大学是地方官方认证。一些学术机构是全国认证。像是英语预备学校。</p>
<p>造成这种情况的主要原因有两个：</p>
<ul>
<li>
<p>认证的学校能够让学生拿到美国签证</p>
</li>
<li>
<p>认证的学校能帮助学生获得联邦助学金或联邦学生贷款</p>
</li>
</ul>
<p>为了得到官方认证，学校必须接受独立教育工作者的审核。审查员研究很多文件，并确保学校符合所有的法律。他们也确保学生在毕业后能获得工作。</p>
<p>如果来自学校的毕业生不能找到专业领域内的工作，这是一个危险信号。学校或许会失去它们的官方认证。</p>
<p>你可能听过“文凭工厂”。这是很多学院和大学失去了他们的官方认证后（或是从来就没有认证过）。他们售卖没有价值的课程和没有价值的文凭。</p>
<p>在美国，公立大学是由政府运营的。这些大学都经过认证。大多数私立大学是由非营利机构运营——通常是宗教组织。这些大学通常的也经过认证。</p>
<p>但还有第三类大学：私立营利性大学。这一类大学的情况就比较复杂了。这些大学有的通过了认证，有的则没有。</p>
<p>这些私立营利性大学在深夜电视上大肆宣传，并在 Facebook 上购买大量广告。他们诱骗不谙世事的学生入学。</p>
<blockquote>
<p>“蠢人难聚财”——Dr.John Bridges，1587</p>
</blockquote>
<p>在某些情况下，这些学校有资格获取联邦学生贷款、符合美国军人权利法案的补贴和其他形式的政府援助。</p>
<p>简单来说，很多私立营利性大学是骗局。美国政府是慢慢的关闭这些它们。但是很多人仍然坠入他们的营销陷阱中，最终背着数千美元的欠款和一个没有价值的证书。</p>
<p>尽管如此，骗局的消息传播的很慢。在一个学校听起来好的难以置信的时候，人们仍然会相信。</p>
<p>让我们回到编码训练营。</p>
<p>在没有某种形式的认证的情况下，一些训练营他们关注在短期收益上 —— 而不是训练营的健康长期收益 —— 乘上训练营流行的浪潮。他们在为学生提供低标准结果同时致富。</p>
<p>训练营认证系统可以帮助防止这种情况发生。</p>
<p>上述我提到的，很多训练营没有资源获取认证。或者它们没有存在很长时间，没有资格。这是自我监管发生作用的地方。</p>
<p>结果报告诚信委员会（CIRR）是训练营共同发起的一个项目，旨在以人人都能理解的方式公开分享其毕业生的就业统计数据。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Launch_Academy_Boston__H1_2018__Audited.png" alt="Launch_Academy_Boston__H1_2018__Audited" width="600" height="400" loading="lazy"></p>
<p>这是一个标准的结果报告征信委员会给出的一家训练营的报告。</p>
<p>许多知名训练营都参与了这项计划。但有些知名训练营并未参与或已停止分享其数据。</p>
<p>训练营的 CIRR 会员资格并不等同于获得认证，但这是一个好的开始。</p>
<h1 id="">训练营的毕业生能拿到什么样的薪水</h1>
<p>基于公共数据，训练营毕业生赚到的薪水和初级开发者一样。这包括计算机科学专业和其他自学编程的大学毕业生。</p>
<p>作为一名初级开发人员，你能挣多少钱的最大因素是城市的生活成本。旧金山的初级开发人员的工资是美国中部初级开发人员的两倍。。</p>
<p>如果一个训练营说他们的毕业生可以获得比其他训练营毕业生更高的起薪，那就意味着他们的大多数毕业生都在旧金山等生活成本更高的城市找到工作。</p>
<h1 id="">训练营毕业生需要多长时间才能获得工作</h1>
<p>这取决于训练营。一些训练营有很广泛的职业指导。有些训练营与本地行业联系密切，可以帮你安排面试。这两种方法都可以大大缩短你之后申请工作的时间。</p>
<p>训练营结束后，找工作平均需要六个月。</p>
<h1 id="">训练营的完成率是多少</h1>
<p>大多数参加训练营的人都会完成训练。</p>
<p>大多数好的训练营是有选择性的。如果他们不觉得你在课程之后能够获得开发者的工作，他们就不会允许你进入。</p>
<p>训练营出于短期收益而接受你，这样他们就能拿到你的学费。但是他们也有长期收益，如果他们觉得你破坏了他们的就业统计数据，就不会接受你。</p>
<p>尽管如此，不是所有的训练营都关心他们的就业统计数据。有些训练营可能因为财务困境而专注于短期收益。（近年来，许多知名训练营都关闭了。）</p>
<p>在一些案例中，训练营可能在课程进行到一半就驱逐学生。</p>
<p>如果训练营是一个“预付现金”的训练营，他们就准备好了通过招生从你身上收割短期收益。如果你表现不佳，尝试挽救你比给你退款更有意义。</p>
<p>这些训练营确实仍希望从你提高他们的就业率中获得长期利益。但是这样的收益就比你直接给他们钱更抽象。</p>
<p>另一方面，如果你表现不佳，扣发工资的训练营（记住那些收入共享协议）有更大的动机将你踢出局。这是因为他们只会在长期内受益（未来 2 年内你工资的 17%）。</p>
<p>此外，一些训练营学员会因为各种原因而退学。这些原因可能与训练营本身无关，例如重大的生活事件。</p>
<h1 id="">来自训练营的毕业生转行技术失败有多常见</h1>
<p>通常，当人们报名参加更有选择性的训练营时，他们已经花了很多时间编写代码，并且几乎能够找到一份开发人员的工作。因此，这些训练营毕业生找不到工作的情况并不常见。</p>
<p>但不管你的技术如何，找到一份开发者工作是本身是困难的。训练营可以在过程中辅助你，其中许多训练营都有职业顾问来帮助你。</p>
<p>即使是精英训练营的毕业生，也常常需要申请数百个开发职位（并在一打公司面试）才能获得满意的工作机会。</p>
<blockquote>
<p>“有时人们付出所有努力，而且很有才华，但就是在很长时间内都不够幸运。其他时候，许多人会点击求职板发送垃圾邮件。这种方法很难奏效。</p>
</blockquote>
<blockquote>
<p>最好（目前为止）的做法是与行业建立联系和一些真正的人会面。向其他开发者寻求帮助，寻求岗位，和推荐。内部推荐通常更好。” —— 我在研究这本手册时和其他训练营经理谈论过。</p>
</blockquote>
<p>在很多案例中，当一个人转行技术失败，这不是训练营的错。一些人低估了找工作过程的艰辛，在他们成功之前就放弃了。</p>
<h1 id="">有哪些类型的训练营</h1>
<p><img src="https://images.unsplash.com/photo-1519155031214-e8d583928bf2?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="下午在咖啡厅合伙办公" width="1080" height="720" loading="lazy"></p>
<p>一些训练营关注特定技术栈，例如 Ruby on Rails, Python / Django, or Java / Android。有些甚至专注于特定的技术职业，例如用户体验设计。</p>
<p>但从所有权结构的角度来考虑训练营会更有帮助。他们的激励措施是什么？</p>
<h2 id="">私有的本地训练营</h2>
<p>这些机构通常由一名或多名当地开发人员创办。创始人可能会亲自教授一些课程。（通常来说是好事）</p>
<p>很多训练营一开始都是由当地人开办的，像是一个校区的学校。</p>
<h2 id="">私有连锁训练营</h2>
<p>随着本地训练营的发展，创始人或许会在其他城市开设更多的校区。</p>
<p>通过经营多个校园，业主可以获得规模经济和范围经济的优势。他们可以将固定成本（如营销和课程）的负担分摊到几所学校。</p>
<p>这就是说，很难保证多个学校一样的质量。</p>
<p>我给未来学生的建议是，把每个城市校园都当成一所独立的学校。不要依赖训练营连锁店的整体声誉。相反，要做好调查。寻找该特定校园的校友并与他们聊聊。</p>
<h2 id="">大学训练营</h2>
<p>很多大学训练营不是大学自己运营的，而是由营利性教育公司运营的。</p>
<p>大学与这些私营公司签订合同来运营训练营。这些训练营付很多费用给大学来用他们的教室空间和 —— 更重要的 —— 他们富有名望的名字。这是一种有争议的做法。</p>
<p>当你考察一个大学的训练营时，不要依赖大学本身的声誉。相反，要自己研究。</p>
<h2 id="">免费非营利训练营</h2>
<p>非营利训练营与当地营利训练营类似。主要区别在于非营利训练营没有营利激励。</p>
<p>传统的 501(c)(3) 非营利组织没有所有权，没有人拥有组织的股份，它们属于公众。</p>
<p>红十字会、无国界医生组织和 freeCodeCamp 就是这样组成的。</p>
<p>一些训练营的结构也采用这样的结构。</p>
<p>有些非营利训练营是完全免费的。它们由捐赠者资助，或通过政府拨款资助。</p>
<p>其中有多个项目旨在重新培训退伍军人和难民。</p>
<h2 id="">免费的营利训练营</h2>
<p>出人意料的，有一些训练营是营利性的，但仍然是免费的。而且这些项目也不会使用收入共享协议来扣留你的工资。</p>
<p>这些项目都是选择性的。他们可能要求申请人拥有博士学位或其他高级学位。</p>
<p>他们规划让 100%的钱来自雇主。这计划招募费用是雇主出，你在他们的公司。招募费用高达你的第一年的薪水 33%。</p>
<p>但是你不用像学生一样支付任何东西。你未来的雇主为你出了训练营的费用。</p>
<h3 id="">在线训练营</h3>
<p>我最后提是因为它们是新的实验产物。</p>
<p>让一名编程新手在几个月内为第一份工作做好准备是一回事，而完全在线上完成这项工作又是另一回事。</p>
<p>“学习编码” 类型的网站可以在线教你因为它们是为长期使用而设计的。如果你准备准备在一年或两年内每周在线练习编码，你的技术会稳步前进。</p>
<p>但是在线训练营要求你在更短的时间内集中完成所有这些工作。</p>
<p>训练营的价值很大程度上来自于与其他学习者并肩而坐。你们互相帮助，克服错误和考试失败。你们一起做项目。你们建立人际关系。</p>
<p>所有这些都很难在短期时间内在线完成。</p>
<p>公司提供在线训练营的主要原因很简单：它们可以带来更高的利润。</p>
<ul>
<li>公司不需要租借办公空间来建立分校。</li>
<li>他们不用担心房子。</li>
<li>他们可以从世界各地聘请讲师。这比在旧金山聘请讲师要便宜得多。</li>
<li>他们让学生们进入一个大的聊天室，让他们以最少的监督来解决问题。</li>
</ul>
<p>也就是 - 一个编码训练营十分之一的成本。很多在线训练营收费相当于线下训练营。</p>
<p>所以在你在在线训练营登记之前，你应该留意。看看你的城市是否有类似的线下训练营。</p>
<h1 id="">通常什么人会去训练营</h1>
<p>各种各样的人都会参加训练营。</p>
<p>但最常见的人口统计数据是：</p>
<ul>
<li>尚未进入职场且可以再等待 6 个月的应届大学毕业生。</li>
<li>想要转换行业且有能力在未来 6 个月内不工作的富裕职业人士。</li>
</ul>
<p>很少见，但是值得注意的是：</p>
<ul>
<li>退伍军人进行再培训以从事平民职业</li>
<li>贷款支付训练营费用（或签署“收入共享协议”）的失业人员</li>
<li>高中和大学生在暑假学习编码（且不计划在暑假后进入职场）</li>
</ul>
<p>训练营的学生平均在 28 岁，但年龄远高于这个数字的人参加训练营的情况也很常见。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/Bootcamp_Success_vs_Age___Kaggle.png" alt="Bootcamp_Success_vs_Age___Kaggle" width="600" height="400" loading="lazy"></p>
<p>这是参加过编码训练营的 freeCodeCamp 新开发者调查受访者的年龄数据可视化。</p>
<p>他们中的大多数人至少有一位父母是大学毕业的。</p>
<p>他们大多数在训练营开始前六个月或更早自学过编码。</p>
<p>很多训练营学生已经有了大学学历证书 —— 通常不是计算机科学专业。</p>
<h1 id="">训练营适合你吗</h1>
<p>这归结于几个因素：</p>
<ol>
<li>你有多少时间</li>
<li>你有多少钱</li>
<li>你当前是否在工作</li>
<li>和你有多少编码经验</li>
</ol>
<p>让我们来谈谈所有这些因素，从钱开始。</p>
<h1 id="">我能负担的起训练营吗</h1>
<p><img src="https://images.unsplash.com/photo-1553788085-7a4ef80b42d6?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="photo-1553788085-7a4ef80b42d6?ixlib=rb-1.2" width="1080" height="720" loading="lazy"></p>
<p>如果有无限的时间和无限的金钱，我 100%建议人们：是的 —— 去训练营吧。</p>
<p>但是时间和金钱是有限的，我们需要讨论更多的细节。</p>
<h2 id="">训练营的学费是多少</h2>
<p>我们讨论过，一些训练营是完全免费的，但这些并不代表这个领域。</p>
<p>大多数训练营收费在$10,000美元到$20,000 美元之间。课程时间越长，费用通常越高。</p>
<p>有些训练营并不要求你预付学费。反而，他们通过所谓的“薪资共享协议”来扣留你未来的工资。</p>
<h2 id="isas">什么是薪资共享协议（ISAs）</h2>
<p>基本上，你和训练营签订的协议。这些训练营和美国税务局密切合作。他们准确的知道你赚多少钱，在几年内（通常是两年）从你交税前的收入分走一部分（通常是 17%）。</p>
<p>如果你在训练营毕业后的第一份工作年薪为 $50,000 美元，这意味着你需要付：</p>
<pre><code class="language-plain">($50,000 * 17% = $8,500) * 2 years = $17,000 total

</code></pre>
<p>如果你每年赚 $80,000 美元：</p>
<pre><code class="language-plain">($80,000 * 17% = $13,600) * 2 years = $27,400 total

</code></pre>
<p>如果你每年赚 $100,000 美元：</p>
<pre><code class="language-plain">($100,000 * 17% = $17,000) * 2 years = $34,000 total

</code></pre>
<p>在你每年赚到的钱少于$50,000时，很多薪资共享协议不会生效。而且如果你在五年内每年赚的钱都少于$50,000。薪资共享协议就会解除，你不需要再继续。一些薪资共享协议有“最高限额” —— 你需要偿还的最大金额。</p>
<p>但是一个很重要的事需要注意，薪资共享协议是一种新的贷款。不像其他的客户贷款 —— 像学生贷款 —— 薪资共享协议在法律的灰色地带中。</p>
<p>薪资共享协议是一种新的“金融工程”。它们看起来合法，但是这些都没有经过法庭验证。</p>
<p>这就意味着如果你进入了薪资共享协议，当训练营破产的时候，不清楚会发生什么。（经常发生——即使是连锁训练营）。你不能控制你贷款的所有权。不清楚他们会怎么让你偿还。</p>
<p>所以再次，<strong>你研究研究</strong>。</p>
<h1 id="">在训练营中的生活费用要多少</h1>
<p>你的生活费用取决于在哪个城市的训练营进行，以及这个城市的生活成本。</p>
<p>和你的父母一起生活在美国中西部？你的费用将会远远低于你在搬到旧金山且租了公寓。</p>
<p>你应该存够训练营的钱，再加上 6 个月。这样，你就有足够的时间找到合适的工作并领取第一份薪水。</p>
<h2 id="">什么是“机会成本”</h2>
<p>机会成本是来自于经济学的理念，大概的意思是“放弃的收益”。</p>
<p>要了解参加编码训练营的真实成本，你应该考虑机会成本。</p>
<p><strong>例子</strong>：你现在每个月赚$3000美元。你搬到旧金山，在那$2,000 美元只能租到一个卧室。你参加 12 周的训练营费用是$15,00  0美元。</p>
<p>这是你的真实成本，假设加上你在 6 个月的时间内找到工作并领取第一份薪水：</p>
<pre><code class="language-plain">训练营学费：$15,000

生活成本：（9个月 * $2,000）= $18,000
机会成本：（9个月 * $3,000）= $27,000
</code></pre>
<p>你可以看到，这种情况下，编码训练营的学费只占实际费用的 1/4。</p>
<p>所以$20,000美元的训练营帮你在4个月内找到工作比$15,000 美元的训练营帮助你在 6 个月找到工作便宜。这就是说，你再次考虑生活成本和机会成本。</p>
<p>这一课很简单：不要太关注编码训练营的学费本身。它只是真实成本的一部分。</p>
<h1 id="">我的编程技能够参加训练营吗？</h1>
<p>一个天真的答案是：“只要申请，就可以看看你是否被录取”</p>
<p>但是，让我们从训练营的激励机制的角度来思考一下。</p>
<p>有一个训练营的适宜区：不用太初级，不用太高级 —— 只是刚好。</p>
<h2 id="1">场景 #1：你的技术太高级，无法从训练营中学到很多东西</h2>
<p>如果你是一个很强的候选人，训练营确信你后面会找到工作。他们唯一合理的决定是接收你。即使他们不觉得你可以从他们项目中学到更多。</p>
<p>为什么？</p>
<ul>
<li>训练营会收取你的学费</li>
<li>他们不需要教你更多</li>
<li>在你获得工作之后，你将会提高他们的就业统计数据。</li>
</ul>
<p>在他们的角度上，他们应该录取你。</p>
<h2 id="2">场景 #2：你对编程陌生，无法从训练营中学到很多东西</h2>
<p>如果你是一个很弱的候选人，那么就得由训练营的决策者来决定了。他们关注的是长期利益还是短期利益？他们有多关注他们的就业统计数据？</p>
<p>如果训练营是关注长期利益的，他们会拒绝你。或允许你学习预习课程，然后让你几个月后再来面试。</p>
<p>但经济困难的训练营无暇担心他们的就业数据。他们可能不会长期存在。他们的理性决定可能是无论如何都会接受你 —— 不管你是否做好准备。</p>
<blockquote>
<p>“从长远来看我们都会死去” —— John Maynard Keynes 1923</p>
</blockquote>
<p>也许你能应付自如，克服重重困难取得成功。也许你不能。不管怎样，训练营都能拿到他们的学费，而且还能多维持几个月。</p>
<h2 id="3">场景 #3：你是“刚刚好”</h2>
<p>这种情况训练营会接受你，这对于双方来说是共赢。</p>
<p>再次声明，你不知道场景会是怎样。场景 #3 你会是“刚刚好” ? 或训练营只是这样说？ 事实上你在场景#1 或 场景#2 呢？</p>
<p>所以训练营有强烈的动机让你通过，甚至在你不够匹配的时候。</p>
<p>这就是为什么我说“只要申请，就可以看看你是否被录取”是天真的。</p>
<p>这里我推荐你采用的策略：</p>
<h2 id="1">步骤 #1: 花几个月自学编码</h2>
<p>尝试构建 freeCodeCamp 的 5 个响应式网页设计认证项目。尝试构建 5 个 JavaScript 算法和数据结构认证项目。</p>
<p>这会确保你懂得基础。而且很多编码训练营都要求将这些作为课前作业的一部分。</p>
<h2 id="2">步骤 #2：申请开发职位</h2>
<p>你怎么处理雇主的简历筛选和电话筛选？你能通过他们的现场编码面试吗？如果可以，你可能只是想要继续申请工作。你或许不需要编码训练营。</p>
<h2 id="3">步骤 #3：申请编码训练营</h2>
<p>如果你已经完成了步骤#3，你现在知道确认你有一些基础技能。而且你知道你还没有准备好开发者的工作。</p>
<p>你现在有信心申请编码训练营。你的水平不是很高。如果他们让你通过，你可以确信你不是太过初级。</p>
<p>如果他们拒绝你，你可以继续自学，并在之后继续申请训练营。</p>
<h1 id="">我该怎么选择训练营？</h1>
<p>首先要确认的是：你的城市有哪些训练营？如果有，我鼓励你去看看它们，你可以更多了解它们。</p>
<p>通过留在你当前所在城市，你可以减少你的生活成本。你可以减少你的压力。你不需要将你的时间花在找公寓和了解新社区。你可以花时间编码。</p>
<h1 id="">我是否需要搬到旧金山参加训练营</h1>
<p>你或许会想：“但我难道不应该搬到开发人员集中的旧金山吗？”</p>
<p>没错，旧金山湾区——包括硅谷——是西半球的科技圣地。</p>
<p>旧金山是几个出色的训练营之家。当然也有很多雇主。此外，还有庞大的生态系统，包括夜间科技活动，黑客马拉松，创业社区和招聘人员。</p>
<p>但是旧金山生活成本很高，生活压力很大。我曾经在那做过 4 年开发，短期内我都不计划搬回去。</p>
<p>需要记住的重要一点是：<strong>软件仍然是软件——无论你在世界任何地方</strong></p>
<p>你可以从那些在科技行业工作了 5 年或 10 年的资深开发人员身上学到很多东西。这几乎和你从旧金山的技术公司的精英开发者那边学到的一样多。</p>
<p>（很多精英开发者每年能赚数百万美元。你可能找不到他们在训练营教书。）</p>
<h1 id="">训练营应该教什么工具？</h1>
<p><img src="https://images.unsplash.com/photo-1534190239940-9ba8944ea261?ixlib=rb-1.2.1&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1080&amp;fit=max&amp;ixid=eyJhcHBfaWQiOjExNzczfQ" alt="伞兵备忘工具集" width="1080" height="608" loading="lazy"></p>
<p>这听起来是反直觉的，但是我还是要讲出来。<strong>工具并不重要</strong></p>
<p>一些训练营教 Ruby 和 Rails 或 Sinatra。尽管它们越来越不流行，它们对于新开发者来说仍然是好工具。</p>
<p>一些训练营教全栈 JavaScript 和 Node.js。这个工具链在新项目中广泛使用。但是它们有点难学。</p>
<p>一些训练营教 Python 和 Flask 或 Django。一些教.NET 生态系统。或许一些直接教移动端开发和 Android 和 Java 或 Kotlin。有些会使用 C 语言进行深入开发。</p>
<p>再次声明，工具并不重要。重要的是你把一套工具学好。</p>
<p>学习一套工具并在概念上理解所有东西怎么组合在一起。这样你会更容易学习一套新的工具。</p>
<p>你可以在编码训练营学到的大部分内容总结如下：</p>
<ul>
<li>计算机科学基础</li>
<li>编程本身（更多编码练习）</li>
<li>其他你需要的开发者的技能。像是软件开发方法论，调试技巧，测试，阅读文档。</li>
<li>懂得工作申请流程本身</li>
</ul>
<p>所以，不要因为训练营，“他们教 Rails 但我想学 Node”或“我想成为移动设备开发者”或“我不想学习前端开发。”而拒绝参加训练营。</p>
<p>无论训练营教授哪种工具，你都会学到相同的基本知识。</p>
<h1 id="">训练营的业绩记录有多重要？</h1>
<p>如果一家训练营已经存在了几年，你应该密切关注它们的业绩记录。</p>
<h2 id="">老牌训练营</h2>
<p>首先，了解该训练营是否是结果报告诚信委员会的一部分。如果不是，请询问原因。</p>
<p>你应该问他们就业统计数据。如果他们不情愿给你看——或是不愿意分享过去一年的数据——这是一个危险信号。</p>
<p>不管怎样，你应该找到它们在领英上的毕业生。联系他们，问问他们的经历。</p>
<h2 id="">新开的训练营</h2>
<p>如果这个编码训练营是新开的，那么可用的信息会少得多。你就只有相信你的直觉了。</p>
<p>询问训练营老师们的姓名。在领英上查他们。</p>
<p>他们过去有学生吗？在领英上找到他们的毕业生，并且询问他们的经历。</p>
<p>作为编码训练营的第一批学生之一训练存在高风险/高收益。</p>
<p>像是所有种类的小公司，如果得不到关注，训练营或许会很快关闭。如果发生，你向未来雇主介绍在学校的境遇时。很尴尬。</p>
<p>但是与此同时，新的编码训练营需要证明一些东西。他们的老师和员工会像疯子一样工作来确保学校的成功。他们会对你进行高难度训练。他们会帮你获得好工作，这样他们就会获得成功，并把成功记录在他们的推荐页面上。</p>
<p>面对稀疏的数据，你需要自己决定。管理这个训练营的人看起来是否知道自己在做什么？他们对此充满热情吗？</p>
<h1 id="">我在入学之前应该参观校园吗</h1>
<p>应该。关于你做的这个决定——当你关注生活成本和机会成本时——数万美元和你几个月的时间。</p>
<p>无论如何，定机票。就像你一个人的一日飞行旅行。与老师和工作人员聊聊。看一看学校。观察一下学生们。</p>
<p>这是非常重要的决定。从长远来看，这趟旅行只是你的时间和金钱中一笔很小的投资。</p>
<h1 id="">我应该联系过去的校友吗</h1>
<p>答案永远都是是。不要跳过这一步。</p>
<p>在领英上找到他们。如果他们在领英上一些天内没有回复你的消息，找到他们的邮箱或是他们的推特给他们发消息。看看你是否可以通过电话联系到他们。</p>
<p>要求他们尽可能坦诚。向他们保证，他们与你分享的一切都是保密的。</p>
<p>告诉他们你的情况。告诉他们这个决定对你来说有多么重要。</p>
<p>我推荐像这样联系几位校友。</p>
<p>这是对于训练营的调查步骤中很困难的一部分。你或许会想“我是一个内向的人”或“我不想打扰这些忙碌的人”。</p>
<p>但是这些人就是你一年后的处境。他们是了解你对这次训练营的期望的最佳窗口。</p>
<p>如果你最终去了训练营，这些人就会成为你的校友。这是一个让他们成为你导师的机会。</p>
<h1 id="">有没有免费的训练营替代品？</h1>
<p>有很多方法可以让你免费学习编程。其中一些方法已经存在了几十年，比如当地图书馆的计算机区。</p>
<p>其他的免费资源可以帮助初学者提升他们的技能，并被聘为开发者。</p>
<p>大多数开发人员都认为自己至少部分是自学成才的。他们使用过各种各样的学习资源。</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/08/1_BDcBo7UJ86Br5O5J1Va_6A.jpeg" alt="1_BDcBo7UJ86Br5O5J1Va_6A" width="600" height="400" loading="lazy"></p>
<p>这是 Stack Overflow 年度的开发者调查图表。</p>
<h2 id="">什么是编码学习网站</h2>
<p>一些网站是可以在你的浏览器上学习编码。例如—— 像是 freeCodeCamp —— 完全免费的。</p>
<p>有些学习编码的网站需要收费。但你可以通过当地的图书馆免费使用它们。</p>
<p>这些网站涵盖了许多与编码训练营相同的概念和工具。</p>
<p>训练营专注于面对面教学。他们运营一个设有教室和教师的校园。</p>
<p>相比之下，学习编码网站采用教学设计，以低成本大规模地教授人们。</p>
<p>这些编码学习网站有论坛，甚至会有本地学习小组。</p>
<p>不过，许多人还是更喜欢训练营提供的传统课堂环境。</p>
<h2 id="">什么是“在线公开课”？</h2>
<p>在线公开课 (MOOCs) 是免费课程，通常是大学教授来教。这些课程往往以讲座形式进行，可能会有家庭作业或考试。</p>
<p>在线公开课在 2012 年开始流行，并且一直是一种很好的学习概念的方式。编程和计算机科学有数百的在线公开课——很多是自定进度的。</p>
<p>一些在线公开课也有提供认证，但你需要付费。</p>
<p>在这里需要注意的事情是你可以学习很多主题——直接从全世界的教授——根据你的需要。</p>
<p>当你考虑付费学习资源时，一定要记住这一点。它们需要满足很高的标准才能证明其成本是合理的。</p>
<h2 id="">有免费的编码教科书吗</h2>
<p>有数以千计的免费的编程和计算机科学的教科书。其中许多都是知识共享许可的，甚至是公共领域的。</p>
<p>开发人员还出售了一些书籍，并决定免费提供。</p>
<p>当然，一些开发商免费发行数字版书籍，并且出售实体副本。</p>
<h1 id="">有哪些付费训练营的替代方案？</h1>
<p>当前，如果你有钱，你可以好好利用它。有很多职业培训选择可以考虑。</p>
<h2 id="">我应该回大学吗？</h2>
<p>如果你已经有了大学学位，你或许不应该回大学。</p>
<p>是的，有计算机科学硕士课程。但是这是设计给正在工作的开发者进一步的扩展他们的技能。</p>
<p>我不推荐报读本科的计算机科学课程来作为第二学位。这需要花一些年来额外学习。并且很多本科计算机科学课程更多专注的是数学和概念性的知识，而不是动手编码。</p>
<p>编码训练营是进行编码练习的更快方式。它们可以帮助您建立作为开发人员所需的概念基础。</p>
<h2 id="">夜间课程能帮助我学习编码吗</h2>
<p>你或许在你的社区能找到课程，帮助成年人在夜间学习计算机技能的项目。</p>
<p>查看你本地的社区学院，图书馆，和成人教育课程。看他们是否教授软件开发课程。</p>
<p>注意，很多课程关注的是很基础的计算机技能。你或许不需要操作系统，电子表格，或是打字课程。</p>
<p>在你报名参加任何课程之前，问他们有关的校友，哪些是现在在从事开发者的工作。如果他们不能提供，这课程对于你来说太基础了。</p>
<h2 id="">我能雇佣导师来帮助我吗</h2>
<p>一些开发者会兼职当导师。你可以在在线分类广告中找到他们。</p>
<p>也有一些网站专门为学生配对在线导师。</p>
<p>这会很贵。但是如果你能自学编码，这或许对于你来说也是一个好的选择。你只需花费训练营费用的一小部分，就能享受每周辅导课程的好处。</p>
<h1 id="">训练营的未来会怎样？</h1>
<p>第一个编程训练营成立还不到十年。这仍然是一个年轻的行业。</p>
<p>尽管如此，该行业正在整合。一些训练营连锁店已被传统的营利性教育公司收购。教科书公司、营利性大学系统——甚至还有一家共享办公空间初创公司。</p>
<p>一些连锁训练营已经倒闭。</p>
<p>但这些失败并不代表编程训练营模式存在根本缺陷。目前仍有数百个训练营在蓬勃发展。而且开发人员一直在开设新的训练营。</p>
<p>但用于开办训练营的资金比用于资助学生债务的资金要少。并且我们会看到很多新的事物——“金融工程”领域。</p>
<blockquote>
<p>“在经济学中，货币研究是利用复杂性来掩盖真相或逃避真相，而不是揭示真相的研究，这比经济学中所有其他领域都要重要。” —— John Kenneth Galbraith ，哈佛大学教授，1975</p>
</blockquote>
<p>很难预言训练营会发生些什么。这里有一些可能的方向—— 其中一些对学生来说不太有利。</p>
<h2 id="">去训练营将会变得和上大学一样普遍吗</h2>
<p>就目前而言，训练营不会替代大学教育。它们会是一种补充。</p>
<p>很多训练营的学生已经从大学毕业了。他们职业生涯中报名参加训练营学习新的技能。</p>
<p>大学涵盖了很多训练营之外的内容。从英语写作到历史再到数学，无所不包。</p>
<p>编码训练营包括——好的——编码。他们也会教一些计算机科学概念和办公室软技能。</p>
<p>如果你看到一个训练营营销它自己能替代大学。这是一个巨大的危险信号。</p>
<p>我们有数个世纪的大学的数据。我们知道大学在提高你的终生收入方面有多有效。一个学士学位和一个对的专业会让你的收入翻两倍或三倍。</p>
<p>编码训练营很新。没有太多数据。而关于没有完成大学学业的训练营毕业生的数据就更少了。</p>
<p>不要将编码训练营视为大学的替代品，而应将其视为职业学院的替代品。</p>
<p>如果你不去学院。训练营比没有更好。而且你学到的技能可能比传统职业学校更有用。</p>
<p>在未来，或许编码训练营的确成为很多人会高中毕业后直接进入的地方。</p>
<p>但是我们首先需要更多的有效地数据。我不想送我的孩子进入训练营来替代大学，我也敦促你们也采取类似的谨慎态度。</p>
<p>另一方面，如果你还在读高中，并且正在读这篇文章，在这里我的建议是：去一所你能负担得起的、没有学生贷款的最好的大学。</p>
<p>在你申请学生贷款之前，先了解一下社区大学。那里也有经认可的超低成本的大学课程。你可以边工作边在网上学习很多课程。总而言之，不要相信他们的营销，自己研究。</p>
<h2 id="">训练营最终是否有资格获得联邦学生贷款？</h2>
<p>训练营很贵。没有多少人能负担的起。</p>
<p>训练营或许会尝试用一些大学的方式来补救。对联邦政府进行游说。</p>
<p>训练营或许能成功打开政府的金库。这就能得到学生补助贷款。训练营也可能针对退伍军人法案和其他为了帮助人们从大学毕业的项目。</p>
<p>美国持有$1.5万亿学生贷款债务。那是一百万亿美元。这意味着美国每个男人、女人和孩子都背负着 $16,000 美元。学生贷款债务是美国年轻人买不起房子的主要原因。</p>
<p>我们应该让训练营产生更多的学生债务吗？</p>
<p>这对于消费者来说是一场灾难。</p>
<p>但是不意味着这不会发生。</p>
<p>看看现在谁在管理美国教育部。一切都有可能，不管从长远来看会造成多大的破坏。</p>
<p>财务复杂性有其他解决方法。训练营可以找到新方法，让现金支付的人们更能负担得起。</p>
<h2 id="">训练营是否会合并为几家大的连锁机构</h2>
<p>在某些程度上已经发生。</p>
<p>训练营通过分担固定成本到多个校区来节省金钱。他们获得了规模经济和范围经济。</p>
<p>但是这些好处的收益是递减的。一些连锁机构倒闭了。其他的——不再能够独立运营——被教育集团收购。</p>
<p>任何操作都有合适的规模。目前尚不清楚编码训练营链的合适规模是多少。</p>
<p>一些最好的训练营只有一个地点。其他的训练营能够在多个校区保持高质量。他们的成功很大程度上归功于他们的领导能力。</p>
<h2 id="">训练营模式会扩展到法律和会计等非编码领域吗</h2>
<p>软件开发是一个独特的职业。它不受监管机构的约束。</p>
<p>其他领域有官僚机构的地方将人们拒之门外。例如在美国：</p>
<ul>
<li>要成为律师，你必须上 4 年大学，3 年的法学院，然后通过你所在州的律师资格考试获得美国律师协会的认证。</li>
<li>要成为会计，你必须上 4 年大学，参加一堆研究生水平的课程，通过一系列考试获得美国注册会计师协会的认证，然后再做一年低薪学徒。</li>
<li>要成为医生，你必须上 4 年大学，4 年的医药学院，然后作为低薪“住院医生”工作 3 至 7 年，然后通过你所在州的委员会认证考试获得美国医学协会的认证。</li>
</ul>
<p>如果不进行重大变革，训练营将不适用于这些受到严格监管的领域。</p>
<h2 id="">训练营是否会与传统大学课程合并来创造一种新种类的学院</h2>
<p>已经有一些项目从大学和训练营中汲取灵感。</p>
<p>也就是说，很多知名大学已经存在了几百年。转向更短的学习周期对他们来说很难。不过，奇怪的事情也曾发生过。</p>
<p>更可能的情况是：训练营（及其营利性教育集团母公司）购买已获认证的大学并重新命名，通过这种方式他们可以跳过漫长的认证流程。</p>
<p>需要说明的是，我反对将大学课程与编程训练营混为一谈。这是两种不同的教育模式，针对的是两类不同的学习者。</p>
<p>相反，我们需要新类型的教育机构。最好是低成本的、更加注重终身教育和在职培训的教育机构。</p>
<p>想象一下，一所学校有不间断的实习机会，在你期望工作的领域。你有足够的钱去生活，不需要背负债务。</p>
<p>在欧洲已经有了这样的课程。在美国这个地方——我们可以做梦，不是吗？</p>
<h1 id="">你最终的建议是什么</h1>
<p>你搜索开发者工作最好符合以下 3 点：</p>
<ol>
<li>你的技能</li>
<li>你的知名度</li>
<li>你的人脉</li>
</ol>
<p>不要错误的只关注于一点或者两点。想想如何同时构建三点。</p>
<p>参加训练营可能是你做过的最好决定。但也可能带来尴尬的财务挫折。</p>
<p>你去研究。存钱。先学习编码基础知识。</p>
<p>训练营不是魔法。他们不能替你去工作。</p>
<p>最终，经验取决于你如何利用它。所以要充分利用它。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 什么是端对端测试？什么时候使用它？ ]]>
                </title>
                <description>
                    <![CDATA[ 每个重要的应用都应该配备一些测试套件，以验证其稳定性和性能。 有许多类型的测试，每种测试都有自己的目的，涵盖应用的特定方面。因此，当你测试你的应用时，应确保各种测试的良好平衡。 但是有一种测试通常受到开发者的青睐，因此往往被过度使用，这是端对端测试 [https://www.freecodecamp.org/news/end-to-end-tests-with-selenium-and-docker-the-ultimate-guide/] （E2E）。 什么是端对端测试 对于那些仍在探索软件测试世界的人来说，E2E 测试是在完成应用程序及其所有依赖项时进行验证的过程。 在 E2E 测试中，你创建一个与真实用户将使用的环境相同的环境。然后测试用户可能在你的应用程序上执行的所有操作。 通过端到端测试，你可以测试整个流程，比如登录网站或从在线商店购买产品。 但是，如果你过度使用 E2E 测试，你就是在颠倒测试金字塔。我曾经遇到过这样的情况。在我的一个项目中，我计划用 E2E 测试覆盖大部分情况，甚至更糟糕的是，只使用 E2E 测试。幸运的是，我改变了主意。所以现在我想和 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/end-to-end-testing-tutorial/</link>
                <guid isPermaLink="false">64df2b0527eec403cd3f2809</guid>
                
                    <category>
                        <![CDATA[ 软件测试 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ herosql ]]>
                </dc:creator>
                <pubDate>Fri, 18 Aug 2023 08:30:39 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/08/iam_os-Gr7q7kqfnVU-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/end-to-end-testing-tutorial/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">What is End-to-End Testing and When Should You Use It?</a>
      </p><!--kg-card-begin: markdown--><p>每个重要的应用都应该配备一些测试套件，以验证其稳定性和性能。</p>
<p>有许多类型的测试，每种测试都有自己的目的，涵盖应用的特定方面。因此，当你测试你的应用时，应确保各种测试的良好平衡。</p>
<p><img src="http://media.giphy.com/media/UsmcxQeK7BRBK/giphy.gif" alt="giphy" width="500" height="300" loading="lazy"></p>
<p>但是有一种测试通常受到开发者的青睐，因此往往被过度使用，这是<a href="https://www.freecodecamp.org/news/end-to-end-tests-with-selenium-and-docker-the-ultimate-guide/">端对端测试</a>（E2E）。</p>
<h2 id="">什么是端对端测试</h2>
<p>对于那些仍在探索软件测试世界的人来说，E2E 测试是在完成应用程序及其所有依赖项时进行验证的过程。</p>
<p>在 E2E 测试中，你创建一个与真实用户将使用的环境相同的环境。然后测试用户可能在你的应用程序上执行的所有操作。</p>
<p>通过端到端测试，你可以测试整个流程，比如登录网站或从在线商店购买产品。</p>
<p><img src="https://paper-attachments.dropbox.com/s_EA4D61AC224EF8447071464ABC3123BDD99BABBB705D8D6423915F4DE15DDD1B_1603950228233_2_++1+.jpg" alt="s_EA4D61AC224EF8447071464ABC3123BDD99BABBB705D8D6423915F4DE15DDD1B_1603950228233_2_++1+" width="600" height="400" loading="lazy"></p>
<p>但是，如果你过度使用 E2E 测试，你就是在颠倒测试金字塔。我曾经遇到过这样的情况。在我的一个项目中，我计划用 E2E 测试覆盖大部分情况，甚至更糟糕的是，只使用 E2E 测试。幸运的是，我改变了主意。所以现在我想和你分享我学到的东西。</p>
<h2 id="">为什么你应该遵循测试金字塔</h2>
<p>乱七八糟地编写的测试起初可能看起来和感觉很正常，但最终总是令人痛苦的。</p>
<p>我们编写测试是为了节省更多时间，我们通过自动化测试来实现这一点。当然，我们可以自己打开应用程序并手动测试它们。如果我们只需要做一次，那么就没有问题。但这种情况很少见。</p>
<p>软件总是在不断更新。因此，你需要执行持续测试，以便随时掌握情况。你不可能在每次应用程序更新时都手动运行所有测试。如果你能编写一次测试套件，然后每次想要测试应用程序的某个方面时都运行它，你将节省大量时间。</p>
<p>每个测试都有其自己的目的。如果你超越了每种测试类型的界限，你的测试将开始对你造成伤害而不是帮助。这是因为你最终会花费更多的时间编写和维护测试，而不是开发应用程序本身。换句话说，你将失去自动化测试的最大优势之一。</p>
<p>一个好的起点是遵循测试金字塔。它可以帮助你找到测试的正确平衡。它代表了行业标准的指南，自2000年代中期以来一直持续，因为它仍然具有实用性。</p>
<p>那么这是否意味着开发人员总是遵循其指南？并非如此。有时金字塔看起来像一个倒置的金字塔，其中大部分测试都是 E2E。或者它看起来像一个沙漏，有很多单元测试和 E2E 测试，但没有很多集成测试。</p>
<p><img src="https://paper-attachments.dropbox.com/s_EA4D61AC224EF8447071464ABC3123BDD99BABBB705D8D6423915F4DE15DDD1B_1603950198553_02.jpg" alt="s_EA4D61AC224EF8447071464ABC3123BDD99BABBB705D8D6423915F4DE15DDD1B_1603950198553_02" width="600" height="400" loading="lazy"></p>
<h2 id="">测试金字塔的三层</h2>
<p>测试金字塔通常有三层：单元测试、集成测试和E2E测试。现在让我们详细了解它们。</p>
<h3 id="1">1. 单元测试</h3>
<p><a href="https://www.tutorialspoint.com/software_testing_dictionary/unit_testing.htm">单元测试</a>关注代码的最小单元，如函数或类。</p>
<p>它们简短且没有任何外部依赖。如果它们有外部依赖，你可以使用模拟对象代替。</p>
<p>如果一个单元测试失败，找到问题通常是一个简单的过程。它们的测试范围有限，使得编写、运行和维护变得简单、快速和容易。</p>
<h3 id="2">2. 集成测试</h3>
<p><a href="https://www.tutorialspoint.com/software_testing_dictionary/integration_testing.htm">集成测试</a>关注两个独立实体之间的交互。它们通常运行速度较慢，因为需要设置更多内容。</p>
<p>如果集成测试失败，找到问题会更具挑战性，因为失败范围更大。</p>
<p>编写和维护集成测试也相对困难，主要是因为它们需要更高级的模拟和更广泛的测试范围。</p>
<h3 id="3">3. 端对端测试</h3>
<p>最后，E2E 测试关注从最简单到最复杂的流程。它们可以被看成多步骤的集成测试。</p>
<p>这些测试运行速度最慢，因为它们涉及构建、部署、启动浏览器以及在应用程序中执行操作。</p>
<p>如果E2E测试失败，找到问题通常很困难，因为现在失败范围扩展到整个应用程序。基本上，在路径中，任何东西都可能出问题。</p>
<p>在这里考虑的三种测试类型中，它们无疑是最难编写和维护的，因为测试范围巨大，而且涉及整个应用程序。</p>
<p>希望你现在能明白为什么测试金字塔是这样设计的。从底层到顶层，每一层测试都代表着<strong>速度</strong>的降低以及<strong>范围、复杂性</strong>和<strong>维护需求</strong>的增加。</p>
<p>这就是为什么要记住的一件重要事情是，E2E测试不能取代其他方法——它是为了扩展它们。E2E测试的目的是定义明确的，测试不应超出这个范围。</p>
<p>理想情况下，测试应该尽可能地在金字塔的根部捕捉到错误。E2E测试的目的是验证按钮、表单、更改、链接、外部流程以及整个工作流程的功能是否正常运行。</p>
<h2 id="">用代码测试与无代码测试</h2>
<p>总的来说，有两种测试类型：手动测试和自动化测试。这意味着我们可以通过手动或使用脚本进行测试。</p>
<p>第二种方法是最常用的。但是自动化测试可以进一步分为两部分：<br>
<strong>用代码测试</strong> 和 <strong>无代码测试</strong>。</p>
<h3 id="">用代码测试</h3>
<p>当你使用代码进行测试时，你可以使用能够自动化浏览器的框架。最受欢迎的工具之一是 <a href="https://www.selenium.dev/">Selenium</a>，但我更喜欢并经常在我的项目中使用 <a href="https://www.cypress.io/">Cypress</a>（仅适用于 JavaScript）。然而，它们的工作方式大致相同。</p>
<p>基本上，使用这样的工具，你可以模拟网络浏览器并指示它们在目标应用程序上执行不同的操作。之后，你可以测试应用程序是否对相应的操作做出了响应。</p>
<p>下面是一个简单的模拟示例，摘自 Cypress 文档，以帮助你更好地了解这个工具的工作原理：</p>
<p><img src="https://paper-attachments.dropbox.com/s_EA8BC9D2CF83E24BF57AB3EC5A73F372F5ADA41ABD62DE1DA2D26BB58DE3CD82_1603530185695_carbon.png" alt="s_EA8BC9D2CF83E24BF57AB3EC5A73F372F5ADA41ABD62DE1DA2D26BB58DE3CD82_1603530185695_carbon" width="600" height="400" loading="lazy"></p>
<p><a href="https://docs.cypress.io/guides/getting-started/writing-your-first-test.html#Step-4-Make-an-assertion">上图是来自文档的原始代码</a></p>
<p>让我们看看发生了什么：</p>
<ol>
<li>假设用户访问 <a href="https://example.cypress.io">https://example.cypress.io</a></li>
<li>当他们点击标签为 “type” 的链接时，URL 应该包含 /commands/actions</li>
<li>如果他们在 .action-email 输入框中输入 “<a href="mailto:fake@email.com">fake@email.com</a>”，那么 .action-email 输入框的值应为 “<a href="mailto:fake@email.com">fake@email.com</a>”。</li>
</ol>
<h3 id="">无代码测试</h3>
<p>在<strong>无代码</strong>测试情况下，你使用由人工智能驱动的框架来记录你的操作。根据一些额外的信息，它们测试目标应用程序是否按预期响应。</p>
<p>这些工具通常看起来像低代码平台，你可以拖放不同的面板。其中一个工具是 <a href="https://www.testcraft.io/">TestCraft</a>，它是基于 Selenium 的 <strong>无代码</strong> 解决方案。</p>
<p><img src="https://paper-attachments.dropbox.com/s_EA8BC9D2CF83E24BF57AB3EC5A73F372F5ADA41ABD62DE1DA2D26BB58DE3CD82_1603531312592_ezgif-3-e3440d13da31.gif" alt="s_EA8BC9D2CF83E24BF57AB3EC5A73F372F5ADA41ABD62DE1DA2D26BB58DE3CD82_1603531312592_ezgif-3-e3440d13da31" width="600" height="400" loading="lazy"></p>
<p>由于它们提供的功能（如使用简单的拖放选项创建、维护和运行测试，无需编码知识），这类工具通常价格较高。但我想提一下 <a href="https://www.perfecto.io/blog">TestCraft</a> ，因为它们有一个基本包含所有内容的免费计划。</p>
<p>当然，如果你想要速度和省钱，无代码解决方案可能有优势，但这些解决方案很新。因此，它们还无法解决你自己通过代码才能解决的复杂测试。</p>
<p>如果目标应用程序具有一些非常复杂的流程，包括多个移动部件，那么传统的测试方法是最佳选择。但是，如果你有简单的流程，那么无代码解决方案正是你所需要的。</p>
<h2 id="">总结</h2>
<p>为任何应用编写测试是必不可少的。如果你遵循基本的原则并根据测试类型编写测试套件，那么你的测试将只会改善你的应用程序，并且编写和维护起来也相对容易。</p>
<p>你应该只在适当的场景下使用端到端测试，就像其他测试一样。它们的目的是通过模拟真实用户场景，从头到尾测试应用程序的工作流程。但最后，请记住，大多数错误应尽可能在根源处被捕获。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 什么是 SQL 注入以及如何防止 SQL 注入 ]]>
                </title>
                <description>
                    <![CDATA[ SQL 注入是指你通过客户端向应用程序输入数据来插入或注入一个 SQL 查询。 成功的攻击使攻击者能够访问数据库中的敏感数据，修改数据库数据，可能关闭数据库或发出其他管理员命令，恢复文件内容，以及偶尔向操作系统发出命令。 这种类型的攻击相对容易检测和利用，因此迅速修复任何易受攻击的系统尤为重要。 SQL 注入如何工作 当数据从不可信任的来源进入程序，并被用于动态构建 SQL 查询时，就会发生 SQL 注入。 因为 SQL 无法区分控制平面和数据平面，攻击者可以在数据输入中插入元字符（一种不被解释为数据的字符，例如下划线 _，在 SQL 中，它会被解释为单个字符的通配符），然后在控制平面中输入 SQL 命令。 例如，在下面的漫画中，如果字符串 Robert'); DROP TABLE Students;–- 被输入到一个请求 studentName 的查询中，那么查询将变为以下内容： AND studentName = 'Robert'; DROP TABLE Students; --' DROP TABLE 命令用于删除一个表及其所有行数据，而一对连字符（hyphens ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/what-is-sql-injection-how-to-prevent-it/</link>
                <guid isPermaLink="false">64d36a18c97d1206aa0f8ea3</guid>
                
                    <category>
                        <![CDATA[ SQL ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 网络安全 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ herosql ]]>
                </dc:creator>
                <pubDate>Thu, 10 Aug 2023 01:50:58 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/08/5fff3e0c98be260817e495da.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/what-is-sql-injection-how-to-prevent-it/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">SQL Injection Tutorial - What is SQL Injection and How to Prevent it</a>
      </p><!--kg-card-begin: markdown--><p>SQL 注入是指你通过客户端向应用程序输入数据来插入或注入一个 SQL 查询。</p>
<p>成功的攻击使攻击者能够访问数据库中的敏感数据，修改数据库数据，可能关闭数据库或发出其他管理员命令，恢复文件内容，以及偶尔向操作系统发出命令。</p>
<p>这种类型的攻击相对容易检测和利用，因此迅速修复任何易受攻击的系统尤为重要。</p>
<h2 id="sql">SQL 注入如何工作</h2>
<p>当数据从不可信任的来源进入程序，并被用于动态构建 SQL 查询时，就会发生 SQL 注入。</p>
<p>因为 SQL 无法区分控制平面和数据平面，攻击者可以在数据输入中插入元字符（一种不被解释为数据的字符，例如下划线 _，在 SQL 中，它会被解释为单个字符的通配符），然后在控制平面中输入 SQL 命令。</p>
<p>例如，在下面的漫画中，如果字符串 <code>Robert'); DROP TABLE Students;–-</code> 被输入到一个请求 studentName 的查询中，那么查询将变为以下内容：</p>
<pre><code>AND studentName = 'Robert';
DROP TABLE Students;
--'
</code></pre>
<p>DROP TABLE 命令用于删除一个表及其所有行数据，而一对连字符（hyphens）告诉大多数数据库服务器，该语句的剩余部分应被视为注释（这使得服务器可以忽略修改后的查询中留下的尾部）。</p>
<p><img src="https://megankaczanowski.com/content/images/2020/12/Screen-Shot-2020-12-30-at-6.23.14-PM.png" alt="Screen-Shot-2020-12-30-at-6.23.14-PM" width="600" height="400" loading="lazy"></p>
<p><a href="https://xkcd.com/327/">https://xkcd.com/327/</a></p>
<p>许多数据库服务器允许一次执行多个查询，只要它们用分号隔开。如果允许这样做，这种攻击类型就允许攻击者对数据库执行多个命令（有些数据库服务器，包括 Oracle，不允许这种执行方式）。</p>
<p>防止 SQL 注入实际上相当简单 - 要么不允许动态查询，要么阻止包含恶意 SQL 的用户输入影响查询逻辑。</p>
<h2 id="sql">其他类型的 SQL 注入</h2>
<p>还有一些其他类型的 SQL 注入需要注意：</p>
<h3 id="sql">基于错误的 SQL 注入</h3>
<p>攻击者依赖于数据库的详细错误信息来了解数据库结构。为了防止这种情况，只应显示通用错误信息。</p>
<h3 id="sql">盲注 SQL 注入</h3>
<p>当应用程序容易受到 SQL 注入攻击时，但只显示通用错误信息（而不是详细的错误信息或查询结果）。</p>
<p>一种访问信息的方法是使用 true/false 查询并一次提取一个问题的信息。另一种选择是发送一个命令，要求数据库在返回响应之前等待特定的时间。</p>
<p>根据数据库返回错误信息所需的时间长短，攻击者可以推断命令是返回 true 还是 false。</p>
<h3 id="unionsql">UNION SQL 注入</h3>
<p>利用 UNION 操作符从数据库中的多个表中检索数据。</p>
<h3 id="sql">带外 SQL 注入</h3>
<p>相对罕见，但当攻击者无法在提交命令的同一通道中接收到响应时会发生。</p>
<p>相反，它依赖于服务器使用另一种协议（如 HTTP 或 DNS）来向攻击者的查询传递响应的能力。</p>
<h2 id="sql">如何防止 SQL 注入攻击</h2>
<h3 id="">预处理语句（带参数化查询）</h3>
<p>参数化查询要求开发者定义所有 SQL 代码并稍后将每个参数传递给查询。然后，数据库可以区分代码和数据，而不受用户输入的影响。</p>
<p>例如，如果攻击者输入名字  <code>Robert'); DROP TABLE Students;–-</code> ，参数化查询将不再容易受到攻击，而是寻找一个与整个字符串 <code>Robert'); DROP TABLE Students;–-</code> 完全匹配的名字。</p>
<p>预处理语句的优点是 SQL 代码保留在应用程序内，使其（大部分）独立于数据库。</p>
<p>在极少数情况下，这可能会影响性能。如果确实如此，开发者需要验证所有数据，或者使用针对数据库的转义程序转义所有用户提供的输入。</p>
<h3 id="">存储过程</h3>
<p>存储过程是带参数的预创建 SQL 语句，不包括任何动态 SQL 生成（可以做，但不应该做）。为了设置存储过程，开发者需要为所需的输入构建带参数的 SQL 语句。</p>
<p>存储过程和预处理语句之间的区别在于，存储过程是在数据库中定义和存储的，但是从应用程序中调用的。</p>
<p>此外，由于存储过程在某些 DBMS 中需要执行权限（默认情况下不可用），因此创建一个具有最少权限的单独帐户而不是授予所有者访问权限非常重要。</p>
<h3 id="">白名单输入验证</h3>
<p>白名单输入验证将外部输入与一组已知的、批准的输入进行比较，对于不匹配的输入将失败。这只应用于不允许绑定变量的情况（SQL 语句中实际值的占位符）。</p>
<p>白名单输入验证还可以作为在将输入传递给查询之前检测输入的备选方案。</p>
<h3 id="">转义所有用户提供的输入</h3>
<p>只有在前面的选项不可行时，才应使用此方法，因为它无法防止所有 SQL 注入。仅对无法重写以使用前面推荐方法的遗留代码使用它。遗憾的是，这是一个非常针对特定数据库的实现。</p>
<p>每个 DBMS 都支持字符转义方案。如果使用正确的方案转义所有用户输入，DBMS 将能够区分输入和开发者编写的 SQL 代码。</p>
<h3 id="">最小权限</h3>
<p>最小权限不是防御 SQL 注入的手段，而是一种限制任何攻击可能造成的损害的方法。</p>
<p>确保应用程序帐户只拥有所需的权限，而不是更多，可能会令人沮丧（给予它们 DBA 或管理员权限肯定更容易，但会提供更多的攻击面）。</p>
<p>与其剥夺帐户的访问权限，不如从头开始，只授予确切需要的访问权限。</p>
<p>例如，如果一个帐户只需要只读访问权限，请确保它只对所需的表（甚至是表的一部分）具有只读访问权限。如果可以的话，避免授予数据库帐户创建或删除访问权限。每个用户/应用程序应该有一个单独的帐户。</p>
<p>此外，审查数据库管理系统（DBMS）运行所在的操作系统帐户的权限。默认情况下，许多帐户具有非常强大的权限 - 将其更改为更合适的权限。</p>
<h3 id="">来源/进一步阅读</h3>
<ul>
<li><a href="https://owasp.org/www-community/attacks/SQL_Injection">OWASP SQL Injection</a></li>
<li><a href="https://portswigger.net/web-security/sql-injection">PortSwigger SQL Injection</a></li>
<li><a href="https://www.netsparker.com/blog/web-security/sql-injection-cheat-sheet/">Netsparker SQL Injection</a></li>
</ul>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 如何使用 TDD 和 React 测试库构建健壮的 React 应用程序 ]]>
                </title>
                <description>
                    <![CDATA[ 在我开始学习 React 时，我曾经挣扎于如何以一种既有用又直观的方式测试我的 web 应用。每次我想要测试一个组件时，我都会使用 Enzyme [http://airbnb.io/enzyme/docs/api/] 和 Jest [https://facebook.github.io/jest/]  进行表层渲染。 当然，我绝对是在滥用快照测试功能。 好吧，至少我写了一个测试，对吧？ 你可能在某个地方听说过，编写单元测试和集成测试将提高你编写的软件的质量。另一方面，糟糕的测试会滋生虚假的信心。 最近，我参加了 workshop.me [https://workshop.me/] 上由 Kent C. Dodds [https://www.freecodecamp.org/news/how-to-build-sturdy-react-apps-with-tdd-and-the-react-testing-library-47ad3c5c8e47/undefined]  主持的一个研讨会，他教我们如何为 React 应用编写更好的集成测试。 他还指导我们使用他的新的测试库 [h ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-to-build-sturdy-react-apps-with-tdd-and-the-react-testing-library/</link>
                <guid isPermaLink="false">64cba65db05f0606c70fb7c9</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 测试 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ herosql ]]>
                </dc:creator>
                <pubDate>Wed, 02 Aug 2023 13:10:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/08/1691068282139.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/how-to-build-sturdy-react-apps-with-tdd-and-the-react-testing-library-47ad3c5c8e47/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to build sturdy React apps with TDD and the React Testing Library</a>
      </p><!--kg-card-begin: markdown--><p>在我开始学习 React 时，我曾经挣扎于如何以一种既有用又直观的方式测试我的 web 应用。每次我想要测试一个组件时，我都会使用 <a href="http://airbnb.io/enzyme/docs/api/">Enzyme</a> 和 <a href="https://facebook.github.io/jest/">Jest</a> 进行表层渲染。</p>
<p>当然，我绝对是在滥用快照测试功能。</p>
<p>好吧，至少我写了一个测试，对吧？</p>
<p>你可能在某个地方听说过，编写单元测试和集成测试将提高你编写的软件的质量。另一方面，糟糕的测试会滋生虚假的信心。</p>
<p>最近，我参加了 <a href="https://workshop.me/">workshop.me</a> 上由 <a href="https://www.freecodecamp.org/news/how-to-build-sturdy-react-apps-with-tdd-and-the-react-testing-library-47ad3c5c8e47/undefined">Kent C. Dodds</a> 主持的一个研讨会，他教我们如何为 React 应用编写更好的集成测试。</p>
<p>他还指导我们使用他的<a href="https://github.com/kentcdodds/react-testing-library">新的测试库</a>，以强调以用户可能遇到的相同方式测试应用程序。</p>
<p>在本文中，我们将通过创建评论反馈来学习如何在构建稳定的 React 应用程序中运用 TDD。当然，这个过程适用于几乎所有的软件开发，而不仅仅是 React 或 JavaScript 应用。</p>
<h3 id="">开始</h3>
<p>我们将首先运行 <code>create-react-app</code> 并安装依赖项。我的假设是，如果你正在阅读关于测试应用程序的文章，你可能已经熟悉安装和启动 JavaScript 项目。在这里，我将使用 yarn 而不是 npm。</p>
<pre><code class="language-plain">create-react-app comment-feed
</code></pre>
<pre><code class="language-plain">cd comment-feed
</code></pre>
<pre><code class="language-plain">yarn
</code></pre>
<p>首先，我们将删除 src 目录中除 index.js 之外的所有文件。然后，在 src 文件夹内部，创建一个名为 components 的新文件夹和另一个名为 containers 的文件夹。</p>
<p>在测试工具方面，我将使用 Kent 的 <a href="https://github.com/kentcdodds/react-testing-library">React 测试库</a> 构建此应用程序。它是一款轻量级的测试工具，鼓励开发者以实际使用时相同的方式测试他们的应用程序。</p>
<p>与 Enzyme 一样，它导出一个渲染函数，但这个渲染函数始终对你的组件进行完整挂载。它导出辅助方法，允许你通过标签、文本甚至测试 ID 来定位元素。Enzyme 也通过其 <code>mount</code> API 实现了这一点，但它创建的抽象实际上提供了更多选项，其中许多选项允许你摆脱测试实现细节。</p>
<p>我们不想要测试所有的实现细节。我们想要渲染一个组件，看看当点击或更改 UI 上的某些内容时是否会发生正确的事情。就是这样！不再直接检查 props 、state 或类名。</p>
<p>现在让我们安装它们并开始工作。</p>
<pre><code class="language-plain">yarn add react-testing-library
</code></pre>
<h3 id="tdd">通过 TDD 构建评论反馈</h3>
<p>让我们以 TDD 风格进行第一个组件的开发。启动你的测试运行器。</p>
<pre><code class="language-plain">yarn test --watch
</code></pre>
<p>在 <code>containers</code> 文件夹中，我们将添加一个名为 CommentFeed.js 的文件。与之相伴的，添加一个名为 CommentFeed.test.js 的文件。在第一个测试中，让我们验证用户是否可以创建评论。太早了？好吧，既然我们还没有任何代码，我们将从一个较小的测试开始。检查一下是否可以渲染反馈。</p>
<h3 id="reacttestinglibrary">关于 react-testing-library 的一些说明</h3>
<p>首先，让我们注意这里的渲染函数。它类似于 <code>react-dom</code> 将组件渲染到 DOM 的方式，但它返回一个对象，我们可以解构该对象以获得一些实用的测试辅助工具。在这种情况下，我们得到 <code>queryByText</code>，它会返回我们期望在 DOM 上看到的 HTML 元素。</p>
<p><a href="https://github.com/kentcdodds/react-testing-library#faq">React 测试库文档</a>提供了一个层次结构，帮助你决定使用哪个查询或获取方法。通常，顺序如下：</p>
<ul>
<li><code>getByLabelText</code>（表单输入）</li>
<li><code>getByPlaceholderText</code>（仅在你的输入没有标签时使用 — 很少使用！）</li>
<li><code>getByText</code>（按钮和标题）</li>
<li><code>getByAltText</code>（图片）</li>
<li><code>getByTestId</code>（用于动态文本或其他你想要测试的奇怪元素）</li>
</ul>
<p>每个方法都有一个相关的 <code>queryByFoo</code>，除了在找不到元素时不会使测试失败之外，它们的功能相同。如果你只是测试元素的<strong>存在</strong>，请使用这些方法。</p>
<p>如果这些方法都无法满足你的需求，<code>render</code> 方法还返回映射到 <code>container</code> 属性的 DOM 元素，因此你可以像 <code>container.querySelector(‘body #root’)</code> 这样使用它。</p>
<h3 id="">首次实现代码</h3>
<p>现在，实现看起来相当简单。我们只需要确保“评论反馈”是一个组件。</p>
<p>它可能会更糟糕 - 我的意思是，我在编写整篇文章的过程中，还要编写组件的样式。幸运的是，测试并不太关心样式，所以我们可以专注于应用逻辑。</p>
<p>接下来的测试将验证我们是否可以渲染评论。但是我们甚至还没有任何评论，所以也添加一个组件，在测试之后添加。</p>
<p>我还将创建一个 props 对象来存储我们可能在这些测试中重用的数据。</p>
<p>在这种情况下，我正在检查评论的数量是否等于传入 CommentFeed 的项目数量。这是无关紧要的，但测试失败给了我们创建 Comment.js 文件的机会。</p>
<p>这为我们的测试套件亮起了绿灯，我们就可以放心地继续了。向 TDD 致敬。当我们给它一个空数组时，它当然会工作。但是，如果我们给它一些真实的对象，会发生什么呢？</p>
<p>我们必须更新实现以实际渲染内容。现在我们知道要去哪里，这很简单，对吧？</p>
<p>看看这个，我们的测试再次通过了。这是一个美妙的截图。</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*vGkFKnUkA9ms5PbaOWoQ_A.png" alt="1*vGkFKnUkA9ms5PbaOWoQ_A" width="800" height="458" loading="lazy"></p>
<p>注意我从未说过我们应该用 <code>yarn start</code> 启动程序吗？我们继续保持这种方式一段时间。关键是，你必须用心去感受代码。</p>
<p>样式只是外部表现 - 重要的是内部的东西。</p>
<p>以防你想启动应用程序，将 index.js 更新为以下内容：</p>
<h3 id="">添加评论表单</h3>
<p>这是事情开始变得更有趣的地方。这是我们从困倦地检查 DOM 节点的存在到实际使用它并<strong>验证行为</strong>的地方。所有其他的东西都是热身。</p>
<p>让我们从描述我想要的表单开始。它应该：</p>
<ul>
<li>包含一个作者的文本输入</li>
<li>包含一个评论条目的文本输入</li>
<li>有一个提交按钮</li>
<li>最终调用 API 或处理创建和存储评论的其他服务。</li>
</ul>
<p>我们可以在一个集成测试中完成这个列表。对于之前的测试用例，我们进行得相当缓慢，但现在我们要加快速度，一举完成。</p>
<p>注意我们的测试套件是如何发展的吗？我们从在各自的测试用例中硬编码 props 转变为为它们创建一个工厂。</p>
<h4 id="">准备、执行、断言</h4>
<p>以下集成测试可以分为三个部分：准备，执行和断言。</p>
<ul>
<li><strong>准备：</strong> 为测试用例创建props和其他测试用例</li>
<li><strong>执行：</strong> 模拟对元素的更改，例如文本输入或按钮点击</li>
<li><strong>断言：</strong>  断言所需的函数被正确次数调用，并使用正确的参数</li>
</ul>
<p>关于代码，我们做了一些假设，比如我们的标签命名或我们将拥有一个 <code>createComment</code> prop。</p>
<p>在查找输入时，我们希望尝试通过它们的标签找到它们。这样在构建应用程序时，可以优先考虑可访问性。使用 <code>container.querySelector</code> 是获取表单的最简单方法。</p>
<p>接下来，我们必须为输入分配新值，并模拟更改以更新它们的状态。这一步可能感觉有点奇怪，因为通常一次输入一个字符，为每个新字符更新组件的状态。</p>
<p>这个测试的行为更像是复制/粘贴的行为，从空字符串变为 “Socrates” 。目前没有中断问题，但我们可能需要注意一下，以防以后出现问题。</p>
<p>在提交表单后，我们可以对诸如调用了哪些 props 以及使用了哪些参数等事项进行断言。我们还可以利用这个时刻来验证表单输入是否已清除。</p>
<p>这让人望而生畏吗？不要害怕。首先将表单添加到渲染函数中。</p>
<p>我可以将这个表单分解成一个单独的组件，但现在我会保持不变。相反，我会将其添加到我桌子旁边的“重构愿望清单”中。</p>
<p>这是 TDD 的方式。当某件事看起来可以重构时，做个记录然后继续。只有在抽象的存在对你有益且不感到多余时才进行重构。</p>
<p>还记得我们通过创建 <code>createProps</code> 工厂来重构测试套件的时候吗？就像那样。我们也可以重构测试。</p>
<p>现在，让我们添加 <code>handleChange</code> 和 <code>handleSubmit</code> 类方法。当我们更改输入或提交表单时，这些方法会被触发。我还将初始化状态。</p>
<p>这样做就可以了。我们的测试通过了，我们有一些类似于真实应用程序的东西。我们的覆盖率看起来如何？</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Q4coAIT2yaP120pDWGxoAQ.png" alt="1*Q4coAIT2yaP120pDWGxoAQ" width="800" height="564" loading="lazy"></p>
<p>还不错。如果我们忽略 index.js 中的所有设置，我们就有一个完全覆盖的 Web 应用程序，至少在执行的行数方面是这样。</p>
<p>当然，为了验证应用程序是否按照我们的意图工作，我们可能还需要测试其他用例。覆盖率的数字只是你的老板在谈论其他同事时可以炫耀的东西。</p>
<h3 id="">点赞评论</h3>
<p>如何检查我们可以点赞评论呢？这可能是在我们的应用程序中建立某种身份验证概念的好时机。但我们现在还不会跳得太远。首先，让我们更新 props 工厂，为生成的评论添加一个 <code>auth</code> 字段和 ID。</p>
<p>“认证”用户将通过应用程序传递其 <code>auth</code> 属性。与他们是否经过身份验证相关的任何操作都将被记录。</p>
<p>在许多应用程序中，此属性可能包含在向服务器发出请求时发送的某种访问令牌或 cookie 中。</p>
<p>在客户端，此属性的存在使应用程序知道它们可以让用户查看其个人资料或其他受保护的路由。</p>
<p>然而，在这个测试示例中，我们不会过多地处理身份验证。想象这样一个场景：当你进入聊天室时，你提供网名。从那时起，你将负责使用此网名的每个评论，尽管其他人也使用同样的名称登录。</p>
<p>虽然这不是一个很好的解决方案，即使在这个人为的例子中，我们只关心 CommentFeed 组件的行为是否符合预期。我们不关心用户<strong>如何</strong>登录。</p>
<p>换句话说，我们可能有一个完全不同的登录组件来处理特定用户的身份验证，以获得让用户在我们的应用程序中大显身手的全能 <code>auth</code> 属性。</p>
<p>让我们“喜欢”一条评论。添加下一个测试用例，然后更新 props 工厂以包含 <code>likeComment</code> 。</p>
<p>现在，对于实现，我们将首先更新 Comment 组件，使其具有一个点赞按钮以及一个 <code>data-testid</code> 属性，以便我们可以找到它。</p>
<p>我将测试 ID 直接放在按钮上，以便我们可以立即模拟对它的点击，而无需嵌套查询选择器。我还在按钮上附加了一个 onClick 处理程序，以便它传递给它的 onLike 函数。</p>
<p>现在我们只需将此类方法添加到我们的 CommentFeed。</p>
<p>你可能想知道为什么我们不直接将 <code>likeComment</code> 通过 prop 传递给 Comment 组件。为什么我们要将其作为类属性？</p>
<p>在这种情况下，因为它相当简单，我们不必构建这个抽象。将来，我们可能会决定添加其他 onClick 处理程序，例如处理分析事件或启动对该帖子评论的订阅。</p>
<p>在此容器组件的 <code>handleLike</code> 方法中捆绑多个不同的函数调用具有其优势。如果我们愿意，在成功“点赞”后，我们还可以使用此方法更新组件的状态。</p>
<h3 id="">不喜欢评论</h3>
<p>到目前为止，我们已经有了渲染、创建和喜欢评论的工作测试。当然，我们还没有实现实际执行此操作的逻辑——我们没有更新存储或写入数据库。</p>
<p>你可能还注意到，我们正在测试的逻辑很脆弱，不太适用于真实世界的评论反馈。例如，如果我们尝试喜欢我们已经喜欢的评论，会发生什么？它会无限地增加喜欢的计数，还是会取消喜欢？我可以喜欢我自己的评论吗？</p>
<p>我将把组件的功能扩展留给你的想象，但一个好的开始是编写一个新的测试用例。这里有一个基于我们想要实现不喜欢已经喜欢的评论的假设。</p>
<p>请注意，我们正在构建的评论反馈允许我喜欢我自己的评论。谁会这样做？</p>
<p>我已经更新了 Comment 组件，添加了一些逻辑来确定当前用户是否喜欢该评论。</p>
<p>好吧，我有点作弊：在我们之前将 <code>author</code> 传递给 <code>onLike</code> 函数的地方，我改为 <code>currentUser</code> ，这是传递给 Comment 组件的 <code>auth</code> 属性。</p>
<p>毕竟，当其他人喜欢他们的评论时，评论的作者出现在那里是没有意义的。</p>
<p>我之所以意识到这一点，是因为我一直在努力编写测试。如果我只是偶尔编写代码，这一点可能会被我忽略，直到我的一位同事斥责我的无知。</p>
<p>但是这里没有无知，只有测试和随之而来的代码。确保更新 CommentFeed，以便它期望传递 <code>auth</code> 属性。对于 <code>onClick</code> 处理程序，我们可以省略传递 <code>auth</code> 属性，因为我们可以从父级的 <code>handleLike</code> 和 <code>handleDislike</code> 方法中获取 <code>auth</code> 属性。</p>
<h3 id="">总结</h3>
<p>希望你的测试套件看起来像一棵未点亮的圣诞树。</p>
<p>我们可以采取很多不同的方法，这可能会让人有点不知所措。每当你想到某个想法时，只需将其写下来，无论是在纸上还是在新的测试块中。</p>
<p>例如，假设你希望在一个单独的类方法中实现 <code>handleLike</code> 和 <code>handleDislike</code>，但你现在有其他优先事项。你可以通过在测试用例中编写文档来实现这一点：</p>
<p>这并不意味着你需要编写一个全新的测试。你也可以更新前两个案例。但关键是，你可以将测试运行器用作应用程序更加紧迫的“待办事项“列表。</p>
<h4 id="">有用的链接</h4>
<p>有一些很棒的内容涉及到大规模的测试。以下是一些特别启发了本文以及我自己实践的内容。</p>
<ul>
<li><a href="https://www.freecodecamp.org/news/how-to-build-sturdy-react-apps-with-tdd-and-the-react-testing-library-47ad3c5c8e47/undefined">Kent C. Dodds</a> 编写的<a href="https://blog.kentcdodds.com/introducing-the-react-testing-library-e3a274307e65">介绍 React 测试库</a>。了解这个测试库背后的哲学是个好主意。</li>
<li>Kostis Kapelonis 写的<a href="http://blog.codepipes.com/testing/software-testing-antipatterns.html">软件测试反模式</a>，一篇非常深入的文章，讨论了单元和集成测试。还有如何避免错误的方法。</li>
<li>Kent Beck 写的<a href="https://www.amazon.com/Test-Driven-Development-Kent-Beck/dp/0321146530">测试驱动开发的示例</a>。这是一本讨论 TDD 模式的实体书。它不太长，写作风格通俗易懂，便于理解。</li>
</ul>
<p>我希望这些能帮你处理测试问题。</p>
<p>想了解更多文章或机智的评论吗？如果你喜欢这篇文章，请给我一些掌声，并在 <a href="http://medium%5D%28https//medium.com/@iwilsonq">Medium</a>、<a href="https://github.com/iwilsonq">Github</a> 和 <a href="https://twitter.com/iwilsonq">Twitter</a> 上关注我！</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 写给开发者的 SEO 建议 ]]>
                </title>
                <description>
                    <![CDATA[ SEO（搜索引擎优化）对你的在线营销策略至关重要。它是优化网站和内容的过程，以帮助你从搜索引擎获得尽可能多的流量。 让你的网站在搜索引擎结果页面（SERPs）中排名靠前是将流量引导至你的网站并提高转化率的最快方法之一。 本文将介绍提升SEO并发展你的网站的前10个技巧。 如何创建内容销售策略 你想要创建内容销售策略 [https://en.wikipedia.org/wiki/Marketing_strategy]。这将帮助你提升排名并触达目标用户。 什么是内容销售策略？内容销售策略将把你的业务放在首位，并帮助你成为你所在领域的专家，但它需要大量的工作。 例如，考虑到关键词和搜索者的意图，公司的内容销售经理通常每周至少要发布一篇文章。 这听起来好像很多，但它是有回报的。你发布的内容要与读者相关，并表明公司关心他们。它与消费者建立信任，帮助他们与品牌保持联系。 最好的方式是根据你的业务创建特殊的内容，针对你的潜在客户，优化 SEO，并通过社交媒体分发，但不要忘记-一致性是最重要的。 为你想要发布的内容类型和发布频率制定计划。跟踪哪些有效，哪些无效，以便于你可以根据需要进行 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/tips-to-boost-your-seo/</link>
                <guid isPermaLink="false">64c1df751071b2066bd03c69</guid>
                
                    <category>
                        <![CDATA[ SEO ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ herosql ]]>
                </dc:creator>
                <pubDate>Thu, 27 Jul 2023 03:15:50 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2023/07/10-Tips-to-Boost-Your-SEO.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/tips-to-boost-your-seo/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">SEO Tips for Developers</a>
      </p><!--kg-card-begin: markdown--><p>SEO（搜索引擎优化）对你的在线营销策略至关重要。它是优化网站和内容的过程，以帮助你从搜索引擎获得尽可能多的流量。</p>
<p>让你的网站在搜索引擎结果页面（SERPs）中排名靠前是将流量引导至你的网站并提高转化率的最快方法之一。</p>
<p>本文将介绍提升SEO并发展你的网站的前10个技巧。</p>
<h2 id="">如何创建内容销售策略</h2>
<p>你想要创建内容<a href="https://en.wikipedia.org/wiki/Marketing_strategy">销售策略</a>。这将帮助你提升排名并触达目标用户。</p>
<p>什么是内容销售策略？内容销售策略将把你的业务放在首位，并帮助你成为你所在领域的专家，但它需要大量的工作。</p>
<p>例如，考虑到关键词和搜索者的意图，公司的内容销售经理通常每周至少要发布一篇文章。</p>
<p>这听起来好像很多，但它是有回报的。你发布的内容要与读者相关，并表明公司关心他们。它与消费者建立信任，帮助他们与品牌保持联系。</p>
<p>最好的方式是根据你的业务创建特殊的内容，针对你的潜在客户，优化 SEO，并通过社交媒体分发，但不要忘记-一致性是最重要的。</p>
<p>为你想要发布的内容类型和发布频率制定计划。跟踪哪些有效，哪些无效，以便于你可以根据需要进行调整。</p>
<p>以下是内容策略过程中要遵循的几个初始步骤：</p>
<ol>
<li>明确你的目标。你想要更多的流量还是更多的转换？你想让你的读者了解新功能，还是想教育他们？</li>
<li>找出哪些内容渠道最适合你的业务。创建内容不仅仅是发布一些文章。这也是关于分发它们的问题。在 LinkedIn、Reddit 或 Hacker News 等网站上共享你的内容。</li>
<li>研究人们在浏览你的解决方案时搜索的关键字，解决内容中的关键词，并迎合受众的痛点。</li>
</ol>
<h2 id="">怎么让你的网站在移动端更加友好</h2>
<p>你怎么知道你的网站对于移动端是否友好？</p>
<p>移动端友好不是唯一重要的事情。拥有一个快速的网站、响应式的设计、移动端友好的内容和导航也是至关重要的。</p>
<p>谷歌有一些工具可以用来检查你的网站对于移动端是否友好，以及其他测试速度的工具。</p>
<p>如果你的网站没有对移动端进行优化，你应该开始这样做：</p>
<ol>
<li>保证你的网站没有全负荷运行一些插件，如果你使用 WordPress ，很多插件会拖慢你网站的速度。</li>
<li>为图片和多媒体使用适当的大小和格式。图片太大将会让你的网站更慢，并会对用户的体验产生不良的影响。</li>
<li>定期检查你的核心页面指标（Core Web Vitals）。确保你的网站能在两秒内加载，并且你的 CWV 指标在限制范围内。</li>
</ol>
<p>你的客户和 Google 将会感谢你，它将在流量和转化率方面带来红利。</p>
<h2 id="">如何创造拥有持续影响力的内容</h2>
<p>另外一个促进你的 SEO 是创建拥有持续影响力的内容。具有持续影响力的内容是指任何类型的信息在长远的时间中都具有适应性，意思是具有永不过时和适时性（不会过时的）。它也是经过搜索引擎优化的，适应移动端设备，有多种格式进行使用。</p>
<p>具有持续影响力的内容能够极好的帮助 SEO ，因为它会在搜索引擎中得到一个好的等级，这可以帮助你的网站在其他网站之上赢得关注。</p>
<p>在合适的时候，具有持续影响力的内容给你的生意提供了更多的机会，推销自己，而不必时间敏感的事件或新闻项目来吸引流量到你的网站。具有持续影响力的帖子经常更新，但总是关注一些新的或已经更新的旧的东西。</p>
<p>具有持续影响的内容的例子包括“如何开始一门生意”、“组织工作的重要性”和“你可以用剩下的意大利面做的五件事”。</p>
<p>像这样的内容永远不会失去价值，因为它不依赖于当前事件或趋势来保持相关性。正因为此，你可以构建有持续影响力的资料库，随着时间的推移，它将继续推动流量。</p>
<h2 id="">如何降低你的跳出率</h2>
<p>当每一个人进入你的网站，你需要想办法留住他们，低的跳出率对于谷歌来说表明你的内容是有效的，同时你会在搜索结果中的排名会高。</p>
<p>记住，这对于你的网站不仅仅是关于带来更多的流量，而且保持流量来源于好的SEO策略。</p>
<p>我知道你在想什么:怎么做可以减低我的跳出率。</p>
<p>这里有一些方法：</p>
<ul>
<li>减少与用户的摩擦。让你页面的内容容易阅读，让人愉快。改善你的导航栏，这样读者就不会迷路或是找不到他们想要的东西。去除烦人的弹窗，这会使用户在阅读你的内容时分心，甚至完全阻止用户访问内容。</li>
<li>在恰当的地方使用可视化的元素，如图片和视频。这可以使得文章阅读更加容易，更多的舒适感，让很长的文章看起来更短。</li>
<li>使用更短的章节和重点符号标记，这可以使得读者可以跳过他们不感兴趣的部分，从而阅读的很快。</li>
<li>在长文章中使用标题和副标题，使得读者在阅读整篇文章前获得关于每个章节的观点。</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/Screen-Shot-2022-04-21-at-3.41.52-PM.png" alt="GTmetrix报告显示，limey.io的评分为A，加载时间为552毫秒" width="600" height="400" loading="lazy"></p>
<p>GTmetrix 报告来自于 <a href="https://limey.io/">Limey</a>。</p>
<h2 id="">如何优化页面的速度</h2>
<p>或许你知道，优化页面加载速度并非易事。它需要经过几个步骤，可能具有一定的挑战性。如果这看起来难以应对，可以考虑雇佣一名 SEO 专家来帮助你识别 WordPress 或 Shopify 网站上的页面速度问题。</p>
<p>你可以采取的一些实际措施包括：</p>
<p>一些你可以做的步骤：</p>
<ul>
<li>减少未使用的 JavaScript</li>
<li>消除阻止渲染的资源</li>
<li>减少服务器初始响应时间</li>
</ul>
<h2 id="">如何开始客座博客写作</h2>
<p>客座博客（投稿）是一种很好的手段，用于保持你的网站内容新鲜和相关。让其他博主接受你提交的文章可以帮助你的网站吸引新的流量，并鼓励产生反向链接，从而提高你在搜索引擎的排名。</p>
<p>这可能会为你带来更多的流量、注册和销售。</p>
<p>客座发表文章还有助于品牌推广和触及新受众。</p>
<p>最后，你需要找到在你的领域接受投稿的博客，或者有一个“为我们写作”板块，你可以在那里提交你的内容。</p>
<p>获得接受并不容易，所以在作为客座博主推销自己时，一定要确保你写了一篇很棒的文章！</p>
<p>客座博客不仅适用于大型企业——如果你刚刚起步，有多种方法可以利用客座博客为你带来好处。,这是一种验证的方式,你可以用客座博客为你做广告。</p>
<p>首先，如果你刚刚起步并且尚未拥有大量关注者，客座博客是一种很好的方式来建立你的受众。那些已经进行客座博客多年的大公司每次发布新内容时都会吸引成千上万的新读者。</p>
<p>如果你的博客上有一些好的想法，并且你有动力向其他博客和网站推销自己，那么为什么不试一试呢？</p>
<p>此外，如果你的公司或产品背后有任何有趣的故事，这是另一种展示你独特之处的好方法。谁知道呢——你的故事可能会使你的博客文章脱颖而出，吸引人们关注。</p>
<h2 id="seo">如何优化页面的 SEO</h2>
<p>现在你已经掌握了一些基础知识，让我们来看看如何优化页面SEO。</p>
<ul>
<li>内容相关性：确保你的内容相关且最新。页面SEO的一个关键元素是拥有一个涵盖相关查询和用户可能提出的其他相关问题的页面。</li>
<li>标题标签：确保你的标题标签相关且准确。你的标题标签应该让用户在访问你的网站之前了解他们将要点击的内容，所以确保它准确地反映了页面的主题，并对搜索者有意义（它不需要与你搜索的内容完全相同）。</li>
<li>Meta 描述：不要忘记 meta 描述！虽然它不会直接影响排名，但meta描述仍然很重要，因为它们会出现在搜索结果页面（SERPs），如果正确优化，可以吸引用户点击。</li>
<li>标题：通过将内容分成具有清晰标题的部分来适当使用标题，尽可能包含关键词。</li>
<li>链接：链接到其他相关网站，并确保在可能的情况下进行内部链接，以帮助用户浏览你的网站（并包含带有上下文关键词的锚文本）。这也可以帮助提高页面权威。</li>
<li>图片和视频：有策略地使用图片和视频，使你的内容对不喜欢阅读长篇文字或更喜欢视觉效果的访问者更具吸引力。你还可以使用图片 alt 标签或视频字幕/文字记录（非自动化）来优化内容，因为谷歌也会抓取这些类型的媒体。</li>
</ul>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/04/ahrefs-wordpress-tips.png" alt="Ahrefs关键字浏览器显示搜索结果“WordPress提示”" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Ahrefs 关键词浏览器</figcaption>
</figure>
<h2 id="">如何为你的内容寻找合适的关键词</h2>
<p>关键词研究可以说是 SEO 中最重要的部分。正确的关键词可以使文章获得高排名，而错误的关键词可能导致文章完全被忽视。</p>
<p>在开始时，理解你在自然搜索流量方面的目标非常重要。你可以选择一个关键词策略来帮助你实现这些目标。</p>
<p>在为你的内容选择关键词时，请考虑以下几个因素：</p>
<ul>
<li>相关性：关键词与什么相关？选择与你的内容或产品紧密相关的关键词。</li>
<li>搜索量：如果你希望人们看到你的内容，请选择具有高搜索量的关键词（即很多人搜索的关键词）</li>
<li>独特性：独特的关键词可能比更普遍、常见的短语更难以成为你的竞争对手在自然搜索结果中的目标和排名。</li>
</ul>
<h2 id="seo">如何利用社交媒体提升 SEO 、流量和销售</h2>
<p>在当今的 SEO 环境中，社交媒体在获取自然流量方面发挥着重要作用。原因很简单：社交媒体网站是访问量最大的网站之一。</p>
<p>更重要的是，谷歌和其他搜索引擎使用社交信号来确定一个网站对于给定关键词的相关性。这就是为什么在 Facebook 、Pinterest 、Twitter 和其他社交媒体网站上分享你的内容可以帮助你在搜索结果中获得更高的排名。</p>
<p>值得注意的是，并非所有社交信号对 SEO 的影响都相同。有些信号比其他信号更能提升网站的权威，因此你需要明智地判断哪些信号对你的业务目标更有价值。</p>
<h2 id="">如何鼓励高质量的反向链接指向你的网站</h2>
<p>提高 SEO 的最有效方法之一是通过在社交网络上分享你的博客文章并鼓励影响者分享你的内容，从而鼓励高质量的反向链接指向你的网站。</p>
<p>但是什么是“反向链接”？</p>
<p>反向链接就是从其他网站指向你网站的链接。</p>
<p>例如，如果这个页面有一个指向社交媒体 Examiner 主页的链接，那就被认为是一个反向链接。反向链接至关重要，因为它们向谷歌表明其他人或企业认为你的网站足够重要和有价值，值得在他们的页面中包含。</p>
<p>你的网站获得的高质量（重要）链接越多，你在搜索引擎结果中的排名就越高。</p>
<p>获得高质量链接的一些好策略包括在行业网站上进行客座博客发表、与你所在行业的影响者进行访谈，以及询问客户/客户是否愿意在线发布你的服务评价。</p>
<p>一旦你获得了一些高质量的反向链接，就需要跟踪它们以确定它们在提高流量和排名方面的效果。你可以使用 Ahrefs 或 Semrush 等工具来实现这一目标。</p>
<h2 id="">如果你希望你的网站可以被搜索到，这里有一些技巧可以帮助你</h2>
<p>实现更高可见性的最佳方法是确保你的网站出现在搜索结果的第一页（最好是前三个位置）。</p>
<p>如果你的网站没有出现在第一页，潜在客户可能永远看不到它。事实上，如果你的网站从第二页移到第一页，你可以显著增加网站的流量。要做到这一点，你需要一个强大的 SEO 策略。</p>
<p>SEO 可能看起来是一个令人生畏的话题，但不必害怕。无论你是否已经熟悉 SEO ，上面的指南中可能有一些你可以立即开始实施的方法，以便更好地优化你的网站以适应搜索引擎，并提高潜在客户的用户体验。你只需开始行动，越早越好！</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Go 语言手册 ]]>
                </title>
                <description>
                    <![CDATA[ Golang 是一门非常棒的简洁且快速的现代编程语言。 它是编译的、开源的、强类型的。 Golang——也被称为 Go——由谷歌工程师创建，主要目标如下：  * 让他们的项目编译（和运行）更快  * 要简单，这样人们可以在短时间内学会  * 要足够底层，但也要避免过于关注底层  * 可移植（经过编译的 Go 程序不需要其他文件的支持，就可以跨平台运行，因此它们很轻易分发）  * 简洁而枯燥、稳定、可预测，从而减少犯错的机会  * 易于利用多处理器系统 Go 是为了取代 C 和 C++ 代码库。它旨在通过垃圾回收机制使并发和内存管理等事情变得更简单。 此外，由于其与 C 语言的互操作特性，可以在 C 和 C++ 代码库中使用 Go 语言。 你可以用 Go 做很多不一样的任务，它既可以解决简单的问题也可以解决复杂的问题。 你可以使用 Go 来创建命令行工具和网络服务器，它广泛用于许多不同的场景。 举个例子，Docker 和 K8s 是用 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/go-beginners-handbook/</link>
                <guid isPermaLink="false">6374f4897cdd940712f7c6b2</guid>
                
                    <category>
                        <![CDATA[ Golang ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ herosql ]]>
                </dc:creator>
                <pubDate>Wed, 16 Nov 2022 04:19:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/11/golang.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/go-beginners-handbook/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">The Go Handbook – Learn Golang for Beginners</a>
      </p><!--kg-card-begin: markdown--><p>Golang 是一门非常棒的简洁且快速的现代编程语言。</p>
<p>它是编译的、开源的、强类型的。</p>
<p>Golang——也被称为 Go——由谷歌工程师创建，主要目标如下：</p>
<ul>
<li>让他们的项目编译（和运行）更快</li>
<li>要简单，这样人们可以在短时间内学会</li>
<li>要足够底层，但也要避免过于关注底层</li>
<li>可移植（经过编译的 Go 程序不需要其他文件的支持，就可以跨平台运行，因此它们很轻易分发）</li>
<li>简洁而枯燥、稳定、可预测，从而减少犯错的机会</li>
<li>易于利用多处理器系统</li>
</ul>
<p>Go 是为了取代 C 和 C++ 代码库。它旨在通过垃圾回收机制使并发和内存管理等事情变得更简单。</p>
<p>此外，由于其与 C 语言的互操作特性，可以在 C 和 C++ 代码库中使用 Go 语言。</p>
<p>你可以用 Go 做很多不一样的任务，它既可以解决简单的问题也可以解决复杂的问题。</p>
<p>你可以使用 Go 来创建命令行工具和网络服务器，它广泛用于许多不同的场景。</p>
<p>举个例子，Docker 和 K8s 是用 Go 编写的。</p>
<p>我最喜欢的静态网站生成工具 Hugo 是用 Go 编写的。</p>
<p>Caddy，一个非常流行的 web 服务器是用 GO 编写的。</p>
<p>众多被广泛使用的工具都是基于这门语言创建的。</p>
<p>本手册将向你介绍 Go 编程语言，以便你开始使用 Go 编程。</p>
<p><a href="https://thevalleyofcode.com/download/go/">你可以点击链接获得 GO 初学者手册的 pdf 版本和 ePub 版本</a>。</p>
<h2 id="">目录</h2>
<ol>
<li><a href="#how-to-get-started-with-go">如何开始使用 Go</a></li>
<li><a href="#how-to-install-go">如何安装 Go</a></li>
<li><a href="#how-to-setup-your-editor">如何设置编辑器</a></li>
<li><a href="#how-to-write-hello-world-in-go">如何用 Go 编写 Hello, World!</a></li>
<li><a href="#how-to-compile-and-run-a-go-program">如何编译和运行 Go 程序</a></li>
<li><a href="#the-go-workspace">Go 的工作空间</a></li>
<li><a href="#diving-into-the-go-language">深入 Go 语言</a></li>
<li><a href="#variables-in-go">Go 中的变量</a></li>
<li><a href="#basic-types-in-go">Go 的基础类型</a></li>
<li><a href="#strings-in-go">Go 中的字符串</a></li>
<li><a href="#arrays-in-go">Go 中的数组</a></li>
<li><a href="#slices-in-go">Go 中的切片</a></li>
<li><a href="#maps-in-go">Go 中的 map</a></li>
<li><a href="#loops-in-go">Go 中的循环</a></li>
<li><a href="#conditionals-in-go">Go 中的条件语句</a></li>
<li><a href="#operators-in-go">Go 中的运算符</a></li>
<li><a href="#structs-in-go">Go 中的结构体</a></li>
<li><a href="#functions-in-go">Go 中的函数</a></li>
<li><a href="#pointers-in-go">Go 中的指针</a></li>
<li><a href="#methods-in-go">Go 中的方法</a></li>
<li><a href="#interfaces-in-go">Go 中的接口</a></li>
<li><a href="#where-to-go-from-here">更多内容</a></li>
</ol>
<h2 id="dividhowtogetstartedwithgogodiv"><div id="how-to-get-started-with-go">如何开始使用 Go</div></h2>
<p>在我们深入了解语言的细节之前，你应该了解以下几点。</p>
<p>首先， <a href="https://go.dev/">https://go.dev</a> 是语言的官网。 在官网你可以获得以下资源：</p>
<ul>
<li>下载 Go 的二进制文件（<code>go</code> 命令和其他相关工具）<a href="https://go.dev/doc/install">https://go.dev/doc/install</a></li>
<li>参考 Go 的官方文档 <a href="https://go.dev/doc/">https://go.dev/doc/</a></li>
<li>查看 Go 的所有第三方库 <a href="https://pkg.go.dev/">https://pkg.go.dev/</a></li>
<li>进入 Go 游乐园 <a href="https://go.dev/play/">https://go.dev/play/</a></li>
</ul>
<p>......等等。</p>
<h2 id="dividhowtoinstallgogodiv"><div id="how-to-install-go">如何安装 Go</div></h2>
<p>去 <a href="https://go.dev/doc/install">https://go.dev/doc/install</a> 下载适用于你电脑操作系统的软件包。</p>
<p>运行并安装，在最后一个步骤你需要在命令行设置 <code>go</code> 命令：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_10.19.21.png" alt="欢迎进行 Go 的安装" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>欢迎进行 Go 的安装</figcaption>
</figure>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_10.20.54.png" alt="安装成功" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>安装成功</figcaption>
</figure>
<p>打开命令行并运行 <code>go version</code> 你会看到以下内容：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_10.21.32.png" alt="展示你当前的 Go 版本" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>展示你当前的 Go 版本</figcaption>
</figure>
<p>注意： 在运行程序之前，你可能需要打开一个新的命令行，因为安装程序将 Go 二进制文件文件夹添加到了路径中。</p>
<p>Go 安装文件的确切位置取决于你的操作系统。</p>
<p>在 Mac 系统中，它在 <code>/usr/local/go</code> ， 运行文件在 <code>/usr/local/go/bin</code> 。</p>
<p>在 Windows 系统中，它在 <code>C:\Program Files\go</code>。</p>
<p>在 Windows 和 Mac 安装中 Go 执行文件路径都是自动设定的。</p>
<p>在 Mac 你可以在 Homebrew 中使用 <code>brew install golang</code> 命令安装。 这样方式更容易升级到最新版本。</p>
<p>在 Linux 上，你必须将 Go 二进制文件文件夹添加到你的环境变量中，然后才能在使用以下命令将 Linux 包解压缩到 <code>/usr/local/go</code>之后运行 <code>go</code> 命令：</p>
<pre><code class="language-bash">echo 'export PATH=$PATH:/usr/local/go/bin' &gt;&gt; $HOME/.profile
source $HOME/.profile
</code></pre>
<h2 id="dividhowtosetupyoureditordiv"><div id="how-to-setup-your-editor">如何设置编辑器</div></h2>
<p>我推荐使用 <a href="https://code.visualstudio.com/"><strong>Visual Studio Code</strong></a>（也叫 VS Code）作为你的编辑器。</p>
<p>请阅读<a href="https://code.visualstudio.com/docs/languages/go">在 Visual Studio Code 的 Go</a>，了解快速安装并运行的方法。安装<a href="https://marketplace.visualstudio.com/items?itemName=golang.go">Go 的扩展</a>。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_10.54.06.png" alt="VS Code 中的 Go 扩展" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>VS Code 中的 Go 扩展</figcaption>
</figure>
<p>这个扩展将让你的生活更加轻松。因为它提供智能感知（语法高亮显示、自动补全、悬停信息、错误高亮显示…）和其他功能，如自动格式化、安装软件包的菜单选项、测试等。</p>
<h2 id="dividhowtowritehelloworldingogohelloworlddiv"><div id="how-to-write-hello-world-in-go">如何用 Go 编写 Hello, World!</div></h2>
<p>现在我们准备创建我们第一个 Go 程序！</p>
<p>程序员的传统是让第一个程序打印 “Hello, World!” 字符串到命令行。所以我们先做这个，然后解释我们是如何做到的。</p>
<p>或许你可以在你主目录下创建一个文件夹,保存你所有编写的项目和测试。</p>
<p>在这，创建一个新的文件夹，比如取名叫 <code>hello</code>。</p>
<p>在这，创建一个叫 <code>hello.go</code> 的文件（你可以用任何想要用的名字）。</p>
<p>文件内容如下：</p>
<pre><code class="language-go">package main

import "fmt"

func main() {
	fmt.Println("Hello, World!")
}
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_12.17.14.png" alt="Go " hello,="" world!"="" 代码"="" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Go "Hello, World!" 代码</figcaption>
</figure>
<p>这是你编写的第一个 Go 程序！</p>
<p>让我们逐行分析一下。</p>
<pre><code class="language-go">package main
</code></pre>
<p>我们以包的形式组织 Go 程序。</p>
<p>每个<code>.go</code> 文件首先要声明它是哪个包的一部分。</p>
<p>一个包可以由多个文件组成，也可以仅由一个文件组成。</p>
<p>一个程序可以由多个包组成。</p>
<p>这个 <code>main</code> 是程序识别可执行程序的入口。</p>
<pre><code class="language-go">import "fmt"
</code></pre>
<p>我们使用 <code>import</code> 关键字导入包。</p>
<p><code>fmt</code> 是Go提供的内置包，提供输入/输出的工具函数。</p>
<p>我们有一个<a href="https://pkg.go.dev/std">大的标准库</a>，可以随时使用，从网络连接到数学、加密、图像处理、文件系统访问等等。</p>
<p>你可以在<a href="https://pkg.go.dev/fmt">官方文档</a>阅读 <code>fmt</code> 包提供的所有功能。</p>
<pre><code class="language-go">func main() {
	
}
</code></pre>
<p>这里我们声明 <code>main()</code> 函数。</p>
<p>什么是函数？稍后我们将看到更多关于它们的信息，但同时让我们假设一个函数是一个分配了名称的代码块，包含一些指令。</p>
<p><code>main</code> 函数是特殊的，因为这也是程序启动的地方。</p>
<p>在这个简单的例子中，我们只是有一个函数——程序从这个函数开始，然后结束。</p>
<pre><code class="language-go">fmt.Println("Hello， World!")
</code></pre>
<p>这是我们定义的函数的内容。</p>
<p>我们调用了我们之前导入的 <code>fmt</code> 包中 <code>Println()</code> 函数，将字符串作为参数传递。</p>
<p>根据<a href="https://pkg.go.dev/fmt#Printf">文档</a> “<em>根据格式指定器进行格式化并写入标准输出</em>”。</p>
<p>看看这些文档，因为它们很棒。它们甚至有你可以运行的示例：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_14.18.46.png" alt="Go 基础的函数模板" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Go 基础的函数模板</figcaption>
</figure>
<p>我们可以用 “dot” 语法 <code>fmt.Println()</code> 指定该函数由该包提供。</p>
<p>代码在执行 <code>main</code> 函数之后，它没有做其他的事就结束了执行。</p>
<h2 id="dividhowtocompileandrunagoprogramgodiv"><div id="how-to-compile-and-run-a-go-program">如何编译和运行 Go 程序</div></h2>
<p>现在在 <code>hello</code> 文件夹下打开命令行，用以下命令运行程序：</p>
<pre><code class="language-bash">go run hello.go
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_12.18.23.png" alt="Go 输出 Hello world" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Go 输出 Hello world</figcaption>
</figure>
<p>我们的程序运行成功，它会在命令行输出 “Hello, World!” 。</p>
<p><code>go run</code> 会先编译并运行程序。</p>
<p>你可以用 <code>go build</code> 创建一个<strong>可执行文件</strong>：</p>
<pre><code class="language-bash">go build hello.go
</code></pre>
<p>这里会创建一个名为 <code>hello</code> 的可执行文件，你可以执行：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_12.19.50.png" alt="执行 Go 的可执行文件" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>执行 Go 的可执行文件</figcaption>
</figure>
<p>在引言部分我提到过 Go 是可移植的。</p>
<p>现在你可以分发这个二进制文件，每个人都可以按原样运行程序，因为二进制文件已经打包好了，可以执行了。</p>
<p>该程序将在我们构建它的相同架构上运行。</p>
<p>我们可以使用 <code>GOOS</code> 和 <code>GOARCH</code> 环境变量为不同的架构创建不同的二进制文件，如下所示：</p>
<pre><code class="language-go">GOOS=windows GOARCH=amd64 go build hello.go
</code></pre>
<p>这将会创建 <code>hello.exe</code> 文件，可以在 64 位的 Windows 机器上运行：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_15.36.55.png" alt="Hello.exe 执行" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Hello.exe 执行</figcaption>
</figure>
<p>设置 64 位的 Mac 的环境变量为 <code>GOOS=darwin GOARCH=amd64</code> ，Linux 的环境变量是<code>GOOS=linux GOARCH=amd64</code>。</p>
<p>这是 Go 最好的特性之一。</p>
<h2 id="dividthegoworkspacegodiv"><div id="the-go-workspace">Go 的工作空间</div></h2>
<p>关于 Go 中的一个特殊的点，我们称为<strong>工作区</strong>。</p>
<p>这个工作区是 Go 中的“家目录”。</p>
<p>Go 默认的路径在 <code>$HOME/go</code> 下，所以你可以在你的家目录中看到 <code>go</code> 文件夹。</p>
<p>它会在你安装包（待会儿我们看一下）进行创建。</p>
<p>例如我在 VS Code 中加载 <code>hello.go</code> 文件那一刻，它提示我安装<code>[ gopls ](https://pkg.go.dev/golang.org/x/tools/gopls)</code> 命令，开发调试工具（<code>dlv</code>）， 和<a href="https://staticcheck.io/"><code>静态检查</code>行</a>。</p>
<p>它们在 <code>$HOME/go</code> 下自动安装：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_12.27.27.png" alt="`$HOME/go`" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>`$HOME/go`</figcaption>
</figure>
<p>当你使用 <code>go install</code> 安装库时，它们会保存在这里。</p>
<p>这就是我们所说的 <strong>GOPATH</strong>。</p>
<p>你可以修改环境变量 <code>GOPATH</code> 以更改 Go 安装包的位置。</p>
<p>这在同时处理不同的项目并且希望隔离所使用的库时非常有用。</p>
<h2 id="dividdivingintothegolanguagegodiv"><div id="diving-into-the-go-language">深入 Go 语言</div></h2>
<p>现在我们已经理解了第一部分，我们运行了第一个 Hello, World! 程序， 我们可以深入 Go 语言了。</p>
<p>该语言没有语义上有意义的空格。它像 C、C++、Rust、Java、JavaScript，但是不像 Python，其中空格是有意义的，用于创建块，而不是花括号。</p>
<p>分号是可选的，就像在 JavaScript 中一样（不同于 C、C++、Rust 或 Java）。</p>
<p>Go 非常重视缩进和视觉顺序。</p>
<p>在我们安装Go程序的时候自带了 <code>gofmt</code> 命令，我们可以用它对 Go 程序进行格式化。VSCode 中在底层使用它对 Go 源码文件进行格式化。</p>
<p>这是非常有趣和创新的，因为格式和问题，如制表符与空格或“我应该把花括号放在循环定义的同一行还是下一行”，都是浪费时间。</p>
<p>语言创造者定义了规则，每个人都使用这些规则。</p>
<p>这对于拥有大型团队的项目非常有用。</p>
<p>我推荐你在 VS Code 中设置“<strong>保存时格式化</strong>” 和“<strong>粘贴时格式化</strong>”：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_14.39.42.png" alt="在 VS Code 中设置 Go 的粘贴时格式化和保存时格式化" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>在 VS Code 中设置 Go 的粘贴时格式化和保存时格式化</figcaption>
</figure>
<p>你可以使用常用的 C / C++ / JavaScript / Java 语法在 Go 中写注释：</p>
<pre><code class="language-go">// this is a line comment

/*
multi
line
comment
*/
</code></pre>
<h2 id="dividvariablesingogodiv"><div id="variables-in-go">Go 中的变量</div></h2>
<p>在编程语言中，首先要做的事情之一是定义变量。</p>
<p>在 Go 中我们用 <code>var</code> 关键字声明变量：</p>
<pre><code class="language-go">var age = 20
</code></pre>
<p>你可以在包级别定义变量：</p>
<pre><code class="language-go">package main

import "fmt"

var age = 20

func main() {
	fmt.Println("Hello， World!")
}
</code></pre>
<p>或是在函数中：</p>
<pre><code class="language-go">package main

import "fmt"

func main() {
	var age = 20

	fmt.Println("Hello， World!")
}
</code></pre>
<p>在包级别定义，变量在组成包的所有文件中都可见。一个包可以由多个文件组成，你只需要创建另一个文件并在顶部使用相同的包名。</p>
<p>在函数级别定义，变量仅在该函数中可见。它在调用函数时初始化，在函数结束执行时销毁。</p>
<p>我们使用以下示例：</p>
<pre><code class="language-go">var age = 20
</code></pre>
<p>我们设置变量 <code>age</code> 的值为 <code>20</code>。</p>
<p>这使得 Go 确定变量 <code>age</code> 的<strong>类型</strong>是 <code>int</code>。</p>
<p>稍后我们将看到更多关于类型的信息，但你应该知道有很多不同的类型，从 <code>int</code>、<code>string</code> 和 <code>bool</code> 开始。</p>
<p>我们可以不给变量设置值，但是必须设置它们的类型如下：</p>
<pre><code class="language-go">var age int
var name string
var done bool
</code></pre>
<p>当你知道值时，你可以直接用短的变量声明如 <code>:=</code> 运算符：</p>
<pre><code class="language-go">age := 10
name := "Roger"
</code></pre>
<p>变量名你可以使用小写字母，数字和 <code>_</code> 或者可以使用类似于 <code>_</code> 的其他字符。</p>
<p>名字是<strong>区分大小写</strong>的。</p>
<p>如果名字太长，通常可使用驼峰命名法，例如我们想表现车的名字就用 <code>carName</code>。</p>
<p>你可以使用赋值运算符 <code>=</code> 给一个变量赋予新的值。</p>
<pre><code class="language-go">var age int
age = 10
age = 11
</code></pre>
<p>如果你有一个变量在编程过程中永远都不会变，你可以使用 <code>const</code> 关键字来声明这个变量：</p>
<pre><code class="language-go">const age = 10
</code></pre>
<p>你可以一行代码中声明多个变量：</p>
<pre><code class="language-go">var age， name
</code></pre>
<p>将它们全部都初始化：</p>
<pre><code class="language-go">var age， name = 10， "Roger"

//or

age， name := 10， "Roger"
</code></pre>
<p>在程序中使用未声明的变量，程序会报错，而且无法通过编译。</p>
<p>在 VS Code 中你可以看到警告如下：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_15.45.31.png" alt="使用未声明变量的警告" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用未声明变量的警告</figcaption>
</figure>
<p>以下是编译的报错：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_15.45.44.png" alt="使用未声明变量的编译器报错" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用未声明变量的编译器报错</figcaption>
</figure>
<p>如果你声明了一个变量且没有给这个变量一个初始值，它会自动初始化一个对应类型的初始值，例如 integer 类型的值为 <code>0</code> 而字符串的值是一个空的字符串。</p>
<h2 id="dividbasictypesingogodiv"><div id="basic-types-in-go">Go 的基础类型</div></h2>
<p>Go 是一门强类型语言。</p>
<p>我们可以看到你怎么定义一个变量，指定它的类型：</p>
<pre><code class="language-go">var age int
</code></pre>
<p>或者你可以直接给变量赋予初始值，让 Go 来推断它的类型：</p>
<pre><code class="language-go">var age = 10
</code></pre>
<p>这些是 Go 中的基础类型：</p>
<ul>
<li>整型（<code>int</code>， <code>int8</code>， <code>int16</code>， <code>int32</code>， <code>rune</code>， <code>int64</code>， <code>uint</code>， <code>uintptr</code>， <code>uint8</code>， <code>uint16</code>， <code>uint64</code>）</li>
<li>浮点型（<code>float32</code>， <code>float64</code>），用于表示带小数点的数</li>
<li>复数类型（<code>complex64</code>， <code>complex128</code>），常用于科学计算中</li>
<li>字符型（<code>byte</code>），表示一个 ASCII 字符</li>
<li>字符串（<code>string</code>）， 一个<code>byte</code>的集合</li>
<li>布尔型（<code>bool</code>）表示 true 或 false</li>
</ul>
<p>我们有很多不同类型的整数类型，在大多数情况下你只会用到 <code>int</code>，并且你可能会选择一个更专业的方法进行优化（而不是在学习时需要考虑的事情）。</p>
<p>在你使用 64 位系统的时候 <code>int</code> 类型默认为 64 位， 使用 32 位系统的时候 <code>int</code> 类型默认为32位，其他的与此类似。</p>
<p><code>uint</code> 类型是无符号的 <code>int</code> 类型，如果你知道这个数字不是负数，你可以用这个类型存储比现在大两倍的数字。</p>
<p>所有的基础类型都是<strong>值类型</strong>， 这意味着当它们作为参数传递或从函数返回时，它们通过<strong>值传递</strong>给函数。</p>
<h2 id="dividstringsingogodiv"><div id="strings-in-go">Go 中的字符串</div></h2>
<p>Go 中的字符串是 <code>byte</code> 序列。</p>
<p>像我们所看到的一样，你可以定义字符串如下：</p>
<pre><code class="language-go">var name = "test"
</code></pre>
<p>其中很重要一点是不像其他语言，字符串定义只能使用双引号表示，而不是单引号。</p>
<p>获得字符串的长度，可以使用内置函数 <code>len()</code>：</p>
<pre><code class="language-go">len(name) //4
</code></pre>
<p>你可以用方括号访问单独字符，使用索引来取得你想要的字符：</p>
<pre><code class="language-go">name[0] //"t" (indexes start at 0)
name[1] //"e"
</code></pre>
<p>你可以使用切片获取到字符串：</p>
<pre><code class="language-go">name[0:2] //"te"
name[:2]  //"te"
name[2:]  //"st"
</code></pre>
<p>你可以创建一个字符串的副本：</p>
<pre><code class="language-go">var newstring = name[:]
</code></pre>
<p>你可以将字符串赋值给一个新的变量，如下：</p>
<pre><code class="language-go">var first = "test"
var second = first
</code></pre>
<p>字符串是 <strong>不可变</strong> 的， 所以你无法修改字符串的值。</p>
<p>如果你给 <code>first</code> 赋予一个新值，<code>second</code> 的值依然是 <code>"test"</code>：</p>
<pre><code class="language-go">var first = "test"
var second = first

first = "another test"

first  //"another test"
second //"test"
</code></pre>
<p>字符串是引用类型，意味着如果你将一个字符串传递给一个方法，字符串<strong>引用</strong>会被复制，而不是它的值，但是字符串是不可变的，所在在使用过程中它和 <code>int</code> 类型并没有很大的区别，例如。</p>
<p>你可以通过 <code>+</code> 运算符连接两个字符串：</p>
<pre><code class="language-go">var first = "first"
var second = "second"

var word = first + " " + second  //"first second"
</code></pre>
<p>Go提供了 <code>strings</code> 库来进行字符串的操作。</p>
<p>我们已经知道怎么在 “Hello, World!” 的案例中导入一个包。</p>
<p>这里你可以导入 <code>strings</code>：</p>
<pre><code class="language-go">package main

import (
    "strings"
)
</code></pre>
<p>你可以使用它了。</p>
<p>在例子中我们使用 <code>HasPrefix()</code> 函数来判断一个字符串的开头是否是以另一个子串开头的：</p>
<pre><code class="language-go">package main

import (
    "strings"
)

func main() {
    strings.HasPrefix("test"， "te") // true
}
</code></pre>
<p>你可以在这找到所有的函数列表：<a href="https://pkg.go.dev/strings">https://pkg.go.dev/strings</a>。</p>
<p>以下是你经常会用到的函数列表：</p>
<ul>
<li><code>strings.ToUpper()</code> 返回一个新的字符串， 大写</li>
<li><code>strings.ToLower()</code> 返回一个新的字符串， 小写</li>
<li><code>strings.HasSuffix()</code> 检查是否以某子串结尾</li>
<li><code>strings.HasPrefix()</code> 检查是否以某子串开头</li>
<li><code>strings.Contains()</code> 检查是否包含某字串</li>
<li><code>strings.Count()</code> 计算一个某子串在当前字符串出现的次数</li>
<li><code>strings.Join()</code> 创建一个新的字符串并连接多个字符串</li>
<li><code>strings.Split()</code> 创建一个数组来保存通过特殊字符串对字符串进行分割的结果，例如通常使用空格</li>
<li><code>strings.ReplaceAll()</code> 使用替换，可以使用一个新的字符串替换掉原字符中的字符串</li>
</ul>
<h2 id="dividarraysingogodiv"><div id="arrays-in-go">Go 中的数组</div></h2>
<p>数组是单个类型的序列。</p>
<p>我们可以这种方式定义数组：</p>
<pre><code class="language-go">var myArray [3]string //an array of 3 strings
</code></pre>
<p>或者你也可以给数组赋予初始值：</p>
<pre><code class="language-go">var myArray = [3]string{"First"， "Second"， "Third"}
</code></pre>
<p>在这个例子中，你可以让 Go 来帮你进行数组长度的推断：</p>
<pre><code class="language-go">var myArray = [...]string{"First"， "Second"， "Third"}
</code></pre>
<p>数组只能包含同一种类型的数据。</p>
<p>数组不能动态扩容——在 Go 中你必须声明数组的长度。这和类型一样是数组的一部分。当然，你不能使用一个没有声明长度的数组。</p>
<p>由于这个限制，数组在 Go 中很少使用，我们经常用到<strong>切片</strong>（稍后我们会讲到更多）。切片的底层是数组。所以我们需要知道它的工作原理。</p>
<p>你可以通过中括号获得数组中的每一个值正如我们之前获取字符串中单个字符一样：</p>
<pre><code class="language-go">myArray[0] //indexes start at 0
myArray[1]
</code></pre>
<p>你可以给数组中的成员设置新的值：</p>
<pre><code class="language-go">myArray[2] = "Another"
</code></pre>
<p>你可以通过 <code>len()</code> 函数来获取数组的长度：</p>
<pre><code class="language-go">len(myArray)
</code></pre>
<p>数组是 <strong>值类型</strong>。 这意味着复制一个数组：</p>
<pre><code class="language-go">anotherArray := myArray
</code></pre>
<p>传递一个数组给一个函数，或者一个函数返回数组，创建的都是原数组的副本。</p>
<p>这也是和其他编程语言的不同之处。</p>
<p>这里创建一个简单的示例，我们将一个新的值赋值给副本的一个元素，这个过程中不会修改原数组的元素：</p>
<pre><code class="language-go">var myArray = [3]string{"First"， "Second"， "Third"}
myArrayCopy := myArray
myArray[2] = "Another"

myArray[2]     //"Another"
myArrayCopy[2] //"Third"
</code></pre>
<p>记住你只能在数组中加入同一类型的数据，所以在例子中设置 <code>myArray[2] = 2</code> 会报错。</p>
<p>底层的元素存储在连续的内存当中。</p>
<h2 id="dividslicesingogodiv"><div id="slices-in-go">Go 中的切片</div></h2>
<p>切片是一种很像数组的数据结构，但它的大小是可以改变的。</p>
<p>底层，切片使用了数组且它是建立在数组之上的抽象概念使得本身变得更加灵活和方便使用（可以把数组想象为底层结构）。</p>
<p>你可以把切片作为高级语言中使用数组的一种使用方式。</p>
<p>你可以类似于数组定义切片，省略长度：</p>
<pre><code class="language-go">var mySlice []string //a slice of strings
</code></pre>
<p>你可以初始化切片的值：</p>
<pre><code class="language-go">var mySlice = []string{"First"， "Second"， "Third"}

//or

mySlice := []string{"First"， "Second"， "Third"}
</code></pre>
<p>你可以用 <code>make()</code> 函数创建一个有明确长度的空切片：</p>
<pre><code class="language-go">mySlice := make([]string， 3) //a slice of 3 empty strings
</code></pre>
<p>你可以用已经存在的切片创建一个新的切片，添加一个或多个元素进去：</p>
<pre><code class="language-go">mySlice := []string{"First"， "Second"， "Third"}

newSlice := append(mySlice， "Fourth"， "Fifth")
</code></pre>
<p>注意我们需要给 <code>append()</code> 的结果赋给一个新的切片，否则我们将得到一个编译器错误。原来的切片没有改变，我们将得到一个全新的切片。</p>
<p>你也可以使用 <code>copy()</code> 函数来创建一个重复的切片，这样它就不会共享另一个切片的内存，而是独立的：</p>
<pre><code class="language-go">mySlice := []string{"First"， "Second"， "Third"}

newSlice := make([]string， 3)

copy(newSlice， mySlice)
</code></pre>
<p>如果你复制的切片没有足够的空间（比原来的短），则只复制第一个元素（只到有空间为止）。</p>
<p>你可以从数组中初始化一个切片：</p>
<pre><code class="language-go">myArray := [3]string{"First"， "Second"， "Third"}

mySlice = myArray[:]
</code></pre>
<p>多个切片可以使用与底层数组相同的数组：</p>
<pre><code class="language-go">myArray := [3]string{"First"， "Second"， "Third"}

mySlice := myArray[:]
mySlice2 := myArray[:]

mySlice[0] = "test"

fmt.Println(mySlice2[0]) //"test"
</code></pre>
<p>这两个切片现在共享同一块内存。修改一个切片底层数组也会跟着改变，并导致从该数组生成的另一个切片也会被修改。</p>
<p>与数组一样，每一个切片中的元素同样存储在内存的连续内存之中。</p>
<p>如果你知道你必须用到切片，你可以在初始化的时候设置所需的容量。这种方式，在你需要更多空间的时候，这些空间已经准备好了（替代了选择和移动切片到从老的内存空间到新的内存的方式，并减少了垃圾回收）。</p>
<p>我们可以通过 <code>make()</code> 函数的第三个参数来指定<strong>容量</strong>：</p>
<pre><code class="language-go">newSlice := make([]string， 0， 10)
//an empty slice with capacity 10
</code></pre>
<p>例如字符串，你可以以下语法获得切片中的一部分：</p>
<pre><code class="language-go">mySlice := []string{"First"， "Second"， "Third"}

newSlice := mySlice[:2] //get the first 2 items
newSlice2 := mySlice[2:] //ignore the first 2 items
newSlice3 := mySlice[1:3] //new slice with items in position 1-2
</code></pre>
<h2 id="dividmapsingogomapdiv"><div id="maps-in-go">Go 中的 map</div></h2>
<p>map 是 Go 中一种常见的数据类型。</p>
<p>在其他语言被称为_字典_ 或 <em>哈希表</em> 或 <em>关联数组</em>。</p>
<p>下面是创建一个 map 的方式：</p>
<pre><code class="language-go">agesMap := make(map[string]int)
</code></pre>
<p>你不需要对 map 设置容纳多少项。</p>
<p>你可以通过这种方式添加新的元素到 map 中：</p>
<pre><code class="language-go">agesMap["flavio"] = 39
</code></pre>
<p>你可以用以下语法初始化 map 并赋值：</p>
<pre><code class="language-go">agesMap := map[string]int{"flavio": 39}
</code></pre>
<p>你可以通过键来获取对应的值：</p>
<pre><code class="language-go">age := agesMap["flavio"]
</code></pre>
<p>你可以通过 <code>delete()</code> 函数来删除 map 中的元素：</p>
<pre><code class="language-go">delete(agesMap， "flavio")
</code></pre>
<h2 id="dividloopsingogodiv"><div id="loops-in-go">Go 中的循环</div></h2>
<p>Go 中最好的特性是它只提供最少的选择。</p>
<p>我们只有一个循环关键字：<code>for</code>。</p>
<p>你可以像这样使用它：</p>
<pre><code class="language-go">for i := 0; i &lt; 10; i++ {
	fmt.Println(i)
}
</code></pre>
<p>我们首先初始化一个循环的变量， 我们设置一个条件用于检查我们的循环是否应该结束。最后我们设置 <em>post 语句</em>， 在每一次循环后执行， 这里例子中是增长 <code>i</code>。</p>
<p><code>i++</code> 增长 <code>i</code> 变量.</p>
<p><code>&lt;</code> <em>运算符</em> 用于比较 <code>i</code> 和 <code>10</code> 的值,会返回 <code>true</code> 或 <code>false</code>， 决定循环体是否执行。</p>
<p>我们不需要用圆括号来包围代码块，与 C 和 JavaScript 不太一样。</p>
<p>其他语言有各种不同的循环结构，但是 Go 中只有这一个，我们可以有像 <code>while</code> 一样的循环，如果你熟悉一门有它的语言，像这样：</p>
<pre><code class="language-go">i := 0

for i &lt; 10 {
	fmt.Println(i)
  i++
}
</code></pre>
<p>我们完全可以忽略条件，在我们想要中止可以使用 <code>break</code>：</p>
<pre><code class="language-go">i := 0

for {
	fmt.Println(i)

	if i &lt; 10 {
		break
	}

  i++
}
</code></pre>
<p>我在循环体中使用了一个 <code>if</code> 声明，但是在我们没有看到条件语句！我们下一步看。</p>
<p>我现在想要介绍一种东西 <code>range</code>。</p>
<p>我们可以使用以下语法通过 <code>for</code> 来迭代数组：</p>
<pre><code class="language-go">numbers := []int{1， 2， 3}

for i， num := range numbers {
	fmt.Printf("%d: %d\n"， i， num)
}

//0: 1
//1: 2
//2: 3
</code></pre>
<p>注意：我使用 <code>fmt.Printf()</code> 它允许我们使用占位符 <code>%d</code>，意思是整数，而 <code>\n</code> 意思是加入一个换行符。</p>
<p>在你不需要索引时，一般可以使用以下语法：</p>
<pre><code class="language-go">for _， num := range numbers {
  //...
}
</code></pre>
<p>我们可以使用 <code>_</code> 语法，它表示 “忽略 这个”，以避免Go编译器产生一个错误：“你没有使用 <code>i</code> 变量！”。</p>
<h2 id="dividconditionalsingogodiv"><div id="conditionals-in-go">Go 中的条件语句</div></h2>
<p>我们使用 <code>if</code> 声明一个条件从而执行不同的代码：</p>
<pre><code class="language-go">if age &lt; 18 {
	//underage
}
</code></pre>
<p><code>else</code> 部分是可选的：</p>
<pre><code class="language-go">if age &lt; 18 {
	//underage
} else {
  //adult
}
</code></pre>
<p>或者可以使用多个 <code>if</code>：</p>
<pre><code class="language-go">if age &lt; 12 {
	//child
} else if age &lt; 18  {
  //teen
} else {
	//adult
}
</code></pre>
<p>如果你在 <code>if</code> 中定义了任何的变量，那么只能在 <code>if</code> 中使用（<code>else</code> 中也一样且你需要在 <code>{}</code> 中写新的代码块）。</p>
<p>如果你有很多不同的 if 声明来检查同一个条件，使用 <code>switch</code> 是更好的选择：</p>
<pre><code class="language-go">switch age {
case 0: fmt.Println("Zero years old")
case 1: fmt.Println("One year old")
case 2: fmt.Println("Two years old")
case 3: fmt.Println("Three years old")
case 4: fmt.Println("Four years old")
default: fmt.Println(i + " years old")
}
</code></pre>
<p>与 C， JavaScript和其他语言相比，你不需要在每一个 <code>case</code> 中写 <code>break</code>。</p>
<h2 id="dividoperatorsingogodiv"><div id="operators-in-go">Go 中的运算符</div></h2>
<p>到目前为止，我们已经在代码示例中使用了一些运算符， 如 <code>=</code>、<code>:=</code> 和 <code>&lt;</code>。</p>
<p>让我们了解更多。</p>
<p>我们有赋值运算符 <code>=</code> 和 <code>:=</code> 我们用来定义和初始化变量：</p>
<pre><code class="language-go">var a = 1

b := 1
</code></pre>
<p>我们有比较运算符 <code>==</code> 和 <code>!=</code> 用来比较两个参数并会返回一个布尔值：</p>
<pre><code class="language-go">var num = 1
num == 1 //true
num != 1 //false
</code></pre>
<p>还有 <code>&lt;</code>， <code>&lt;=</code>， <code>&gt;</code>， <code>&gt;=</code>：</p>
<pre><code class="language-go">var num = 1
num &gt; 1 //false
num &gt;= 1 //true
num &lt; 1 //false
num &lt;= 1 //true
</code></pre>
<p>我们有双元（要求有两个参数）算术运算符， 像 <code>+</code>、<code>-</code>、<code>*</code>、<code>/</code>、<code>%</code>.</p>
<pre><code class="language-go">1 + 1 //2
1 - 1 //0
1 * 2 //2
2 / 2 //1
2 % 2 //0
</code></pre>
<p><code>+</code> 可以用来连接字符串：</p>
<pre><code class="language-go">"a" + "b" //"ab"
</code></pre>
<p>我们有单元运算符 <code>++</code> 和 <code>--</code> 用来对一个数字进行自增或自减：</p>
<pre><code class="language-go">var num = 1
num++ // num == 2
num-- // num == 1
</code></pre>
<p>注意：不像 C 和 JavaScript 像这样 <code>++num</code> 让一个数字预先操作。当然，运算符不会返回任何值。</p>
<p>我们有逻辑运算符来帮我们进行基本的 <code>true</code> 和 <code>false</code> 的判断，使用：<code>&amp;&amp;</code>、<code>||</code> 和 <code>!</code>：</p>
<pre><code class="language-go">true &amp;&amp; true  //true
true &amp;&amp; false //false
true || false //true
false || false //false
!true  //false
!false //true
</code></pre>
<p>这三个是最主要。</p>
<h2 id="dividstructsingogodiv"><div id="structs-in-go">Go 中的结构体</div></h2>
<p><strong>结构体</strong> 是一种 <em>类型</em>，它包括一个或多个变量。它像是一个变量的集合。我们将它们称为字段。 它们可以有不同的类型。</p>
<p>这是一个结构体定义的示例：</p>
<pre><code class="language-go">type Person struct {
	Name string
	Age int
}
</code></pre>
<p>注意这里我们使用大写的名字作为字段名，不然它们在包内将会是私有的。且当你想让结构体作为参数传递给另一个库的函数时，像我们使用 JSON 及数据库时，这些字段都将无法访问。</p>
<p>一旦我们定义了一个结构体，我们就可以用这个类型初始化一个变量：</p>
<pre><code class="language-go">flavio := Person{"Flavio"， 39}
</code></pre>
<p>且我们可以用以下语法修改字段的值：</p>
<pre><code class="language-go">flavio.Age //39
flavio.Name //"Flavio"
</code></pre>
<p>你也可以使用这种方式将一个结构体初始化赋给一个变量：</p>
<pre><code class="language-go">flavio := Person{Age: 39， Name: "Flavio"}
</code></pre>
<p>你也可以只初始化一个字段：</p>
<pre><code class="language-go">flavio := Person{Age: 39}
</code></pre>
<p>或者不初始化任何值：</p>
<pre><code class="language-go">flavio := Person{}

//or

var flavio Person
</code></pre>
<p>或者在之后设值：</p>
<pre><code class="language-go">flavio.Name = "Flavio"
flavio.Age = 39
</code></pre>
<p>结构体很常用，因为你可以对不相关的数据进行分组，并将其传递给函数或从函数传递给函数，保存在切片中，等等。</p>
<p>一旦定义，结构体就是像 <code>int</code> 或 <code>string</code> 这样的类型，这意味着你也可以在其他结构中使用它：</p>
<pre><code class="language-go">type FullName struct {
	FirstName string
	LastName string
}

type Person struct {
	Name FullName
	Age int
}
</code></pre>
<h2 id="dividfunctionsingogodiv"><div id="functions-in-go">Go 中的函数</div></h2>
<p>一个函数是一块代码，它被赋予了一个名字且包含了一些指令。</p>
<p>在 “Hello, World!” 示例中我们创建了一个 <code>main</code> 函数， 那是程序的入口。</p>
<pre><code class="language-go">package main

import "fmt"

func main() {
	fmt.Println("Hello, World!")
}
</code></pre>
<p>这是一个特殊的函数。</p>
<p>通常我们使用自定义的名字来定义函数：</p>
<pre><code class="language-go">func doSomething() {

}
</code></pre>
<p>你可以像这样调用它：</p>
<pre><code class="language-go">doSomething()
</code></pre>
<p>一个函数可以设置入参，我们需要给参数设置类型，如下：</p>
<pre><code class="language-go">func doSomething(a int， b int) {

}

doSomething(1， 2)
</code></pre>
<p><code>a</code> 和 <code>b</code> 是函数内部参数的名字。</p>
<p>一个函数可以返回一个值，像这样：</p>
<pre><code class="language-go">func sumTwoNumbers(a int， b int) int {
	return a + b
}

result := sumTwoNumbers(1， 2)
</code></pre>
<p>注意这里我们指定的返回的值的类型。</p>
<p>一个函数可以返回多个或一个值：</p>
<pre><code class="language-go">func performOperations(a int， b int) (int， int) {
	return a + b， a - b
}

sum， diff := performOperations(1， 2)
</code></pre>
<p>有趣的是很多语言只能返回一个值。</p>
<p>函数内部定义的任何变量都是函数的本地变量。</p>
<p>一个函数也可以不限制参数的个数，且这样的函数我们称它为 <em>可变函数</em>：</p>
<pre><code class="language-go">func sumNumbers(numbers ...int) int {
	sum := 0
	for _， number := range numbers {
		sum += number
	}
	return sum
}

total := sumNumbers(1， 2， 3， 4)
</code></pre>
<h2 id="dividpointersingogodiv"><div id="pointers-in-go">Go 中的指针</div></h2>
<p>Go 支持使用指针。</p>
<p>假设你有一个变量：</p>
<pre><code class="language-go">age := 20
</code></pre>
<p>使用 <code>&amp;age</code> 你获得这个变量的指针，它是内存地址。</p>
<p>当你拥有一个变量的指针时，你可以使用 <code>*</code> 运算符获取它的值：</p>
<pre><code class="language-go">age := 20
ageptr = &amp;age
agevalue = *ageptr
</code></pre>
<p>通常当你想要传递一个参数给你调用的函数时。Go 默认会在函数内部复制这个变量的值，所以这不会改变 <code>age</code> 的值：</p>
<pre><code class="language-go">func increment(a int) {
	a = a + 1
}

func main() {
	age := 20
	increment(age)

	//age is still 20
}
</code></pre>
<p>你可以像这样使用指针：</p>
<pre><code class="language-go">func increment(a *int) {
	*a = *a + 1
}

func main() {
	age := 20
	increment(&amp;age)

	//age is now 21
}
</code></pre>
<h2 id="dividmethodsingogodiv"><div id="methods-in-go">Go 中的方法</div></h2>
<p>你可以给一个结构赋值一个函数，在这种情况下我们称它为“方法”。</p>
<p>示例：</p>
<pre><code class="language-go">type Person struct {
	Name string
	Age int
}

func (p Person) Speak() {
	fmt.Println("Hello from " + p.Name)
}

func main() {
	flavio := Person{Age: 39， Name: "Flavio"}
	flavio.Speak()
}
</code></pre>
<p>你可以定义一个方法为指针接收或值接收。</p>
<p>上述例子中我们展示的是值接收，这个接收器会复制结构体的结构。</p>
<p>这里将会有一个指针接收，接收结构实例的指针：</p>
<pre><code class="language-go">func (p *Person) Speak() {
	fmt.Println("Hello from " + p.Name)
}
</code></pre>
<h2 id="dividinterfacesingogodiv"><div id="interfaces-in-go">Go 中的接口</div></h2>
<p>接口是一个类型，可以定义一个或多个方法声明。</p>
<p>方法没有被实现，只有它们的签名：名字、参数类型和返回值类型。</p>
<p>像这样：</p>
<pre><code class="language-go">type Speaker interface {
	Speak()
}
</code></pre>
<p>现在你有一个函数，可以接纳任何类型来实现接口定义所有方法：</p>
<pre><code class="language-go">func SaySomething(s Speaker) {
	s.Speak()
}
</code></pre>
<p>我们可以让任何一个结构体来实现这些方法：</p>
<pre><code class="language-go">type Speaker interface {
	Speak()
}

type Person struct {
	Name string
	Age int
}

func (p Person) Speak() {
	fmt.Println("Hello from " + p.Name)
}

func SaySomething(s Speaker) {
	s.Speak()
}

func main() {
	flavio := Person{Age: 39， Name: "Flavio"}
	SaySomething(flavio)
}
</code></pre>
<h2 id="dividwheretogofromherediv"><div id="where-to-go-from-here">更多内容</div></h2>
<p>这份手册初步介绍了 Go 编程语言。</p>
<p>基于这里的基础，你现在可以学更多东西：垃圾回收、错误处理、并发和网络、文件系统接口，等等。</p>
<p>海阔凭鱼跃，天高任鸟飞。</p>
<p>我的建议是选择一个你想要创建的程序，然后开始学习你需要的东西。</p>
<p>它是有趣且值得做的。</p>
<p>注意：<a href="https://thevalleyofcode.com/download/go/">你可以点击链接获得 GO 初学者手册的 pdf 版本和 ePub 版本</a>。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
