前端:使用 Gatsby.js 和 Node.js 进行静态站点更新
已发表: 2022-03-11对于某些项目需求,静态页面生成不仅足够,而且在速度和可扩展性方面也是最有效的。
在本系列的前半部分,我们结合了 Node.js、Express、MongoDB、cron 和 Heroku,以提供一个 CI 就绪的后端,该后端根据每日计划使用 GitHub 的 API。 现在我们准备好将 Gatsby 添加到组合中以完成我们的静态页面生成项目。
开发前端:使其成为 Gatsby 网站
因为 Gatsby 网站基于 React,所以如果您已经熟悉如何使用 React 构建网站,将会很有帮助。
对于样式,我更喜欢 Bootstrap、reactstrap 和 react-markdown。 您可能知道 GitHub 中的发行说明以 Markdown 格式存储,因此我们需要一个转换器。
静态网站项目结构
我们的文件/文件夹结构如下:
这些文件是干什么用的? 让我们来看看:
-
env.development
和env.production
是环境变量配置文件。 -
all-repositories.js
模板将用于我们的主页,其中包含一个存储库列表。 -
repository.js
模板将用于显示给定存储库的详细信息。 -
gatsby-node.js
是我们使用后端端点并运行我们的createPage
方法的地方。 -
package.json
一如既往地包含依赖项和项目属性。 -
global.css
是我们的主要样式表文件。
盖茨比网站实施
与我们的后端一样,在将所需的依赖项添加到前端的package.json
后,运行npm install
(或yarn
,如果您安装了 Yarn):
{ // ... "dependencies": { "axios": "^0.18.0", "bootstrap": "^4.3.1", "gatsby": "^2.0.0", "react": "^16.5.1", "react-dom": "^16.5.1", "react-markdown": "^4.0.6", "reactstrap": "^7.1.0" } // ... }
env.development
和env.production
文件只有对应环境的后端 URL。 前者有:
API_URL=http://localhost:3000
…而生产有:
API_URL=https://[YOUR_UNIQUE_APP_NAME].herokuapp.com
gatsby-node.js
只会在您构建代码时运行一次。 所以我们必须在这里从后端收集所有必要的信息。 然后,响应将与我们的模板一起使用以生成适当的静态页面。
在我们的例子中, all-repositories.js
需要所有存储库, repository.js
需要每次迭代对应的存储库。 最后,我们可以动态生成页面路径,将存储库owner
和name
参数作为path
字段的一部分传递:
const axios = require('axios'); require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` }); const getRepositoryData = async () => { console.log(process.env.API_URL); return axios.get(`${process.env.API_URL}/repositories`); }; exports.createPages = async ({ actions: { createPage } }) => { let repositories = await getRepositoryData(); repositories = repositories.data; // Create a page that lists all repositories. createPage({ path: `/`, component: require.resolve('./src/templates/all-repositories.js'), context: { repositories } }); // Create a page for each repository. repositories.forEach(repository => { createPage({ path: `/repository/${repository.owner}/${repository.name}`, component: require.resolve('./src/templates/repository.js'), context: { repository } }); }); };
对于all-repositories.js
和repository.js
,如果我们省略样式,我们只是从pageContext
收集数据并像在 React 中使用参数一样使用它。
在all-repositories.js
中,我们将使用pageContext
的repositories
字段,如下所示:
export default ({pageContext: {repositories}}) => ( // ... <ListGroup> {/* Because the repositories parameter is a list, we are iterating over all items and using their fields */} {repositories.map(repository => (repository.tagName && <ListGroupItem className="repository-list-item"> // ... <Row> {`${repository.repositoryDescription}`} </Row> // ... </ListGroupItem> ))} </ListGroup> // ... );
至于repository.js
,我们将使用pageContext
的repository
字段:
export default ({pageContext: {repository}}) => ( <div className="layout"> {repository.tagName && <ListGroupItem className="repository-list-item"> // ... <h1 className="release-notes">{`Release notes`}</h1> <hr/> {/* This the place where we will use Markdown-formatted release notes */} <ReactMarkdown source={`${repository.releaseDescription}`}/> </ListGroupItem> } // ... </div> );
现在,确保您的后端已启动并正在运行。 您会记得在这个项目中,我们将其设置为 http://localhost:3000。
接下来,从前端项目的根目录运行gatsby develop
并打开 http://localhost:8000。
如果您确实向后端添加了一些存储库(所有者/名称)并至少运行一次 update-via-GitHub-API 功能,您应该会看到如下内容:
单击其中一个存储库后,您应该会看到如下内容:
经过上面的修改,我们的前端实现就完成了。
伟大的! 现在我们只需要部署。
部署前端
在这部分我们不需要对我们的前端应用程序进行任何更改。 我们将简单地将其部署到 Netlify — 您需要一个帐户。

但首先,我们必须将代码部署到 GitHub、GitLab 或 Bitbucket。 与后端一样,我将代码部署到 GitHub。
然后,登录 Netlify 并单击仪表板上的“从 Git 新建站点”按钮。 按照屏幕上的步骤选择您的 Git 提供程序并找到您的存储库。
最后一步,如果你的代码结构正确,Netlify 将自动正确设置构建命令和发布目录,如下所示:
然后单击“部署站点”。 它会将您的站点部署到随机生成的 Netlify 子域,但您可以随时更改它——我将部署更改为 https://sample-create-page-api-gatsby.netlify.com,您可以在其中找到现场演示完成的应用程序。
到目前为止,一切都很好。 但是因为它是一个静态页面应用程序,所以我们必须每天重新构建它以使其保持最新状态。
使用 Build Hook 进行每日更新
Netlify 中的构建钩子用作构建触发器,因此您可以在 cron 作业完成后从后端触发它们。 为此,首先在 Netlify 中创建一个构建挂钩。
在“构建和部署→持续部署”部分下,您可以找到“构建挂钩”。 单击“添加构建挂钩”。
给它一个名字并保存它。 (我叫我build-from-backend
。)然后复制它生成的链接。
现在让我们打开我们的后端项目并将这几行添加到cron.controller.js
文件中。 我们只是向 Netlify 的 build-hook URL 发送一个POST
请求。
const Axios = require('axios'); const Config = require('../config/env.config'); const NETLIFY_BUILD_HOOK_URI = Config.netlifyEndpoint; function updateGatsby() { if (NETLIFY_BUILD_HOOK_URI) { console.log('Gatsby build request will be send'); Axios.post(NETLIFY_BUILD_HOOK_URI).then(() => { console.log('Gatsby build request was successful'); }); } }
然后更新我们的updateDaily
函数:
function updateDaily() { RepositoryController.updateRepositories().then(() => { updateGatsby(); }); }
最后,更新我们的env.config.js
文件以设置从环境变量中收集的netlifyEndpoint
属性:
"netlifyEndpoint": process.env.NETLIFY_BUILD_HOOK || ""
现在,您需要设置刚才从 Netlify 复制的NETLIFY_BUILD_HOOK
环境变量。 在 Heroku 中,您可以从应用程序的“设置”部分设置环境变量。
推送您的提交后,后端应用程序将向 Netlify 发送构建请求,并且您的页面将在您的 cron 作业结束时每天更新。 这是添加构建钩子功能后存储库的外观,以及后端存储库和前端存储库的最终版本。
作为该项目的最后润色,我们将展示如何使用由 AWS CloudWatch 触发的 AWS Lambda 函数,该函数将在每次每日更新时及时唤醒您的后端。
AWS Lambda 和 AWS CloudWatch 简单请求
AWS Lambda 是一个无服务器计算平台。 为了我们的目的,我们只需要这个平台的基础知识。 您需要一个 AWS 账户。
首先,使用您的账户登录 AWS 并找到 Lambda 管理控制台。 例如,对于us-east-2
,可以在 https://us-east-2.console.aws.amazon.com/lambda/home 找到它。
如果尚未选择,请转到“功能”部分:
在这里,我们将通过单击“创建函数”从头开始创建函数。 让我们给它一个解释性的名字。 我们将使用 Node.js 作为运行时语言。 然后单击下一个“创建函数”以完成它。
它会将我们重定向到新函数的页面,我们可以在index.js
中编写代码。
让我们实现我们的第一个 lambda 函数。 因为我们没有任何第三方依赖,所以我们必须使用 Node.js 的核心模块。 (如果您想启用第三方依赖项,请遵循 AWS 的本指南。)
确保导出的方法名称(在本例中为handler
)与页面中的“Handler”参数匹配:
其余部分是对后端的简单GET
请求:
const https = require('https'); exports.handler = async (event) => { return new Promise((resolve, reject) => { https.get(process.env.HEROKU_APP_URL, (resp) => { let data = ''; resp.on('data', (chunk) => { data += chunk; }); resp.on('end', () => { resolve(JSON.parse(data)); }); }).on("error", (err) => { reject("Error: " + err.message); }); }); };
确保您在页面中设置了HEROKU_APP_URL
环境变量,并保存您的配置:
最后一步是添加一个 CloudWatch 触发规则,以便在每天更新前十分钟运行此函数 — 在本系列文章中,该规则将持续到 23:50:
我们的 CloudWatch 触发规则类型将是“计划表达式”,根据接受的格式,我们的 cron 表达式将为cron(50 23 * * ? *)
。
我们现在已将 AWS Lambda 函数配置为由 CloudWatch 规则触发。
现在为我们的静态网页提供动力:Gatsby/React 和 Netlify
将少量 AWS Lambda/CloudWatch 添加到我们的 Node.js/MongoDB/Heroku 后端,Gatsby 和 Netlify 生成并托管我们的前端,我们的应用程序就完成了!
我之前分享了一个现场演示链接,但也可以随意查看我的原型的增强版本——它有一些额外的变化,我知道你会喜欢的。
您可以将其用作类似项目的蓝图——我希望这些文章能帮助您以更快、更具成本效益的方式构建您的应用程序原型。 谢谢阅读!