<?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[ Lola Wei - 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[ Lola Wei - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 01 Jun 2026 18:14:07 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/author/lola/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 我是怎么从零自学编程 7 个月，成为软件开发者的 ]]>
                </title>
                <description>
                    <![CDATA[ 原文：How I became a web developer in under 7 months – and how you can too [https://www.freecodecamp.org/news/how-i-became-a-web-developer-in-under-7-months-and-how-you-can-too/] ，作者：Niamh McCooey [https://www.freecodecamp.org/news/author/niamh/] 大概去年这个时候，我开始在闲暇时间学习代码基础，7 个月后发现自己已经成为了一名专业的开发者——我没有计算机科学学位，没有参加过编程训练营，一开始对编程完全没有概念。 所以，你在阅读这篇文章的时候或许会想：这怎么可能？ 好吧，我和你一样惊讶。 在这篇文章中，我会分享这 7 个月我是如何学习，并且找到了自己的第一份开发工作的。我也会分享一些对我学习有益的资源，以及一些我希望自己在之前学习时早一点采纳的建议。 如果你也是编程初学者，想要转行做 Web 开发，那么我希望这篇文章能提供给你一些帮助。 （ ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-i-became-a-web-developer-in-under-7-months/</link>
                <guid isPermaLink="false">5e4160aeca1efa04e196b503</guid>
                
                    <category>
                        <![CDATA[ 科技行业女性 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lola Wei ]]>
                </dc:creator>
                <pubDate>Sat, 05 Mar 2022 02:30:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2020/02/123-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>原文：<a href="https://www.freecodecamp.org/news/how-i-became-a-web-developer-in-under-7-months-and-how-you-can-too/">How I became a web developer in under 7 months – and how you can too</a>，作者：<a href="https://www.freecodecamp.org/news/author/niamh/">Niamh McCooey</a></p><p>大概去年这个时候，我开始在闲暇时间学习代码基础，7 个月后发现自己已经成为了一名专业的开发者——我没有计算机科学学位，没有参加过编程训练营，一开始对编程完全没有概念。</p><p>所以，你在阅读这篇文章的时候或许会想：这怎么可能？</p><p>好吧，我和你一样惊讶。</p><p>在这篇文章中，我会分享这 7 个月我是如何学习，并且找到了自己的第一份开发工作的。我也会分享一些对我学习有益的资源，以及一些我希望自己在之前学习时早一点采纳的建议。</p><p>如果你也是编程初学者，想要转行做 Web 开发，那么我希望这篇文章能提供给你一些帮助。</p><p>（如果你还没开始学习编程，但是想入门编程，或许我的这篇文章<a href="https://chinese.freecodecamp.org/news/the-first-step-towards-learning-to-code/">《我是怎么迈出学习编程的第一步的》</a><em>会对你更有帮助，因为通过这些文章，你会了解如何迈出编程第一步。）</em></p><h2 id="-">🐣 尽早分享自己的兴趣</h2><p>当我认真考虑学习编程的时候，我做的第一件事就是<em>说出来</em> 。</p><p>我询问家人与朋友，他们是否认识愿意讨论自己工作的工程师，结果我认识了很多既聪明又富有创造力的人，他们给予了我很多真诚、实用、丰富且非常重要的观点与看法。</p><p>这一阶段的关键是尽可能去努力认识不同类型的工程师们。如果可以的话，与处于不同职业阶段且具有丰富爱好与特长的人交谈，这样可以帮助你揭开编程的神秘面纱，你将不再认为编程是个令人恐惧的大难题。</p><p>除此之外，最好尽早：</p><h2 id="--1">🙋 咨询他人</h2><p>我问过工程师们这些最基础的问题：</p><ul><li>GitHub 是什么</li><li>你真的需要计算机科学学位吗</li><li>为什么人人都在讨论 React</li><li>你认为 10 年后会有什么类型的技术性工作</li></ul><h2 id="--2">👋 参加交流会</h2><p>如果你所在的城市有很多技术交流会——去吧！</p><p>在学习早期，这是非常好的实践机会，不仅会有前辈带你解决编程问题，而且你还能认识组织者、导师、在职开发者等等。</p><p>通过参加交流会，你也可以建立自己在该行业中的社交圈，了解到适合自己水平的工作机会。</p><p>下面是一些我最喜欢且对新手友好的交流会：</p><ul><li><strong><a href="https://codebar.io/" rel="nofollow">Codebar</a></strong>：该组织每周举办一次交流会，在全世界拥有20多个分会。在这里，你可以与导师一起工作，专心致志写代码（如果你不能到场，也可以参加他们的线上交流会）</li><li><strong><a href="https://www.adaslist.co/" rel="nofollow">Ada’s List</a></strong>：该组织每月在伦敦举办一次交流会，且有线上社区，女性可以在这里认识其他在技术行业工作的女性（不只是开发者）</li><li><strong><a href="https://24pullrequests.com/" rel="nofollow">24 Pull Requests</a></strong>：该组织致力于为开源项目做贡献，每年在伦敦举办一次交流会，还会介绍 GitHub 知识——对新手来说非常方便</li><li><strong><a href="https://nodegirls.com/" rel="nofollow">Node Girls</a></strong>：该组织每年举办几次交流会，地点不固定，是新手学习 Javascript 和 Node.js 的好地方</li></ul><p>（译者注：在世界很多城市都有 freeCodeCamp 技术社区，学员们会时常组织交流会，分享技术知识，互相帮助学习编程，可以添加小助手微信 fcczhongguo，她会邀请你加入所在城市的技术社区。）</p><h2 id="--3">🏃‍♀️ 下一步：多多尝试</h2><p>多年以来，我都认为学编程的第一件事就是决定自己想使用的语言。但是，不知道使用什么语言其实没关系，我甚至觉得这还是一种巨大的优势。</p><p>编程界有太多的语言与主题了，它们对于新手来说就像洪水猛兽。你可以天真无邪地尝试所有语言，不必过分深究（举个例子，学习 CSS 的时候，你不必知道 CSS 的完整历史），现在就要讲到头 7 个月中我最喜欢的建议了......</p><h2 id="--4">😈 学习表现差</h2><p>当我开始学习的时候，特别害怕自己表现很差。但那时我就发现了一个最大的真相：<em>没人关心你有多差</em> 。</p><p>当然你会很差！你会非常糟糕！但因为你是新手，所以你有理由表现差。在初学编程时风险比较低的阶段，享受糟糕吧——因为这很有趣，也很自由。</p><h2 id="--5">👩‍💻 紧跟教程</h2><p>当你想从新手进阶时，教程很有帮助，因为教程的形式不需要你自己去搞懂想要学习的内容和学习的方式（你只是在尝试学习它）。</p><p>以下是一些我最喜欢的教程：</p><ul><li><a href="http://tutorials.codebar.io/" rel="nofollow"><strong>Codebar </strong></a>上的所有内容（非常适合从头学习不同的语言）</li><li>Rachel Andrew 的<a href="https://gridbyexample.com/" rel="nofollow"><strong>视频教程</strong></a> （非常适合学习网格布局）</li><li>NodeGirls 上的<a href="https://nodegirls.com/resources" rel="nofollow"><strong>教程</strong></a>（一步步带你学会 Javascript 和 Node.js）</li></ul><p>当谈到教程的时候，试着去理解你如何能学得最好，而不要仅仅因为它是最流行的或者是别人建议的方法就不知所措。</p><p><strong>在这个阶段，你可能不太了解编程，但是你知道如何才能学得最好，所以相信自己的判断吧。</strong></p><p>以下是一些对我来说行之有效的方法：</p><ul><li><a href="https://learn.freecodecamp.org/" rel="nofollow"><strong>freeCodeCamp 课程</strong></a>：通过一道道练习题目学习不同语言，当你需要从繁重的教程中休息一下，想要尝试一些更轻量级的东西时，这是很好的练习平台</li><li><strong><a href="https://flukeout.github.io/" rel="nofollow">CSS Diner</a></strong> &amp; <a href="https://flexboxfroggy.com/" rel="nofollow"><strong>Flexbox Froggy</strong></a>：这是两个有趣的游戏，对掌握 CSS 选择器和练习 Flexbox 布局大有裨益</li><li><strong><a href="https://codepen.io/" rel="nofollow">Codepen</a></strong>：这是一个可以练习编程的平台，你可以看到人们的项目和源代码</li><li><a href="https://grasshopper.codes/" rel="nofollow"><strong>Grasshopper</strong></a>：这是一个对初学者友好的移动应用程序，为你介绍 JavaScript 基础知识。</li><li><a href="https://eloquentjavascript.net/" rel="nofollow"><strong>Eloquent Javascript</strong></a>：配合使用该电子书与 freeCodeCamp 效果更佳，能让你充分深入理解 JavaScript。</li></ul><h2 id="--6">👀 学习别人的代码</h2><p>很快，你就会觉得是时候做一些更有意义的事情了。一个相当有趣的方法是当你看到一些很酷的东西时，尝试把代码写出来（你可以<strong><a href="https://www.awwwards.com/" rel="nofollow">在这浏览炫酷的网站</a></strong>）。</p><p> 当你浏览时：</p><h2 id="--7">🤔 搞清楚为什么</h2><p>如果你在创建自己的第一个网站，在写第一行代码之前，记得要问自己：<em>为什么</em>。</p><p>你做这件事是为了：</p><ul><li>填充作品集？</li><li>学习一门特定的语言？</li><li>尝试听说过的不同方法？</li></ul><p>作为新手，你做网站的主要原因可能是学习，所以你的网站或应用程序只是学习的一部分，是学习的副产品，不是最终目标——记住这一点。</p><p>特别是对于自学者来说，知道这点区别能够帮助你提高学习效率，因为你很快就会知道：每个人都有自己的做事方式。举个例子，如果你想通过自建网站来学习 Flexbox，不要听别人的话使用 Bootstrap。</p><p>如果有人告诉你，实现这个目标有更快的方式，他们可能是对的，但是他们却可能不知道你真正的目标究竟是什么。</p><h2 id="--8">🗣 大声说出自己的目标</h2><p>首先告诉自己，然后告诉别人，最后决定如何实现目标。这可能听起来十分容易，但在收集信息的过程中，很容易会忘记最初的目标。</p><h2 id="--9">💆 平静下来</h2><p>自学最好的一点是你决定自己学什么，怎么学，对吗？好吧，还有一种好处就是你决定自己的提交期限。即使你逾期完成，也没人关心，所以，放轻松吧！</p><h2 id="--10">⚡️ 时刻充满斗志</h2><p>这个学习过程是双重的：一方面，你需要花时间弄清楚代码中的技术细节；另一方面，你需要时不时地四处看看，看看其他人在做什么。</p><p>将这两种方法结合在一起，可以为你的学习提供一个整体的视角，并帮助你减轻陷入所有细节的压力。以下是我在最初的 7 个月里发现的一些有用的建议：</p><h2 id="--11">🎤 参加会议</h2><ul><li><a href="https://2020.yougotthis.io/" rel="nofollow"><strong>You Got This</strong></a>：这是一个在英国举办的会议，专门针对那些编程新手。我在成为工程师之前参加了这个会议，感觉非常舒适，并且参加那个会议之后我开始准备学习编程。</li><li><a href="https://newadventuresconf.com/2020/" rel="nofollow"><strong>New Adventures</strong></a>：这个会议在诺丁汉举办，内容偏轻技术，介绍技术的本质，以及科技如何在当今世界发挥作用。演讲者众多，话题广泛，十分鼓舞人心。</li></ul><h2 id="--12">📚 多阅读</h2><ul><li><a href="https://www.freecodecamp.org/news/" rel="nofollow"><strong>freeCodeCamp 技术专栏</strong></a> （当然了）</li><li><a href="https://medium.com/@codebar" rel="nofollow"><strong>Codebar</strong></a> （该平台会访谈自学成才的工程师们）</li><li><a href="https://alistapart.com/" rel="nofollow"><strong>A List Apart</strong></a> （发布一系列主题，从设计到编程到职业发展）</li><li><a href="https://www.smashingmagazine.com/" rel="nofollow"><strong>Smashing Magazine</strong></a> （专题文章、书籍、新闻、工作......应有尽有）</li></ul><h2 id="--13">👣 紧跟大牛</h2><ul><li><strong><a href="http://batmandy.com/" rel="nofollow">Mandy Michael</a></strong> – 在 CodePen 上发布很多炫酷的内容</li><li><a href="https://rachelandrew.co.uk/" rel="nofollow"><strong>Rachel Andrew</strong></a> – 发表很多鼓舞人心的实用谈话，你可以在网上找到</li><li><a href="https://jensimmons.com/" rel="nofollow"><strong>Jen Simmons</strong></a> – 在 YouTube 有一个频道，内容丰富，视频讲解对于设计与开发的见解</li><li><a href="https://saron.io/" rel="nofollow"><strong>Saron Yitbarek</strong></a> – <a href="https://www.codenewbie.org/" rel="nofollow"><strong>CodeNewbie</strong></a> 的创始人和执行总裁</li></ul><p>基本上就是这样。回顾过去的一年，我意识到正是这些核心课程支撑着我从一个从未读过一行代码的文案工作者变成了一个全职开发者。</p><p>所以，如果你正在考虑转行，但又担心要做出这么大的改变，试着把它分解成更小的目标和挑战。</p><p>如果这个方法对一位刚开始自学编程时一头雾水的女士有效，那么对你也一定有效。你可以借鉴这些经验教训，根据你自己的学习风格进行调整，然后努力学习。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 你需要知道的 CSS 变量 ]]>
                </title>
                <description>
                    <![CDATA[ 很多编程语言都支持变量，但很遗憾， CSS 从开发之始便不支持原生变量。 你写 CSS 代码吗？那么除非利用像 Sass 这样的预处理器，否则无法使用变量。 Sass 这样的预处理器将变量使用作为其主要的销售卖点，这也是我们值得一试的理由。 但是网站开发发展得很快，而我可以很高兴地告诉你们：CSS 现在也支持变量了。 尽管预处理器支持更多功能，但是 CSS 变量也不错，而且 CSS 变量会推动网站发展。 在本指南中，我将向你展示如何在 CSS 中使用原生变量，如何通过使用它让你的工作更加轻松。 你将学习到什么？ 我会首先向你介绍 CSS 变量的基础知识。这是了解 CSS 变量的第一步。 学习基础知识很酷，但更酷的是用基础知识构建实际应用。 因此，我会教你构建 3 个项目，进而介绍 CSS 变量及其易用性。现在我们来快速浏览这 3 个项目。 项目 1：使用 CSS 变量创建可变组件 目前，你可能使用 React，Angular 或 Vue 创建可变组件，但是 CSS 变量会简化创建过程。 使用 CSS 变量修改元素样式 在Codepen [https://codep ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/everything-you-need-to-know-about-css-variables/</link>
                <guid isPermaLink="false">5ee1f397db4be8080eb70daf</guid>
                
                    <category>
                        <![CDATA[ CSS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web开发 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 变量 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lola Wei ]]>
                </dc:creator>
                <pubDate>Thu, 27 Aug 2020 09:16:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2020/06/lee-campbell-DtDlVpy-vvQ-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>很多编程语言都支持变量，但很遗憾， CSS 从开发之始便不支持原生变量。</p><p>你写 CSS 代码吗？那么除非利用像 Sass 这样的预处理器，否则无法使用变量。</p><p>Sass 这样的预处理器将变量使用作为其主要的销售卖点，这也是我们值得一试的理由。</p><p>但是网站开发发展得很快，而我可以很高兴地告诉你们： &nbsp;<strong>CSS 现在也支持变量了</strong>。</p><p>尽管预处理器支持更多功能，但是 CSS 变量也不错，而且 CSS 变量会推动网站发展。</p><p>在本指南中，我将向你展示如何在 CSS 中使用原生变量，如何通过使用它让你的工作更加轻松。</p><h3 id="-">你将学习到什么？</h3><p>我会首先向你介绍 CSS 变量的基础知识。这是了解 CSS 变量的第一步。</p><p>学习基础知识很酷，但更酷的是用基础知识构建实际应用。</p><p>因此，我会教你构建 3 个项目，进而介绍 CSS 变量及其易用性。现在我们来快速浏览这 3 个项目。</p><h4 id="-1-css-">项目 1：使用 CSS 变量创建可变组件</h4><p>目前，你可能使用 React，Angular 或 Vue 创建可变组件，但是 CSS 变量会简化创建过程。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/82b0d6ac32dfb7e064564d8b0b7ae32cc4df9da8/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f722d6e787077366e4b6646635032714b63316b617a56662d4937336a5577467453454257" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f722d6e787077366e4b6646635032714b63316b617a56662d4937336a5577467453454257" width="600" height="400" loading="lazy"></figure><p>使用 CSS 变量修改元素样式</p><p>在<a href="https://codepen.io/ohansemmanuel/full/PQYzvv/">Codepen</a>上查看项目</p><h4 id="-2-css-">项目 2 ：使用 CSS 变量设置主题风格</h4><p>你可能在其他地方看到过类似功能。我会教你使用 CSS 变量轻松创建全站主题风格。</p><p>使用 CSS 变量设置全站主题风格</p><p>在<a href="https://codepen.io/ohansemmanuel/full/xYKgwE/">Codepen</a>上查看项目</p><h4 id="-3-css-">项目 3 ：创建 CSS 变量盒</h4><p>这是最后一个项目。不要在意项目名字，我实在想不出更好的名字了。</p><p>盒子颜色会动态改变。</p><p>观察一下盒子颜色是如何动态改变的。调节输入值时，盒子容器会 3 维 旋转。</p><p>该项目展示了如何便捷地使用 JavaScript 更新 CSS 变量，并展示出一个很好的交互效果。</p><h4 id="--1">学习过程非常有趣！</h4><p>花点时间在 &nbsp;<a href="https://codepen.io/ohansemmanuel/full/EoBLgd/">Codepen</a> &nbsp;上享受乐趣吧。</p><p>注意：本文假设你已熟练掌握了 CSS，如果你不太解CSS，或者想学习做出炫酷的用户界面，我建议你学习我的<a href="https://bit.ly/learn_css">高级CSS课程</a> &nbsp;(包括85课的付费课程)，这篇文章即摘自此课程。&lt; /厚着脸皮做广告咯&gt; ？</p><h3 id="--2">为什么变量如此重要？</h3><p>如果你尚不了解预处理器中的变量或原生 CSS 变量，那么请看以下理由。</p><h4 id="--3"><strong>理由一：增强代码可读性</strong></h4><p>项目过程中，你可以很快了解变量是如何增强代码库的可读性与可维护性的。</p><h4 id="--4">**理由二：易于跨大文档修改</h4><p>如果所有常量都保存在单个文件中，就不需要在几千行代码里一一修改变量了。使用变量之后，你只需改动一个地方即可实现。</p><h4 id="--5"><strong>理由三：快速定位拼写错误</strong></h4><p>在一行行的代码中寻找错误非常痛苦。如果只是一个简单的拼写错误，那就更烦人了。因为我们很难发现小错误。但合理使用变量便可以解决这个问题。</p><p>为此，可读性与可维护性才是制胜法宝。</p><p>而 CSS 原生变量就可以提高代码可读性与可维护性。</p><h3 id="-css-">定义 CSS 变量</h3><p>先谈你熟悉的内容吧：Javascript 中的变量。</p><p>声明简单 Javascript 变量：</p><pre><code>var amAwesome;

</code></pre><p>为变量赋值：</p><pre><code>amAwesome = "awesome string"

</code></pre><p>在 CSS 中，用双破折号引出 CSS 变量。</p><pre><code>/*你能找到 CSS 变量吗？ */.block { color: #8cacea;--color: blue}

</code></pre><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/167d69a5acb4bd3151e9db5f91139da6cc5cd6f7/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f46436a487a504371452d37724139694b753561474637726f6a554e52782d7330547a6d31" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f46436a487a504371452d37724139694b753561474637726f6a554e52782d7330547a6d31" width="600" height="400" loading="lazy"></figure><p>CSS 变量又名 “自定义属性”</p><h3 id="css-">CSS 变量的作用范围</h3><p>注意：</p><p>在 Javascript 中，变量有作用范围。可能是<code>全局</code>变量或<code>局部</code>变量。</p><p>CSS 变量同样如此。</p><p>举例如下：</p><pre><code>:root {  --main-color: red}

</code></pre><p>使用<code>:root</code> &nbsp;选择器可定位 DOM 或文档树中最高级别的元素。</p><p>这种变量即为全局变量。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/575ff97c083204248eeb3ae085df57a15a5bb885/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f586a526a4f4f7364357839746a372d487443783543786857517166532d4968396272646f" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f586a526a4f4f7364357839746a372d487443783543786857517166532d4968396272646f" width="600" height="400" loading="lazy"></figure><p>局部变量与全局变量</p><h3 id="--6">例一</h3><p>假设你想创建一个 CSS 变量，该变量可定义主题网站的初始颜色。</p><p>如何操作呢？</p><p>1.使用<code>:root</code>选择器。</p><pre><code>:root { }

</code></pre><p>2. 定义变量</p><pre><code>:root { --primary-color: red}

</code></pre><p>CSS 变量前要加双破折号，如 &nbsp;<code>--color</code>。</p><h3 id="-css--1">使用 CSS 变量</h3><p>一旦定义了变量并为其赋值，就可以继续在其他属性中使用它。</p><p>不过要注意一点。</p><p>如果你习惯使用预处理器，那么你肯定习惯通过引用变量名使用变量。例如:</p><pre><code>$font-size: 20px.test {  font-size: $font-size}

</code></pre><p>但如果使用 CSS 原生变量，情况会稍有不同。需要使用<code>var()</code>函数指代变量。</p><p>还是利用上面的例子，以下是使用 CSS 原生变量的赋值方法：</p><pre><code>:root {  --font-size: 20px}.test {  font-size: var(--font-size)}

</code></pre><p>两者大不相同。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/8855e87a3bbcae96ce47c2e1d41ec7aa5a338ed0/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f31414d68662d4e644b644c647a7857343556745536757766615a6a57665763584633776c" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f31414d68662d4e644b644c647a7857343556745536757766615a6a57665763584633776c" width="600" height="400" loading="lazy"></figure><p>记住：使用 var()函数。</p><p>一旦解决了这个问题，你就会越来越爱 CSS 变量！</p><p>另外要注意，不同于 Sass (或其他预处理器) 中的变量————你可以在很多地方使用这些变量，并做数学计算————但使用时要小心。大部分时候，CSS 变量用作属性值。</p><pre><code>/*此写法错误*/.margin {--side: margin-top;var(--side): 20px;}

</code></pre><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/6924918e8bbff89573755ef6de82d0b1242c6690/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f62426b657a3677717a7678445855474e7979794c6448624e346562577339716343504442" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f62426b657a3677717a7678445855474e7979794c6448624e346562577339716343504442" width="600" height="400" loading="lazy"></figure><p>属性名无效，此写法语法错误。</p><p>你也无法直接使用数学运算功能。你需要 CSS &nbsp;<code>calc()</code>函数做数学运算。我将在接下来的案例中讨论该函数。</p><pre><code>/*此写法错误 */.margin {--space: 20px * 2;font-size:  var(--space);  //not 40px}

</code></pre><p>如果你一定要做数学计算，那么可以这样使用 calc() 函数：</p><pre><code>.margin {--space: calc(20px * 2);font-size:  var(--space);  /*equals 40px*/}

</code></pre><h3 id="--7">值得一提的属性</h3><p>以下问题值得一提。</p><h4 id="1-">1. 由于自定义属性是普通属性，所以我们可以在任意元素中声明自定义属性。</h4><p>在段落标签、文档区域标签、侧边栏标签、根标签甚至伪元素中声明变量即可，它们与普通属性作用相同。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/4992b0a55fc1d59fc9f5a16cf72c33c5bba0c60f/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f454f4a35366939716e6b47506c312d493139753354494c63724a71384d612d3552393973" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f454f4a35366939716e6b47506c312d493139753354494c63724a71384d612d3552393973" width="600" height="400" loading="lazy"></figure><p>自定义属性与普通属性作用相同</p><h4 id="2-css-">2. CSS 属性具有继承性与级联性</h4><p>参考下面的例子：</p><pre><code>div {  --color: red;}div.test {  color: var(--color)}div.ew {  color: var(--color)}

</code></pre><p>与普通变量相同，<code>--color</code> &nbsp;变量的值会继承父盒子的属性值。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/e6d6592da205af9a3442d2769588050591a38511/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f672d6b416b4e756c4e4851476b4d64397673743358755447356b766f4a4a324966566666" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f672d6b416b4e756c4e4851476b4d64397673743358755447356b766f4a4a324966566666" width="600" height="400" loading="lazy"></figure><h4 id="3-css-">3. CSS 变量可添加条件规则，如媒体类型</h4><p>与其他属性相同，使用 &nbsp;<code>@media</code> &nbsp;或其他条件规则可改变 CSS 变量的值。</p><p>举个例子，在大屏幕上，以下代码会改变 gutter 的值：</p><pre><code>:root { --gutter: 10px }@media screen and (min-width: 768px) {    --gutter: 30px}

</code></pre><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/283a5bf71303f2a9c7c6ac9b5f3ee5e82a3662c8/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f4946776b456d663333594379433139686a744d5a775067774133566e774b523662677647" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f4946776b456d663333594379433139686a744d5a775067774133566e774b523662677647" width="600" height="400" loading="lazy"></figure><p>这对响应式设计大有裨益</p><h4 id="4-css-html-">4. CSS 变量用于 HTML 的样式属性。</h4><p>设置行内样式：</p><pre><code>&lt;!--HTML--&gt;&lt;html style="--color: red"&gt;&lt;!--CSS--&gt;;body {  color: var(--color)}

</code></pre><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/47beecfe0fc2d5cbd8bcffa4bbbff4a8ae9e6c68/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f625a674e527a385a6f536f4c4d635750636d506e69764d78387037303335637836703545" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f625a674e527a385a6f536f4c4d635750636d506e69764d78387037303335637836703545" width="600" height="400" loading="lazy"></figure><p>在行内设置变量值</p><p>注意：CSS 变量区分大小写。为了方便，我都是用的小写，你可以自行选择大小写。</p><pre><code>/*变量大写与小写*/:root { --color: blue;--COLOR: red;}

</code></pre><h3 id="--8">解决重复声明</h3><p>与其他变量相同，标准级联规则可解决重复声明。</p><p>举个例子：</p><pre><code>/*定义变量*/:root { --color: blue; }div { --color: green; }#alert { --color: red; }/*u使用变量 */* { color: var(--color); }

</code></pre><p>多次声明以上变量后，元素的颜色如何变化呢？</p><pre><code>&lt;;p&gt;What's my color?&lt;/p&gt;&lt;div&gt;and me?&lt;/div&gt;&lt;div id='alert'&gt;  What's my color too?  &lt;p&gt;color?&lt;/p&gt;&lt;/div&gt;

</code></pre><p>你能猜出来吗？</p><p>第一段为 &nbsp;<code>蓝色</code> &nbsp;。因为未给 &nbsp;<code>p</code> &nbsp;元素设置变量，所以它会继承 &nbsp;<code>:root</code> &nbsp;中定义的颜色。</p><pre><code>:root { --color: blue; }

</code></pre><p>第一个 &nbsp;<code>div</code> &nbsp;元素为 &nbsp;<code>绿色</code> &nbsp;。这很显然。因为我们使用变量指定了<code>div</code>元素颜色为绿色。</p><pre><code>div { --color: green; }

</code></pre><p>ID 为 &nbsp;<code>alert</code> &nbsp;的 &nbsp;<code>div</code> &nbsp;元素不为绿色，而为 &nbsp;<code>红色</code>。</p><pre><code>#alert { --color: red; }

</code></pre><p>ID 中的变量有直接作用域，其颜色属性会覆盖掉原来的属性。 &nbsp;<code>#alert</code> &nbsp;选择器作用范围更加明确。</p><p>最后，ID 为 &nbsp;<code>alert</code> &nbsp;的 &nbsp;<code>p</code> &nbsp;元素为 &nbsp;<code>红色</code>。</p><p>因为没有为段落元素声明变量，所以你可能会认为该段落会变为 &nbsp;<code>蓝色</code>，因为我们在 &nbsp;<code>:root</code>中定义了蓝色。</p><pre><code>:root { --color: blue; }

</code></pre><p>与其他属性相同，CSS 变量具有继承性，子元素会继承父元素 &nbsp;<code>#alert</code> &nbsp;的属性值。</p><pre><code>#alert { --color: red; }

</code></pre><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/432b777f4dbcbc34b2c05dd8e668f275c81504e2/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f36357870776b456a526d3930434b5a4557416261583636634348646c3436785772333243" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f36357870776b456a526d3930434b5a4557416261583636634348646c3436785772333243" width="600" height="400" loading="lazy"></figure><p>答案如图所示</p><h3 id="--9">解决循环依赖</h3><p>循环依赖会发生在以下情境中：</p><p>1. 变量需要依赖其自身值。即，定义时就使用了<code>var()</code> &nbsp;引用其值。</p><pre><code>:root {  --m: var(--m)}body {  margin: var(--m)}

</code></pre><p>2. 两个或两个以上的变量互相引用时。</p><pre><code>:root {  --one: calc(var(--two) + 10px);  --two: calc(var(--one) - 10px);}

</code></pre><p>不要在代码中创建这样的循环引用。</p><h3 id="--10">使用无效变量会怎样？</h3><p>语法错误会被忽略，但是无效的 &nbsp;<code>var()</code> &nbsp;会导致错误的初始值或继承到有问题的值。</p><p>举例如下：</p><pre><code>:root { --color: 20px; }p { background-color: red; }p { background-color: var(--color); }

</code></pre><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/6605230e3ec268edcb37486cb48d63ca6ec2e81c/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f4578484e6f6d625869314f626d4b3573633857746254537474494a374d4e47756a625a50" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f4578484e6f6d625869314f626d4b3573633857746254537474494a374d4e47756a625a50" width="600" height="400" loading="lazy"></figure><p>变量<code>--color</code> &nbsp;带入了 &nbsp;<code>var()</code> &nbsp;， 但是属性值<code>background-color: 20px</code>在变量带入后是无效的，因为<code>background-color</code>不是继承属性，所以它的值会默认变成 &nbsp;<code>初始</code> &nbsp;值，即<code>transparent</code>。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/5bd0756ed09564049839379c3d5cd60d18085b9f/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f7464654437734c465255764b436458503259365358755143616b5463762d685635504e52" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f7464654437734c465255764b436458503259365358755143616b5463762d685635504e52" width="600" height="400" loading="lazy"></figure><p>注意，如果你直接写 &nbsp;<code>background-color: 20px</code> &nbsp;，那么这个属性声明即是无效的。该元素会使用前面元素对颜色的声明，即红色。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/d5454634945a338a3d2cd546e401b7575b1f4836/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f3234464c4c57416f434d4c3156433447393547513149474c516e5075776f4a32416f4741" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f3234464c4c57416f434d4c3156433447393547513149474c516e5075776f4a32416f4741" width="600" height="400" loading="lazy"></figure><p>当你这样写声明时，情况就不一样了。</p><h3 id="--11">小心创建单一标记</h3><p>当设置类似下面的属性值时，<code>20px</code> &nbsp;会被编译为单一标记。</p><pre><code>font-size: 20px

</code></pre><p>简单点说， &nbsp;<code>20px</code> &nbsp;是一个整体。</p><p>当用 CSS 变量创建单一标记时，需要小心点。</p><p>举个例子，请看如下代码：</p><pre><code>:root { --size: 20}div {  font-size: var(--size)px /*提醒*/}

</code></pre><p>你可能认为 &nbsp;<code>font-size</code>为<code>20px</code>, 但是你错了。</p><p>浏览器会把它解析为 &nbsp;<code>20 px</code>。</p><p>注意<code>20</code>与<code>px</code>之间的空格。</p><p>因此，如果你不得不创建独立标记，那么一定要用变量表示这个整体。举个例子，使用<code>--size: 20px</code>, 或者<code>calc</code> &nbsp;函数，如<code>calc(var(--size) * 1px)</code>，这里的<code>--size</code>等于 &nbsp;<code>20</code>。</p><p>如果你没看懂，不要担心。我会在下个例子中仔细解释这一概念。</p><h3 id="--12">实战环节！</h3><p>终于到了期待已久的部分了。</p><p>我会带你写几个有用的项目，来看看 CSS 变量的实际用法。</p><p>开始吧。</p><h3 id="-1-css--1">项目1: 使用CSS变量创建可变组件。</h3><p>假设你需要创建两个不同的按钮。两个按钮基础样式相同，但只有一点小小的区别。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/047f09b8e62a1e56ce1e96147ed6f2a24de34a0b/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f456d36794a4b5677496939746a676a41773676624631436935483441525330413961634c" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f456d36794a4b5677496939746a676a41773676624631436935483441525330413961634c" width="600" height="400" loading="lazy"></figure><p>在这个例子中，两个按钮不同的属性为<code>background-color</code>和<code>border-color</code>。</p><p>怎么做呢？</p><p>一般来说，我们都会这么做。</p><p>创建一个基类，比如 &nbsp;<code>.btn</code>，然后加上不同的类，举例如下：</p><pre><code>&lt;button class="btn"&gt;Hello&lt;/button&amp;gt;&lt;button class="btn red"&gt;Hello&lt;/button&gt;

</code></pre><p><code>.btn</code>包含按钮的基本样式。举例如下：</p><pre><code>.btn {  padding: 2rem 4rem;  border: 2px solid black;  background: transparent;  font-size: 0.6em;  border-radius: 2px;}

</code></pre><pre><code>/*悬浮状态*/.btn:hover {  cursor: pointer;  background: black;  color: white;}

</code></pre><p>按钮样式如何变化呢？</p><pre><code>/* 变化 */.btn.red {  border-color: red}.btn.red:hover {  background: red}

</code></pre><p>看到了吗，我们一直在复制代码。这样也好，但是使用 CSS 变量会更简洁。</p><p>第一步怎么做呢？</p><p>使用 CSS 变量替换变化的颜色值，不要忘记为变量设置默认值！</p><pre><code>.btn {   padding: 2rem 4rem;   border: 2px solid var(--color, black);   background: transparent;   font-size: 0.6em;   border-radius: 2px; }

</code></pre><pre><code> /*悬浮状态*/  .btn:hover {  cursor: pointer;   background: var(--color, black);   color: white; }

</code></pre><p>设置 &nbsp;<code>background: **var(--color, black)**</code> &nbsp;是为了让背景颜色变成 &nbsp;<code>--color</code>变量的值。但当变量不存在时，该元素就会自动使用替换值 &nbsp;<code>**black**</code>。</p><p>这就是设置默认属性值的方法，跟在 JavaScript 或其他的编程语言中如出一辙。</p><p>好戏才刚刚开始。</p><p>有了变量，就可以像下面这样设置新的变量值了：</p><pre><code>.btn.red {   --color: red }

</code></pre><p>如果元素 class 使用了<code>.red</code>，浏览器会注意到不同的 &nbsp;<code>--color</code> &nbsp;值，进而改变按钮的外观。</p><p>使用变量创建可变组件可以节约很多时间。</p><p>对比两段代码：</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/e26f6adbad0ecf8a68ff0764281786ab160e9bcf/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f676d457a6c76524e374b69614456574579325a5545376b49356135507041414a73336755" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f676d457a6c76524e374b69614456574579325a5545376b49356135507041414a73336755" width="600" height="400" loading="lazy"></figure><p>使用 CSS 变量 VS 不使用 CSS 变量</p><p>如果有更多的变量，还能剩下很多敲代码的时间。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/5557522a80fff78eb66823be811836edccecc15b/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f4f5579564c6859776b766575756153624941473851486f6f3063674e7471507271576869" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f4f5579564c6859776b766575756153624941473851486f6f3063674e7471507271576869" width="600" height="400" loading="lazy"></figure><p>看到区别了吗？</p><h3 id="-css--2">项目二: 使用 CSS 变量定制网站主题</h3><p>我敢说，你之前肯定浏览过类似网站。主题网站会让用户觉得自己可以定制主题，就好像自己在主管该网站一样。</p><p>以下便是我们要使用的例子。</p><p>使用 CSS 变量会让创建过程变得多么简单呢？</p><p>这个例子非常重要。在这个例子中，我会介绍如何使用 Javascript 来更新 CSS 变量。</p><h3 id="--13">我们实际要做的</h3><p>CSS 变量的魅力在于其互动性。一旦你修改了某个 CSS 变量，其对应的属性都会发生变化。</p><p>下图从概念上解释了整个例子的操作过程。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/c84d5680951c74b96d26d20e07bc2425093a464c/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f4f6d5259416c494e664f48635957724c447a424a666a4a6b4936656a6273673554737463" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f4f6d5259416c494e664f48635957724c447a424a666a4a6b4936656a6273673554737463" width="600" height="400" loading="lazy"></figure><p>流程图</p><p>我们需要使用 JavaScript 监听点击事件。</p><p>在这个简单的例子中，整个页面的背景和文本颜色全部基于 CSS 变量。</p><p>当点击左上方的任意一个按钮时， CSS 变量的值即会改变。因此，页面的背景颜色也会变化。</p><p>该项目的原理就是这样。</p><p>除此之外，我们怎么改变 CSS 变量的值呢？</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/9e785353a3fca8d78bd3fdea00404898ca7c703a/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f55495133696a3943792d695533724f354f586b516331757950684d5047754844317a5862" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f55495133696a3943792d695533724f354f586b516331757950684d5047754844317a5862" width="600" height="400" loading="lazy"></figure><p>行内设置变量</p><p>即便把 CSS 变量设为行内样式，它的值也会改变。和 Javascript 一起，我们可以控制整个文档，并使用 CSS 变量设置行内样式。</p><p>明白了吗？</p><p>废话不多说，开始操作吧。</p><h3 id="--14">初始代码</h3><p>初始代码如下：</p><pre><code>&lt;div class="theme"&gt;  &lt;button value="dark"&gt;dark&lt;/button&gt;  &lt;button value="calm"&gt;calm&lt;/button&gt;  &lt;button value="light"&gt;light&lt;/button&gt;&lt;/div&gt;&lt;article&gt;...&lt;/article&gt;

</code></pre><p>代码中有三个按钮元素，它们都继承自类名为 &nbsp;<code>.theme</code> &nbsp;的父元素。为了简化我没有写<code>article</code>元素，<code>article</code>元素包含页面的主要内容。</p><h3 id="--15">页面样式</h3><p>为了成功实现该项目，首先要改变页面风格。方法很简单。</p><p>没必要为每种风格都设置<code>background-color</code>和<code>color</code>属性, 只需要使用变量来设置。</p><p>如下所示：</p><pre><code>body {  background-color: var(--bg, white);  color: var(--bg-text, black)}

</code></pre><p>原因很明显。按下按钮时，两个 CSS 变量的值都会改变。</p><p>如此一来，整个页面的风格都会发生变化。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/c159b7da4e9c341b04fe90f917310dea95c7786c/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f6c625536322d6d7968346c51616b6c39446f4f594561657462524c685074344e5a503031" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f6c625536322d6d7968346c51616b6c39446f4f594561657462524c685074344e5a503031" width="600" height="400" loading="lazy"></figure><p>现在轮到 JavaScript 了。</p><h4 id="-javascript">使用 JavaScript</h4><p>以下是项目需要用到的 JavaScript 代码：</p><pre><code>const root = document.documentElement const themeBtns = document.querySelectorAll('.theme &gt; button')themeBtns.forEach((btn) =&gt; {  btn.addEventListener('click', handleThemeUpdate)})function handleThemeUpdate(e) {  switch(e.target.value) {    case 'dark':       root.style.setProperty('--bg', 'black')      root.style.setProperty('--bg-text', 'white')      break    case 'calm':        root.style.setProperty('--bg', '#B3E5FC')       root.style.setProperty('--bg-text', '#37474F')      break    case 'light':      root.style.setProperty('--bg', 'white')      root.style.setProperty('--bg-text', 'black')      break  }}

</code></pre><p>别害怕，代码比你想的简单的多。</p><p>首先，获取根元素，<code>const root = document.documentElement</code></p><p>这里的根元素是<code>HTML</code>。一会你就会理解这为什么重要了，如果你很好奇，那么就先认为这是为了设置 CSS 变量使用的。</p><p>除此之外，还要获取按钮元素，<code>const themeBtns = document.querySelectorAll('.theme &gt; button</code>)</p><p><code>querySelectorAll</code>方法会获取伪数组。之后遍历该伪数组，然后添加对应的点击事件。</p><p>代码如下：</p><pre><code>themeBtns.forEach((btn) =&gt; {  btn.addEventListener('click', handleThemeUpdate)})

</code></pre><p><code>handleThemeUpdate</code>函数在哪里呢？请继续往下看。</p><p>点击按钮会触发对应的<code>handleThemeUpdate</code>函数。需要为每一个按钮的点击事件配置对应的操作。</p><p>基于此，这里用了一个 switch 的<code>operator</code>，不同按钮的点击事件不同。</p><p>再看一眼前面 JavaScript 的代码块吧，现在你应该理解的更好了。</p><h3 id="-css--3">项目三: 创建 CSS 变量盒?</h3><p>如果你之前没有看到过效果图，那么我再贴一次图片：</p><p>盒子的颜色会动态改变，盒子容器会根据输入值 3 维旋转。</p><p>在<a href="https://codepen.io/ohansemmanuel/full/EoBLgd/">Codepen</a>上尝试。</p><p>这是使用 CSS 变量和 JavaScript 一同创造出的一个既有交互性有很美妙的例子。</p><p>来看看创建过程。</p><h4 id="--16">代码片段</h4><p>需要以下元素：</p><ol><li>用来输入范围值的输入框</li><li>包含指令的容器</li><li>包含其他盒子的盒子容器，每一个子盒子中都包含一个输入框</li></ol><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/906df79b1c0da07dd3494748181d3544af7f3b05/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f46484136786873466950436f4770424235564c634b7a6f676769766573456c46416b5751" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f46484136786873466950436f4770424235564c634b7a6f676769766573456c46416b5751" width="600" height="400" loading="lazy"></figure><p>代码结构非常简单。</p><p>如下所示：</p><pre><code>&lt;main class="booth"&gt;  &lt;aside class="slider"&gt;    &lt;label&gt;Move this ? &lt;/label&gt;    &lt;input class="booth-slider" type="range" min="-50" max="50" value="-50" step="5"/&gt;  &lt;/aside&gt;    &lt;section class="color-boxes"&gt;    &lt;div class="color-box" id="1"&gt;&lt;input value="red"/&gt;&lt;/div&gt;    &lt;div class="color-box" id="2"&gt;&lt;input/&gt;&lt;/div&gt;    &lt;div class="color-box" id="3"&gt;&lt;input/&gt;&lt;/div&gt;    &lt;div class="color-box" id="4"&gt;&lt;input/&amp;gt;&lt;/div&amp;gt;    &lt;div class="color-box" id="5"&gt;&lt;input/&gt;&lt;/div&gt;    &lt;div class="color-box" id="6"&gt;;&lt;input/&gt;&amp;lt;/div&gt;  &lt;/section&gt;  &lt;footer class="instructions"&gt;    ?? Move the slider&lt;br/&gt;    ?? Write any color in the red boxes   &lt;/footer&gt;&lt;/main&gt;  

</code></pre><p>注意以下几点：</p><ol><li>输入框中输入值的取值范围为<code>-50</code>到<code>50</code> &nbsp;，步长为<code>5</code>。除此之外，默认输入值为 &nbsp;<code>-50</code>。</li><li>如果你不确定输入框的原理，那么请先到 &nbsp;<a href="https://www.w3schools.com/jsref/dom_obj_range.asp">w3schools</a>了解相关概念。</li><li>注意观察类名为 &nbsp;<code>.color-boxes</code> &nbsp;的盒子容器是如何包含其他类名为 &nbsp;<code>.color-box</code>的容器的，子盒子又是如何包含输入框的。</li><li>值得一提的是，第一个输入框的颜色默认为红色。</li></ol><p>理解了代码结构后，继续添加如下样式：</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/8fdf1d36a09cdd90d5ae59ff45b02186a0c22e10/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f705932386c6e5a4678347876617238303747656749673448496e34444e63424147357245" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f705932386c6e5a4678347876617238303747656749673448496e34444e63424147357245" width="600" height="400" loading="lazy"></figure><ol><li>将类名为<code>.slider</code>和<code>.instructions</code>的元素移出文档流，进行绝对定位。</li><li>为<code>body</code>元素添加日出的背景颜色，再用一朵花的背景图做装饰，放在页面左下角。</li><li>居中对齐<code>color-boxes</code>元素 。</li><li>为<code>color-boxes</code>元素设置样式。</li></ol><p>具体代码如下：</p><pre><code>/* Slider */.slider,.instructions {  position: absolute;  background: rgba(0,0,0,0.4);  padding: 1rem 2rem;  border-radius: 5px}.slider {  right: 10px;  top: 10px;}.slider &gt; * {  display: block;}/* Instructions */.instructions {  text-align: center;  bottom: 0;  background: initial;  color: black;}

</code></pre><p>这段代码非常简单。我希望你能读完并理解。如果你还是不理解，请在下面留言或者在推特上@我。</p><p>为<code>body</code>元素设置样式略微复杂。幸运的是，你熟知 CSS 的用法。</p><p>既然想要为页面设置背景颜色并添加背景图案，也许最好使用简化的<code>background</code>属性设置多种样式。</p><p>代码如下：</p><pre><code>body {  margin: 0;  color: rgba(255,255,255,0.9);  background: url('http://bit.ly/2FiPrRA') 0 100%/340px no-repeat, var(--primary-color);  font-family: 'Shadows Into Light Two', cursive;}

</code></pre><p><code>url</code>会链接到太阳花图片。</p><p><code>0 100%</code>设置了背景图片的位置。</p><p>下面的文章介绍了在 CSS 中，如何对背景定位：</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/3052259a57b7b513f2676dd10908f0f3ea51d053/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f34725a756774454b46656179303046736644584675587969484131416d66316947324a64" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f34725a756774454b46656179303046736644584675587969484131416d66316947324a64" width="600" height="400" loading="lazy"></figure><p>来源: &nbsp;<a href="http://bit.ly/learn_css">CSS 高级编程</a></p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/75609007852c21403d0f810f076043d9968dc7d4/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f7a46637575457535526e724757694735446f7167376a53344f532d50794f683748393376" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f7a46637575457535526e724757694735446f7167376a53344f532d50794f683748393376" width="600" height="400" loading="lazy"></figure><p>来源: &nbsp;<a href="http://bit.ly/learn_css">CSS 高级编程</a></p><p>分隔号后面的<code>340px</code>代表<code>background-size</code>。如果你把数值设置的更小，那么图片尺寸也会变小。</p><p>你可能会想<code>no-repeat</code>是什么意思。该属性可以防止背景图片平铺。</p><p>最后，逗号后面的属性值都是备选值。这里把<code>background-color</code>设置为<code>var(primary-color)</code>。</p><p>可以看出，我们使用了变量。</p><p>这意味着在声明之前，必须先定义变量：</p><pre><code>:root {  --primary-color: rgba(241,196,15 ,1)}

</code></pre><p>这里的颜色值就是日出时的黄颜色。这没什么大不了的，接下来会设置更多的变量。</p><p>接下来，把类名为<code>color-boxes</code>的容器居中设置。</p><pre><code>main.booth {  min-height: 100vh;    display: flex;  justify-content: center;  align-items: center;}

</code></pre><p>主要容器使用了弹性布局，将下一级的子元素置于页面中央。因此，类名为<code>color-box</code>的容器都会居中显示。</p><p>接下来，为子元素和其下一级的子元素设置样式。</p><p>首先是子元素：</p><pre><code>.color-box {  padding: 1rem 3.5rem;  margin-bottom: 0.5rem;  border: 1px solid rgba(255,255,255,0.2);  border-radius: 0.3rem;  box-shadow: 10px 10px 30px rgba(0,0,0,0.4); }

</code></pre><p>设置完后，子元素会拥有一个美丽的阴影，这会让最后的效果更加酷炫。</p><p>这还不够，我们要设置类名为 &nbsp;<code>container-boxes</code> &nbsp;的盒子属性。</p><pre><code>/* Color Boxes */.color-boxes {  background: var(--secondary-color);  box-shadow: 10px 10px 30px rgba(0,0,0,0.4);  border-radius: 0.3rem;    transform: perspective(500px) rotateY( calc(var(--slider) * 1deg));  transition: transform 0.3s}

</code></pre><p>好多了！现在来细化一下。</p><p>下面的代码非常简单：</p><pre><code>.color-boxes {   background: var(--secondary-color);   box-shadow: 10px 10px 30px rgba(0,0,0,0.4);   border-radius: 0.3rem;}

</code></pre><p>以上代码用了一个新变量。因此需要在 root 选择器中加上定义。</p><pre><code>:root {  --primary-color: rgba(241,196,15 ,1);  --secondary-color: red;}

</code></pre><p>secondary-color 变量值为红色。因此容器背景为红色。</p><p>但你可能对如下代码感到困惑：</p><pre><code>/* Color Boxes */.color-boxes {  transform: perspective(500px) rotateY( calc(var(--slider) * 1deg));  transition: transform 0.3s}

</code></pre><p>可以暂时简化上面的 transform 属性。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/27dec7862b25d383ec8d1d502112eee9b4f0f632/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f4a7a636c554c44504a2d6644617a43446e48614274324a77373248514159336b30416a4b" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f4a7a636c554c44504a2d6644617a43446e48614274324a77373248514159336b30416a4b" width="600" height="400" loading="lazy"></figure><p>举例如下</p><pre><code>transform: perspective(500px) rotateY( 30deg);

</code></pre><p>transform 合并了两个不同的函数。一是透视，二是以 Y 轴为标准的旋转角度。</p><p>如何处理 &nbsp;<code>perspective</code>和 &nbsp;<code>rotateY</code>函数？</p><p>透视函数可以使元素在 3D 空间内变形。它会激活三维空间，使元素增加一个 z 轴。</p><p>在 &nbsp;<a href="https://tympanus.net/codrops/css_reference/transform/#section_perspective">codrops</a> &nbsp;上阅读更多关于透视函数的内容。</p><p><code>rotateY</code>函数用途为何呢？</p><p>激活三维空间后，元素就有了 x ，y ，z 三个轴。<code>rotateY</code>函数会把元素以 Y 轴为中心旋转。</p><p><a href="https://tympanus.net/codrops/css_reference/transform/#section_rotate3d">codrops</a> &nbsp;上这幅图可以帮助我们直观理解这一点。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/07366921aedcb222e9932d7e8f7065c46774afdc/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f6e6c6d303733557139516d51594c6141324137384962654451714d75543971767130306e" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f6e6c6d303733557139516d51594c6141324137384962654451714d75543971767130306e" width="600" height="400" loading="lazy"></figure><p><a href="https://tympanus.net/codrops/css_reference/transform/#section_rotate3d">Codrops</a></p><p>回到前面的话题。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/ecc315162d1308f8086f7d985e31c47e1d85a0c2/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f506e456e414d5449304c52724a325a4b62463363733730416b4e64496353457541594450" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f506e456e414d5449304c52724a325a4b62463363733730416b4e64496353457541594450" width="600" height="400" loading="lazy"></figure><p>移动滑块的时候，你知道是哪个函数影响了<code>.container-box</code>的旋转效果吗？</p><p>是旋转函数。因此盒子绕 Y 轴旋转。</p><p>由于该值可以通过 JavaScript 传给函数, 所以便用变量来表示这个值。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/26e9ce46ba9eeb3c040f9b99af7668f0ca44c0c5/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f554149327732366f4832496742546c6a6250364767446a51457a6c6a6363617755697a34" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f554149327732366f4832496742546c6a6250364767446a51457a6c6a6363617755697a34" width="600" height="400" loading="lazy"></figure><p>为什么用这个变量乘以 1deg 呢？</p><p>按通常经验来说，也为了自由定义变量，当创建单个标记时，建议创建变量是不带单位。</p><p>这样便可以在任何需要使用单位的时候通过 &nbsp;<code>calc</code> &nbsp;函数达成目标。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/a0f34d6d72e919b2e2732c9a949c57c969545bad/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f596d574d7569695a387976477079365748576c6d7175476244474742657a346d7577534e" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f596d574d7569695a387976477079365748576c6d7175476244474742657a346d7577534e" width="600" height="400" loading="lazy"></figure><p>这样就可以在需要的时候任意使用这些值，既可以转化为<code>deg</code>，也可以转化为相对于用户窗口视图 &nbsp;<code>vw</code> &nbsp;的比例。</p><p>在这个例子中，我们把一个数字乘以一个 1deg的值，得到了一个有单位的数值。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/c4572ee0452107d94706cc7036ae2f846ea83df3/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f6758773230726270746962316b4c673046707853457372494b4f5a476c4543674553726e" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f6758773230726270746962316b4c673046707853457372494b4f5a476c4543674553726e" width="600" height="400" loading="lazy"></figure><p>由于 CSS 不理解数学运算，所以需要把数学运算转移到<code>calc</code>函数中，这样才能得到 CSS 属性需要的对应数值。</p><p>现在来到下一步。在 JavaScript 中可以任意改变变量值。</p><p>还剩下一点 CSS 代码：</p><pre><code>/* Handle colors for each color box */.color-box:nth-child(1) {  background: var(--bg-1)}.color-box:nth-child(2) {  background: var(--bg-2)}.color-box:nth-child(3) {  background: var(--bg-3)}.color-box:nth-child(4) {  background: var(--bg-4)}.color-box:nth-child(5) {  background: var(--bg-5)}.color-box:nth-child(6) {  background: var(--bg-6)}

</code></pre><p>nth-child 选择器用来选择子元素。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/160391ca96b652b86673e18d619552e60cd49f30/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f57577a6b454f47585467786b44476b434a535962676b50457479484545657a6865735854" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f57577a6b454f47585467786b44476b434a535962676b50457479484545657a6865735854" width="600" height="400" loading="lazy"></figure><p>我们将更新每个框的背景色，背景色必须由变量表示，这样便可以通过 JavaScript 更新。 There’s a bit of foresight needed here. We know we will be updating the background color of each box. We also know that this background color has to be represented by a variable so it is accessible via JavaScript. Right?</p><p>我们这样设置：</p><pre><code>.color-box:nth-child(1) {  background: var(--bg-1)}

</code></pre><p>但如果变量不存在，怎么办呢？</p><p>需要默认值。</p><p>如下所示：</p><pre><code>.color-box:nth-child(1) {  background: var(--bg-1, red)}

</code></pre><p>在这个例子中，我选择不提供任何默认值。</p><p>如果这个变量的属性值不合法，那么这个属性值就会用其初始值。</p><p>因此，如果<code>--bg-1</code>元素无效或不合法，那么背景将会自动变为初始值，即透明色。</p><p>没有明确指定时，初始值就是这个属性的默认值。例如，如果你不设置元素的<code>background-color</code>, 那么元素会默认为 &nbsp;<code>transparent</code>。</p><p>初始值也是一种默认值。</p><h3 id="-javascript-1">开始写 Javascript</h3><p>Javascript 部分内容不多。</p><p>首次处理滑块。</p><p>五行代码即可。</p><pre><code>const root = document.documentElementconst range = document.querySelector('.booth-slider')//as slider range's value changes, do something range.addEventListener('input', handleSlider)function handleSlider (e) {  let value = e.target.value   root.style.setProperty('--slider', value)}

</code></pre><p>首先，获取滑块元素， &nbsp;<code>const range = document.querySelector('.booth-slider')</code></p><p>增加事件以处理滑块值变化， &nbsp;<code>range.addEventListener('input', handleSlider)</code></p><p>回调事件 &nbsp;<code>handleSlider</code></p><pre><code>function handleSlider (e) {  let value = e.target.value   root.style.setProperty('--slider', value)}

</code></pre><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/185be3efefa036cdd6e0d1c37dbb690af78e97dd/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f305074586b4c654d6b757745306d4a78754a58335430656d6b756e5041524778524e3854" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f305074586b4c654d6b757745306d4a78754a58335430656d6b756e5041524778524e3854" width="600" height="400" loading="lazy"></figure><p><code>root.style.setProperty('--slider', value)</code> &nbsp;指把<code>root</code>元素的 style 属性设置为对应值。</p><h3 id="--17">处理颜色变化</h3><p>这跟处理滑块变化一样简单。</p><p>代码如下：</p><pre><code>const inputs = document.querySelectorAll('.color-box &gt; input')

</code></pre><pre><code>//as the value in the input changes, do something.inputs.forEach(input =&gt; {  input.addEventListener('input', handleInputChange)})function handleInputChange (e) {  let value = e.target.value  let inputId = e.target.parentNode.id   let inputBg = `--bg-${inputId}`   root.style.setProperty(inputBg, value)}

</code></pre><p>获取每一个输入值，<code>const inputs = document.querySelectorAll('.color-box &gt; inpu</code>t')</p><p>为每一个输入框增加事件：</p><pre><code>inputs.forEach(input =&gt; {   input.addEventListener('input', handleInputChange)})

</code></pre><p>写<code>handleInputChange</code>函数：</p><pre><code>function handleInputChange (e) {  let value = e.target.value  let inputId = e.target.parentNode.id   let inputBg = `--bg-${inputId}`   root.style.setProperty(inputBg, value)}

</code></pre><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/42d0850c17430494ced33989881d279d34c3b8ab/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f48583458316f44537958414a474d456556326f494b5838504262756e53346930736e6742" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f48583458316f44537958414a474d456556326f494b5838504262756e53346930736e6742" width="600" height="400" loading="lazy"></figure><p>完成！</p><h3 id="--18">我怎么能忘记这些呢？</h3><p>当我发现自己在任何地方都没有提到浏览器的兼容性时，我已经完成并编辑了本文的初稿。所以，现在我来修补这个烂摊子吧。</p><p>浏览器对 CSS 变量（又称自定义属性）支持良好。几乎所有现代浏览器都能支持（在撰写本文时，这一比例已超过了 87％）。</p><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/c8f9afafc147742fdda4ed5e0e533707b600c7ad/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f347963706b6131344b673274434a4c4636793362305a632d584857437662774f61794142" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f347963706b6131344b673274434a4c4636793362305a632d584857437662774f61794142" width="600" height="400" loading="lazy"></figure><p><a href="https://caniuse.com/#search=css%20var">caniuse</a></p><p>那么，你现在会在项目中使用 CSS 变量吗？ 我会说是的！不过，请务必检查一下适用的比例。</p><p>往好的方面来看，你可以使用类似 &nbsp;<a href="http://www.myth.io/">Myth</a> &nbsp;的预处理器。它可以把你现在使用的 CSS 变成未来可期的 CSS，非常酷！</p><p>如果你曾经使用过 &nbsp;<a href="http://postcss.org/">postCSS</a>, 那也是一个现在使用未来 CSS 的不错的方法。这里有一些例子：<a href="https://www.npmjs.com/package/postcss-css-variables">postCSS module for CSS variables</a>。</p><h3 id="--19">我还有问题要问呢！</h3><figure class="kg-card kg-image-card"><img src="https://camo.githubusercontent.com/c497ae8a14d705d76b9f1cf129c5deb40910f682/68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f6f665a50364e683061435a4f75367957744459733948524f53744c4472554f6748393744" class="kg-image" alt="68747470733a2f2f63646e2d6d656469612d312e66726565636f646563616d702e6f72672f696d616765732f6f665a50364e683061435a4f75367957744459733948524f53744c4472554f6748393744" width="600" height="400" loading="lazy"></figure><p><a href="https://gum.co/lwaUh">电子书</a> &nbsp;可离线阅读，还能获得<strong>私人</strong>邀请，这样你可以问我任何问题。</p><p>很公平不是吗？</p><p>下次再见！</p><p>原文：<a href="https://www.freecodecamp.org/news/everything-you-need-to-know-about-css-variables-c74d922ea855/">Everything you need to know about CSS Variables</a>，作者：Emmanuel Ohans</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 深度学习入门浅析 ]]>
                </title>
                <description>
                    <![CDATA[ 五年前开始兴起深度学习。计算机的计算能力呈指数级增长，随后成功案例频出，进而出现了深度学习热潮。科技可以驱动汽车，在 Atari 游戏中击败人类，还可以诊断癌症。 我刚开始学习深度学习时，花了两周时间去研究这一领域。我选择了工具，比较了云服务，并研究了在线课程。回想起来，我希望自己从第一天起就能构建起神经网络。而这也正是我写作本文的目的。看懂本文不需要太多的基础知识，只需要了解 Python [https://www.codecademy.com/tracks/python]、命令行 [https://www.codecademy.com/learn/learn-the-command-line] 和Jupyter notebook [https://www.youtube.com/watch?v=HW29067qVWk] 就可以了。 深度学习是机器学习的一个分支。实践证明，这是一种对原始数据 [https://ml4a.github.io/images/figures/mnist-input.png]（如图像或声音等数据）构建模型的有效方法。 假设你要对猫和狗的图像进行分类。如 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/learn-basics-of-deep-learning/</link>
                <guid isPermaLink="false">5efd64a2db4be8080eb7115c</guid>
                
                <dc:creator>
                    <![CDATA[ Lola Wei ]]>
                </dc:creator>
                <pubDate>Thu, 02 Jul 2020 05:19:31 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2020/07/kevin-ku-w7ZyuGYNpRQ-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><strong>五年前开始兴起深度学习。</strong>计算机的计算能力呈指数级增长，随后成功案例频出，进而出现了深度学习热潮。科技可以驱动汽车，在 Atari 游戏中击败人类，还可以诊断癌症。</p><p>我刚开始学习深度学习时，花了两周时间去研究这一领域。我选择了工具，比较了云服务，并研究了在线课程。回想起来，我希望自己从第一天起就能构建起神经网络。而这也正是我写作本文的目的。看懂本文不需要太多的基础知识，只需要了解 &nbsp;<a href="https://www.codecademy.com/tracks/python">Python</a>、<a href="https://www.codecademy.com/learn/learn-the-command-line">命令行</a> &nbsp;和 &nbsp;<a href="https://www.youtube.com/watch?v=HW29067qVWk">Jupyter notebook</a> &nbsp;就可以了。</p><p>深度学习是机器学习的一个分支。实践证明，这是一种对 &nbsp;<a href="https://ml4a.github.io/images/figures/mnist-input.png">原始数据</a>（如图像或声音等数据）构建模型的有效方法。</p><p>假设你要对猫和狗的图像进行分类。如果不使用特定编程语言，则需要首先检测图像边缘，构建模型，识别鼻子、尾巴、爪子，然后神经网络才能实现最终分类。</p><p>另一方面，有更好的机器学习算法可以处理结构化数据。例如，如果你有一张 excel 工作表，上面有序记录了消费者订单，而你想预测他们的下一订单，那么就可以使用 &nbsp;<a href="http://www.r2d3.us/visual-intro-to-machine-learning-part-1/">传统方法</a> &nbsp;和 &nbsp;<a href="http://1.bp.blogspot.com/-ME24ePzpzIM/UQLWTwurfXI/AAAAAAAAANw/W3EETIroA80/s1600/drop_shadows_background.png">简单的机器学习算法</a>。</p><h2 id="-">核心逻辑</h2><p>想象有这么一台机器，它的齿轮 = ✱ 随意啮合重叠在一起且相互影响。这时，机器突然停止了工作，因为它的齿轮是随机调整的，所以需要检查每一个齿轮从而使机器恢复正常工作。</p><p>这时工程师会来检查所有的齿轮，并找出其中的错误。他会从最后一层齿轮开始检查，找到错误后便会逐层回溯。这样，他就可以计算出每一层齿轮造成的误差。此过程即为 &nbsp;<em>单向传播</em>。</p><p>工程师会根据错误逐层修正齿轮，然后运行机器进行检测。他不停地重复这一过程，直到机器正常运转。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/07/image.png" class="kg-image" alt="image" width="600" height="400" loading="lazy"></figure><p>神经网络也以相同的方式处理数据。它知道输入和输出，并逐层调整齿轮以找到两者之间的关系。给定输入后，它便会调整齿轮以预测输出，然后比较预测值与实际值。</p><p>为了使误差（预测值与实际值之间的差异）最小，神经网络会调整齿轮，使得两者之前的差值尽可能的最小。</p><p>找到最佳的方式，使误差最小化的方法叫做 &nbsp;<em>梯度下降</em> &nbsp;。计算误差的函数叫做 &nbsp;<em>误差函数函数/代价函数</em> &nbsp;。</p><h2 id="--1">浅层神经网络</h2><p>许多人认为人工神经网络模仿了大脑皮层的结构，但其实不然，因为我们尚不了解自己的大脑，所以不能妄下定论。不过大脑皮层确实是神经网络发明者弗兰克·罗森布拉特的灵感来源。</p><p>去 &nbsp;<a href="https://www.mladdict.com/neural-network-simulator">the neural network simulator</a> &nbsp;玩上一两个小时吧，这样你便会有更加直观的感受。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/07/image-1.png" class="kg-image" alt="image-1" width="600" height="400" loading="lazy"></figure><p>我们将从实现一个简单的神经网络开始，以了解 TFlearn 中的语法。首先我们要使用 &nbsp;<a href="https://msdn.microsoft.com/en-us/library/f355wky8.aspx">逻辑或 运算符</a> &nbsp;解决最经典的 101 问题。尽管本案例并不是最适宜使用神经网络的例子，但是通过这个案例，我们能够了解神经网络的实现方式。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/07/image-2.png" class="kg-image" alt="image-2" width="600" height="400" loading="lazy"></figure><p>所有深度学习程序都遵循相同的核心逻辑：</p><ul><li>首先调包，然后导入并清洗数据。所有输入（图像、音频、感知数据等）都会被 &nbsp;<a href="https://ml4a.github.io/images/figures/mnist-input.png">翻译成二进制数据</a>。这些长长的数字列表即为神经网络的输入。</li><li>自己设计神经网络，如选择神经网络中要包含的层的类型和数量。</li><li>神经网络开始学习。它已知输入和输出值，会搜索它们之间的相关性。</li><li>通过训练，得到预测值。</li></ul><p>以下是代码部分：</p><pre><code class="language-python"># 1. Import library of functions
import tflearn

2. Logical OR operator / the data
OR = [[0., 0.], [0., 1.], [1., 0.], [1., 1.]]
Y_truth = [[0.], [1.], [1.], [1.]]
3. Building our neural network/layers of functions
neural_net = tflearn.input_data(shape=[None, 2])
neural_net = tflearn.fully_connected(neural_net, 1, activation='sigmoid')
neural_net = tflearn.regression(neural_net, optimizer='sgd', learning_rate=2, loss='mean_square')
4. Train the neural network / Epochs
model = tflearn.DNN(neural_net)
model.fit(OR, Y_truth, n_epoch=2000, snapshot_epoch=False)
5. Testing final prediction
</code></pre><p><strong>输出</strong></p><pre><code>Training Step: 2000  | total loss: 0.00072 | time: 0.002s
| SGD | epoch: 2000 | loss: 0.00072 -- iter: 4/4
Testing OR operator
0 or 0: [[ 0.04013482]]
0 or 1: [[ 0.97487926]]
1 or 0: [[ 0.97542077]]
1 or 1: [[ 0.99997282]]

</code></pre><p><strong>第 1 行:</strong> &nbsp;以＃开头的行是注释，用于解释代码。</p><p><strong>第 2 行:</strong> &nbsp;导入 TFlearn 包。这样我们就可以使用谷歌 Tensorflow 中的函数了。</p><p><strong>第 5 - 6 行:</strong> &nbsp;这段代码表示以列表存储的数据，数字末尾的点号将该数字映射为浮点数。它存储十进制的数字，从而使计算更加精确。</p><p><strong>第 9 行：</strong> &nbsp;这段代码用于初始化神经网络，并指定输入数据的维度/形状。因为 “或” 运算需要两个数据，所以数据形状为 2。<a href="http://tflearn.org/layers/core/">[文档]</a></p><p><strong>第 10 行：</strong> &nbsp;这段代码表示输出层。激活函数将输出映射到某个区间。在这个例子中，我们使用 Sigmoid 函数来把值映射到 0-1。 [<a href="http://tflearn.org/layers/core/">文档</a>]</p><p><strong>第 11 行：</strong> &nbsp;该函数应用回归模型。<a href="http://tflearn.org/optimizers/">优化器</a> &nbsp;会选择最佳算法以最小化损失函数。学习率决定了神经网络的修正速度，损失变量决定了如何计算误差。[<a href="http://tflearn.org/layers/estimator/">文档</a> &nbsp;]</p><p><strong>第 14 行：</strong>选择要使用的神经网络，指定训练记录的存储位置。[<a href="http://tflearn.org/models/dnn/">文档</a> &nbsp;]</p><p><strong>第 15 行：</strong>训练神经网络/模型。选择输入值（“或”运算）和真标签（Y \ _真）。Epochs 决定了神经网络运行所有数据的次数。如果设置 snapshot = True，那么它将在每次训练后都验证模型。 [<a href="http://tflearn.org/models/dnn/">文档</a> &nbsp;]</p><p><strong>第 18-20 行：</strong> &nbsp;用训练出的模型做预测。在此例中，它返回返回值为真/1 的概率。 [<a href="http://tflearn.org/models/dnn/">文档</a> &nbsp;]</p><p><strong>输出标签：</strong> &nbsp;第一个结果表示， [0.] &amp; [0.] 的组合为真的概率为 4％，以此类推。训练步长表明你的训练次数。由于数据与批量大小吻合，因此数据训练一次即可完成。如果数据对于内存而言太大，则需先分割数据再进行训练。损失函数会衡量每次训练的错误总和。SGD 表示随机梯度下降和最小化损失函数的方法。Iter 会显示当前数据索引与输入项总数。</p><p>你几乎可以在任意 TFlearn 神经网络中都能发现以上逻辑和语法。学习代码最好的方法就是修改它并不断试错。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/07/image-3.png" class="kg-image" alt="image-3" width="600" height="400" loading="lazy"></figure><p>Tensorboard 可以对实验进行可视化操作，并直观观察到每个参数将如何改变训练结果。 你可以查看之前的视频，以了解如何使用 Tensorboard。损失曲线会展示每个训练步骤的错误量。</p><p>建议你先玩几个小时以适应运行环境和 TFlearn 参数。而下面我会给出一些学习建议。</p><p><strong>实验环节</strong></p><ul><li>增加训练次数</li><li>尝试添加/更改函数的参数</li><li>原代码：E.g. g = tflearn.fully_connected(g, 1, activation='sigmoid')</li><li>修改后代码： tflearn.fully_connected(g, 1, activation='sigmoid', &nbsp;<em>bias=False</em>)</li><li>输入整数</li><li>更改输入层形状</li><li>更改输出层的激活函数</li><li>更改梯度下降算法</li><li>更改神经网络计算损失的方式</li><li>尝试 “与” “非” 操作</li><li>尝试 “异或” 操作，比如把输出改为 [0.]，这样的话需要多加一层</li><li>提高学习率</li><li>找方法将每次的学习时长提高到 0.1 秒以上</li></ul><h2 id="--2">开始</h2><p>深度学习中最常见的堆栈是将 Python 与 Tensorflow 结合在一起。TFlearn 是一个运行在 Tensorflow 之上的高级框架。另一个常见的框架是 Keras。它的功能更加强大，但是我发现 TFlearn 的语法更加简洁，也更易于理解，不过它们都是运行在 Tensorflow上的高级框架。</p><p>你可以在计算机的 CPU 上运行简单的神经网络。 但是大多数实验都需要几个小时甚至几周才能完成。大多数人都会选择云服务使用现代的 GPU 来搭建神经网络。</p><p>针对 GPU 云服务器的最简单的解决方案是[FloydHub] &nbsp;<a href="https://www.floydhub.com/">17</a>。 如果你会使用命令行，那么不用5分钟即可部署好 FloydHub。<a href="http://docs.floydhub.com/getstarted/quick_start/">参考FloydHub文档</a></p><p>安装 &nbsp;<code>floyd-cli</code> &nbsp;命令行工具。如果你在操作中遇到困难，可前往 FloydHub 官网咨询。</p><p>现在使用 TFlearn，Jupyter Notebook 和 Tensorboard 在 FloydHub 上训练第一个神经网络。安装并登录 FloydHub 后，在终端输入以下命令，下载所需文件。</p><pre><code class="language-shell">git clone https://github.com/emilwallner/Deep-Learning-101.git

</code></pre><p>打开文件夹，初始化 FloydHub。</p><pre><code class="language-shell">cd Deep-Learning-101
floyd init 101

</code></pre><p>FloydHub 网页面板会在浏览器中自动打开，并提示创建一个名为 &nbsp;<code>101</code> &nbsp;的新项目。完成后，返回终端并输入相同的 &nbsp;<code>init</code> &nbsp;命令。</p><pre><code class="language-shell">floyd init 101

</code></pre><p>部署好云主机后，在命令行中：</p><p>输入 &nbsp;<code>--data emilwallner/datasets/cifar-10/1:data</code> &nbsp;在 FloydHub 上上传一个公共数据集 (我已经上传完成了) 到 &nbsp;<code>data</code> &nbsp;文件夹下，你可以在 &nbsp;<a href="https://www.floydhub.com/emilwallner/datasets/cifar-10/1">FloydHub</a> &nbsp;上查看该数据集（及许多其他的公共数据集）</p><p>输入 &nbsp;<code>--gpu</code> &nbsp;使用 GPU 云服务器</p><p>输入 &nbsp;<code>--tensorboard</code> &nbsp;启用 Tensorboard</p><p>输入 &nbsp;<code>--mode jupyter</code> &nbsp;在Jupyter Notebook 中运行程序</p><p>代码段如下：</p><pre><code class="language-shell">floyd run --data emilwallner/datasets/cifar-10/1:data --gpu --tensorboard --mode jupyter

</code></pre><p>在浏览器中启动 Jupyter，点击 start-here.ipnyb 文件。 Start-here.ipnyb 文件是一个简单的神经网络，通过它你可以学习 TFlearn 的语法。它会学习 “或” 运算的逻辑，我会在稍后详细说明。</p><p>在菜单中，点击 &nbsp;<strong>Kernel &gt; Restart &amp; Run All</strong>。 如果你看到有消息弹出，说明配置成功，即可继续工作。然后转到 FloydHub 项目，找到 Tensorboard 的链接。</p><h2 id="--3">深层神经网络</h2><p>深度学习指具有多个隐藏层的神经网络。目前已经有很多关于卷积神经网络的详细教程：<a href="https://www.youtube.com/watch?v=FmpDIaiMIeA&amp;t=1202s">教程一</a>、<a href="http://cs231n.github.io/convolutional-networks/">教程二</a>、<a href="https://www.youtube.com/watch?v=LxfUGhug-iQ">教程三</a>。然而，我们将重点介绍适用于大多数神经网络的相关概念。</p><p>如果你想训练神经网络对未训练的数据做出预测，那么就需要把握好学习与遗忘的平衡。你希望神经网络分清信号与噪声，但是也要忘记那些在训练数据中学习到的信号。</p><p>如果神经网络学习不足，则会欠拟合。如果过度训练，则会过度拟合。</p><p><em>正则化</em> &nbsp;指通过忘记某些信号来减少过拟合。（待定）</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/07/image-4.png" class="kg-image" alt="image-4" width="600" height="400" loading="lazy"></figure><p>To get an intuition for these concepts, we’ll be working with the &nbsp;<a href="https://pgaleone.eu/images/autoencoders/tf/cifar10_io_l2.png">CIFAR-10 dataset</a>. It’s a dataset with 60k images in ten different categories, such as cars, trucks, and birds. The goal is to predict which of the ten categories a new image belongs to.</p><p>为了对这些概念有一个直观的了解，我们使用<a href="https://pgaleone.eu/images/autoencoders/tf/cifar10_io_l2.png">CIFAR-10数据集</a>。该数据集包含包含 10 个类别的 6 万张图像，如汽车，卡车和鸟类。我们的目的是预测图像的类别。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/07/image-5.png" class="kg-image" alt="image-5" width="600" height="400" loading="lazy"></figure><p>通常，我们首先抓取数据、清洗数据、过滤图像数据。但是为了缩小范围，我们仅关注神经网络。你可以在 Jupyter notebook 运行所有示例<a href="https://github.com/emilwallner/Deep-Learning-101">刚刚下载过了。</a></p><p>输入层获取已映射为数字的图像，输出层将图像归为 10 类。隐藏层混合了卷积层、池化层、全连接层。</p><p>我们来比较一下单层神经网络与三层神经网络。 每个神经网络都包括卷积层、池化层、全连接层。</p><p>此案例涵盖了所有的实验。两个实验分别名为 &nbsp;<a href="https://github.com/emilwallner/Deep-Learning-101/blob/master/experiment_0_few_layers.ipynb">experiment-0-few-layers.ipynb</a> &nbsp;和 &nbsp;<a href="https://github.com/emilwallner/Deep-Learning-101/blob/master/experiment_0_three_layer_sets.ipynb">experiment-0-三层sets.ipynb</a>。</p><pre><code class="language-python"># Convolutional network building
network = input_data(shape=[None, 32, 32, 3],
                     data_preprocessing=img_prep,
                     data_augmentation=img_aug)
One set of layers
</code></pre><p>如果你运行这些代码（点击菜单栏 &nbsp;<strong>Kernel &gt; Restart &amp; Run All</strong> &nbsp;），然后看一下 Tensorboard 中的训练记录，你会发现三层神经网络的准确率比单层神经网络的准确率高 15%。因为单层神经网络学习不够会导致欠拟合。</p><p>你可以在之前下载的文件夹中运行代码。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/07/image-6.png" class="kg-image" alt="image-6" width="600" height="400" loading="lazy"></figure><p>看一下训练准确度和验证准确度。在深度学习中，把数据集一分为二是最佳的做法。一部分用于训练神经网络，另一个用于验证神经网络。通过这种方式，你可以判断神经网络对新数据的预测能力或泛化能力。</p><p>从 Tensorboard 中可以看出，训练数据的准确性高于验证数据集的准确性。该神经网络包含了背景噪声和一些阻碍项。</p><p>为解决过拟合问题，可以添加惩罚项并引入噪音。正则化是应对过拟合的常用手段，一般采用丢弃法和添加惩罚项。</p><p>丢弃正则化方法可以与类比于我们的民主制。他们没有强大的神经元来决定最终输出，而是分散了权重。神经网络被迫学习不同的模型，并结合不同的模型做出最终预测。</p><p>以下代码表示加入了丢弃层后的神经网络例子：</p><pre><code class="language-python">network = input_data(shape=[None, 32, 32, 3],
                        data_preprocessing=img_prep,
                        data_augmentation=img_aug)
network = conv_2d(network, 32, 3, activation='relu')
network = max_pool_2d(network, 2)
network = conv_2d(network, 64, 3, activation='relu')
network = conv_2d(network, 64, 3, activation='relu')
network = max_pool_2d(network, 2)
network = fully_connected(network, 512, activation='relu')
#The dropout layer
network = dropout(network, 0.5)
network = fully_connected(network, 10, activation='softmax')
network = regression(network, optimizer='adam',
                        loss='categorical_crossentropy',
                        learning_rate=0.001)
</code></pre><p>可以发现，两者之间唯一的不同就是一个有丢弃层，一个没有丢弃层。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/07/image-8.png" class="kg-image" alt="image-8" width="600" height="400" loading="lazy"></figure><p>在神经网络的每一层中，神经元彼此依赖。有的神经元权重大，有的神经元权重小。丢弃层随机丢弃一部分神经元。这样，每个神经元对最终结果做出的贡献各不相同。</p><p>第二种防止过度拟合的方法是在每一层上应用 L1 或 L2 正则化函数。</p><p>假设你要描述一匹马。如果描述得太详细，那么会排除调很多匹马。相反，如果描述的过于泛化，那么可能会囊括了其他动物的特征。L1 和 L2 正则化有助于神经网络区分不同特征。</p><pre><code class="language-python">network = input_data(shape=[None, 32, 32, 3],
                        data_preprocessing=img_prep,
                        data_augmentation=img_aug)
network = conv_2d(network, 32, 3, activation='relu', regularizer='L2')
network = max_pool_2d(network, 2)
network = conv_2d(network, 64, 3, activation='relu', regularizer='L2')
network = conv_2d(network, 64, 3, activation='relu', regularizer='L2')
network = max_pool_2d(network, 2)
network = fully_connected(network, 512, activation='relu', regularizer='L2')
network = fully_connected(network, 10, activation='softmax')
network = regression(network, optimizer='adam',
                        loss='categorical_crossentropy',
                        learning_rate=0.001)
</code></pre><p>如果我们比较之前的实验的话，会发现结果类似。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/07/image-9.png" class="kg-image" alt="image-9" width="600" height="400" loading="lazy"></figure><p>添加正则化函数的神经网络优于没有正则化函数的神经网络。</p><p>L2 正则化惩罚过于复杂的函数。它测试每个函数对最终输出有多大贡献，并惩罚那些系数较大的函数。</p><p>另一个核心参数是批大小，即每个训练步骤的数据量。下面是批大小的比较。</p><pre><code class="language-python">#Large batch size
model.fit(X, Y, n_epoch=50, shuffle=True, validation_set=(X_test, Y_test), show_metric=True, batch_size=2000, run_id='cifar_large_batch_size')
</code></pre><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/07/image-10.png" class="kg-image" alt="image-10" width="600" height="400" loading="lazy"></figure><p>正如我们在结果中看到的那样，批大小越大，迭代次数越少，下降方向越准。相反，批大小越小，随机性越大，迭代次数越多。尽管批大小越大迭代次数越少，但是却需要更多的内存和时间来进行计算。</p><p>最终实验分别比较了低学习率、中等学习率、高学习率。</p><pre><code class="language-python">#Large learning rate
network = regression(network, optimizer='adam',
                        loss='categorical_crossentropy',
                        learning_rate=0.01)
</code></pre><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/07/image-11.png" class="kg-image" alt="image-11" width="600" height="400" loading="lazy"></figure><p>由于对神经网络的影响较大，学习率通常被认为是最重要的参数之一。它规定了如何在每一步上调整预测的变化。如果学习率太高或太低，则可能会无法收敛。</p><p>没有固定的设计神经网络的方法。我们必须通过实验来验证自己的模型。学习其他人的项目，添加层并调整超参数。如果你可以实现大批量计算能力，那么就可以创建程序来设计和调整超参数。</p><p>工作完成后，你还需要点击 FloydHub 面板中的 “取消”。</p><h2 id="--4">下一步</h2><p>在 TFlearn 的<a href="https://github.com/tflearn/tflearn/tree/master/examples/images">官方演示仓库</a>中，你可以了解到一些效果最好的卷积神经网络模型。你可以尝试复制代码，并改善对 CIFAR-10 数据集的验证。迄今为止，最好的验证结果是 96.53％（格雷厄姆，2014年）。</p><p>你还要学习 Python 语法和命令行。这会减少不必要的认知负担，这样你就可以专注学习深度学习的相关概念。先学习 Codecademy 的<a href="https://www.codecademy.com/tracks/python">Python课程</a>，然后学习<a href="https://www.codecademy.com/learn/learn-the-command-line">命令行</a>。如果你全天学习的话，三天以内就能学完。</p><h2 id="--5">致谢</h2><p>感谢 Ignacio Tonoli、 Per Harald Borgen、 Jean-Luc Wingert、 Sai Soundararaj 和 Charlie Harrington 阅读本文初稿。感谢 &nbsp;<a href="https://github.com/tflearn/tflearn/blob/master/examples/basics/linear_regression.py">TFlearn community</a> &nbsp;提供文档和代码样本。</p><p>原文：<a href="https://blog.floydhub.com/my-first-weekend-of-deep-learning/">My First Weekend of Deep Learning</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 服务生跳出舒适区自学编程，成功挑战人生第一次黑客马拉松 ]]>
                </title>
                <description>
                    <![CDATA[ 我没有编程或工程背景，大学时我学的是生物学，根本不知道毕业要做什么。 我的第一份工作是打陌生电话的销售员，几乎赚不到什么钱，工作起来感觉也非常痛苦。 几次推销失败之后，我辞职找到了一份在餐馆准备蔬菜的工作——我可从来没想到自己会以这种方式跟植物打交道。 我需要新的方向，也准备好了要接受新事物。 而帮助我开启崭新的职业发展之旅的，是良好的职业操守、强烈的学习意愿和一些关键资源。也正是三点带领我参加了一场编程比赛，走出自己的舒适区。 在餐厅工作时，我就听说过那些自学编程并成功转型程序员的故事。因为我愿意尝试新事物，所以下班后，我就开始在 freeCodeCamp 上学习在线课程。 后来我慢慢开始全天候学习，辞职后跟着 freeCodeCamp 上的课程，把学习全栈 JavaScript 当作我的新工作。 我花了一年半的时间学习编程，当然这也给我带来了回报——纽约的一家年销售额 20 多亿美元的时装公司给了我一个初级开发者的 offer，我在那里工作了挺长时间。 对我来说，学习是重中之重。即使已经入职，下班后我也会继续学习，我会重点研究与我的工作职责相关的最佳实践，比如说使用 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/how-i-won-my-first-hackathon/</link>
                <guid isPermaLink="false">5ed70199db4be8080eb70ce2</guid>
                
                <dc:creator>
                    <![CDATA[ Lola Wei ]]>
                </dc:creator>
                <pubDate>Wed, 03 Jun 2020 01:54:49 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2020/06/jantine-doornbos-xt9tb6oa42o-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>我没有编程或工程背景，大学时我学的是生物学，根本不知道毕业要做什么。</p><p>我的第一份工作是打陌生电话的销售员，几乎赚不到什么钱，工作起来感觉也非常痛苦。</p><p>几次推销失败之后，我辞职找到了一份在餐馆准备蔬菜的工作——我可从来没想到自己会以这种方式跟植物打交道。</p><p>我需要新的方向，也准备好了要接受新事物。</p><p>而帮助我开启崭新的职业发展之旅的，是良好的职业操守、强烈的学习意愿和一些关键资源。也正是三点带领我参加了一场编程比赛，走出自己的舒适区。</p><p>在餐厅工作时，我就听说过那些自学编程并成功转型程序员的故事。因为我愿意尝试新事物，所以下班后，我就开始在 freeCodeCamp 上学习在线课程。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/06/image.png" class="kg-image" alt="image" width="600" height="400" loading="lazy"></figure><p>后来我慢慢开始全天候学习，辞职后跟着 freeCodeCamp 上的课程，把学习全栈 JavaScript 当作我的新工作。</p><p>我花了一年半的时间学习编程，当然这也给我带来了回报——纽约的一家年销售额 20 多亿美元的时装公司给了我一个初级开发者的 offer，我在那里工作了挺长时间。</p><p>对我来说，学习是重中之重。即使已经入职，下班后我也会继续学习，我会重点研究与我的工作职责相关的最佳实践，比如说使用 Node.js + Selenium 编写自动化测试。</p><p>我每周会花 10 到 15 个小时学习 Selenium，这能帮助我更快地完成自己的工作，也让我有更多时间向同事们学习。我尽一切可能利用工作时间——在电梯里、办公室里——与同事交流经验，了解他们在做什么，工作职责是什么。</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>即使我觉得自己落后于他人，但我却有自己的一套思考秘诀，这是我从尼尔·拉克拉姆的销售策略书《SPIN Selling》中学到的：通过四个步骤找到大企业内部的问题。</p><p><strong>步骤 1：了解运作机制</strong></p><p>因为要学习电子商务的内部运作机制，所以我开始与策划部门的员工交谈。我们两个部门之间只隔了一个餐厅，他们负责确定进货量和出售价格。</p><p>于是我会偶尔离开办公桌去问他们问题，例如如何决定购买的库存量，如何设置价格，零售业和电子商务是否有不同的定价规则等。通过这些问题，我了解了策划部门如何引入新的服装线，也了解了他们如何计算出售价格。</p><p><strong>步骤 2：<strong>通过提问来发现问题</strong></strong></p><p>在了解了策划的工作原理之后，我开始寻找购买过程中可能出现的问题。当他们设定了库存数量，公司购买时会出错吗？设置价格时会出错吗？</p><p>我试图找到这其中自己能够解决的错误。</p><p><strong>步骤 3：继续提问，探索究竟</strong></p><p>问了几天问题之后，我了解了定价方面的问题，有时候网站上的价格确实会设置错误。这个问题是如何产生的，又意味着什么呢？</p><p>我询问了错误发生的频率以及它们可能导致的问题。</p><p><strong>步骤 4：探索解决方案的价值</strong></p><p>如果自动脚本能够发现所有定价不正确的商品，那将有多大帮助呢？</p><p>我问了策划人员一些问题，这些问题能帮助我弄清楚我可以为他们提供什么样的价值。如果我在公司的黑客马拉松中找到解决方案，那么我想确保这个解决方案能产生好的影响。</p><p>交谈之后，我发现该项目值得一试。3/4 的策划助理每周都会花费 30 分钟人工修改错误定价。自动化系统将为他们节省很多时间——估计每年 100 个小时。</p><p>现在我已经知道了这个黑客马拉松，虽然官方还没有发布通知，但是我心里已经有了一个想法。尽管我不确定自己是否能够成功解决这个问题，但我非常有自信。发布比赛通知之前，我都专注于自己的工作。等通知发布，我就可以准备开始了。</p><h2 id="--1">与策划人员确认项目</h2><p>两星期后，我收到了比赛通知。黑客马拉松将在一个月后进行，比赛将持续两天，第二天（也就是星期五）进行项目展示。</p><p>项目评价标准包括构想原创性，业务影响力，原型完整度以及充分的项目演示。</p><p>由于未知数太多，我无法预测自己来不来得及准备。</p><p>我去了策划部，并确认当时的问题仍然存在——他们仍然在手工修改错误定价。</p><p>我很高兴得知公司有专门的电子商务销售员，她负责报告所有错误定价并进行处理。她能够给我提供更多信息，也能够确认该问题是否值得解决。</p><p>然而，她当时在度假，我等了一个星期才和她说上话。时间越来越紧张了，我当时还毫无进展。</p><p>离比赛还有三周的时候，她终于回到了公司，于是我了解了更多细节。她确认了我听说的那些问题，并说如果有系统可以自动扫描网站并找到错误定价，那帮助太大了！</p><p>在与策划人员的进一步交谈中，我了解到了上传商品价格到网站上的步骤：策划人将价格清单从 Excel 电子表格复制粘贴到 SAP，该软件可以完成许多工作，例如为零售商管理库存。然后，SAP 将价格推到我们的电子商务网站。</p><p>我想到可以将 Excel 表与网站价格进行比较以发现问题。我会搭建一个简单的网站，策划人员可以上传 Excel 价格清单。</p><p>我会写一个脚本，该脚本会读取 Excel 价格清单，然后实时与网站价格进行比较。然后，所有的价格差异都会汇总到一个列表中，并发送给策划人进行审查。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/06/image-1.png" class="kg-image" alt="image-1" width="600" height="400" loading="lazy"></figure><p>我很高兴想到了解决办法，于是我开始向部门其他开发人员解释该项目的想法，询问是否有人愿意加入我的团队。但是，我提出了一个严格的要求，即任何人都必须首先亲自与我们的策划人员交谈，并从他们的角度了解问题。</p><p>作为 SPIN 系统的一部分，在项目开始前，我需要愿意了解工作流程和目标问题的队友。然而，没有人愿意加入我的团队，但是其他工程师传授给了我如何编写项目代码以及学习哪些技术的宝贵建议。</p><p>所以我只能孤军奋战，但是我很兴奋，对自己的解决方案充满信心。</p><h2 id="--2">转变思路</h2><p>黑客马拉松大赛召开两周前，我再次拜访了一位助理策划员，并向她解释了我的想法，即使用自动化脚本来比较 Excel 价格清单和网站价格并汇报价格差异。她很快告诉我，我的想法毫无用处，因为 Excel 的价格和网站的价格是始终相同的。</p><p>助理策划员继续解释说，原始的 Excel 价格清单是由一个工具生成的，该工具会把产品成本、交付成本和其他相关因素考虑在内，然后生成 Excel 价格清单，我们的策划人员随后将其上传到 SAP 软件，从而上传到网站。</p><p>如果策划者不小心输错了制造成本或交付成本，那么 Excel 表上也是错误的价格。</p><p>“因此，你是说没有一个包含全部正确价格的总清单吗？”我问。</p><p>“是的，”助理策划员说。</p><p>我听到这话难过极了，数周的计划、思考、讨论、等待都功亏一篑了。</p><p>距离黑客马拉松仅两周了，在其他领域我又经验不足，而我也没有足够的时间能再调研一番了。</p><p>我不得不重新考虑这个项目。</p><p>由于时间紧迫，我没法像刚开始那样耗费精力调研了。相反，我让策划员为我提出项目需求。</p><p>我回到策划部门，提出了一个不同的问题：“想象一下，如果你有一个脚本可以自动从任何地方（例如 Excel 电子表格，数据库或网站）抓取数据，并自动做数据运算，那么使用这样的脚本，你可以解决什么问题呢？”</p><p>助理策划员想了几分钟，说这个脚本可以用来检查售价。她解释说，每周三，网站都会批量销售，销售额将列在我们的主页上，并使用诸如 “所有男式外套均减 25％” 或 “所有女式连衣裙均减 15％” 之类的标题。</p><p>每周三早上，我们的 3-4 名助理策划员都会花费 30 分钟手动检查网站，确认折扣份额。如果策划员发现售价错误，就会发送消息给电子商务销售商。</p><p>只有 IT 人才能够更改实时销售价格。为了简化 IT 部门的工作，电子商务销售商可能会批量向 IT 人员发送错误列表。</p><p>这意味着，即使发现了定价错误的商品（除非紧急情况），错误也不会得到及时的更正。</p><p>这个问题与我最初的计划相差不多：使用自动化脚本检测错误定价，节省策划人员、销售商和 IT 人员的时间，也会迅速修正错误定价从而改善客户体验。</p><p>我只需要一个可以与网站价格进行比较的价格主清单，但是策划人员告诉我，不存在 “100％ 准确” 的主清单，至少没有纸质清单。</p><p>他们说：“但是我们脑子里有这么一个清单，因为我们非常了解我们的产品。正因如此，我们才能检查网站，发现错误的价格。”</p><p>考虑了片刻之后，我问：“如果你不必遍历网站呢？如果你只需单击一下，就可以把所有的实时网站价格都整理到一个清单中，那会怎么样呢？”</p><p>“那会大大加快我们的检查速度。”</p><p>我们讨论出了一个计划：我搭建一个简单的网站，策划人员向网站上传数十个商品名称，立即就能获得一个清单，清单上记录了这些商品的实时网站价格和销售折扣。</p><p>这个想法是切实可行的，它有实用性——节省人力，加快质检，改善客户体验——我对自己的能力充满信心（即便我还不知道到底该怎么做）。</p><p>我回到办公桌，打开文档写了计划书，然后交给了策划部和销售商，我没有时间可以耽搁了。</p><p>他们确认了我的计划。确定想法后还剩一个星期了，是时候弄清楚我该如何把计划付诸实践了。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/06/image-2.png" class="kg-image" alt="image-2" width="600" height="400" loading="lazy"></figure><p>我去找了公司里更高级的工程师（很多人都比我经验丰富），我问他们：“怎么写一个自动返回网站商品价格的脚本呢？”</p><p>与几位工程师交谈后，我了解到网站产品页面是通过查询内部电子商务数据库来获取价格的，而内部电子商务数据库又是从 SAP 获得价格信息的。这是一个 Redis 数据库，它的文档齐全，可以找到如何获取价格信息的详细解释。</p><p>我找到了数据库工程师，并了解到我可以只用一个数据库调用命令就可以检索整个项目清单的网站价格和销售价格。</p><p>制定好计划之后，我还有很多工作要做。我曾试图招募其他工程师，但大家对这个项目都没有什么兴趣，特别是我还要求他们要与策划人员直接沟通。</p><p>在没有工程团队的情况下，我下班后就独自研究资料，尝试调用数据库，研究如何编 JavaScript 代码以读取 Excel 电子表格。</p><h2 id="--3">转战主营品牌</h2><p>黑客马拉松前一天，出于好奇，我问策划人员他们如何知道要出售哪些库存的。他们回答说，公司采购部门负责做这些决定，所以我又去跟采购员交谈。</p><p>在我们的交谈中，他们提到了要把这个项目介绍给我们主营品牌中的一些同行。</p><p>“我们的主营品牌？” 我问。“ 你们不是做主营品牌相关工作吗？”</p><p>“不，”他们说，“这层的销售和策划专门针对小品牌，占我们总收入的 15％，楼上才是我们的旗舰服装品牌。”</p><p>这可咋办？我知道我们公司有几个品牌，但是在黑客马拉松前一天，我发现我的项目只能用于公司最小的品牌。</p><p>我的编程能力较弱，团队中没有其他工程师，而且我需要做一些大事来打动评委，所以现在看起来，我的项目完全没有希望了。</p><p>我走上楼，开始询问我在哪里可以找到我们主营品牌的策划部。如果要做成有意义的项目，那么我一定要与他们谈谈。</p><p>主营品牌策划部至少有 20-40 位策划人员，我该问谁呢？</p><p>我需要找到一个既理解我的想法，又了解我未触及的领域的人。</p><p>如果主营品牌使用其他系统怎么办？我需要找一个可靠的人，我没有时间了。</p><p>离黑客马拉松还有一天，我走了一条捷径。</p><p>策划部的办公空间是开放式的，所以，当我与主营品牌的一位策划人员谈论我的黑客马拉松想法时，我故意提高嗓门，在过道上走来走去，环顾其他策划人员。这样我能一次吸引多达 8 位策划人员的注意力。</p><p>其中一位兴趣极大，他问了我很多问题，他对 Redis 数据库以及是否能找到除定价之外的其他信息特别感兴趣。</p><p>我向他展示了数据库文档，我们很快就看完了。</p><p>他提到，除了定价外，还有其他一些有用的信息，例如这些物品是否列出在了网站上、商品类别。然后，他向我介绍了主营品牌的销售商，他也认为我的项目不仅仅在定价方面有所用途。</p><p>我没有足够的时间来扩展项目需求了，但是我们同意我可以以一种足够灵活的方式来构建项目，以供小品牌或主营品牌的策划人使用。</p><p>同事的支持和对项目价值的认可正是我前进的动力！</p><p>我有信心，有资源，有研究而且有能力。即使我没有，也无计可施了——黑客马拉松将于第二天早上开始，不能再等了。</p><h2 id="--4">寻找队友</h2><p>黑客马拉松将于周四周五举办。</p><p>当我查看日历时，我发现自己周五下午必须要参加自己的家庭聚会，而展示和评审都将在周五进行，这意味着我绝对、必须找到一个能够做项目展示的队友，否则我还没写代码呢，项目就提前夭折了。</p><p>我去找了公司小品牌的策划员，我与他们已经接触了几周。我问他们是否能够在周五做项目展示，但是看起来没有人感兴趣。有些人告诉我他们周五要开会，而有些人则说他们公开演讲时会紧张。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/06/image-3.png" class="kg-image" alt="image-3" width="600" height="400" loading="lazy"></figure><p>于是我直接去找了我们主营品牌的策划员奥利弗，他对我的项目非常感兴趣。他面容友善，受人欢迎（尽管他才 20 多岁，但头发已经变灰了）。他在大学毕业后短短四年内就成为了高级策划师，而大多数人要花费五到八年才能做到这个职位。他的办公桌上摆了很多奖杯，也有很多小零食。</p><p>奥利弗立刻同意帮我做项目展示。</p><p>我们下楼去了黑客马拉松专用的会议室，并作为一个团队签上了我们的名字。我们的项目名为 PriceSeeker。</p><p>另外还有 9 个团队参加比赛，其中大多数由高级工程师组成。一些项目经理和 UX 设计师也是他们团队的一部分。除了我的队友外，其他竞争者都来自于电子商务部门。</p><h2 id="--5">简化设计</h2><p>我脑海里已经有了一个计划：搭建一个简单的网站，让用户上传 Excel 项目清单。</p><p>我的网站将解析 Excel 电子表格，检索项目列表，并从 Redis 数据库获取价格。然后，网站会返还策划者一个包含项目及其价格的新的 Excel 电子表格。</p><p>这样一来，他们无需手动浏览网站来查看商品价格，只需点击一下按钮，便可以立即查看所有相关的网站价格——这使价格检查既容易又方便。</p><p>我们有两天的时间来搭建项目，然后将其提交给评审，他们是电子商务部门的高级领导。我的直系领导是评委之一——就是他把黑客马拉松介绍给我的。在比赛期间，我可以向他咨询代码问题。</p><p>我满怀激动地开始了。</p><p>但是很快，我便遇到了难题：我代码水平薄弱，不知道如何搭建一个基本的 HTML 网站来读取上传的 Excel 工作表。</p><p>我向高级工程总监寻求建议，他建议我简化设计，只制作一个带有文本域的表格，用户可以复制 Excel 的内容到表格然后提交。</p><p>提交后，系统会从数据库请求价格，然后返回简单的含价格清单的 HTML 表单中，用户可以根据需要将其复制到 Excel 中。</p><p>不通过编程来读取 Excel 文档，这简化了很多步骤。</p><h2 id="-priceseeker">搭建项目 PriceSeeker</h2><p>即使有别人相助，日程依旧紧张。</p><p>我花了一些时间与策划员核对并确定了设计原型。我从奥利弗的桌子上抢走了很多零食。每当需要帮助或卡在编程上时，我也会与其他工程师交谈。</p><p>我并不孤单，很感谢他们的帮助，但我有责任努力克服困难，并付出体力和精力来编写代码。</p><p>我非常清楚自己在比赛中的劣势，但是第一天结束时，我就已经成功地制作了一个不错的原型。如果表格只有 4 列，它会返回一些信息，但是如果表格列数太多，它就毫无反应。</p><p>一天结束了，我没有时间去调查或排除故障，所以我将 PriceSeeker 上传到了GitHub Pages，我通过电子邮件将网站地址发送给了一些策划员，然后回家了。我希望能好好地睡一觉，也期待能收到好消息。</p><p>第二天我到公司时，发现奥利弗给我发了一封邮件—— PriceSeeker 无法正常工作，而且他发送给了我几张操作截图。</p><p>看到截图后，我意识到自己没有正确解释如何将项目复制和粘贴到表单中。我给奥利弗发了一张截图，向他展示操作方法。两分钟后他回了邮件，说系统正常工作了。</p><p>花了一些时间整理自己的思路后，我去了奥利弗的办公桌。在他的办公桌旁还有其他几位策划员，当时他们正在讨论电脑屏幕上的 PriceSeeker。</p><p>奥利弗已将该网站地址发给了他团队中其他策划员。他们正在讨论策划部与工程部合作真是锦上添花。我受到了鼓舞，也很开心这个项目可能促进新的协作。</p><p>奥利弗对此特别兴奋，因为他看好不同部门之间的协作。也许我们可以通过鼓励部门之间的沟通来创造更多的机会来发现问题，并提出有效的解决方案。</p><p>由于我知道自己不会参加项目展示，所以我看奥利弗预演了展示，他展示了 5 分钟。首先，他展示了 PriceSeeker 网站，并进行了实操。他将项目复制并粘贴到表单中，然后提交，并解释了这个网站的意义。</p><p>他继续解释了项目的不足，并举例说明了其他有用的数据。他解释了自动化网站能如何减少人工并改善客户体验。</p><p>他得出结论，尽管 PriceSeeker 当前仅能返回定价，但它还有无限可能。听到他谈论该网站的可能性可真是太神奇了。他完全专注于诸如 “减少人工工作量” 和 “增加年销售额” 之类的业务术语。他本人就是一名策划员，所以他能够比我更清楚地展示我们的项目。</p><p>展示后，我问了一些项目价值相关的问题，这能让他深入解释项目潜在影响力：</p><p>“该项目可以减少多少人工成本？”</p><p>“自动检查我们的网站数据将如何提高订购适量库存的能力？”</p><p>“除了策划部外，自动检查网站数据还能使其他团队或部门受益吗？为什么？”</p><p>我能问出这些问题，是因为我从《Spin Selling》一书中学到了提问技术。书中说道，卖方有时无法直接向买方展示商品，因此他们需要依靠中介来传递消息，因此，要通过向中介提出问题来指导他们，让他们以自己的语言解释商品优点。</p><p>这正是我所做的，我问了很多问题，让奥利弗进一步解释了我们项目的增值点。</p><p>评审开始前大约一个小时，我不得不离开公司。我很抱歉自己无法出席比赛，但我很高兴，因为我们的项目进展十分顺利。</p><p>我祝队友好运，并请一位同事发短信告诉我我们的项目是否排在前三名。</p><h2 id="--6">结果揭晓</h2><p>离开后，我乘了两个小时火车。在路上，我做好了静待比赛结果的准备。</p><p>我觉得我们是唯一一支在黑客马拉松正式开始之前就启动筹备的队伍，也是是唯一一支在项目展示中聚焦于商业影响力的队伍。</p><p>因此我有一定的信心，但我还是感到紧张，因为有太多无法控制的事情了，可能我们无法夺得前三甲或拔得头筹。</p><p>我一直在幻想所有可能的突发事件，而我却无能为力。也许评审会认为我们的项目步骤出错了，于是我们会被取消参赛资格。也许奥利弗没能做项目展示。也许另一支团队会一鸣惊人，震惊全场。</p><p>坐在火车上时，我意识到，计划赢得黑客马拉松并没有什么用，因为这不是我能控制的。我决定——不管结果如何，我都会继续搭建我的项目，并努力让它实现 20,000-80,000 美元的利润。</p><p>这个数字我也只是随口一说，但我感觉是可以实现的。为了实现这个目标，我知道比赛结束后自己还有很多工作要做。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/06/image-4.png" class="kg-image" alt="image-4" width="600" height="400" loading="lazy"></figure><p>我下了火车，然后去参加家庭聚会。我心系比赛，但是知道现在自己也无能为力了。我没有看手机，这就是为什么我隔了一段时间才看到短信的原因。</p><p>结果出来了，我们赢了！</p><p>看到消息后，我既震惊又开心又感到欣慰。做了大量工作，经历了这么多等待和苦苦挣扎，我终于有所收获。</p><p>胜利的喜悦并没有持续太久，因为我又开始考虑所有负面的可能性。为了赢得这场胜利，我在公司做了很多事情，但我却从未得到过明确的允许。</p><p>我给了策划员一份指向数据库文档的链接，但很显然，这些策划员不是我们电子商务部门的成员。我把黑客马拉松项目托管在了自己的个人 GitHub 中，而不是托管在公司的代码存储库中。午休时、下班后，我偷偷溜走跟策划人员谈话，这意味着我的同事们可能会争辩说我忽略了自己的日常工作。</p><p>不仅如此，我还担心项目的社会影响。我资质尚浅，如果我击败了高级工程师，那他们以后会怎么对待我呢？</p><h2 id="--7">比赛结束后</h2><p>周末过后我回去上班，但我担心的情况并没有发生。</p><p>当我到公司时，一群人击掌并祝贺我。但是我的日常工作量与黑客马拉松之前的工作量差不多。上级工程管理人员告诉我，不管是我还是其他工程师，现在都没有时间和官方许可来完成 PriceSeeker。</p><p>我们的计划负责人表示，他们正在重组某个大型项目，至少几个月都无法把时间花在我的项目上。我沮丧地发现，虽然我收到了同事的积极反馈，但我所有的努力只是构成了一个让自己引以为傲的的副业，但实际上却毫无用处。</p><p>黑客马拉松结束后，我有很多工作要做，所以我继续回到了工作上来，下班后每天花费 5-15 个小时研究 Selenium 最佳实践。我花了一两个月的时间来提升技能，这样我便能在规定时间内完成自己的本职工作，白天也能有几个小时研究自己的项目。</p><p>PriceSeeker 差不多完成了，只剩一点小问题了。我觉得可能还需要一两天才能完成，但是如果没有官方许可，我便不得不自己秘密完成项目。</p><p>同事们都不知道我花时间在 PriceSeeker 上，他们也不在乎，因为我每次都按时完成并提交本职工作。</p><p>经过大量的努力和修补，我设法解决了 PriceSeeker 的所有错误。</p><p>我兴奋地跑到品牌策划部向他们展示了项目模型，但他们告诉我他们不再需要它了。我简直不敢相信。我非常震惊，以至于我连问题都问不出来。</p><p>后来我发现，错误定价的问题是暂时的，这是由于最近显示定价方式的变化而引起的。黑客马拉松后，定价显示系统已经更新了，可以让策划员更快地找到任何错误定价。</p><p>事后看来，我本应该在黑客马拉松的准备阶段就意识到这一点，但是当时我对自己的计划非常有信心，所以没能对可能的问题进行适当的调查。不管我再怎么不情愿，PriceSeeker 在相当长的一段时间里都将毫无用处。</p><p>奥利弗告诉我，对于我们的主营品牌来说，PriceSeeker 毫无用处。但是，他告诉我网站上的有些商品有时会平白无故消失，这意味着库存中的某些商品和颜色会意外地从我们的电子商务商店中被删除。</p><p>这种情况很少发生，但是直到查看每周的电子商务销售报告，策划员才能注意到某些商品的销售额为零，然后他们会检查这些商品是否展示在了网站上，到那时他们才会标记要退回网站的商品。</p><p>这种工作方式又增加了人工成本，奥利弗建议我们可以修改 PriceSeeker，从而让它检测网站上是否有某商品。</p><p>浏览完数据库文档后，我了解到查询数据库以查看某项是否在我们的网站上非常容易实现，于是我迅速为 PriceSeeker 更新了这一功能。</p><p>奥利弗告诉我，我们的电子商务销售商负责检查网站库存质量，他们将从 PriceSeeker 的这项新功能中受益颇多。我与每个品牌的电子商务销售商接洽，发现他们对我向他们展示的产品感到非常满意。</p><p>他们立即将数百个项目复制并粘贴到其中，发现 PriceSeeker 能够准确找到所有未在电子商务商店中列出的所有项目，然后他们开始每周使用几次 PriceSeeker。</p><p>在接下来的几周中，我继续与策划员和销售商接洽，试图找到 PriceSeeker 其他可能有用的功能。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/06/image-5.png" class="kg-image" alt="image-5" width="600" height="400" loading="lazy"></figure><p>我了解到，策划部最近成立了一个数据分析团队，该团队负责构建自动化脚本和面板，以帮助策划员更好做出决策。报告未展出的电子商务库存实际上已经在他们的待办事项列表中。</p><p>我很喜欢与数据分析团队聊天交流，但是由于没有正式批准 PriceSeeker 或其他产品的搭建工作，我们谈论的大部分都是理论内容。不过，我很高兴知道我的项目已经取得了一些成就，并且终于看到了曙光。</p><p>不久之后，我收到了咨询公司人力资源部门的电子邮件，他们想安排一个会议。</p><p>我进去跟他们聊了聊，他们告诉我公司即将裁员。他们要辞掉几位顾问，我是其中之一。在劳动合同终止前，我收到了三份通知。</p><p>我知道时尚品牌一直面临着销量下降的问题，解雇顾问比解雇全职雇员要容易得多。尽管如此，我仍然很惊讶我竟然会被裁员。</p><p>最后几周里，我整理了日常工作，并特别把精力放在了 PriceSeeker 上。我想看看自己是否可以量化它所产生的财务影响，但是检测网站未列出的库存的价值并不容易量化。</p><p>当我去拜访我们的电子商务销售商时，他们告诉我其影响是无法衡量的，因为其中涉及许多复杂因素，例如 PriceSeeker 能检测多少未列出的商品，它们来自哪个品牌，这些商品的受欢迎程度以及剩余的库存量。</p><p>奥利弗总结说：“尽管我们不能量化 PriceSeeker 带来的价值，但我们确信它一定产生了积极影响。”</p><p>劳动合同结束后，我回顾了自己在这家公司的经历：我入职 7 个月，有了很多激动人心的体验，其中之一就是黑客马拉松。</p><p>黑客马拉松结束后，许多同事告诉我我胜利的原因是我 “专注于业务影响”。尽管他们夸奖了我，但是我觉得这个原因听起来太过空泛。</p><p>就个人而言，我将自己的胜利归功于充足的准备：我在黑客马拉松正式开始之前就做好了积极的准备。这样，我能不断获得改进方案所需的反馈，以便更好地改进。</p><p>如果没有别人的帮助，我不会取得比赛的胜利。在项目过程中，我是主要负责人，我要不断推进、探索、自学，而且还要努力工作。</p><p>事实上，我并不孤单。那些向我展示出热情的人——我的工程总监和队友奥利弗——他们就是促使我参加比赛的人，而两年前我根本不会参加这种比赛。</p><p>我还在学习，还在尝试新事物，我希望能参与更多的协作，为公司创造有价值的产品，这也会让我的生活更有意义。</p><p>回想起来，我仍然很难相信，像我这样奋力挣扎才能转行的人，竟然能够进入企业，并且能带动同事做些事情。</p><p>对我而言，黑客马拉松是第二次机会，而我充分利用了它。</p><p>原文：<a href="https://www.freecodecamp.org/news/how-i-won-the-hackathon/">How I Won my First Ever Hackathon – 2 Wild Days of Research, Design, and Coding</a>，作者：Moshe Siegel</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
