{"componentChunkName":"component---src-templates-best-practice-detail-tsx","path":"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component","result":{"data":{"currentBlog":{"id":"70178fa6-74b6-5606-9467-7c4fa830b240","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/20191226/1577353101878-vue.js.png","authors":["yugasun"],"categories":["best-practice"],"date":"2019-12-05T00: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":278,"sentences":57,"paragraphs":57},"fileAbsolutePath":"/opt/build/repo/content/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component.md","fields":{"slug":"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/","keywords":["serverless","spa","vue","vuejs","website","website 组件","云函数","serverless","express","tencent","res"]},"html":"<h2 id=\"什么是-serverless-component\"><a href=\"#%E4%BB%80%E4%B9%88%E6%98%AF-serverless-component\" 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><a href=\"https://github.com/serverless/components\">Serverless Component</a> 是 <a href=\"https://github.com/serverless/serverless\">Serverless Framework</a> 的，支持多个云资源编排和组织的场景化解决方案。</p>\n<p>Serverless Component 的目标是磨平不同云服务平台之间差异，你可以将它看作是可以更轻松地构建应用程序的依赖模块。目前 Serverless Component 已经形成一个由社区贡献驱动的生态系统，你可以浏览和使用社区的所有组件，快速开发一款自己想要的应用。</p>\n<h2 id=\"serverless-component-工作原理\"><a href=\"#serverless-component-%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86\" 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 架构，你可以将任何云服务打包成一个组件。这个组件将含有一份 <code class=\"language-text\">serverless.yml</code> 配置文件，并且通过简单地进行配置就可以使用。本文以 <a href=\"https://github.com/serverless-components/tencent-express\">@serverless/tencent-express</a> 来举例。</p>\n<p>如果我们要使用它，只需要新建一个项目 <code class=\"language-text\">express-demo</code>，然后修改 <code class=\"language-text\">serverless.yml</code> 配置如下：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"6317276126834015000\"\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(`express:\n  component: '@serverless/tencent-express'\n  inputs:\n    region: ap-shanghai`, `6317276126834015000`)\"\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\">express</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-express'</span>\n  <span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">region</span><span class=\"token punctuation\">:</span> ap<span class=\"token punctuation\">-</span>shanghai</code></pre></div>\n<p>因为 <code class=\"language-text\">serverless</code> 框架部署到云的鉴权都是基于 <a href=\"https://github.com/bkeepers/dotenv\">dotenv</a> 注入全局的变量来实现的，所以还得在根目录下新增 <code class=\"language-text\">.env</code> 文件，并配置对应的鉴权参数。</p>\n<p>之后我们就可以在 <code class=\"language-text\">app.js</code> 中轻松的编写基于 <code class=\"language-text\">express</code> 的接口服务了：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"98725947636023540000\"\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 express = require('express')\nconst app = express()\napp.get('/', function(req, res) {\n  res.send('Hello Express')\n})\n// 不要忘了导出，因为该组件会对它进行包装，输出成云函数\nmodule.exports = app`, `98725947636023540000`)\"\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> express <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'express'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> app <span class=\"token operator\">=</span> <span class=\"token function\">express</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">function</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">req<span class=\"token punctuation\">,</span> res</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  res<span class=\"token punctuation\">.</span><span class=\"token function\">send</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Hello Express'</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n<span class=\"token comment\">// 不要忘了导出，因为该组件会对它进行包装，输出成云函数</span>\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> app</code></pre></div>\n<p>这背后所有的流程逻辑都是组件内部实现的，包括：云函数的部署，API 网关的生成等。</p>\n<p>下面是一张简单的组件依赖图：</p>\n<p><img src=\"https://static.yugasun.com/serverless/component-framework.png\" alt=\"Component Dependency Structure\"></p>\n<p>通过此图可以清晰地查看组件带来的收益，借助社区现有的 <a href=\"https://github.com/serverless-components/tencent-express\">@serverless/tencent-express</a> 和 <a href=\"https://github.com/serverless-components/tencent-website\">@serverless/tencent-website</a> 组件，我们就可以很快构建想要的全栈应用。</p>\n<h2 id=\"全栈应用实战\"><a href=\"#%E5%85%A8%E6%A0%88%E5%BA%94%E7%94%A8%E5%AE%9E%E6%88%98\" 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 Component 快速开发全栈 Web 应用。</p>\n<p><img src=\"https://liujiang-1253970226.cos.ap-chengdu.myqcloud.com/ncby5-dltv8.gif\" alt=\"full-stack\"></p>\n<blockquote>\n<p>在开始所有步骤前，需执行 <code class=\"language-text\">npm install -g serverless</code> 命令，全局安装 <code class=\"language-text\">serverless cli</code>。</p>\n</blockquote>\n<h3 id=\"准备\"><a href=\"#%E5%87%86%E5%A4%87\" 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>新建项目目录 <code class=\"language-text\">fullstack-application-vue</code>，在该项目目录下新增 <code class=\"language-text\">api</code> 和 <code class=\"language-text\">dashboard</code> 目录。然后新增 <code class=\"language-text\">serverless.yml</code> 和 <code class=\"language-text\">.env</code> 配置文件，项目目录结构如下：</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">├── README.md \t\t// 项目说明文档\n├── api\t\t\t\t\t  // Restful api 后端服务\n├── dashboard\t\t\t// 前端页面\n├── .env\t\t\t\t\t// 腾讯云相关鉴权参数：TENCENT_APP_ID，TENCENT_SECRET_ID，TENCENT_SECRET_KEY\n└── serverless.yml\t// serverless 文件</code></pre></div>\n<h3 id=\"后端服务开发\"><a href=\"#%E5%90%8E%E7%AB%AF%E6%9C%8D%E5%8A%A1%E5%BC%80%E5%8F%91\" 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>进入目录 <code class=\"language-text\">api</code>，新增 <code class=\"language-text\">app.js</code> 文件，编写 <code class=\"language-text\">express</code> 服务代码，这里先新增一个路由 <code class=\"language-text\">/</code>，并返回当前服务器时间：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"13725993579520446000\"\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 express = require('express')\nconst cors = require('cors')\nconst app = express()\n\napp.use(cors())\napp.get('/', (req, res) => {\n  res.send(JSON.stringfy({ message: \\`Server time: \\${new Date().toString()}\\` }))\n})\nmodule.exports = app`, `13725993579520446000`)\"\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> express <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'express'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> cors <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'cors'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> app <span class=\"token operator\">=</span> <span class=\"token function\">express</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\napp<span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span><span class=\"token function\">cors</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">req<span class=\"token punctuation\">,</span> res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  res<span class=\"token punctuation\">.</span><span class=\"token function\">send</span><span class=\"token punctuation\">(</span><span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">stringfy</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> message<span class=\"token punctuation\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Server time: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> app</code></pre></div>\n<h3 id=\"前端页面开发\"><a href=\"#%E5%89%8D%E7%AB%AF%E9%A1%B5%E9%9D%A2%E5%BC%80%E5%8F%91\" 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>本案例使用的是 <code class=\"language-text\">Vue.js</code> + <code class=\"language-text\">Parcel</code> 的前端模板，当然你可以使用任何前端项目脚手架，比如 Vue.js 官方推荐的 <a href=\"https://github.com/vuejs/vue-cli\">Vue CLI</a> 生成的项目。进入 <code class=\"language-text\">dashboard</code> 目录，静态资源你可以直接复制我准备好的 <a href=\"https://github.com/yugasun/tencent-serverless-demo/tree/master/fullstack-application-vue\">项目模板</a>，编写入口文件 <code class=\"language-text\">src/index.js</code>:</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"21977248069864360000\"\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(`// 这里初始是没有 env.js 模块的，第一次部署后会自动生成\nrequire('../env')\n\nconst Vue = require('vue')\n\nmodule.exports = new Vue({\n  el: '#root',\n  data: {\n    message: 'Click me!',\n    isVisible: true,\n  },\n  methods: {\n    async queryServer() {\n      const response = await fetch(window.env.apiUrl)\n      const result = await response.json()\n      this.message = result.message\n    },\n  },\n})`, `21977248069864360000`)\"\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\">// 这里初始是没有 env.js 模块的，第一次部署后会自动生成</span>\n<span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'../env'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> Vue <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'vue'</span><span class=\"token punctuation\">)</span>\n\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Vue</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  el<span class=\"token punctuation\">:</span> <span class=\"token string\">'#root'</span><span class=\"token punctuation\">,</span>\n  data<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n    message<span class=\"token punctuation\">:</span> <span class=\"token string\">'Click me!'</span><span class=\"token punctuation\">,</span>\n    isVisible<span class=\"token punctuation\">:</span> <span class=\"token boolean\">true</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  methods<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">async</span> <span class=\"token function\">queryServer</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">fetch</span><span class=\"token punctuation\">(</span>window<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span>apiUrl<span class=\"token punctuation\">)</span>\n      <span class=\"token keyword\">const</span> result <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> response<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>message <span class=\"token operator\">=</span> result<span class=\"token punctuation\">.</span>message\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div>\n<h3 id=\"配置\"><a href=\"#%E9%85%8D%E7%BD%AE\" 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>前后端代码都准备好了，现在我们还需要简单配置下 <code class=\"language-text\">serverless.yml</code> 文件了：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"75082980826948030000\"\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(`name: fullstack-application-vue\n\nfrontend:\n  component: '@serverless/tencent-website'\n  # inputs 为 @serverless/tencent-website 组件的输入\n  # 具体配置说明参考：https://github.com/serverless-components/tencent-website/blob/master/docs/configure.md\n  inputs:\n    code:\n      src: dist\n      root: frontend\n      hook: npm run build\n    env:\n      # 下面的 API服务部署后，获取对应的 api 请求路径\n      apiUrl: \\${api.url}\n\napi:\n  component: '@serverless/tencent-express'\n  # inputs 为 @serverless/tencent-express 组件的输入\n  # 具体配置说明参考：https://github.com/serverless-components/tencent-express/blob/master/docs/configure.md\n  inputs:\n    code: ./api\n    functionName: fullstack-vue-api\n    apigatewayConf:\n      protocol: https`, `75082980826948030000`)\"\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\">name</span><span class=\"token punctuation\">:</span> fullstack<span class=\"token punctuation\">-</span>application<span class=\"token punctuation\">-</span>vue\n\n<span class=\"token key atrule\">frontend</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-website'</span>\n  <span class=\"token comment\"># inputs 为 @serverless/tencent-website 组件的输入</span>\n  <span class=\"token comment\"># 具体配置说明参考：https://github.com/serverless-components/tencent-website/blob/master/docs/configure.md</span>\n  <span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">code</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span> dist\n      <span class=\"token key atrule\">root</span><span class=\"token punctuation\">:</span> frontend\n      <span class=\"token key atrule\">hook</span><span class=\"token punctuation\">:</span> npm run build\n    <span class=\"token key atrule\">env</span><span class=\"token punctuation\">:</span>\n      <span class=\"token comment\"># 下面的 API服务部署后，获取对应的 api 请求路径</span>\n      <span class=\"token key atrule\">apiUrl</span><span class=\"token punctuation\">:</span> $<span class=\"token punctuation\">{</span>api.url<span class=\"token punctuation\">}</span>\n\n<span class=\"token key atrule\">api</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-express'</span>\n  <span class=\"token comment\"># inputs 为 @serverless/tencent-express 组件的输入</span>\n  <span class=\"token comment\"># 具体配置说明参考：https://github.com/serverless-components/tencent-express/blob/master/docs/configure.md</span>\n  <span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">code</span><span class=\"token punctuation\">:</span> ./api\n    <span class=\"token key atrule\">functionName</span><span class=\"token punctuation\">:</span> fullstack<span class=\"token punctuation\">-</span>vue<span class=\"token punctuation\">-</span>api\n    <span class=\"token key atrule\">apigatewayConf</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">protocol</span><span class=\"token punctuation\">:</span> https</code></pre></div>\n<blockquote>\n<p>简单的介绍下配置：首先，该文件定义了 <code class=\"language-text\">frontend</code> 和 <code class=\"language-text\">api</code> 两个模块，分别通过 <code class=\"language-text\">component</code> 属性指定依赖的 Serverless Component。对于一个标准的 Serverless Component，都会接受一个 <code class=\"language-text\">inputs</code> 属性参数，然后组件会根据 <code class=\"language-text\">inputs</code> 的配置进行处理和部署，具体有关配置的参数说明，请参考相关组件的官方配置说明。</p>\n</blockquote>\n<h3 id=\"部署\"><a href=\"#%E9%83%A8%E7%BD%B2\" 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>为什么不是直接联调开发呢？因为后端服务是云函数，但是到目前为止，所有代码都是在本地编写，前端页面接口请求链接还不存在。所以需要先将云函数部署到云端，才能进行前后端调试。这个也是本人目前遇到的痛点，因为每次修改后端服务后，都需要重新部署，然后进行前端开发调试。如果你有更好的建议，欢迎评论指教~</p>\n<p>部署时，只需要运行 <code class=\"language-text\">serverless</code> 命令就行，当然如果你需要查看部署中的 <code class=\"language-text\">DEBUG</code> 信息，还需要加上 <code class=\"language-text\">--debug</code> 参数，如下：</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">$ serverless\n# or\n$ serverless --debug</code></pre></div>\n<p>然后终端会 <code class=\"language-text\">balabalabala~</code>, 输出一大堆 <code class=\"language-text\">DEBUG</code> 信息，最后只需要看到绿色的 <code class=\"language-text\">done</code> 就行了：</p>\n<p><img src=\"https://static.yugasun.com/serverless/deploy-success.png\" alt=\"Deploy Success Result\"></p>\n<p>这样一个基于 Serverless Component 的全栈应用就开发好了。赶紧点击你部署好的链接体验一下吧~</p>\n<p><a href=\"https://br1ovx-efmogqe-1251556596.cos-website.ap-guangzhou.myqcloud.com/\">在线 Demo</a></p>\n<h2 id=\"数据库连接\"><a href=\"#%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5\" 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<h3 id=\"准备-1\"><a href=\"#%E5%87%86%E5%A4%87-1\" 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>准备</h3>\n<p>想要操作数据库，必须先拥有一台数据库实例，<a href=\"https://console.cloud.tencent.com/cdb\">腾讯云 MySQL 云数据库</a> 现在也很便宜，可以购买一个最基本按量计费 <code class=\"language-text\">1 核 1G 内存</code> 的 1 小时收费不到 <code class=\"language-text\">4 毛钱</code>，是不是非常划算。购买好之后初始化配置，然后新增一个 <code class=\"language-text\">serverless</code> 数据库，同时新增一张 <code class=\"language-text\">users</code> 表：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"8155798166142736000\"\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(`CREATE TABLE if not exists \\`test\\` ( \\`name\\` varchar (32) NOT NULL ,\\`email\\` varchar (64) NOT NULL ,\\`site\\` varchar (128) NOT NULL ) ENGINE = innodb DEFAULT CHARACTER SET = &quot;utf8mb4&quot; COLLATE = &quot;utf8mb4_general_ci&quot;`, `8155798166142736000`)\"\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=\"sql\"><pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">CREATE</span> <span class=\"token keyword\">TABLE</span> <span class=\"token keyword\">if</span> <span class=\"token operator\">not</span> <span class=\"token keyword\">exists</span> <span class=\"token punctuation\">`</span>test<span class=\"token punctuation\">`</span> <span class=\"token punctuation\">(</span> <span class=\"token punctuation\">`</span>name<span class=\"token punctuation\">`</span> <span class=\"token keyword\">varchar</span> <span class=\"token punctuation\">(</span><span class=\"token number\">32</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span> <span class=\"token punctuation\">,</span><span class=\"token punctuation\">`</span>email<span class=\"token punctuation\">`</span> <span class=\"token keyword\">varchar</span> <span class=\"token punctuation\">(</span><span class=\"token number\">64</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span> <span class=\"token punctuation\">,</span><span class=\"token punctuation\">`</span>site<span class=\"token punctuation\">`</span> <span class=\"token keyword\">varchar</span> <span class=\"token punctuation\">(</span><span class=\"token number\">128</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">NOT</span> <span class=\"token boolean\">NULL</span> <span class=\"token punctuation\">)</span> <span class=\"token keyword\">ENGINE</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">innodb</span> <span class=\"token keyword\">DEFAULT</span> <span class=\"token keyword\">CHARACTER</span> <span class=\"token keyword\">SET</span> <span class=\"token operator\">=</span> <span class=\"token string\">\"utf8mb4\"</span> <span class=\"token keyword\">COLLATE</span> <span class=\"token operator\">=</span> <span class=\"token string\">\"utf8mb4_general_ci\"</span></code></pre></div>\n<h3 id=\"前端修改\"><a href=\"#%E5%89%8D%E7%AB%AF%E4%BF%AE%E6%94%B9\" 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>首先修改前端入口文件 <code class=\"language-text\">frontend/src/index.js</code> 新增相关函数操作：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"68645721266070340000\"\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(`require('../env')\n\nconst Vue = require('vue')\nconst axios = require('axios')\nmodule.exports = new Vue({\n  el: '#root',\n  data: {\n    // ...\n    form: {\n      name: '',\n      email: '',\n      site: '',\n    },\n    userList: [],\n  },\n  methods: {\n    // ...\n    // 获取用户列表\n    async getUsers() {\n      const res = await axios.get(window.env.apiUrl + 'users')\n      this.userList = (res.data && res.data.data) || []\n    },\n    // 新增一个用户\n    async addUser() {\n      const data = this.form\n      const res = await axios.post(window.env.apiUrl + 'users', data)\n      console.log(res)\n      if (res.data) {\n        this.getUsers()\n      }\n    },\n  },\n  mounted() {\n    // 视图挂在后，获取用户列表\n    this.getUsers()\n  },\n})`, `68645721266070340000`)\"\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 function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'../env'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">const</span> Vue <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'vue'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> axios <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'axios'</span><span class=\"token punctuation\">)</span>\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Vue</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n  el<span class=\"token punctuation\">:</span> <span class=\"token string\">'#root'</span><span class=\"token punctuation\">,</span>\n  data<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n    form<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n      name<span class=\"token punctuation\">:</span> <span class=\"token string\">''</span><span class=\"token punctuation\">,</span>\n      email<span class=\"token punctuation\">:</span> <span class=\"token string\">''</span><span class=\"token punctuation\">,</span>\n      site<span class=\"token punctuation\">:</span> <span class=\"token string\">''</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    userList<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  methods<span class=\"token punctuation\">:</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// ...</span>\n    <span class=\"token comment\">// 获取用户列表</span>\n    <span class=\"token keyword\">async</span> <span class=\"token function\">getUsers</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> axios<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span>window<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span>apiUrl <span class=\"token operator\">+</span> <span class=\"token string\">'users'</span><span class=\"token punctuation\">)</span>\n      <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>userList <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">.</span>data <span class=\"token operator\">&amp;&amp;</span> res<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">)</span> <span class=\"token operator\">||</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token comment\">// 新增一个用户</span>\n    <span class=\"token keyword\">async</span> <span class=\"token function\">addUser</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> data <span class=\"token operator\">=</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>form\n      <span class=\"token keyword\">const</span> res <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> axios<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span>window<span class=\"token punctuation\">.</span>env<span class=\"token punctuation\">.</span>apiUrl <span class=\"token operator\">+</span> <span class=\"token string\">'users'</span><span class=\"token punctuation\">,</span> data<span class=\"token punctuation\">)</span>\n      console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">)</span>\n      <span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span>res<span class=\"token punctuation\">.</span>data<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">getUsers</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n      <span class=\"token punctuation\">}</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token function\">mounted</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">// 视图挂在后，获取用户列表</span>\n    <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">getUsers</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>当然你还需要修改视图模板文件 <code class=\"language-text\">frontend/index.html</code>，在页面模板中新增用户列表和用户表单：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"74225609964732090000\"\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(`<!-- user form -->\n<section class=&quot;user-form&quot; action=&quot;#&quot;>\n  <div class=&quot;form-item&quot;>\n    <label for=&quot;name&quot;>\n      Name:\n    </label>\n    <input name=&quot;name&quot; v-model=&quot;form.name&quot; type=&quot;text&quot; /><br />\n  </div>\n  <div class=&quot;form-item&quot;>\n    <label for=&quot;email&quot;>\n      Email:\n    </label>\n    <input name=&quot;email&quot; v-model=&quot;form.email&quot; type=&quot;email&quot; /><br />\n  </div>\n  <div class=&quot;form-item&quot;>\n    <label for=&quot;site&quot;>\n      Site:\n    </label>\n    <input name=&quot;site&quot; v-model=&quot;form.site&quot; type=&quot;text&quot; /><br />\n  </div>\n  <button @click=&quot;addUser&quot;>Submit</button>\n</section>\n\n<!-- user list -->\n<section class=&quot;user-list&quot;>\n  <ul v-if=&quot;userList.length > 0&quot;>\n    <li v-for=&quot;item in userList&quot; :key=&quot;item.id&quot;>\n      <p>\n        <b>Name: {{ item.name }}</b>\n        <b>Email: {{ item.email }}</b>\n        <b>Site: {{ item.site }}</b>\n      </p>\n    </li>\n  </ul>\n  <span v-else>No Data</span>\n</section>`, `74225609964732090000`)\"\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=\"html\"><pre class=\"language-html\"><code class=\"language-html\"><span class=\"token comment\">&lt;!-- user form --></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>section</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>user-form<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">action</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>#<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>form-item<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>label</span> <span class=\"token attr-name\">for</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>name<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n      Name:\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>label</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token attr-name\">name</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>name<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">v-model</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>form.name<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>text<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>br</span> <span class=\"token punctuation\">/></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>form-item<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>label</span> <span class=\"token attr-name\">for</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>email<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n      Email:\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>label</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token attr-name\">name</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>email<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">v-model</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>form.email<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>email<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>br</span> <span class=\"token punctuation\">/></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>div</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>form-item<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>label</span> <span class=\"token attr-name\">for</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>site<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n      Site:\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>label</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>input</span> <span class=\"token attr-name\">name</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>site<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">v-model</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>form.site<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">type</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>text<span class=\"token punctuation\">\"</span></span> <span class=\"token punctuation\">/></span></span><span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>br</span> <span class=\"token punctuation\">/></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>div</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>button</span> <span class=\"token attr-name\">@click</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>addUser<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>Submit<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>button</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>section</span><span class=\"token punctuation\">></span></span>\n\n<span class=\"token comment\">&lt;!-- user list --></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>section</span> <span class=\"token attr-name\">class</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>user-list<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>ul</span> <span class=\"token attr-name\">v-if</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>userList.length > 0<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>li</span> <span class=\"token attr-name\">v-for</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>item in userList<span class=\"token punctuation\">\"</span></span> <span class=\"token attr-name\">:key</span><span class=\"token attr-value\"><span class=\"token punctuation\">=</span><span class=\"token punctuation\">\"</span>item.id<span class=\"token punctuation\">\"</span></span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>p</span><span class=\"token punctuation\">></span></span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>b</span><span class=\"token punctuation\">></span></span>Name: {{ item.name }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>b</span><span class=\"token punctuation\">></span></span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>b</span><span class=\"token punctuation\">></span></span>Email: {{ item.email }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>b</span><span class=\"token punctuation\">></span></span>\n        <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>b</span><span class=\"token punctuation\">></span></span>Site: {{ item.site }}<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>b</span><span class=\"token punctuation\">></span></span>\n      <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>p</span><span class=\"token punctuation\">></span></span>\n    <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>li</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>ul</span><span class=\"token punctuation\">></span></span>\n  <span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;</span>span</span> <span class=\"token attr-name\">v-else</span><span class=\"token punctuation\">></span></span>No Data<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>span</span><span class=\"token punctuation\">></span></span>\n<span class=\"token tag\"><span class=\"token tag\"><span class=\"token punctuation\">&lt;/</span>section</span><span class=\"token punctuation\">></span></span></code></pre></div>\n<blockquote>\n<p>注意：如果还不熟悉 Vue.js 语法，请移至 <a href=\"https://cn.vuejs.org/\">官方文档</a>，当然如果你想快速上手 Vue.js 开发，也可以阅读这份 <a href=\"https://yugasun.github.io/You-May-Not-Know-Vuejs/\">Vue 从入门到精通</a> 教程。</p>\n</blockquote>\n<h3 id=\"后端修改\"><a href=\"#%E5%90%8E%E7%AB%AF%E4%BF%AE%E6%94%B9\" 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>这里使用 <code class=\"language-text\">.env</code> 来进行数据库连接参数配置，在 <code class=\"language-text\">api</code> 目录下新增 <code class=\"language-text\">.env</code> 文件，将之前的数据库配置填入文件中，参考 <code class=\"language-text\">api/.env.example</code> 文件。然后添加并安装 <code class=\"language-text\">dotenv</code> 依赖，同时添加 <code class=\"language-text\">mysql2</code> 模块进行数据库操作，<code class=\"language-text\">body-parser</code> 模块进行 <code class=\"language-text\">POST</code> 请求时的 <code class=\"language-text\">body</code> 解析。</p>\n<p>之后新增后端 api，进行数据库读写，修改后的 <code class=\"language-text\">api/app.js</code> 代码如下：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"75108442283384680000\"\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(`'use strict'\nrequire('dotenv').config()\nconst express = require('express')\nconst cors = require('cors')\nconst mysql = require('mysql2')\nconst bodyParser = require('body-parser')\n\n// init mysql connection\nfunction initMysqlPool() {\n  const { DB_HOST, DB_PORT, DB_DATABASE, DB_USER, DB_PASSWORD } = process.env\n\n  const promisePool = mysql\n    .createPool({\n      host: DB_HOST,\n      user: DB_USER,\n      port: DB_PORT,\n      password: DB_PASSWORD,\n      database: DB_DATABASE,\n      connectionLimit: 1,\n    })\n    .promise()\n\n  return promisePool\n}\n\nconst app = express()\napp.use(bodyParser.json())\napp.use(cors())\n\nif (!app.promisePool) {\n  app.promisePool = initMysqlPool()\n}\n\napp.get('/', (req, res) => {\n  res.send(JSON.stringify({ message: \\`Server time: \\${new Date().toString()}\\` }))\n})\n\n// get user list\napp.get('/users', async (req, res) => {\n  const [data] = await app.promisePool.query('select * from users')\n  res.send(\n    JSON.stringify({\n      data: data,\n    })\n  )\n})\n\n// add new user\napp.post('/users', async (req, res) => {\n  let result = ''\n  try {\n    const { name, email, site } = req.body\n    const [res] = await app.promisePool.query('INSERT into users SET ?', {\n      name: name,\n      email: email,\n      site: site,\n    })\n    result = {\n      data: res && res.insertId,\n      message: 'Insert Success',\n    }\n  } catch (e) {\n    result = {\n      data: e,\n      message: 'Insert Fail',\n    }\n  }\n\n  res.send(JSON.stringify(result))\n})\n\nmodule.exports = app`, `75108442283384680000`)\"\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 string\">'use strict'</span>\n<span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'dotenv'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">config</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> express <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'express'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> cors <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'cors'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> mysql <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'mysql2'</span><span class=\"token punctuation\">)</span>\n<span class=\"token keyword\">const</span> bodyParser <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'body-parser'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// init mysql connection</span>\n<span class=\"token keyword\">function</span> <span class=\"token function\">initMysqlPool</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> <span class=\"token constant\">DB_HOST</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">DB_PORT</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">DB_DATABASE</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">DB_USER</span><span class=\"token punctuation\">,</span> <span class=\"token constant\">DB_PASSWORD</span> <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> process<span class=\"token punctuation\">.</span>env\n\n  <span class=\"token keyword\">const</span> promisePool <span class=\"token operator\">=</span> mysql\n    <span class=\"token punctuation\">.</span><span class=\"token function\">createPool</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      host<span class=\"token punctuation\">:</span> <span class=\"token constant\">DB_HOST</span><span class=\"token punctuation\">,</span>\n      user<span class=\"token punctuation\">:</span> <span class=\"token constant\">DB_USER</span><span class=\"token punctuation\">,</span>\n      port<span class=\"token punctuation\">:</span> <span class=\"token constant\">DB_PORT</span><span class=\"token punctuation\">,</span>\n      password<span class=\"token punctuation\">:</span> <span class=\"token constant\">DB_PASSWORD</span><span class=\"token punctuation\">,</span>\n      database<span class=\"token punctuation\">:</span> <span class=\"token constant\">DB_DATABASE</span><span class=\"token punctuation\">,</span>\n      connectionLimit<span class=\"token punctuation\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token punctuation\">.</span><span class=\"token function\">promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n\n  <span class=\"token keyword\">return</span> promisePool\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> app <span class=\"token operator\">=</span> <span class=\"token function\">express</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span>bodyParser<span class=\"token punctuation\">.</span><span class=\"token function\">json</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">use</span><span class=\"token punctuation\">(</span><span class=\"token function\">cors</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">if</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">!</span>app<span class=\"token punctuation\">.</span>promisePool<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  app<span class=\"token punctuation\">.</span>promisePool <span class=\"token operator\">=</span> <span class=\"token function\">initMysqlPool</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span>\n\napp<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">req<span class=\"token punctuation\">,</span> res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  res<span class=\"token punctuation\">.</span><span class=\"token function\">send</span><span class=\"token punctuation\">(</span><span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">stringify</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> message<span class=\"token punctuation\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Server time: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token keyword\">new</span> <span class=\"token class-name\">Date</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token template-punctuation string\">`</span></span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// get user list</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">get</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/users'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">req<span class=\"token punctuation\">,</span> res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>data<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> app<span class=\"token punctuation\">.</span>promisePool<span class=\"token punctuation\">.</span><span class=\"token function\">query</span><span class=\"token punctuation\">(</span><span class=\"token string\">'select * from users'</span><span class=\"token punctuation\">)</span>\n  res<span class=\"token punctuation\">.</span><span class=\"token function\">send</span><span class=\"token punctuation\">(</span>\n    <span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">stringify</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n      data<span class=\"token punctuation\">:</span> data<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token comment\">// add new user</span>\napp<span class=\"token punctuation\">.</span><span class=\"token function\">post</span><span class=\"token punctuation\">(</span><span class=\"token string\">'/users'</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">req<span class=\"token punctuation\">,</span> res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">let</span> result <span class=\"token operator\">=</span> <span class=\"token string\">''</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> name<span class=\"token punctuation\">,</span> email<span class=\"token punctuation\">,</span> site <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> req<span class=\"token punctuation\">.</span>body\n    <span class=\"token keyword\">const</span> <span class=\"token punctuation\">[</span>res<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> app<span class=\"token punctuation\">.</span>promisePool<span class=\"token punctuation\">.</span><span class=\"token function\">query</span><span class=\"token punctuation\">(</span><span class=\"token string\">'INSERT into users SET ?'</span><span class=\"token punctuation\">,</span> <span class=\"token punctuation\">{</span>\n      name<span class=\"token punctuation\">:</span> name<span class=\"token punctuation\">,</span>\n      email<span class=\"token punctuation\">:</span> email<span class=\"token punctuation\">,</span>\n      site<span class=\"token punctuation\">:</span> site<span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    result <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n      data<span class=\"token punctuation\">:</span> res <span class=\"token operator\">&amp;&amp;</span> res<span class=\"token punctuation\">.</span>insertId<span class=\"token punctuation\">,</span>\n      message<span class=\"token punctuation\">:</span> <span class=\"token string\">'Insert Success'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    result <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n      data<span class=\"token punctuation\">:</span> e<span class=\"token punctuation\">,</span>\n      message<span class=\"token punctuation\">:</span> <span class=\"token string\">'Insert Fail'</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span>\n  <span class=\"token punctuation\">}</span>\n\n  res<span class=\"token punctuation\">.</span><span class=\"token function\">send</span><span class=\"token punctuation\">(</span><span class=\"token constant\">JSON</span><span class=\"token punctuation\">.</span><span class=\"token function\">stringify</span><span class=\"token punctuation\">(</span>result<span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> app</code></pre></div>\n<h3 id=\"配置修改\"><a href=\"#%E9%85%8D%E7%BD%AE%E4%BF%AE%E6%94%B9\" 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>这里数据库访问需要通过腾讯云私有网络，所以还需要为云函数配置私有网络（VPC），同时还需要配置能够操作数据库的角色（关于角色配置，可以直接到 <a href=\"https://console.cloud.tencent.com/cam/role\">角色管理页面</a>），这里我新建了一个 <code class=\"language-text\">QCS_SCFFull</code> 的角色，可以用来访问数据库。然后修改 <code class=\"language-text\">serverless.yml</code> 中的配置：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"22579804576666770000\"\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(`# ...\napi:\n  component: '@serverless/tencent-express'\n  # more configuration for @serverless/tencent-website,\n  # refer to: https://github.com/serverless-components/tencent-express/blob/master/docs/configure.md\n  inputs:\n    code: ./api\n    functionName: fullstack-vue-api\n    role: QCS_SCFFull # 此角色必须具备访问数据库权限\n    functionConf:\n      # 这个是用来访问新创建数据库的私有网络，可以在你的数据库实例管理页面查看\n      vpcConfig:\n        vpcId: vpc-6n5x55kb\n        subnetId: subnet-4cvr91js\n    apigatewayConf:\n      protocol: https`, `22579804576666770000`)\"\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 comment\"># ...</span>\n<span class=\"token key atrule\">api</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-express'</span>\n  <span class=\"token comment\"># more configuration for @serverless/tencent-website,</span>\n  <span class=\"token comment\"># refer to: https://github.com/serverless-components/tencent-express/blob/master/docs/configure.md</span>\n  <span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">code</span><span class=\"token punctuation\">:</span> ./api\n    <span class=\"token key atrule\">functionName</span><span class=\"token punctuation\">:</span> fullstack<span class=\"token punctuation\">-</span>vue<span class=\"token punctuation\">-</span>api\n    <span class=\"token key atrule\">role</span><span class=\"token punctuation\">:</span> QCS_SCFFull <span class=\"token comment\"># 此角色必须具备访问数据库权限</span>\n    <span class=\"token key atrule\">functionConf</span><span class=\"token punctuation\">:</span>\n      <span class=\"token comment\"># 这个是用来访问新创建数据库的私有网络，可以在你的数据库实例管理页面查看</span>\n      <span class=\"token key atrule\">vpcConfig</span><span class=\"token punctuation\">:</span>\n        <span class=\"token key atrule\">vpcId</span><span class=\"token punctuation\">:</span> vpc<span class=\"token punctuation\">-</span>6n5x55kb\n        <span class=\"token key atrule\">subnetId</span><span class=\"token punctuation\">:</span> subnet<span class=\"token punctuation\">-</span>4cvr91js\n    <span class=\"token key atrule\">apigatewayConf</span><span class=\"token punctuation\">:</span>\n      <span class=\"token key atrule\">protocol</span><span class=\"token punctuation\">:</span> https</code></pre></div>\n<p>最后重新部署一下就行了。</p>\n<p>以上基于腾讯云 <a href=\"https://cloud.tencent.com/product/sf\">Serverless Framework</a> 来实现：</p>\n<ul>\n<li><a href=\"https://github.com/yugasun/tencent-serverless-demo/tree/master/fullstack-application-vue\">完整的模板仓库</a></li>\n<li><a href=\"https://br1ovx-efmogqe-1251556596.cos-website.ap-guangzhou.myqcloud.com\">在线 Demo</a></li>\n</ul>\n<h2 id=\"小结\"><a href=\"#%E5%B0%8F%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>当然全栈方案，并没有这么简单，这里只是简单介绍，如何使用 Serverless Component，快速实现一个全栈应用。如果要应用到实际的业务场景，我们还需考虑更多的问题，期待大家去探索和发现！</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-5-Full-stack-solution-based-on-serverless-component/#%E4%BB%80%E4%B9%88%E6%98%AF-serverless-component\">什么是 Serverless Component</a></li>\n<li><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#serverless-component-%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86\">Serverless Component 工作原理</a></li>\n<li>\n<p><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#%E5%85%A8%E6%A0%88%E5%BA%94%E7%94%A8%E5%AE%9E%E6%88%98\">全栈应用实战</a></p>\n<ul>\n<li><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#%E5%87%86%E5%A4%87\">准备</a></li>\n<li><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#%E5%90%8E%E7%AB%AF%E6%9C%8D%E5%8A%A1%E5%BC%80%E5%8F%91\">后端服务开发</a></li>\n<li><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#%E5%89%8D%E7%AB%AF%E9%A1%B5%E9%9D%A2%E5%BC%80%E5%8F%91\">前端页面开发</a></li>\n<li><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#%E9%85%8D%E7%BD%AE\">配置</a></li>\n<li><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#%E9%83%A8%E7%BD%B2\">部署</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5\">数据库连接</a></p>\n<ul>\n<li><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#%E5%87%86%E5%A4%87-1\">准备</a></li>\n<li><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#%E5%89%8D%E7%AB%AF%E4%BF%AE%E6%94%B9\">前端修改</a></li>\n<li><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#%E5%90%8E%E7%AB%AF%E4%BF%AE%E6%94%B9\">后端修改</a></li>\n<li><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#%E9%85%8D%E7%BD%AE%E4%BF%AE%E6%94%B9\">配置修改</a></li>\n</ul>\n</li>\n<li><a href=\"/best-practice/2019-12-5-Full-stack-solution-based-on-serverless-component/#%E5%B0%8F%E7%BB%93\">小结</a></li>\n</ul>"},"previousBlog":{"id":"1e54dd30-fe20-593b-952b-869b890aced3","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020114/1579000616928-HEAD.png","authors":["Anycodes"],"categories":["guides-and-tutorials"],"date":"2019-12-10T00:00:00.000Z","title":"Serverless 的资源评估与成本探索","description":"本文介绍了使用 Serverless 架构布局业务时，需要关注的资源和费用","authorslink":["https://www.zhihu.com/people/liuyu-43-97"],"translators":null,"translatorslink":null,"tags":["资源成本","Serverless"],"keywords":"Serverless 架构,Serverless 布局业务,Serverless 资源评估","outdated":null},"wordCount":{"words":341,"sentences":66,"paragraphs":66},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-12-10-resource-cost.md","fields":{"slug":"/blog/2019-12-10-resource-cost/","keywords":["serverless","云函数","内存","serverlesscloud"]}},"nextBlog":{"id":"74a25989-4611-5393-85ef-698f2c988635","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/20191226/1577353111488-website.png","authors":["liujiang"],"categories":["best-practice"],"date":"2019-12-04T00:00:00.000Z","title":"Hexo + Serverless Framework，简单三步搭建你的个人博客","description":"简单三步，即可通过 Serverless Website 组件快速构建一个 Serverless Hexo 站点。","authorslink":["https://github.com/jiangliu5267"],"translators":null,"translatorslink":null,"tags":["Serverless","Hexo"],"keywords":"Hexo Serverless,Serverless Website,Serverless Hexo 站点","outdated":true},"wordCount":{"words":146,"sentences":34,"paragraphs":34},"fileAbsolutePath":"/opt/build/repo/content/best-practice/2019-12-4-Quickly-build-personal-blog.md","fields":{"slug":"/best-practice/2019-12-4-Quickly-build-personal-blog/","keywords":["nodejs","serverless","website","website 组件","无服务器","hexo","serverless","Serverless","yml","Hexo","website","tencent"]}}},"pageContext":{"isCreatedByStatefulCreatePages":false,"blogId":"70178fa6-74b6-5606-9467-7c4fa830b240","previousBlogId":"1e54dd30-fe20-593b-952b-869b890aced3","nextBlogId":"74a25989-4611-5393-85ef-698f2c988635"}}}