<?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[ 长河渐落晓星沉 - 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[ 长河渐落晓星沉 - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 14 May 2026 19:59:00 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/author/chang/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Web 前端开发教程丨用 TypeScript 自动化发布博客文章 ]]>
                </title>
                <description>
                    <![CDATA[ 我最近尝试养成写作习惯，写的文章比较多，包括一些前端开发教程。我常用 Medium [https://medium.com/@leandrotk_], dev.to [https://dev.to/teekay] 和 Hashnode [https://hashnode.com/@teekay]  发布博客，同时也想在我的个人站点 [http://leandrotk.github.io/tk]发一份。 我的个人站点很简单，只采用了基本的 HTML、CSS，以及少量 JavaScript 这些 Web 前端开发常用语言。但是我考虑改进发布过程。 从何入手呢？ 我在 Notion 上做了这个博客写作路线图： 这是一种简单的看板，我喜欢用它把所有想法呈现出来（或者说把想法数字化）。我还使用它来创建草稿，不断修改，然后发布。 所以我用 Notion 写草稿，完成后，复制 Notion 的内容并将其粘贴到在线工具中，将 Markdown 转换为 HTML 格式，然后使用 HTML 来创建实际发布的文章。 但这只是正文，即页面的内容。我需要创建整个 HTML，包括头部内容，正文和页脚。 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/automating-my-blog-posts-publishing-process-with-typescript/</link>
                <guid isPermaLink="false">5f3b3652c8da7105cbc14b76</guid>
                
                <dc:creator>
                    <![CDATA[ 长河渐落晓星沉 ]]>
                </dc:creator>
                <pubDate>Tue, 18 Aug 2020 02:07:12 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2020/08/cover.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>我最近尝试养成写作习惯，写的文章比较多，包括一些前端开发教程。我常用 <a href="https://medium.com/@leandrotk_">Medium</a>, &nbsp;<a href="https://dev.to/teekay">dev.to</a> 和 <a href="https://hashnode.com/@teekay">Hashnode</a> 发布博客，同时也想在<a href="http://leandrotk.github.io/tk">我的个人站点</a>发一份。</p><p>我的个人站点很简单，只采用了基本的 HTML、CSS，以及少量 JavaScript 这些 Web 前端开发常用语言。但是我考虑改进发布过程。</p><p>从何入手呢？</p><p>我在 Notion 上做了这个博客写作路线图：</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2020/08/blog-roadmap.png" class="kg-image" alt="blog-roadmap" width="600" height="400" loading="lazy"></figure><p>这是一种简单的看板，我喜欢用它把所有想法呈现出来（或者说把想法数字化）。我还使用它来创建草稿，不断修改，然后发布。</p><p>所以我用 Notion 写草稿，完成后，复制 Notion 的内容并将其粘贴到在线工具中，将 Markdown 转换为 HTML 格式，然后使用 HTML 来创建实际发布的文章。</p><p>但这只是正文，即页面的内容。我需要创建整个 HTML，包括头部内容，正文和页脚。</p><p>这个过程乏味无聊，但好消息是它可以自动化操作。这篇文章会阐述如何自动化。我想向你展示我创建的这个新工具的幕后花絮，以及我从这个过程中学到的知识。</p><h2 id="-">功能</h2><p>我的主要想法是准备发布整个 HTML 文章。正如我之前提到的， <code>&lt;head&gt;</code> 和 <code>&lt;footer&gt;</code> 部分的变化不大。所以我可以将它们用作“模板”。</p><p>使用此模板，我提供的数据可以随着我撰写和发布的每篇文章而改变。数据是模板中包含 <code>{{ variableName }}</code> 的变量，举个例子：</p><pre><code class="language-html">&lt;h1&gt;{{ title }}&lt;/h1&gt;

</code></pre><p>现在，我可以使用模板并将变量替换为实际数据，即每篇文章的特定信息。</p><p>第二部分是博客正文。在模板中，用 <code>{{ article }}</code> 表示。该变量将被从 Notion Markdown 生成的 HTML 代替。</p><p>当我们从 Notion 复制和粘贴笔记时，我们得到了带有 Markdown 样式的内容。本项目会将 Markdown 转换为 HTML，并将其用作模板中的 <code>article</code> 变量。</p><p>为了创建理想的模板，我总结了所需创建的所有变量：</p><ul><li><code>title</code></li><li><code>description</code></li><li><code>date</code></li><li><code>tags</code></li><li><code>imageAlt</code></li><li><code>imageCover</code></li><li><code>photographerUrl</code></li><li><code>photographerName</code></li><li><code>article</code></li><li><code>keywords</code></li></ul><p>使用这些变量，我创建了 <a href="https://github.com/leandrotk/publisher/blob/master/examples/template.html">template</a>。</p><p>要通过一些信息来构建 HTML，我创建了一个 <code>json</code> 的文章配置文件 <code>article.config.json</code>，文件内容如下：</p><pre><code class="language-json">{
  "title": "React Hooks, Context API, and Pokemons",
  "description": "Understanding how hooks and the context api work",
  "date": "2020-04-21",
  "tags": [
    "javascript",
    "react"
  ],
  "imageAlt": "The Ash from Pokemon",
  "photographerUrl": "&lt;https://www.instagram.com/kazuh.illust&gt;",
  "photographerName": "kazuh.yasiro",
  "articleFile": "article.md",
  "keywords": "javascript,react"
}

</code></pre><p>第一步是项目应该知道如何打开和读取模板以及文章配置。我使用此数据填充模板。</p><p>首先是模板：</p><pre><code class="language-typescript">const templateContent: string = await getTemplateContent();

</code></pre><p>因此，我们首先需要实现 <code>getTemplateContent</code> 功能。</p><pre><code class="language-typescript">import fs, { promises } from 'fs';
import { resolve } from 'path';

const { readFile } = promises;

</code></pre><p>使用 <code>dirname</code> 参数的 <code>resolve</code> 将获得正在运行的源文件的绝对路径目录，然后访问 <code>examples/template.html</code> 文件， <code>readFile</code> 将从模板路径异步读取并返回内容。</p><p>现在我们有了模板内容，需要对文章配置执行相同的操作。</p><pre><code class="language-typescript">const getArticleConfig = async (): Promise&lt;ArticleConfig&gt; =&gt; {
  const articleConfigPath = resolve(dirname, '../examples/article.config.json');
  const articleConfigContent = await readFile(articleConfigPath, 'utf8');
  return JSON.parse(articleConfigContent);
};

</code></pre><p>这里发生两件事：</p><ul><li>由于 <code>article.config.json</code> 具有 JSON 格式，因此我们需要在读取文件后将此 JSON 字符串转换为 JavaScript 对象。</li><li><code>articleConfigContent</code> 的返回将是我在函数返回类型中定义的 <code>ArticleConfig</code>。细节如下：</li></ul><pre><code class="language-typescript">type ArticleConfig = {
  title: string;
  description: string;
  date: string;
  tags: string[];
  imageCover: string;
  imageAlt: string;
  photographerUrl: string;
  photographerName: string;
  articleFile: string;
  keywords: string;
};

</code></pre><p>获得相关内容后，我们还将使用这一新类型。</p><pre><code class="language-typescript">const articleConfig: ArticleConfig = await getArticleConfig();

</code></pre><p>现在，我们可以使用 <code>replace</code> 方法在模板内容中填充配置数据，示例如下：</p><pre><code class="language-typescript">templateContent.replace('title', articleConfig.title)

</code></pre><p>但是某些变量在模板中出现了不止一次，正则表达式会有助于解决这一问题：</p><pre><code class="language-typescript">new RegExp('\{\{(?:\\s+)?(title)(?:\\s+)?\}\}', 'g');

</code></pre><p>得到所有匹配 <code>{{ title }}</code> 的字符串。我可以构建一个接收目标参数的函数，并使用它替换 <code>title</code>。</p><pre><code class="language-typescript">const getPattern = (find: string): RegExp =&gt;
  new RegExp('\{\{(?:\\s+)?(' + find + ')(?:\\s+)?\}\}', 'g');

</code></pre><p>现在我们可以替换所有匹配项。<code>title</code>变量的示例：</p><pre><code class="language-typescript">templateContent.replace(getPattern('title'), articleConfig.title)

</code></pre><p>但是我们并不想只替换 <code>title</code>变量，而是要替换文章配置中的所有变量，全部替换！</p><pre><code class="language-typescript">const buildArticle = (templateContent: string) =&gt; ({
  with: (articleConfig: ArticleAttributes) =&gt;
    templateContent
      .replace(getPattern('title'), articleConfig.title)
      .replace(getPattern('description'), articleConfig.description)
      .replace(getPattern('date'), articleConfig.date)
      .replace(getPattern('tags'), articleConfig.articleTags)
      .replace(getPattern('imageCover'), articleConfig.imageCover)
      .replace(getPattern('imageAlt'), articleConfig.imageAlt)
      .replace(getPattern('photographerUrl'), articleConfig.photographerUrl)
      .replace(getPattern('photographerName'), articleConfig.photographerName)
      .replace(getPattern('article'), articleConfig.articleBody)
      .replace(getPattern('keywords'), articleConfig.keywords)
});

</code></pre><p>现在全部替换！我们这样使用它：</p><pre><code class="language-typescript">const article: string = buildArticle(templateContent).with(articleConfig);

</code></pre><p>但是我们在这里缺少两部分：</p><ul><li><strong><code>tags</code></strong></li><li><strong><code>article</code></strong></li></ul><p>在 JSON 配置文件中，<code>tags</code> 是一个列表。对于列表：</p><pre><code class="language-typescript">['javascript', 'react'];

</code></pre><p>最终的 HTML 将是：</p><pre><code class="language-html">&lt;a class="tag-link" href="../../../tags/javascript.html"&gt;javascript&lt;/a&gt;
&lt;a class="tag-link" href="../../../tags/react.html"&gt;react&lt;/a&gt;

</code></pre><p>因此，我使用 <code>{{ tag }}</code> 变量创建了另一个模板： <code>tag_template.html</code>。我们只需要遍历 <code>tags</code> 列表并使用模板为每一项创建 HTML Tag。</p><pre><code class="language-typescript">const getArticleTags = async ({ tags }: { tags: string[] }): Promise&lt;string&gt; =&gt; {
  const tagTemplatePath = resolve(`**`dirname, '../examples/tag_template.html');
  const tagContent = await readFile(tagTemplatePath, 'utf8');
  return tags.map(buildTag(tagContent)).join('');
};

</code></pre><p>在这里，我们：</p><ul><li>获取标签模板路径</li><li>获取标签模板的内容</li><li>遍历 <code>tags</code> 并根据标签模板构建最终的 HTML Tag</li></ul><p><code>buildTag</code> 是返回另一个函数的函数。</p><pre><code class="language-typescript">const buildTag = (tagContent: string) =&gt; (tag: string): string =&gt;
  tagContent.replace(getPattern('tag'), tag);

</code></pre><p>它接收参数 <code>tagContent</code> - 这是标签模板的内容 - 并返回一个接收 tag 参数并构建最终标签 HTML 的函数。现在我们调用它以获得标签。</p><pre><code class="language-typescript">const articleTags: string = await getArticleTags(articleConfig);

</code></pre><p>现在这篇文章看起来像这样：</p><pre><code class="language-typescript">const getArticleBody = async ({ articleFile }: { articleFile: string }): Promise&lt;string&gt; =&gt; {
  const articleMarkdownPath = resolve(__dirname, ../examples/${articleFile});
  const articleMarkdown = await readFile(articleMarkdownPath, 'utf8');
  return fromMarkdownToHTML(articleMarkdown);
};

</code></pre><p>它收到 <code>articleFile</code>，我们尝试获取路径，读取文件并获取 Markdown 内容。然后将此内容传递给 <code>fromMarkdownToHTML</code> 函数，以将 Markdown 转换为 HTML。</p><p>对于这一部分，我将使用一个外部库 <code>showdown</code>。它处理所有边角案例，以将 Markdown 转换为 HTML。</p><pre><code class="language-typescript">import showdown from 'showdown';

</code></pre><p>现在，我有了 tag 和文章的 HTML：</p><pre><code class="language-typescript">const templateContent: string = await getTemplateContent();
const articleConfig: ArticleConfig = await getArticleConfig();
const articleTags: string = await getArticleTags(articleConfig);
const articleBody: string = await getArticleBody(articleConfig);

</code></pre><p>我漏掉了一件事！以前，我总是需要将图像封面路径添加到文章配置文件中，像这样：</p><pre><code class="language-typescript">{
  "imageCover": "an-image.png",
}

</code></pre><p>但是我们可以假设图像名称为 <code>cover</code>。主要问题是扩展名，它可以是 <code>.png</code>，<code>.jpg</code>，<code>.jpeg</code>，或 <code>.gif</code>。</p><p>因此，我建立了一个函数来获取正确的图像扩展名。这个想法是在文件夹中搜索图像。如果它存在于文件夹中，则返回扩展名。</p><p>我从 existing 部分开始。</p><pre><code class="language-typescript">fs.existsSync(`${folder}/${fileName}.${extension}`);

</code></pre><p>在这里，我正在使用 <code>existsSync</code> 方法来查找文件。如果它存在于文件夹中，则返回true，否则为 false。</p><p>我将此代码添加到一个函数中：</p><pre><code class="language-typescript">const existsFile = (folder: string, fileName: string) =&gt; (extension: string): boolean =&gt;
  fs.existsSync(`${folder}/${fileName}.${extension}`);

</code></pre><p>为什么要这样做？</p><p>使用这个功能，我需要传递参数 <code>folder</code> ,<code>filename</code>，<code>extension</code>。 <code>folder</code> 和 <code>filename</code> 总是相同的，区别在于 <code>extension</code>。</p><p>因此，我可以构建柯里化函数。这样，我可以为相同的 <code>folder</code> 和 <code>filename</code> 建立不同的函数，像这样：</p><pre><code class="language-typescript">const hasFileWithExtension = existsFile(examplesFolder, imageName);

hasFileWithExtension('jpeg'); // true or false
hasFileWithExtension('jpg'); // true or false
hasFileWithExtension('png'); // true or false
hasFileWithExtension('gif'); // true or false
</code></pre><p>完整函数如下：</p><pre><code class="language-typescript">const getImageExtension = (): string =&gt; {
  const examplesFolder: string = resolve(__dirname, ../examples);
  const imageName: string = 'cover';
  const hasFileWithExtension = existsFile(examplesFolder, imageName);
  if (hasFileWithExtension('jpeg')) {
    return 'jpeg';
  }
  if (hasFileWithExtension('jpg')) {
    return 'jpg';
  }
  if (hasFileWithExtension('png')) {
    return 'png';
  }

</code></pre><p>但我不喜欢用硬编码的字符串来表示图像扩展名。<code>enum</code> 真的很酷！</p><pre><code class="language-typescript">enum ImageExtension {
  JPEG = 'jpeg',
  JPG = 'jpg',
  PNG = 'png',
  GIF = 'gif'
};

</code></pre><p>现在使用我们的新的枚举类型 <code>ImageExtension</code> 的函数：</p><pre><code class="language-typescript">const getImageExtension = (): string =&gt; {
  const examplesFolder: string = resolve(__dirname, ../examples);
  const imageName: string = 'cover';
  const hasFileWithExtension = existsFile(examplesFolder, imageName);
  if (hasFileWithExtension(ImageExtension.JPEG)) {
    return ImageExtension.JPEG;
  }
  if (hasFileWithExtension(ImageExtension.JPG)) {
    return ImageExtension.JPG;
  }
  if (hasFileWithExtension(ImageExtension.PNG)) {
    return ImageExtension.PNG;
  }

</code></pre><p>现在，我获得了用以填充模板的所有数据。</p><p>HTML 完成后，我想使用此数据创建实际的 HTML 文件。我大致需要获取正确的路径，HTML，并使用该 <code>writeFile</code> 函数创建此文件。</p><p>要获取路径，我需要确定我的博客的形式。它使用年、月、标题组织文件夹，文件名为 <code>index.html</code>。</p><p>一个例子是：</p><pre><code class="language-bash">2020/04/publisher-a-tooling-to-blog-post-publishing/index.html

</code></pre><p>最初，我考虑过将这些数据添加到文章配置文件中。因此，我需要在每次更新时文章配置中的此属性以获取正确的路径。</p><p>但是另一个有趣的想法是根据文章配置文件中已有的一些数据来推断路径。我们有 <code>date</code>（例如 <code>"2020-04-21"</code>）和 <code>title</code>（例如 <code>"Publisher: tooling to automate blog post publishing"</code>）。从 <code>date</code> 中，我可以得到年和月。从标题中，我可以生成文章所在文件夹。 <code>index.html</code> 文件始终是不变的。</p><p>最终字符串如下所示：</p><pre><code class="language-typescript">`${year}/${month}/${slugifiedTitle}`
</code></pre><p>对于日期，这真的很简单，我可以拆分 <code>-</code>：</p><pre><code class="language-typescript">const [year, month]: string[] = date.split('-');

</code></pre><p>对于 <code>slugifiedTitle</code>，我构建了一个函数：</p><pre><code class="language-typescript">const slugify = (title: string): string =&gt;
  title
    .trim()
    .toLowerCase()
    .replace(/[^\w\s]/gi, '')
    .replace(/[\s]/g, '-');

</code></pre><p>它从字符串的开头和结尾删除空格，然后将字符串小写，然后删除所有特殊字符（仅保留单词和空格字符），最后，将所有空白替换为 <code>-</code>。</p><p>整个函数如下所示：</p><pre><code class="language-typescript">const buildNewArticleFolderPath = ({ title, date }: { title: string, date: string }): string =&gt; {
  const [year, month]: string[] = date.split('-');
  const slugifiedTitle: string = slugify(title);

</code></pre><p>这一函数尝试获取文章文件夹，它不会生成新文件，这就是为什么我没有在最终字符串的末尾添加 <code>/index.html</code> 的原因。</p><p>为什么这样做呢？因为在写入新文件之前，我们始终需要创建文件夹。我使用 <code>mkdir</code> 此文件夹路径来创建它。</p><pre><code class="language-typescript">const newArticleFolderPath: string = buildNewArticleFolderPath(articleConfig);
await mkdir(newArticleFolderPath, { recursive: true });

</code></pre><p>现在，我可以使用新建的文件夹，在其中创建新的文章文件。</p><pre><code class="language-typescript">const newArticlePath: string = `${newArticleFolderPath}/index.html`;
await writeFile(newArticlePath, article);

</code></pre><p>我们漏了一件事：当我将图像封面添加到文章配置文件夹中时，我需要将其复制粘贴到正确的位置。</p><p>对于 <code>2020/04/publisher-a-tooling-to-blog-post-publishing/index.html</code> 示例, 图像封面位于 assets 文件夹中：</p><p><code>2020/04/publisher-a-tooling-to-blog-post-publishing/assets/cover.png</code></p><p>为此，我需要做两件事：</p><ul><li>使用 <code>mkdir</code> 创建一个新的 <code>assets</code> 文件夹</li><li>使用 <code>copyFile</code> 复制图像文件并将其粘贴到新文件夹中</li></ul><p>要创建新文件夹，我只需要文件夹路径。要复制和粘贴图像文件，我需要当前图像路径和文章图像路径。</p><p>对于文件夹，因为我现有 <code>newArticleFolderPath</code>，我只需要将此路径连接到 asset 文件夹。</p><pre><code class="language-typescript">const assetsFolder: string = `${newArticleFolderPath}/assets`;

</code></pre><p>对于当前的图像路径，已有带正确扩展名的 <code>imageCoverFileName</code>，我只需要获取图像封面路径：</p><pre><code class="language-typescript">const imageCoverExamplePath: string = resolve(__dirname, `../examples/${imageCoverFileName}`);

</code></pre><p>为了获得将来的图像路径，我需要将图像封面路径和图像文件名连接起来：</p><pre><code class="language-typescript">const imageCoverPath: string = `${assetsFolder}/${imageCoverFileName}`;

</code></pre><p>使用所有这些数据，我可以创建新文件夹：</p><pre><code class="language-typescript">await mkdir(assetsFolder, { recursive: true });

</code></pre><p>并复制并粘贴图像封面文件：</p><pre><code class="language-typescript">await copyFile(imageCoverExamplePath, imageCoverPath);

</code></pre><p>在实现这一 &nbsp;<code>paths</code> 部分时，我看到可以将它们全部组合成一个函数 <code>buildPaths</code>。</p><pre><code class="language-typescript">const buildPaths = (newArticleFolderPath: string): ArticlePaths =&gt; {
  const imageExtension: string = getImageExtension();
  const imageCoverFileName: string = `cover.${imageExtension}`;
  const newArticlePath: string = `${newArticleFolderPath}/index.html`;
  const imageCoverExamplePath: string = resolve(__dirname, `../examples/${imageCoverFileName}`);
  const assetsFolder: string = `${newArticleFolderPath}/assets`;
  const imageCoverPath: string = `${assetsFolder}/${imageCoverFileName}`;

  return {
    newArticlePath,
    imageCoverExamplePath,
    imageCoverPath,
    assetsFolder,
    imageCoverFileName
  };
};

</code></pre><p>我还创建了 <code>ArticlePaths</code> 类型：</p><pre><code class="language-typescript">type ArticlePaths = {
  newArticlePath: string;
  imageCoverExamplePath: string;
  imageCoverPath: string;
  assetsFolder: string;
  imageCoverFileName: string;
};

</code></pre><p>而且我可以使用该函数来获取所需的所有路径数据：</p><pre><code class="language-typescript">const {
  newArticlePath,
  imageCoverExamplePath,
  imageCoverPath,
  assetsFolder,
  imageCoverFileName
}: ArticlePaths = buildPaths(newArticleFolderPath);

</code></pre><p>现在是算法的最后一部分！我想快速验证创建的帖子。那么，如果可以在浏览器选项卡中打开创建的帖子怎么样？</p><p>所以我做到了：</p><pre><code class="language-typescript">await open(newArticlePath);

</code></pre><p>在这里，我使用 <code>open</code> 库来模拟终端打开命令。</p><p>就是这样！</p><h2 id="--1">结语</h2><p>这个项目很有趣！通过这个过程，我学到了一些很酷的东西：</p><ul><li>在<a href="https://leandrotk.github.io/tk/2020/04/typescript-learnings/index.html">学习 Typescript </a>时，我想快速验证我正在编写的代码。因此，我配置 <code>nodemon</code>为在每次保存文件时编译并运行代码，使开发过程如此动态是很酷的。</li><li>尝试用新 nodejs 的 <code>fs</code> 的 <code>promises</code> API：<code>readFile</code>，<code>mkdir</code>，<code>writeFile</code>，和 <code>copyFile</code>，它目前在规范 <code>Stability: 2</code>。</li><li>对某些函数进行<a href="https://leandrotk.github.io/tk/2020/03/closure-currying-and-cool-abstractions/index.html">柯里化</a>，使其可重复使用。</li><li>枚举和<a href="https://leandrotk.github.io/tk/2020/04/typescript-learnings-002-type-system/index.html">类型</a>是使状态在 Typescript 中保持一致的好方法，也能很好地展示和记录项目数据。<a href="https://leandrotk.github.io/tk/2020/04/thinking-in-data-contracts/index.html">数据契约</a>确实很棒。</li><li>工具化思维，这是我喜欢编程的方面，构建工具有助于自动执行重复性任务并简化工作。</li></ul><p>我希望你希望这篇文章！继续学习编程！</p><p>该帖子最初发布在<a href="https://leandrotk.github.io/tk/2020/04/publisher-a-tooling-to-automate-the-process-to-publish-my-blog-posts/index.html">我的博客</a>上。</p><p>我的 <a href="https://twitter.com/leandrotk_">Twitter</a> 和 <a href="https://github.com/leandrotk/">Github</a>。</p><h2 id="--2">资源</h2><ul><li><a href="https://github.com/leandrotk/publisher">Publisher Tooling: 源码</a></li><li><a href="https://leandrotk.github.io/tk/2020/04/thinking-in-data-contracts/index.html">深入思考数据协定</a></li><li><a href="https://leandrotk.github.io/tk/2020/04/typescript-learnings/index.html">Typescript 学习</a></li><li><a href="https://leandrotk.github.io/tk/2020/03/closure-currying-and-cool-abstractions/index.html">闭包，柯里化和炫酷的抽象</a></li><li><a href="https://alterclass.io/?ref=5ec57f513c1321001703dcd2">通过构建 app 学习 react</a></li></ul><p>原文：<a href="https://www.freecodecamp.org/news/automating-my-blog-posts-publishing-process-with-typescript/">How to Automate Your Blog Post Publishing Process with Typescript</a>，作者：TK</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 2020 年 Web 开发者学习路线 ]]>
                </title>
                <description>
                    <![CDATA[ 今天，我最喜欢的老师之一，Brad Traversy，发布了一个视频，主题是在 2020 年，要成为一名 Web 开发者需要学习哪些知识。 Brad 主要强调了一点——也是我经常强调的——那就是：学习的时候不要感到畏惧或者手足无措。 我想提醒读者：freeCodeCamp 的课程涵盖了 Brad 所介绍的大部分工具，已经帮助 40,000 多学员获得开发者工作。如果你犹疑不决，大可以专注在 freeCodeCamp 学习。 Brad 过滤掉你在社交媒体上听过的各种炒作宣传，给出实事求是的建议。他的建议是切实可行的。我赞同他所建议学习的几乎所有知识。 为了方便起见，我在下面总结了他的一些主要见解。同时，我建议你观看他的完整视频，链接地址：https://youtu.be/0pThnRneDjw  。除了本文所总结的内容之外，他还在视频中详述了许多细节。这是一个时长 73 分钟的视频。 我还建议你订阅他的 Traversy Media YouTube 频道。在过去的十年中，Brad 一直是一位非常出色的老师，他发表了近 800 部有关技术的视频。 Brad 的订阅者数量已经快达 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/web-development-in-2020/</link>
                <guid isPermaLink="false">5e04bc64ca1efa04e196aad4</guid>
                
                    <category>
                        <![CDATA[ 自学编程 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ 开发者 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ 长河渐落晓星沉 ]]>
                </dc:creator>
                <pubDate>Sun, 03 May 2020 04:39:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2021/04/photo-1576441335949-c4f4a2baf347.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>今天，我最喜欢的老师之一，Brad Traversy，发布了一个视频，主题是在 2020 年，要成为一名 Web 开发者需要学习哪些知识。</p><p>Brad 主要强调了一点——也是我经常强调的——那就是：学习的时候不要感到畏惧或者手足无措。</p><p>我想提醒读者：freeCodeCamp 的课程涵盖了 Brad 所介绍的大部分工具，已经帮助 40,000 多学员获得开发者工作。如果你犹疑不决，大可以专注在 freeCodeCamp 学习。</p><p>Brad 过滤掉你在社交媒体上听过的各种炒作宣传，给出实事求是的建议。他的建议是切实可行的。我赞同他所建议学习的几乎所有知识。</p><p>为了方便起见，我在下面总结了他的一些主要见解。同时，我建议你观看他的完整视频，链接地址：<a href="https://youtu.be/0pThnRneDjw" rel="nofollow">https://youtu.be/0pThnRneDjw</a> 。除了本文所总结的内容之外，他还在视频中详述了许多细节。这是一个时长 73 分钟的视频。</p><p>我还建议你订阅他的 Traversy Media YouTube 频道。在过去的十年中，Brad 一直是一位非常出色的老师，他发表了近 800 部有关技术的视频。</p><p>Brad 的订阅者数量已经快达到一百万。让我们看看是否可以帮助他在新的一年里达到一百万。<a href="https://www.youtube.com/user/TechGuyWeb" rel="nofollow">你可以在这里订阅</a>.</p><h1 id="2020-web-">2020 年的 Web 开发：概述</h1><p>该视频的主要目的是使你熟悉一些可供 Web 开发者使用的更主流的 Web 开发工具。</p><p>“我并不想用海量技术淹没你。这些只是你拥有的选择……目前存在很多技术。当你听到类似 Nuxt 或 Gatsby 的声音时，我想让你知道它是什么。然后你可以选择是否要学习。”</p><p>Brad 首先鼓励人们先问自己想做什么：是在一家产品公司工作，还是担任顾问，还是成为自由职业者，或者是构建自己的产品？</p><p>他关于学习什么的很多建议都归结为你的目标。因此，他提出了许多工具建议。</p><p>首先，他建议学习所谓的“必需品”。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chinese.freecodecamp.org/news/content/images/2019/12/download--2-.png" class="kg-image" alt="download--2-" width="600" height="400" loading="lazy"><figcaption>该幻灯片和所有后续幻灯片均来自 Brad 的视频。下划线表示他个人推荐的工具。</figcaption></figure><p>他指出了一件反直觉的事：你不需要花哨的计算机来进行 Web 开发。“这不像游戏引擎开发之类的东西，因此你可以使用一台中档笔记本或者台式机，有些情况下甚至一台低档计算机就够用了。”</p><p>Brad 使用 MacOS，Windows 10 和 Linux 配置云服务和一些媒体服务。</p><p>他非常喜欢 VS Code 的编辑器，并使用 Chrome 及其内置的 DevTools 进行基于浏览器的调试。另外，他认为 Firefox 也已经很成熟，并且是可行的替代方案。</p><p>他建议从标准 HTML 和 CSS 开始，并学习更新的 CSS 工具，例如 Flexbox 和 Grid。</p><p>他认为在 2020 年学习响应式网页设计是必须的，“你创建的每一个正式项目应该在各种设备上运行良好”。</p><p>他建议你练习构建自己的模块化 CSS 组件，以便在整个项目中重用，而不是依赖于 Bootstrap 之类的 CSS 框架。</p><p>说到这，他认为你在为不同雇主开发项目时可能会遇到选择 CSS 框架的问题。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chinese.freecodecamp.org/news/content/images/2019/12/download--4-.png" class="kg-image" alt="download--4-" width="600" height="400" loading="lazy"><figcaption>Bootstrap 目前仍然是最流行的 CSS 框架，但是也有其他一些被广泛使用的框架。</figcaption></figure><p>他特别提到了 Tailwind，这是我以前从未听说过的。Tailwind 与其他 CSS 框架略有不同。它着重于 "utility classes"，你可以将其组合以完成设计样式。</p><p>意料之中的是，Brad 推荐你掌握 JavaScript。</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chinese.freecodecamp.org/news/content/images/2019/12/download--5-.png" class="kg-image" alt="download--5-" width="600" height="400" loading="lazy"><figcaption>这是一些你应该掌握的基础知识。Brad 有这些主题的大部分视频，其中大部分内容也包含在 freeCodeCamp 课程中。</figcaption></figure><h2 id="-">在实践中学习</h2><p>在 Brad 的整个指南中，他反复强调实用性的必要性。</p><p>是的————每件事都有最佳实践。但是在很多情况下，有一种足够好的方法可以帮助你更快地前进并完成更多的工作，而不会陷入工具的泥潭。</p><p>他说过的最有趣（或许也是最和主流观点唱反调的）的一句话是：</p><p>“无需为小型站点学习 DevOps 和 AWS 以及所有这些东西。托管站点或托管主机站点（如 InMotion 或 Hostgator）就可以了。它可以让你直接在浏览器中进行 [部署]。不需要让事情变得太复杂。”</p><p>他认为，即便是在2020年，使用熟悉的工具也是有好处的。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/12/download--6-.png" class="kg-image" alt="download--6-" width="600" height="400" loading="lazy"></figure><p>对于刚接触 Web 开发的人员，Brad 建议要首先能够成为基本的前端 Web 开发人员。这样你就可以作为自由职业者为本地企业建立网站。</p><p>“潮人会说你绝对需要使用前端框架。我不赞成。如果你想在不使用前端框架的情况下在服务器上渲染模板，我认为这是完全可以的，但是有很多很多的工作要求你具有使用类似 React 或 Vue 框架的技能。”</p><p>但是一旦你准备好应对前端框架，他建议你探索 React，Vue 和 Angular。“尝试所有三个，找出最喜欢的一个。”</p><p>他还认可了 Svelte————它不是一个框架，而是一个编译器。但他表示，这可能太新了，无法在 2020 年尝试实用。</p><p>他讨论了服务器端渲染（SSR）的日益普及。基于文件系统的路由意味着你不必创建复杂的路由文件。相反，你可以将文件放在任何你认为合适的目录中。</p><p>两种流行的服务器端渲染工具是 Next.js（用于 React）和 Nuxt.js（用于 Vue）。</p><p>另一个新兴的趋势是静态网站渲染，他说，你不需要学习，但你应该知道。例如，Gatsby 站点非常快，不需要服务器。</p><h2 id="--1">后端开发工具</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://chinese.freecodecamp.org/news/content/images/2019/12/download--7-.png" class="kg-image" alt="download--7-" width="600" height="400" loading="lazy"><figcaption>Brad 演讲的幻灯片，其中包含每种语言最流行的框架。</figcaption></figure><p>对于服务器端开发，Brad 更喜欢 Node.js 的速度，因为它的速度快，并且他可以在前端和后端都使用 JavaScript。</p><p>对于服务器端开发框架，他建议学习 Express，因为它是最流行的，而且“它给了你很多自由，可以按照自己的方式构建东西”。</p><p>他还在一些项目中使用Python，并推荐使用它。</p><p>“Python 有两个很棒的框架。Django 是一个大型的，功能齐全的框架，Flask 更加简约。它提供了你所需的内容，但你可以做出其余的决定。老实说，我对这两个框架没有偏好，我两个都喜欢，将它们用于不同需求。”</p><p>他快速简要地为PHP辩护：</p><p>“很多人都讨厌 PHP，这很不幸，因为 PHP 可以是一门很棒的语言。它很实用，并且可以在任何地方轻松部署。PHP &nbsp;非常适合需要快速出手的自由职业者。如果你打算在一家大公司工作，PHP 可能不是最佳选择，但是如果你要自由职业和构建个人项目，PHP &nbsp;可能是个不错的选择。抨击 PHP 现在是很流行的行为。PHP 是我学到的第一门语言，我仍然非常喜欢它……如果你查看 Laravel &nbsp;代码，它非常优雅。”</p><p>Brad 还快速介绍了一下数据库，并推荐了长期受欢迎的 PostgreSQL。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/12/download--8-.png" class="kg-image" alt="download--8-" width="600" height="400" loading="lazy"></figure><h2 id="--2">其他可能会持续存在的工具</h2><p>GraphQL 是 REST API 的替代品。在 Brad 的频道和 freeCodeCamp 的频道上都有一些课程介绍 &nbsp;GraphQL。但是他说，“GraphQL 现在还不是你必须学习的东西，但它已经足够流行了，我认为它会一直存在下去。这不仅仅是一种趋势。”</p><p>Brad 还讨论了内容管理系统（CMS）以及它如何演变成新的“无头”CMS，你可以将它用作后端并编写自己的前端。</p><p>“CMS 对于自由职业者来说非常方便，他们的客户希望能够登录并创建他们的博客和类似的东西。很多人抨击 WordPress，但在互联网上有很大比例的网站是由 WordPress 运行的，它仍然很受欢迎。”</p><p>对于 Web 服务器，他更喜欢 NGINX 而不是 Apache，因为“它看起来没那么复杂”。我们在 freeCodeCamp 广泛使用 NGINX，我倾向于同意。</p><p>Brad 还解释了虚拟化的工作原理，并讨论了虚拟化如何通过跨多台计算机标准化环境来简化更复杂的项目。但是他告诫不要尝试虚拟化所有内容：</p><p>“Docker 确实对团队非常有用。我不喜欢别人说你应该始终使用 Docker。这只是首选项。如果你只想运行本地 LAMP 服务器，那没有什么错。对学习类似 Docker 的东西不要感到有压力。”</p><p>他补充道，“我知道很多书呆子只想把事情复杂化，我讨厌这样。我列出了所有这些东西，并不意味着我把它强加给你”。这一点我自己没有足够地强调。</p><p>他还快速简要地指出，大多数大公司都有专门的 DevOps 团队（也称为“站点可靠性工程师”），他们可以为你做很多事情。</p><h2 id="--3">掌握一些更彰显专业度的技能是很棒的事情</h2><p>如果你想尝试移动应用程序的开发，Brad 强烈建议使用 Flutter 框架。它使用 Dart，这是一种比较晦涩的语言。他将 Dart 描述为介于 Java 和 JavaScript 之间的某个地方，并说如果你知道这两种语言中的任何一种，都应该可以使用它。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/12/download--9-.png" class="kg-image" alt="download--9-" width="600" height="400" loading="lazy"></figure><p>他在移动应用程序开发中的第二个选择是 React Native，如果你已经知道 React，就很容易上手。</p><p>然后 Brad 很好地解释了什么是渐进式 Web 应用（PWA）以及它们的一些好处。这是幻灯片，但我还是强烈建议你们看他的完整视频。</p><figure class="kg-card kg-image-card"><img src="https://chinese.freecodecamp.org/news/content/images/2019/12/download--10-.png" class="kg-image" alt="download--10-" width="600" height="400" loading="lazy"></figure><p>然后，他谈到了 Electron，它使你可以使用 JavaScript 构建桌面应用程序。他最初表示怀疑，但提到他最喜欢的一些桌面应用程序（例如 VS Code 和 Discord）现在使用 Electron 构建。</p><p>他提到了 JAMstack（客户端 JavaScript，可重用 API 和预构建标记 Markup）和 serverless 架构。这些都是更高级的主题，他的频道和 freeCodeCamp 的 YouTube 频道都提供了视频教程。</p><p>然后他描述了2020年的大趋势。</p><h2 id="-webassembly">机器学习和 WebAssembly</h2><p>尽管其他所有人都在谈论机器学习，但是这不意味着你应该放弃所有东西来学习它。这是他的整个视频分享的基调。</p><p>是的，你也可以使用 JavaScript 进行机器学习。</p><p>“Python 是机器学习的首选语言。不过，如果你也可以使用 JavaScript 里的 TensorFlow.js 和 Brain.js 来创建神经网络并做一些非常酷的事情。”</p><p>从他谈论 WebAssembly 的方式可以明显看出，他对此感到非常兴奋。</p><p>“WebAssembly 仍处于早期阶段，但我认为我们今年会看到更多。传统上，我们在网页上使用 JavaScript 来操纵 DOM &nbsp;并运行计算。JavaScript 在速度方面有局限性。一种语言就像 C 或 C++ 比 JavaScript &nbsp;快得多。因此，WebAssembly 是一种高效的低级字节代码，可以由浏览器执行，并且速度非常快。它可以由 C，C++ 和 Rust &nbsp;等语言生成。”</p><p>Rust 是 WebAssembly 可以使用的语言之一（除了 C 和 C++）。它比 C 和 C++ 更容易学习和使用。</p><p>但是需要特别注意的是，WebAssembly 不会让你不必学习 JavaScript。</p><p>“你几乎可以将 JavaScript 视为老板，它可以告诉 Web Assembly 该怎么做。因此，它使我们能够在我们的 Web &nbsp;应用程序中使用非常快速的低级语言（例如 C++），这为我们提供了更多功能————例如下一代视频游戏和视频编辑工具，就可以在浏览器中找到，这是 &nbsp;JavaScript 所无法想象的。”</p><p>最后他说：“学到的越多，就更容易学习更多，将所有这些技术融合在一起就越容易。请不要原地踌躇。一次迈出一步，做一些研究，弄清楚你想做什么。”</p><p>再一次，我要感谢 Brad Traversy，是他为我们提供了这段全面的视频介绍。</p><p>人们总是问我“如果我想成为一名web开发人员，我应该学习什么?”我的回答是“把freeCodeCamp作为你的核心课程，然后扩展到其他学习资源。</p><p>我很高兴地说，<a href="https://www.youtube.com/user/TechGuyWeb" rel="nofollow">Brad 的 Traversy Media YouTube 频道</a>是我最推荐的其他资源之一。Brad，如果你正在读这篇文章，恭喜你拥有十年的YouTube视频。期待下个十年！</p><p>原文：<a href="https://www.freecodecamp.org/news/web-development-2020/">https://www.freecodecamp.org/news/web-development-2020/</a>，作者：<a href="https://www.freecodecamp.org/news/author/quincylarson/">Quincy Larson</a></p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
