{"componentChunkName":"component---src-templates-best-practice-detail-tsx","path":"/best-practice/2019-12-12-how-write-first-serverless-components","result":{"data":{"currentBlog":{"id":"d2a60cba-0dfd-5c94-9848-dc29a897e81e","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/20191230/1577673977066-16ef85f25ee1af09.jpg","authors":["yugasun"],"categories":["best-practice"],"date":"2019-12-12T00:00:00.000Z","title":"如何开发自己的第一个 Serverless Component","description":"我们分享了不少基于 Component 的最佳实践案例，本文教你如何自己开发一个 Component！","authorslink":["https://yugasun.com"],"translators":null,"translatorslink":null,"tags":["Wintersmith","Component"],"keywords":"Serverless Component,最佳实践案例,开发Component,实践案例","outdated":null},"wordCount":{"words":399,"sentences":63,"paragraphs":63},"fileAbsolutePath":"/opt/build/repo/content/best-practice/2019-12-12-how-write-first-serverless-components.md","fields":{"slug":"/best-practice/2019-12-12-how-write-first-serverless-components/","keywords":["serverless","website","serverless","tencent","组件","Serverless","Component","cdn","npm"]},"html":"<h2 id=\"前言\"><a href=\"#%E5%89%8D%E8%A8%80\" 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>上一篇 <a href=\"https://serverlesscloud.cn/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/\">基于 Serverless Component 的全栈解决方案</a> 介绍 Serverless Component 是什么和如何使用 Serverless Component 开发一个全栈应用。但是目前社区还不够完善，当我们需要一个还没有的组件时，怎么办呢？</p>\n<p>与其向官方提交  <code class=\"language-text\">issue</code>，说明需求，不如自己动手撸一个，岂不快哉~</p>\n<h2 id=\"serverless-component-运行机制\"><a href=\"#serverless-component-%E8%BF%90%E8%A1%8C%E6%9C%BA%E5%88%B6\" aria-label=\"serverless component 运行机制 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>Serverless Component 运行机制</h2>\n<p>在开始开发之前，我们先来了解下 Serverless Component 的运行机制：</p>\n<p>每个 Serverless Component 实际上就是一个 <code class=\"language-text\">npm</code> 包，你可以通过 <code class=\"language-text\">npm install</code> 命令直接安装。当我们在一个依赖 Serverless Component 的应用中，执行命令 <code class=\"language-text\">serverless --debug</code> 部署时，它首先会读取 <code class=\"language-text\">serverless.yml</code> 文件中的 <code class=\"language-text\">component</code> 参数指定组件模块，它会像安装 <code class=\"language-text\">npm</code> 包一样，自动安装到本地，然后自动注入该组件模块，同时执行组件中的 <code class=\"language-text\">default</code> 函数（之后会讲到），从而完成部署流程。</p>\n<h2 id=\"开发步骤\"><a href=\"#%E5%BC%80%E5%8F%91%E6%AD%A5%E9%AA%A4\" 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>一个完整组件的开发流程应该包括以下流程：</p>\n<ol>\n<li>明确功能需求</li>\n<li>定义组件配置：输入和输出参数</li>\n<li>组件开发：default 函数、remove 函数（可选）</li>\n<li>测试组件</li>\n<li>发布 npm 包</li>\n</ol>\n<p>接下来将按照以上步骤，一步一步实现腾讯云 CDN 组件。</p>\n<h2 id=\"1-明确功能需求\"><a href=\"#1-%E6%98%8E%E7%A1%AE%E5%8A%9F%E8%83%BD%E9%9C%80%E6%B1%82\" aria-label=\"1 明确功能需求 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>1. 明确功能需求</h2>\n<p><a href=\"https://console.cloud.tencent.com/cdn\">腾讯云 CDN 控制台</a> 已经提供了手动配置加速域名的功能，但是作为一名懒惰的程序员，「手动」 一直都是我尝试规避的问题。于是去看了看腾讯云文档，看看官方有没有提供相应便捷的方式。果不其然腾讯云 API 已经提供了相关接口，那么我们为什么不借助 API 实现一个能够帮助我们自动配置的 CDN 组件呢？</p>\n<p>需求很明确：开发一个能够自动配置 CDN 加速域名的组件，帮助我们节省手动配置时间。</p>\n<h2 id=\"2-定义组件配置\"><a href=\"#2-%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E9%85%8D%E7%BD%AE\" aria-label=\"2 定义组件配置 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>2. 定义组件配置</h2>\n<p>要实现 CDN 域名的添加，需要借助 2 个腾讯云 API 接口：<a href=\"https://cloud.tencent.com/document/product/228/1406\">新增加速域名</a>、<a href=\"https://cloud.tencent.com/document/product/228/12965\">HTTPS 配置</a>。通过阅读这两份接口文档，总结出一份配置说明文件 <code class=\"language-text\">config.md</code> ，内容如下：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"96720127408907780000\"\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(`MyCDN:\n  component: '@serverless/tencent-cdn'\n  inputs:\n    host: abc.com\n    hostType: cos\n    origin: www.test.com\n    backupOrigin: www.test.com\n    serviceType: web\n    fullUrl: on\n    fwdHost: ww.test.com\n    cache:\n      - type: 0\n        rule: all\n        time: 1000\n      - type: 0\n        rule: all\n        time: 1000\n    cacheMode: simple\n    refer:\n      - type: 1\n        list:\n          - 'qq.baidu.com'\n          - '*.baidu.com'\n    accessIp:\n      type: 1\n      list:\n        - '1.2.3.4'\n        - '2.3.4.5'\n    https:\n      certId: 123\n      cert: 123\n      privateKey: 123\n      http2: off\n      httpsType: 2\n      forceSwitch: -2`, `96720127408907780000`)\"\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=\"yaml\"><pre class=\"language-yaml\"><code class=\"language-yaml\"><span class=\"token key atrule\">MyCDN</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">component</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'@serverless/tencent-cdn'</span>\n  <span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">host</span><span class=\"token punctuation\">:</span> abc.com\n    <span class=\"token key atrule\">hostType</span><span class=\"token punctuation\">:</span> cos\n    <span class=\"token key atrule\">origin</span><span class=\"token punctuation\">:</span> www.test.com\n    <span class=\"token key atrule\">backupOrigin</span><span class=\"token punctuation\">:</span> www.test.com\n    <span class=\"token key atrule\">serviceType</span><span class=\"token punctuation\">:</span> web\n    <span class=\"token key atrule\">fullUrl</span><span class=\"token punctuation\">:</span> on\n    <span class=\"token key atrule\">fwdHost</span><span class=\"token punctuation\">:</span> ww.test.com\n    <span class=\"token key atrule\">cache</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">type</span><span class=\"token punctuation\">:</span> <span class=\"token number\">0</span>\n        <span class=\"token key atrule\">rule</span><span class=\"token punctuation\">:</span> all\n        <span class=\"token key atrule\">time</span><span class=\"token punctuation\">:</span> <span class=\"token number\">1000</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">type</span><span class=\"token punctuation\">:</span> <span class=\"token number\">0</span>\n        <span class=\"token key atrule\">rule</span><span class=\"token punctuation\">:</span> all\n        <span class=\"token key atrule\">time</span><span class=\"token punctuation\">:</span> <span class=\"token number\">1000</span>\n    <span class=\"token key atrule\">cacheMode</span><span class=\"token punctuation\">:</span> simple\n    <span class=\"token key atrule\">refer</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">type</span><span class=\"token punctuation\">:</span> <span class=\"token number\">1</span>\n        <span class=\"token key atrule\">list</span><span class=\"token punctuation\">:</span>\n          <span class=\"token punctuation\">-</span> <span class=\"token string\">'qq.baidu.com'</span>\n          <span class=\"token punctuation\">-</span> <span class=\"token string\">'*.baidu.com'</span>\n    <span class=\"token key atrule\">accessIp</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">type</span><span class=\"token punctuation\">:</span> <span class=\"token number\">1</span>\n      <span class=\"token key atrule\">list</span><span class=\"token punctuation\">:</span>\n        <span class=\"token punctuation\">-</span> <span class=\"token string\">'1.2.3.4'</span>\n        <span class=\"token punctuation\">-</span> <span class=\"token string\">'2.3.4.5'</span>\n    <span class=\"token key atrule\">https</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">certId</span><span class=\"token punctuation\">:</span> <span class=\"token number\">123</span>\n      <span class=\"token key atrule\">cert</span><span class=\"token punctuation\">:</span> <span class=\"token number\">123</span>\n      <span class=\"token key atrule\">privateKey</span><span class=\"token punctuation\">:</span> <span class=\"token number\">123</span>\n      <span class=\"token key atrule\">http2</span><span class=\"token punctuation\">:</span> off\n      <span class=\"token key atrule\">httpsType</span><span class=\"token punctuation\">:</span> <span class=\"token number\">2</span>\n      <span class=\"token key atrule\">forceSwitch</span><span class=\"token punctuation\">:</span> <span class=\"token number\">-2</span></code></pre></div>\n<p>其中 <code class=\"language-text\">inputs</code> 就是组件的输入参数，其实这些参数都是从接口文档中拷贝出来而已，实际开发时，需根据自己组件功能，定制化配置就好。</p>\n<blockquote>\n<p>无服务框架的配置都是 <code class=\"language-text\">yaml</code> 文件，所以在定义组件配置时，需要将 API 的参数做好 <code class=\"language-text\">yaml</code> 规范映射。比如  <code class=\"language-text\">yaml</code> 文件中，符号 <code class=\"language-text\">-</code>  是用来定义数组的。如果对 yaml 语法还不太熟，可以参考这份 <a href=\"https://www.ruanyifeng.com/blog/2016/07/yaml.html\">YAML 语言教程</a>。</p>\n</blockquote>\n<p>组件输入定义好了，还需要定义输出内容，只需要大致的组织 API 请求返回结构就行，尽量简洁明了：</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">{\n\thost: &#39;abc.com&#39;,\n\thostId: &#39;123&#39;\n\torigin: &#39;www.test.com&#39;,\n\tcname: &#39;www.test.com.cdn.dnsv1.com&#39;,\n\thttps: true\n}</code></pre></div>\n<h2 id=\"3-组件开发\"><a href=\"#3-%E7%BB%84%E4%BB%B6%E5%BC%80%E5%8F%91\" aria-label=\"3 组件开发 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>3. 组件开发</h2>\n<p>对于一个标准的 Serverless Component  ，结构如下：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"53096433109079370000\"\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(`// serverless.js\nconst { Component } = require('@serverless/core')\nclass MyComponent extends Component {\n  /*\n   * Default (必须)\n   * - default 是用来执行、准备和更新你的组建的函数\n   * - 执行命令 \\`\\$ serverless\\` 会运行此函数\n   * - You can run this function by running the &quot;\\$ serverless&quot; command\n   */\n  async default(inputs = {}) {\n    return {}\n  }\n\n  /*\n   * Remove (可选)\n   * - 如果你的组件需要删除基础设施，推荐你添加他\n   * - 执行命令 \\`\\$ serverless remove\\` 会运行此函数\n   */\n  async remove(inputs = {}) {\n    return {}\n  }\n\n  /*\n   * Anything (可选)\n   * - 如果你想发布带有额外功能的组件，你可以将逻辑写在一个函数里，函数名可以自定义\n   * - 执行命令 \\`\\$ serverless anything\\` 会运行此函数\n   */\n  async anything(inputs = {}) {\n    return {}\n  }\n}\nmodule.exports = MyComponent`, `53096433109079370000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">// serverless.js</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> Component <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'@serverless/core'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MyComponent</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Component</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token comment\">/*\n   * Default (必须)\n   * - default 是用来执行、准备和更新你的组建的函数\n   * - 执行命令 `$ serverless` 会运行此函数\n   * - You can run this function by running the \"$ serverless\" command\n   */</span>\n  <span class=\"token keyword\">async</span> <span class=\"token keyword\">default</span><span class=\"token punctuation\">(</span>inputs <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">/*\n   * Remove (可选)\n   * - 如果你的组件需要删除基础设施，推荐你添加他\n   * - 执行命令 `$ serverless remove` 会运行此函数\n   */</span>\n  <span class=\"token keyword\">async</span> <span class=\"token function\">remove</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">inputs <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token comment\">/*\n   * Anything (可选)\n   * - 如果你想发布带有额外功能的组件，你可以将逻辑写在一个函数里，函数名可以自定义\n   * - 执行命令 `$ serverless anything` 会运行此函数\n   */</span>\n  <span class=\"token keyword\">async</span> <span class=\"token function\">anything</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">inputs <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> MyComponent</code></pre></div>\n<p>了解了组件的结构，接下来，就开始开发吧~</p>\n<h4 id=\"31-初始化项目\"><a href=\"#31-%E5%88%9D%E5%A7%8B%E5%8C%96%E9%A1%B9%E7%9B%AE\" aria-label=\"31 初始化项目 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>3.1 初始化项目</h4>\n<p>创建项目目录 <code class=\"language-text\">tencent-cdn</code>，执行 <code class=\"language-text\">npm init</code> 初始化项目，根据命令指引，填写相关信息就行：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"65862936715852280000\"\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(`\\$ npm init\nThis utility will walk you through creating a package.json file.\nIt only covers the most common items, and tries to guess sensible defaults.\n\nSee \\`npm help json\\` for definitive documentation on these fields\nand exactly what they do.\n\nUse \\`npm install <pkg>\\` afterwards to install a package and\nsave it as a dependency in the package.json file.\n\nPress ^C at any time to quit.\npackage name: (cdn-module) tencent-cdn\nversion: (1.0.0)\ndescription: Tencent Cloud CDN Component\nentry point: (index.js) serverless.js\ntest command:\ngit repository:\nkeywords: cdn,serverless,serverless-component,serverlesscomponent,tencent\nauthor: yugasun\nlicense: (ISC) MIT\nAbout to write to /Users/yugasun/Desktop/Develop/serverless/cdn-module/package.json:\n\n{\n  &quot;name&quot;: &quot;tencent-cdn&quot;,\n  &quot;version&quot;: &quot;1.0.0&quot;,\n  &quot;description&quot;: &quot;Tencent Cloud CDN Component&quot;,\n  &quot;main&quot;: &quot;serverless.js&quot;,\n  &quot;scripts&quot;: {\n    &quot;test&quot;: &quot;echo \\&quot;Error: no test specified\\&quot; && exit 1&quot;\n  },\n  &quot;keywords&quot;: [\n    &quot;cdn&quot;,\n    &quot;serverless&quot;,\n    &quot;serverless-component&quot;,\n    &quot;serverlesscomponent&quot;,\n    &quot;tencent&quot;\n  ],\n  &quot;author&quot;: &quot;yugasun&quot;,\n  &quot;license&quot;: &quot;MIT&quot;\n}\n\n\nIs this OK? (yes)`, `65862936715852280000`)\"\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=\"shell\"><pre class=\"language-shell\"><code class=\"language-shell\">$ <span class=\"token function\">npm</span> init\nThis utility will walk you through creating a package.json file.\nIt only covers the <span class=\"token function\">most</span> common items, and tries to guess sensible defaults.\n\nSee <span class=\"token variable\"><span class=\"token variable\">`</span><span class=\"token function\">npm</span> <span class=\"token builtin class-name\">help</span> json<span class=\"token variable\">`</span></span> <span class=\"token keyword\">for</span> definitive documentation on these fields\nand exactly what they do.\n\nUse <span class=\"token variable\"><span class=\"token variable\">`</span><span class=\"token function\">npm</span> <span class=\"token function\">install</span> <span class=\"token operator\">&lt;</span>pkg<span class=\"token operator\">></span><span class=\"token variable\">`</span></span> afterwards to <span class=\"token function\">install</span> a package and\nsave it as a dependency <span class=\"token keyword\">in</span> the package.json file.\n\nPress ^C at any <span class=\"token function\">time</span> to quit.\npackage name: <span class=\"token punctuation\">(</span>cdn-module<span class=\"token punctuation\">)</span> tencent-cdn\nversion: <span class=\"token punctuation\">(</span><span class=\"token number\">1.0</span>.0<span class=\"token punctuation\">)</span>\ndescription: Tencent Cloud CDN Component\nentry point: <span class=\"token punctuation\">(</span>index.js<span class=\"token punctuation\">)</span> serverless.js\n<span class=\"token builtin class-name\">test</span> command:\n<span class=\"token function\">git</span> repository:\nkeywords: cdn,serverless,serverless-component,serverlesscomponent,tencent\nauthor: yugasun\nlicense: <span class=\"token punctuation\">(</span>ISC<span class=\"token punctuation\">)</span> MIT\nAbout to <span class=\"token function\">write</span> to /Users/yugasun/Desktop/Develop/serverless/cdn-module/package.json:\n\n<span class=\"token punctuation\">{</span>\n  <span class=\"token string\">\"name\"</span><span class=\"token builtin class-name\">:</span> <span class=\"token string\">\"tencent-cdn\"</span>,\n  <span class=\"token string\">\"version\"</span><span class=\"token builtin class-name\">:</span> <span class=\"token string\">\"1.0.0\"</span>,\n  <span class=\"token string\">\"description\"</span><span class=\"token builtin class-name\">:</span> <span class=\"token string\">\"Tencent Cloud CDN Component\"</span>,\n  <span class=\"token string\">\"main\"</span><span class=\"token builtin class-name\">:</span> <span class=\"token string\">\"serverless.js\"</span>,\n  <span class=\"token string\">\"scripts\"</span><span class=\"token builtin class-name\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token string\">\"test\"</span><span class=\"token builtin class-name\">:</span> <span class=\"token string\">\"echo <span class=\"token entity\" title=\"\\&quot;\">\\\"</span>Error: no test specified<span class=\"token entity\" title=\"\\&quot;\">\\\"</span> &amp;&amp; exit 1\"</span>\n  <span class=\"token punctuation\">}</span>,\n  <span class=\"token string\">\"keywords\"</span><span class=\"token builtin class-name\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">\"cdn\"</span>,\n    <span class=\"token string\">\"serverless\"</span>,\n    <span class=\"token string\">\"serverless-component\"</span>,\n    <span class=\"token string\">\"serverlesscomponent\"</span>,\n    <span class=\"token string\">\"tencent\"</span>\n  <span class=\"token punctuation\">]</span>,\n  <span class=\"token string\">\"author\"</span><span class=\"token builtin class-name\">:</span> <span class=\"token string\">\"yugasun\"</span>,\n  <span class=\"token string\">\"license\"</span><span class=\"token builtin class-name\">:</span> <span class=\"token string\">\"MIT\"</span>\n<span class=\"token punctuation\">}</span>\n\n\nIs this OK? <span class=\"token punctuation\">(</span>yes<span class=\"token punctuation\">)</span></code></pre></div>\n<p>然后新建 <code class=\"language-text\">serverless.js</code> 文件，复制上面的模板代码到 <code class=\"language-text\">serverless.js</code> 文件中。</p>\n<h4 id=\"32-编写-default-函数\"><a href=\"#32-%E7%BC%96%E5%86%99-default-%E5%87%BD%E6%95%B0\" aria-label=\"32 编写 default 函数 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>3.2 编写 default 函数</h4>\n<p><code class=\"language-text\">default</code> 函数代码，这里就不贴出来了，有点多 o(╯□╰)o。</p>\n<p>主要思路就是，根据 <code class=\"language-text\">inputs</code> 输入参数，规范成接口请求参数，然后请求接口，执行配置就好。</p>\n<p>对于腾讯云 API，所有的接口请求都需要鉴权，所以这里需要先实例化一个 <code class=\"language-text\">Capi</code>，如下：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"11838388381858644000\"\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(`import { Capi } from '@tencent-sdk/capi'\nconst capi = new Capi({\n\tSecretId: this.context.credentials.tencent.SecretId,\n  SecretKey: this.context.credentials.tencent.SecretKey,\n  ServiceType: 'cdn',\n})`, `11838388381858644000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">import</span> <span class=\"token punctuation\">{</span> Capi <span class=\"token punctuation\">}</span> <span class=\"token keyword\">from</span> <span class=\"token string\">'@tencent-sdk/capi'</span>\n<span class=\"token keyword\">const</span> capi <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Capi</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n\tSecretId<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>context<span class=\"token punctuation\">.</span>credentials<span class=\"token punctuation\">.</span>tencent<span class=\"token punctuation\">.</span>SecretId<span class=\"token punctuation\">,</span>\n  SecretKey<span class=\"token punctuation\">:</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>context<span class=\"token punctuation\">.</span>credentials<span class=\"token punctuation\">.</span>tencent<span class=\"token punctuation\">.</span>SecretKey<span class=\"token punctuation\">,</span>\n  ServiceType<span class=\"token punctuation\">:</span> <span class=\"token string\">'cdn'</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div>\n<blockquote>\n<p>注意：关于请求云 API 库 <a href=\"https://www.npmjs.com/package/@tencent-sdk/capi\">@tencent-sdk/capi</a> 说明文档已经很全面了，当然你也可以在这里看到 <a href=\"https://github.com/yugasun/tencent-sdk/blob/master/packages/capi/README.md\">源码</a>.</p>\n</blockquote>\n<p>它需要传入 <code class=\"language-text\">SecretId</code>、<code class=\"language-text\">SecretKey</code>、<code class=\"language-text\">ServiceType</code> 三个参数，<code class=\"language-text\">SecretId</code> 和 <code class=\"language-text\">SecretKey</code> 可以通过 <code class=\"language-text\">this.context.credentials.tencent</code> 来获取，执行  <code class=\"language-text\">serverless</code> 命令在执行时，它会根据用户项目根目录配置的 <code class=\"language-text\">.env</code> 文件，自动注入到 <code class=\"language-text\">this.context.credentials.tencent</code> 上。<code class=\"language-text\">ServiceType</code> 是当前服务类型，这是腾讯云 API 定义的，针对不同业务配置相应参数就行。</p>\n<blockquote>\n<p>注意：不同的云服务商挂到 <code class=\"language-text\">this.context.credentials</code> 上的属性也是不一样，比如这里腾讯云是 <code class=\"language-text\">tencent</code>，AWS 是 <code class=\"language-text\">aws</code>，目前支持的所有云服务商的属性配置源码，在这里可以找到，<a href=\"https://github.com/serverless/cli/blob/master/src/Context.js#L105\">@serverless/cli</a></p>\n</blockquote>\n<p>然后请求 <a href=\"https://cloud.tencent.com/document/product/228/1406\">新增加速域名</a> 接口：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"35308743866664006000\"\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(`// cdnInputs 就是我们组装好的请求参数\nawait AddCdnHost(capi, cdnInputs)`, `35308743866664006000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">// cdnInputs 就是我们组装好的请求参数</span>\n<span class=\"token keyword\">await</span> <span class=\"token function\">AddCdnHost</span><span class=\"token punctuation\">(</span>capi<span class=\"token punctuation\">,</span> cdnInputs<span class=\"token punctuation\">)</span></code></pre></div>\n<p>这里有个重点：请求 <code class=\"language-text\">新增加速域名</code> 接口成功返回后，CDN 并不会立即部署成功，这个是需要时间的，所以我们执行后，需要轮训当前新增域名的状态，当为部署成功时，我们才能进行之后的逻辑。</p>\n<h4 id=\"33-组件状态保存\"><a href=\"#33-%E7%BB%84%E4%BB%B6%E7%8A%B6%E6%80%81%E4%BF%9D%E5%AD%98\" aria-label=\"33 组件状态保存 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>3.3 组件状态保存</h4>\n<p> Serverless Component 在执行  <code class=\"language-text\">default</code> 函数时，它会产生一些状态，比如新增 CDN 域名成功后，会产生一个 <code class=\"language-text\">hostId</code>，我们可以保存在 <code class=\"language-text\">this.state</code> 对象中，通过执行 <code class=\"language-text\">this.save()</code> 函数，它会将 <code class=\"language-text\">this.state</code> 保存到项目根目录的 <code class=\"language-text\">.serverless</code> 文件夹中一个名为 <code class=\"language-text\">Template.MyCDN.json</code> （<code class=\"language-text\">MyCDN</code> 是我定义的当前 Serverless 应用的名称）文件中，方便之后在做组件建删除时使用。</p>\n<h4 id=\"34-编写-remove-函数\"><a href=\"#34-%E7%BC%96%E5%86%99-remove-%E5%87%BD%E6%95%B0\" aria-label=\"34 编写 remove 函数 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>3.4 编写 remove 函数</h4>\n<p>Serverless Component 删除的逻辑，就是再 <code class=\"language-text\">serverless remove</code> 命令时，它会读取 <code class=\"language-text\">default</code> 函数执行保存到 <code class=\"language-text\">.serverless</code> 中的状态文件，并注入到 <code class=\"language-text\">this.state</code> 上 , 然后我们可以根据 <code class=\"language-text\">state</code> 中的值进行移除，比如我这里会用到 <code class=\"language-text\">host</code>, 因为 <a href=\"https://cloud.tencent.com/document/product/228/1396\">删除加速域名接口</a> 需要传递 <code class=\"language-text\">host</code> 参数。</p>\n<h4 id=\"35-完善说明文档\"><a href=\"#35-%E5%AE%8C%E5%96%84%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3\" aria-label=\"35 完善说明文档 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>3.5 完善说明文档</h4>\n<p>开源项目的 README 一定要写的清晰明了，方便开发者顺利的使用和开发。</p>\n<h2 id=\"4-测试组件\"><a href=\"#4-%E6%B5%8B%E8%AF%95%E7%BB%84%E4%BB%B6\" aria-label=\"4 测试组件 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>4. 测试组件</h2>\n<p>到这里我们组件的基本开发完成了，在发布之前，还得进行本地测试，Serverless Framework 提供了一个很好地本地调试方法，就是应用的 <code class=\"language-text\">serverless.yml</code> 中 <code class=\"language-text\">component</code> 可以指定本地项目路径，比如在 <code class=\"language-text\">tencent-cdn</code> 目录下，创建 <code class=\"language-text\">test</code> 文件夹，然后新增 <code class=\"language-text\">serverless.yml</code> 配置如下：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"79676702143334530000\"\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(`MyCDN:\n  component: ../\n  inputs:\n    host: abc.com\n    ...`, `79676702143334530000`)\"\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=\"yml\"><pre class=\"language-yml\"><code class=\"language-yml\"><span class=\"token key atrule\">MyCDN</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">component</span><span class=\"token punctuation\">:</span> ../\n  <span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">host</span><span class=\"token punctuation\">:</span> abc.com\n    <span class=\"token punctuation\">...</span></code></pre></div>\n<p>这里的 <code class=\"language-text\">../</code> 就是相对路径，因为 <code class=\"language-text\">tencent-cdn</code> 组件的 <code class=\"language-text\">serverless.js</code> 文件在 <code class=\"language-text\">tencent-cdn</code> 根目录下，之后我们就可以进入 <code class=\"language-text\">test</code> 目录，执行部署和移除操作，来测试我们的组件了。</p>\n<blockquote>\n<p>注意：虽然一个 Serverless Component 是一个 npm 模块，我们可以通过 <code class=\"language-text\">package.json</code> 中的 <code class=\"language-text\">main</code> 属性指定项目中任意的文件入口，但是如果没有 <code class=\"language-text\">serverless.js</code> 文件，<code class=\"language-text\">serverless</code> 命令是没法通过 <code class=\"language-text\">component</code> 指定的本地路径调试的。</p>\n</blockquote>\n<h2 id=\"5-发布-npm-包\"><a href=\"#5-%E5%8F%91%E5%B8%83-npm-%E5%8C%85\" aria-label=\"5 发布 npm 包 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>5. 发布 npm 包</h2>\n<p>发布 npm 包，首先需要你拥有一个 npm 账号，请先前往 <a href=\"https://www.npmjs.com\">npm官网</a> 注册，然后本地执行 <code class=\"language-text\">npm login</code> 登录你的账号。</p>\n<p>经过测试没问题，就可以执行 <code class=\"language-text\">npm publish</code> 就可以发布到 npm 仓库了。</p>\n<h2 id=\"源码\"><a href=\"#%E6%BA%90%E7%A0%81\" 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>最终实现源码：<a href=\"https://github.com/serverless-components/tencent-cdn\">@serverless/tencent-cdn</a>。</p>\n<h2 id=\"组件引用\"><a href=\"#%E7%BB%84%E4%BB%B6%E5%BC%95%E7%94%A8\" 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>每个组件实例，都会有个 <code class=\"language-text\">load</code> 方法，我们可以通过此方法来加载其他组件，如下：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"48402322099106420000\"\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(`const cdnComp = await this.load('@serverless/tencent-cdn', 'cdnComp');`, `48402322099106420000`)\"\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=\"js\"><pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">const</span> cdnComp <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">load</span><span class=\"token punctuation\">(</span><span class=\"token string\">'@serverless/tencent-cdn'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'cdnComp'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>借助此功能，我们可以实现很多高阶组件，比如 <a href=\"https://github.com/serverless-components/tencent-website\">@serverless/tencent-website</a> 就是一个很好地案例。</p>\n<p>至于如何组合你的组件，实现自己的需求，就靠你自己去天马行空了，是不是已经跃跃欲试了？快来社区贡献你的一份力量吧~</p>\n<h2 id=\"组件开发模板\"><a href=\"#%E7%BB%84%E4%BB%B6%E5%BC%80%E5%8F%91%E6%A8%A1%E6%9D%BF\" 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>以上基于腾讯云 <a href=\"https://cloud.tencent.com/product/sf\">Serverless Framework</a> 来实现，这里还有个 Serverless Component 开发模板，可以帮你快速开发一个 Serverless Component：<a href=\"https://github.com/yugasun/serverless-component-template\">模板链接</a>，英文好的话可以查看这两篇内容<a href=\"https://serverless.com/blog/how-write-first-serverless-component\">《How to write your first Serverless Component》</a>和<a href=\"https://github.com/serverless/components/#building-components\">《Building Serverless Components》</a></p>\n<hr>\n<div id='scf-deploy-iframe-or-md'></div>\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=\"/best-practice/2019-12-12-how-write-first-serverless-components/#%E5%89%8D%E8%A8%80\">前言</a></li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#serverless-component-%E8%BF%90%E8%A1%8C%E6%9C%BA%E5%88%B6\">Serverless Component 运行机制</a></li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#%E5%BC%80%E5%8F%91%E6%AD%A5%E9%AA%A4\">开发步骤</a></li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#1-%E6%98%8E%E7%A1%AE%E5%8A%9F%E8%83%BD%E9%9C%80%E6%B1%82\">1. 明确功能需求</a></li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#2-%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E9%85%8D%E7%BD%AE\">2. 定义组件配置</a></li>\n<li>\n<p><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#3-%E7%BB%84%E4%BB%B6%E5%BC%80%E5%8F%91\">3. 组件开发</a></p>\n<ul>\n<li>\n<ul>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#31-%E5%88%9D%E5%A7%8B%E5%8C%96%E9%A1%B9%E7%9B%AE\">3.1 初始化项目</a></li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#32-%E7%BC%96%E5%86%99-default-%E5%87%BD%E6%95%B0\">3.2 编写 default 函数</a></li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#33-%E7%BB%84%E4%BB%B6%E7%8A%B6%E6%80%81%E4%BF%9D%E5%AD%98\">3.3 组件状态保存</a></li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#34-%E7%BC%96%E5%86%99-remove-%E5%87%BD%E6%95%B0\">3.4 编写 remove 函数</a></li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#35-%E5%AE%8C%E5%96%84%E8%AF%B4%E6%98%8E%E6%96%87%E6%A1%A3\">3.5 完善说明文档</a></li>\n</ul>\n</li>\n</ul>\n</li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#4-%E6%B5%8B%E8%AF%95%E7%BB%84%E4%BB%B6\">4. 测试组件</a></li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#5-%E5%8F%91%E5%B8%83-npm-%E5%8C%85\">5. 发布 npm 包</a></li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#%E6%BA%90%E7%A0%81\">源码</a></li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#%E7%BB%84%E4%BB%B6%E5%BC%95%E7%94%A8\">组件引用</a></li>\n<li><a href=\"/best-practice/2019-12-12-how-write-first-serverless-components/#%E7%BB%84%E4%BB%B6%E5%BC%80%E5%8F%91%E6%A8%A1%E6%9D%BF\">组件开发模板</a></li>\n</ul>"},"previousBlog":{"id":"a5889900-617d-5aa4-ac8b-56b0aab7d572","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/202058/1588928638838-v2-b6f54fc4d2939af6f53a5e5592f9d19c_1200x500.jpg","authors":["alanoluo"],"categories":["best-practice"],"date":"2019-12-13T00:00:00.000Z","title":"用 Serverless 搭建个人静态相册网站","description":"通过 Serverless 组件，基于 ThumbsUp 快速搭建个人静态相册网站","authorslink":["https://github.com/alanoluo"],"translators":null,"translatorslink":null,"tags":["Wintersmith","ThumbsUp"],"keywords":"Serverless 组件,ThumbsUp相册,搭建个人静态相册网站,ThumbsUp搭建","outdated":null},"wordCount":{"words":107,"sentences":29,"paragraphs":29},"fileAbsolutePath":"/opt/build/repo/content/best-practice/2019-12-13-Build-personal-album-website-with-serverless.md","fields":{"slug":"/best-practice/2019-12-13-Build-personal-album-website-with-serverless/","keywords":["nodejs","serverless","website","website 组件","无服务器","serverless","website","Serverless","thumbsup","yml","photos","Framework","github"]}},"nextBlog":{"id":"3db31f60-9d84-53dc-b9b9-ae47809f7202","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/20191226/1577353101878-vue.js.png","authors":["yugasun"],"categories":["best-practice"],"date":"2019-12-11T00:00:00.000Z","title":"基于 Serverless Component 全栈解决方案 Ⅱ","description":"如何借助 Serverless Component 快速开发全栈 Web 应用 - 续集","authorslink":["https://github.com/yugasun"],"translators":null,"translatorslink":null,"tags":["Serverless","全栈应用"],"keywords":"Serverless Component,全栈 Web 应用,全栈解决方案","outdated":null},"wordCount":{"words":275,"sentences":54,"paragraphs":53},"fileAbsolutePath":"/opt/build/repo/content/best-practice/2019-12-11-serverless-fullstack-vue-practice-pro.md","fields":{"slug":"/best-practice/2019-12-11-serverless-fullstack-vue-practice-pro/","keywords":["serverless","spa","vue","vuejs","website","serverless","fullstack","配置","域名","证书","serverlesscloud"]}}},"pageContext":{"isCreatedByStatefulCreatePages":false,"blogId":"d2a60cba-0dfd-5c94-9848-dc29a897e81e","previousBlogId":"a5889900-617d-5aa4-ac8b-56b0aab7d572","nextBlogId":"3db31f60-9d84-53dc-b9b9-ae47809f7202"}}}