{"componentChunkName":"component---src-templates-blog-detail-tsx","path":"/blog/2019-08-08-typescript-nodejs","result":{"data":{"currentBlog":{"id":"8bea29f1-825b-5b54-b276-79b877da385a","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020414/1586850934183-%E5%B0%81%E9%9D%A2%E5%9B%BE%20%285%29.png","authors":["朱理锋"],"categories":["guides-and-tutorials","user-stories"],"date":"2019-08-08T00:00:00.000Z","title":"云函数 + TypeScript + Node.js 最佳实践探索","description":"本文使用了 Typescript 和 nodejs 开发，实现一个部署在腾讯云云函数 SCF 上的小工具","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["TypeScript","Node.js"],"keywords":"Serverless","outdated":null},"wordCount":{"words":613,"sentences":94,"paragraphs":93},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-08-08-typescript-nodejs.md","fields":{"slug":"/blog/2019-08-08-typescript-nodejs/","keywords":["go","java","nodejs","serverless","serverlesscloud","文件"]},"html":"<h2 id=\"目的\"><a href=\"#%E7%9B%AE%E7%9A%84\" aria-label=\"目的 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>目的</h2>\n<p>最近 Serverless 愈来愈火，我刚好在培训，比较有时间去尝试一些新东西，所以趁这个时候去使用下 Serverless，尝试使用 Typescript 和 nodejs 开发，部署在腾讯云 SCF 上的一个小工具，探讨下 Typescript+ Node.js + SCF 的最好实践模式，并同时抛钻引玉，希望有同学提供更好的方案。</p>\n<h2 id=\"项目介绍\"><a href=\"#%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D\" aria-label=\"项目介绍 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>项目介绍</h2>\n<h3 id=\"一、想法\"><a href=\"#%E4%B8%80%E3%80%81%E6%83%B3%E6%B3%95\" aria-label=\"一、想法 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>一、想法</h3>\n<p>本人有时候会追剧，但是剧集更新时，我一般都是不知道的。只有空闲的时候，才会特地去查看它们有没有更新。如果有这么一个工具，能够在剧集更新的时候，主动告诉我，那么我就不用时不时去查询，对我来说就会方便很多。</p>\n<p>在我没有接触到 Serverless 之前，我的想法是这样的 —— 写这样的程序并不难，但是我得买个机器部署啊？如果有问题不能及时发现，还得上机器查日志，或者自己去控制程序定时爬取的逻辑等等。</p>\n<p><strong>总的来说就是，实现与维护一个这样的程序的成本远大于了其带给我的便利，让我有想法却懒于行动。</strong></p>\n<p>但是有了 Serverless 后，以上问题将不再是问题。例如部署难题，使用 Serverless 就是使用云供应商提供的开发者工具，用它创建函数，打包上传代码即部署成功；又例如定时爬取逻辑，使用其提供的定时触发器能力即可。这让我能更专注于代码实现。</p>\n<p>在此，我不会很官方地去讲 Serverless 的概念以及好处，仅仅是从一个开发者的角度来阐述我的想法。</p>\n<h3 id=\"二、实践\"><a href=\"#%E4%BA%8C%E3%80%81%E5%AE%9E%E8%B7%B5\" aria-label=\"二、实践 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>二、实践</h3>\n<p><strong>1、流程图</strong></p>\n<p>程序的整个流程图如下图所示，逻辑很简单，这个项目的目的不在于实现一个多厉害的功能，而在于 Typescript + Node.js + SCF 的实践方式的探索。</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190824497.png\"></p>\n<p><strong>2、开发</strong></p>\n<p>开发能在 SCF 运行的Node.js 程序的其实与传统的开发Node.js 程序在语言编写上并没有太大区别。比较明显的不同在于，我们开发时得有一个入口的函数，比如像这样：</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190829367.png\"></p>\n<p>更具体的入门文档，可以看<a href=\"https://cloud.tencent.com/product/scf/getting-started\">此处</a>，跟着文档一步步学习编写一个简单的函数。接下来回归正题。</p>\n<p><strong>a. 环境搭建</strong></p>\n<p>首先为了方便开发，建议安装腾讯云 SCF 提供的命令行工具或者 vscode 插件。但是这里我开发的时候 vscode 插件还没发布，所以这里主要使用命令行工具，命令行工具的安装与使用的文档，具体可以看<a href=\"https://cloud.tencent.com/document/product/583/33445\">此处</a>。</p>\n<p>安装好后，使用 scf init（<strong>具体参数得去看文档填写，这个提个建议，scf init提供交互式操作，采取问答的模式去创建</strong>）创建一个项目、项目文件很简单，一共就四个文件，前三个，应该不多做介绍。第四个文件 template.yaml 称为模板文件，简单来说是描述这个函数的文件，比如函数的环境变量，触发器类型等等，具体还是前往<a href=\"https://cloud.tencent.com/document/product/583/33454\">文档处</a>查看吧。</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190339028.png\"></p>\n<p>接下来，就是正常配置 tsconfig.json，如果没有安装 typescript 的同学请去官网安装，然后 tsc --init 就可以快速生成一个 tsconfig.json，然后根据自己的需求配置即可。</p>\n<p>然后，就是编写 npm scripts。</p>\n<p>主要就三个操作，build，dev，deploy。</p>\n<p>可以使用 npm scripts 把 typescript 的编译和 SCF CLI 的本地调试，打包和部署串联在一起，使需要敲打的命令简洁和语义化</p>\n<p><img src=\"data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==\"></p>\n<p>最后，将本地仓库与远程仓库关联起来。（<strong>这里提一个优化：有一种场景是用户已经创建了一个 git 仓库，现在需要将仓库里的代码写成 SCF 模式下的代码，并配合 SCF CLI 使用，目前 SCF CLI 只支持 init 一个完整的项目，如果支持在一个已有项目中快速生成调试和部署的 yaml，对开发者来说是一个比较方便的功能</strong>）</p>\n<p><strong>b. 编码</strong></p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190346222.png\"></p>\n<p>我的主要逻辑代码分为上面的文件。</p>\n<ul>\n<li>index，没什么好说的，就是一个入口文件，负责组合其余模块的逻辑。</li>\n<li>config 以及 config_extra，config_extra 文件放了我的隐私配置，例如 redis 的 host，port 和密码以及邮件服务的授权码等，这些配置通过配合 .gitignore 是不会提交到远程 git 仓库，而 config 文件 则是引入 config_extra 文件中的配置，并与一些通用配置进行 merge，然后输出到各个模块。（注：此处也可以好好利用scf提供的环境变量功能，很适合这种场景，<a href=\"https://cloud.tencent.com/document/product/583/30228\">具体文档</a>）</li>\n<li>config_extra_demo，告诉别的开发者，config_extra 文件应该如何编写</li>\n<li>mailer，封装邮件服务的初始化以及发送邮件方法</li>\n<li>redis，封装 redis 的连接以及同步 set 以及 get 方法</li>\n<li>task，暂时简单封装了下初始化以及执行的通用逻辑。但日后该工具扩展，此处仍得考虑如何抽象以及通用化。</li>\n<li>util，封装了一些公用方法，例如封装了 retry 方法，来包装一些异步函数。</li>\n</ul>\n<p>上面简单介绍下主要逻辑代码的文件，具体的实现，有兴趣可以移步到 <a href=\"https://github.com/Juliiii/scf-crawler\">Github 地址</a> 查看</p>\n<p><strong>3、调试</strong></p>\n<p>上面也有提到我编写的 npm scripts 里有 npm run dev 的一条。本人开发这个项目时，调试都执行 npm run dev 来进行调试。这里提一下，测试环境一般是需要和正式环境隔离的。所以可以新建一个 env.json 文件，里面填写</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"39174496978777014000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`  {    &quot;NODE_ENV&quot;: development  }`, `39174496978777014000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\">  <span class=\"token punctuation\">{</span>    <span class=\"token string\">\"NODE_ENV\"</span><span class=\"token punctuation\">:</span> development  <span class=\"token punctuation\">}</span></code></pre></div>\n<p>并将 npm script 中的 dev 命令改成 npm run build &#x26;&#x26; scf local generate-event timer timeup | scf native invoke --template template.yaml --env-vars env.json</p>\n<p>然后在配置文件中根据 process.env.NODE_ENV 变量来判断是测试环境还是正式环境，并填写对应环境依赖的服务的配置即可。</p>\n<p><strong>4、部署</strong></p>\n<p>上面讲了这么多，其实都不是我最想表达的，因为我并没有在上面遇到一些很棘手的问题。而在部署的时候，我才发现在使用 typescript 时，<del>无法在</del><del><strong>腾讯云 SCF 目前的部署要求</strong></del><del>以及</del><del><strong>项目的文件目录管理</strong></del><del>中做到完美的配合</del>。</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190354330.png\"></p>\n<p>后面和同事讨论后，还是有不错的方法是达到两者的平衡。<strong>下面是我的多次尝试的一个过程</strong>。</p>\n<p>如果不使用 typescript，仅使用 js 编写 nodejs 程序，则不需要编译的过程，部署函数时，只需要打包然后部署即可；但是使用 typescript 后，则多了一步将 ts 代码编译成 js 代码的步骤。为了管理好项目的文件目录，我倾向于 ts 和 js 文件分别存放在不同的文件夹，例如，src 文件夹存放 ts 文件，dist 则是编译后得到的 js 文件。我一开始的文件目录便是如此。</p>\n<p><strong>第一次尝试</strong></p>\n<p>→ 文件目录：</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190400410.png\"></p>\n<p>→ tsconfig.json 指定编译 src 文件夹下的 ts 文件，输出到 dist 文件夹</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190406007.png\"></p>\n<p>→ template.yaml CodeUri 指向 dist 文件夹</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190409495.png\"></p>\n<p>根据上面的配置，在本地调试是可以的。但是当部署到云上，测试是失败的。如果大家熟练的话可以立刻发现问题所在，打包没有把 node_modules 打包进去。主要逻辑代码依赖的第三方库全都找不到，测试当然失败了。</p>\n<p><strong>第二次尝试</strong></p>\n<p>根据第一次尝试，我使用 npm scripts 的 pre 钩子，在执行部署前，编辑 ts 代码，同时把 node_modules 拷贝到 dist 文件夹，然后再打包部署解决了这个问题。</p>\n<p>→ package.json</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190416798.png\"></p>\n<p>→ copy_node_modules.js</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190421368.png\"></p>\n<p>→ dist 文件夹下的文件</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190425716.png\"></p>\n<p>虽然这样做可以运行了，在本地文件目录管理合理，但是提交到云上的代码是编译后的，基本没啥可读性，就是一坨能运行的东西，项目代码也不完整。所以个人认为，<strong>最完美的是本地开发的项目代码和交到云上的项目代码是一致的，不需要通过额外的脚本去阉割</strong>。虽然目前腾讯云 SCF 控制台的 webIDE 还只是能看入口文件，不过之后会接入 Cloud Studio，起码可以看到整个代码文件夹的每个文件，说不定以后就支持在线支持 typescript 编译（虽然不知道可不可能）。所以本人开始了第三次尝试。</p>\n<p><strong>第三次尝试</strong></p>\n<p>我有一个想法：<strong>template.yaml 中指定的 Handler，即入口函数，从 index.main_handler 写成 文件夹 /index.main_handler，即入口函数可以在某个文件夹里</strong>。</p>\n<p>我在 template.yaml 处的 Handler 写成 dist/index.main_handler，CodeUri 写成了根目录，这样就可以打包整个文件夹，然后指定 Handler 为 dist 文件夹的 index 文件的 main_handler 函数。</p>\n<p>→ template.yaml</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190431134.png\"></p>\n<p><strong>本地调试时，是成功的！</strong></p>\n<p>但是在部署的时候，</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190436409.png\"></p>\n<p>额，好吧，我觉得是这个方案是不行的了，因为不符合 SCF 的要求，通过不了校验。</p>\n<p><strong>第四次尝试</strong></p>\n<p>这是我第四次尝试。但是不是最完美的，在<strong>文件管理退了一步，允许 ts 和编译后的 js 放在一起</strong>。这样能做到把整个项目都打包上去，而且可运行，但是 ts 和 js 放在一起，文件管理不太合理。修改的地方如下：</p>\n<p>index.ts 文件从 src 文件夹移动到根目录</p>\n<p>→ tsconfig.json</p>\n<p>编辑根目录下的 index.ts 和 src 文件夹下的 ts 文件，剔除 node_modules，输出到根目录</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190441537.png\"></p>\n<p><img src=\"data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==\"></p>\n<p>→ template.yaml</p>\n<p>CodeUri 改成根目录，Handler 改成 index.main_handler，即跟 CLI 生成的一样</p>\n<p><img src=\"data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==\"></p>\n<p>编译后结果</p>\n<p><img src=\"data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==\"></p>\n<p>最后部署到云上 SCF，是可以运行的，而且是把整个项目都打包了上去，日后腾讯云 SCF 接入了 Cloud Studio，WebIDE 看到的文件架构和本地看到的文件架构是一致的。</p>\n<p><strong>第五次尝试</strong></p>\n<p>兜兜转转，有时候问题解决很简单。和组内同事讨论后，一位大佬同事点出 —— <strong>可不可以在根目录写一个 index 文件，然后调用编译后的 index 文件的入口方法?</strong>。</p>\n<p>一语惊醒梦中人！是的，一开始就没注意到，还可以这样解决，思维一直在一个圈子里绕来绕去，没有跳出来。这样做的成本很低，而且能达到了我之前说到的理想状态：</p>\n<p><strong>本地开发的项目代码和交到云上的项目代码是一致的，不需要通过额外的脚本去阉割</strong></p>\n<p>实施方法即是，<strong>把 typescript 文件放在 src 文件夹下，编辑后的 js 文件放在 dist 文件夹下，在根目录编写一个 index.js 文件，文件里的 main_hanlder 方法调用编译后的 index 文件的入口函数</strong>，下面是一些核心代码。</p>\n<p>→ index.js</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190446646.png\"></p>\n<p>→ tsconfig.json</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190500120.png\"></p>\n<p>→ template.json</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190504191.png\"></p>\n<p>→ 编译后结果</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414190510798.png\"></p>\n<h3 id=\"三、成果\"><a href=\"#%E4%B8%89%E3%80%81%E6%88%90%E6%9E%9C\" aria-label=\"三、成果 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>三、成果</h3>\n<p>简单展示下代码线上运行后的结果。</p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414180355894-20200414190516921.png\"></p>\n<p><img src=\"https://img.serverlesscloud.cn/tmp/640-20200414180325016.png\"></p>\n<h2 id=\"总结\"><a href=\"#%E6%80%BB%E7%BB%93\" aria-label=\"总结 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>总结</h2>\n<p>上面说了这么多，这里给一个总结就是 —— 虽然腾讯云 SCF 没有原生支持 TypeScript，但是经过一些方法还是可以做到两者的完美配合。</p>\n<p>首先本地开发是没啥问题的，上面提到的尝试，都是为了能够在本地调试成功的同时可以部署到云上。</p>\n<p>主要是部署的问题，其中可行的三个尝试：</p>\n<ul>\n<li>第一个 是通过一些额外的方法去适配，但是做不到云上的项目和实际的项目的一致，如第二次尝试。</li>\n<li>第二个 是文件管理上退了一步，不做到极致的分明，如第四次尝试。</li>\n<li>第三个 是在根目录写一个 index.js 文件，调用具有真正逻辑的入口函数，做个转发，如第五次尝试，也就是本人认为目前最好的实践方式。</li>\n</ul>\n<p>最后，以上的五个尝试，是本人开发的时候的想法与实践，也许不太正确，有误欢迎大家来批评。如果大家有更好的方法，欢迎讨论。五次尝试的源码都在 <a href=\"https://github.com/Juliiii/scf-crawler\">Github 地址</a>，前四次尝试均有对应分支，master 分支为第五次尝试。</p>\n<hr>\n<blockquote>\n<p><strong>传送门：</strong></p>\n<ul>\n<li>GitHub: <a href=\"https://github.com/serverless/serverless/blob/master/README_CN.md\">github.com/serverless</a> </li>\n<li>官网：<a href=\"https://serverless.com/\">serverless.com</a></li>\n</ul>\n</blockquote>\n<p>欢迎访问：<a href=\"https://serverlesscloud.cn/\">Serverless 中文网</a>，您可以在 <a href=\"https://serverlesscloud.cn/best-practice\">最佳实践</a> 里体验更多关于 Serverless 应用的开发！</p>","tableOfContents":"<ul>\n<li><a href=\"/blog/2019-08-08-typescript-nodejs/#%E7%9B%AE%E7%9A%84\">目的</a></li>\n<li>\n<p><a href=\"/blog/2019-08-08-typescript-nodejs/#%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D\">项目介绍</a></p>\n<ul>\n<li><a href=\"/blog/2019-08-08-typescript-nodejs/#%E4%B8%80%E3%80%81%E6%83%B3%E6%B3%95\">一、想法</a></li>\n<li><a href=\"/blog/2019-08-08-typescript-nodejs/#%E4%BA%8C%E3%80%81%E5%AE%9E%E8%B7%B5\">二、实践</a></li>\n<li><a href=\"/blog/2019-08-08-typescript-nodejs/#%E4%B8%89%E3%80%81%E6%88%90%E6%9E%9C\">三、成果</a></li>\n</ul>\n</li>\n<li><a href=\"/blog/2019-08-08-typescript-nodejs/#%E6%80%BB%E7%BB%93\">总结</a></li>\n</ul>"},"previousBlog":{"id":"22c72191-2b2a-5d57-8a0c-aa1616ee296c","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020414/1586850758698-%E5%B0%81%E9%9D%A2%E5%9B%BE%20%284%29.png","authors":["陈杰文"],"categories":["guides-and-tutorials","user-stories"],"date":"2019-08-12T00:00:00.000Z","title":"基于 Node.js 的轻量级云函数功能实现","description":"在万物皆可云的时代，你的应用甚至不需要服务器。云函数功能在各大云服务中均有提供，那么，如何用「无所不能」的 Node.js 实现呢？","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["Node.js","Serverless"],"keywords":"Serverless","outdated":null},"wordCount":{"words":227,"sentences":56,"paragraphs":56},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-08-12-nodejs.md","fields":{"slug":"/blog/2019-08-12-nodejs/","keywords":["java","koa","nodejs","serverless","无服务器","无服务器架构","云函数","函数","javascript","sandbox","func","执行"]}},"nextBlog":{"id":"2dc78814-9d77-555b-a1bb-ad202c8ec2d1","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/cloudforecast/thumbnail.png","authors":["FrancoisLagier"],"categories":["user-stories"],"date":"2019-08-07T00:00:00.000Z","title":"Serverless：初创企业的理想选择？（CloudForecast 案例分析）","description":"CloudForecast 是 2018 年成立的一家独立初创企业，本文将介绍他们决定选择 Serverless 的原因。","authorslink":["https://serverless.com/author/francoislagier/"],"translators":["Aceyclee"],"translatorslink":["zhihu.com/people/Aceyclee"],"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":211,"sentences":29,"paragraphs":29},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-08-07-serverless-for-startups.md","fields":{"slug":"/blog/2019-08-07-serverless-for-startups/","keywords":["serverless","云函数","serverless","函数","Serverless","utm","Framework","blog","CloudForecast","cloudforecast"]}},"recommendBlogs":{"edges":[{"node":{"id":"4300b21c-7209-5256-86ff-0d38e3daec9b","frontmatter":{"thumbnail":"https://main.qcloudimg.com/raw/14f1c8eed372e76c1b139703b2f6d0fa.jpg","authors":["KieranMcCarthy"],"categories":["user-stories","engineering-culture"],"date":"2018-01-09T00:00:00.000Z","title":"我是如何在四年时间里，从厨师转行为 Serverless 应用开发者","description":"我是厨师出身，现在成为了一名 Serverless 应用开发者。","authorslink":["https://serverless.com/author/kieranmccarthy/"],"translators":["Aceyclee"],"translatorslink":["https://www.zhihu.com/people/Aceyclee"],"tags":["应用开发","Serverless"],"keywords":"Serverless 应用开发,Serverless 管理,厨师转行为 Serverless 应用开发者","outdated":null},"wordCount":{"words":285,"sentences":38,"paragraphs":36},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-01-09-from-chef-to-serverless-developer-in-4-years.md","fields":{"slug":"/blog/2018-01-09-from-chef-to-serverless-developer-in-4-years/","keywords":["无服务器","无服务器开发","云函数","学习","Serverless","构建","Framework","开发者","服务器","应用","学位","简历"]}}},{"node":{"id":"665f9ce2-4451-59fd-bf98-1861789d3b3b","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/Serverless_logo.png","authors":["AndreaPasswater"],"categories":["guides-and-tutorials","engineering-culture"],"date":"2018-03-19T00:00:00.000Z","title":"如何为无服务器开放源代码项目做贡献","description":"想要为无服务器开放源代码项目做贡献？您可以遵循下面的指南。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":96,"sentences":36,"paragraphs":36},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-03-19-how-contribute-to-serverless-open-source.md","fields":{"slug":"/blog/2018-03-19-how-contribute-to-serverless-open-source/","keywords":["serverless","无服务器","serverless","github","插件","服务器","贡献","示例","blog","contribute"]}}},{"node":{"id":"a3e92579-65c3-5159-937c-32d18c5df7d7","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/why-not/why-not-header.png","authors":["AndreaPasswater"],"categories":["guides-and-tutorials","operations-and-observability","engineering-culture"],"date":"2018-03-21T00:00:00.000Z","title":"不适合选择无服务器的情境及原因","description":"无服务器既有优点也有缺点。那么，哪些情境下不适合选择无服务器？原因又是什么呢？","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":163,"sentences":43,"paragraphs":43},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-03-21-when-why-not-use-serverless.md","fields":{"slug":"/blog/2018-03-21-when-why-not-use-serverless/","keywords":["faas","react","serverless","spa","无服务器","无服务器函数","无服务器架构","无服务器开发","服务器","twitter","serverless","blockquote","lang","script","en"]}}},{"node":{"id":"6a16520b-7886-582e-9182-64e50712d486","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/vendor+choice/serverless-data-portability.jpg","authors":["NickGottlieb"],"categories":["engineering-culture","guides-and-tutorials"],"date":"2018-06-20T00:00:00.000Z","title":"浅谈无服务器、数据锁定和供应商选择","description":"供应商选择是如今 IT 领导者需要考虑的最重要事项，而这一点可利用数据可移植性来实现。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":205,"sentences":33,"paragraphs":33},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-06-20-data-lockin-vendor-choice-portability.md","fields":{"slug":"/blog/2018-06-20-data-lockin-vendor-choice-portability/","keywords":["go","serverless","无服务器","无服务器架构","供应商","serverless","开发人员","数据","锁定","选择","服务"]}}},{"node":{"id":"94741abb-10ba-5db1-9756-cd1d573473fa","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/webstorm-ide/streamline-webstorm-serverless2.jpg","authors":["EslamHefnawy"],"categories":["guides-and-tutorials","engineering-culture"],"date":"2018-08-15T00:00:00.000Z","title":"如何使用 WebStorm 简化无服务器工作流程","description":"在本文中，我将和您分享如何使用 WebStorm 进行无服务器特定的 IDE 设置以及如何利用它来极大地加快无服务器工作流程。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":234,"sentences":54,"paragraphs":54},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-08-15-streamline-serverless-workflow-webstorm.md","fields":{"slug":"/blog/2018-08-15-streamline-serverless-workflow-webstorm/","keywords":["nodejs","serverless","无服务器","无服务器开发","serverless","WebStorm","webstorm","服务器","blog","assets"]}}},{"node":{"id":"713a0563-4bf9-5721-bacb-3b4ef609fe4a","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/camp-fire/camp-fire-housing-thumb.jpg","authors":["EricWyne"],"categories":["guides-and-tutorials","user-stories"],"date":"2018-12-05T00:00:00.000Z","title":"Serverless Twitter 机器人帮助为坎普山火受灾者安置住房","description":"加利福尼亚州的坎普山火致使数千人流离失所，为此，我构建了一个简单的 Serverless Twitter 机器人来帮助将受灾者安置在临时住房！","authorslink":["https://serverless.com/author/ericwyne/"],"translators":["Aceyclee"],"translatorslink":["zhihu.com/people/Aceyclee"],"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":157,"sentences":26,"paragraphs":26},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-12-05-serverless-twitter-camp-fire.md","fields":{"slug":"/blog/2018-12-05-serverless-twitter-camp-fire/","keywords":["serverless","无服务器","云函数","Serverless","org","住房","Twitter","函数","受灾","机器人","山火"]}}},{"node":{"id":"98602143-b837-5f50-a24f-3b1ec76044d7","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/sqquid/sqquid-serverless-thumb.jpg","authors":["RonPeled"],"categories":["user-stories"],"date":"2018-12-17T00:00:00.000Z","title":"SQQUID：100% 无服务器初创公司","description":"SQQUID 将 AWS Lambda 和无服务器框架用于其核心产品和营销网站。我们来看看一个完全无服务器的初创公司是怎样的。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":266,"sentences":42,"paragraphs":42},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-12-17-sqquid-one-hundred-percent-serverless.md","fields":{"slug":"/blog/2018-12-17-sqquid-one-hundred-percent-serverless/","keywords":["go","serverless","无服务器","无服务器架构","服务器","架构","Lambda","集成","FaaS","串行","系统"]}}},{"node":{"id":"29dc2e58-d2ba-56f9-aee1-d21b0bc62e0e","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/ao-com-story/ao-serverless-thumbnail.png","authors":["NickGottlieb"],"categories":["user-stories"],"date":"2019-04-24T00:00:00.000Z","title":"AO.com：逐渐转向无服务器优先","description":"AO.com 的 SCV 团队率先尝试无服务器服务。折服于无服务器框架的快速周转时间和低维护成本，整个团队逐渐转向无服务器优先。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":236,"sentences":42,"paragraphs":35},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-04-24-ao-serverless-first.md","fields":{"slug":"/blog/2019-04-24-ao-serverless-first/","keywords":["serverless","无服务器","服务器","团队","Lambda","功能","构建"]}}}],"totalCount":89}},"pageContext":{"isCreatedByStatefulCreatePages":false,"blogId":"8bea29f1-825b-5b54-b276-79b877da385a","previousBlogId":"22c72191-2b2a-5d57-8a0c-aa1616ee296c","nextBlogId":"2dc78814-9d77-555b-a1bb-ad202c8ec2d1","categories":["guides-and-tutorials","user-stories"]}}}