使用定制的静态站点生成器简化您的堆栈

已发表: 2022-03-10
快速总结 ↬在现代开发中,有很多很棒的网站开发工具,但它们往往超出了给定项目所必需的工具。 在本文中,我们将探讨如何在没有框架和客户端 JavaScript 的 CMS 中制作一个不起眼的 HTML 页面并使其内容可编辑。

随着 Jamstack 运动的出现,静态服务站点再次风靡一时。 大多数提供静态 HTML 的开发人员并未创作原生 HTML。 为了获得可靠的开发人员体验,我们经常求助于称为静态站点生成器 (SSG) 的工具。

这些工具具有许多使创作大型静态网站变得愉快的功能。 无论是像 Gatsby 的数据源那样提供简单的第三方 API 挂钩,还是像 11ty 的大量模板引擎那样提供深入的配置,静态站点生成总能满足每个人的需求。

因为这些工具是为不同的用例而构建的,所以它们必须具有很多功能。 这些功能使它们功能强大。 对于新开发人员来说,它们也使它们变得非常复杂和不透明。 在本文中,我们将介绍 SSG 的基本组件并创建我们自己的组件。

什么是静态站点生成器?

静态站点生成器的核心是一个程序,它对一组文件执行一系列转换,以将它们转换为静态资产,例如 HTML。 它可以接受什么类型的文件,如何转换它们,以及输出什么类型的文件来区分 SSG。

Jekyll 是一个早期且仍然很流行的 SSG,它使用 Ruby 将 Liquid 模板和 Markdown 内容文件处理成 HTML。

Gatsby 使用 React 和 JSX 将组件和内容转换为 HTML。 然后它更进一步,创建一个可以静态服务的单页应用程序。

11ty 从 Liquid、Handlebars、Nunjucks 或 JavaScript 模板文字等模板引擎渲染 HTML。

这些平台中的每一个都具有使我们的生活更轻松的附加功能。 它们提供主题、构建管道、插件架构等。 每增加一个特性都会带来更多的复杂性、更多的魔力和更多的依赖。 可以肯定,它们是重要的功能,但并非每个项目都需要它们。

在这三个不同的 SSG 之间,我们可以看到另一个共同的主题:数据 + 模板 = 最终站点。 这似乎是生成器静态站点的核心功能。 这是我们将基于我们的 SSG 的功能。

静态站点生成器的核心是一个程序,它对一组文件执行一系列转换,以将它们转换为静态资产,例如 HTML。

我们新的静态站点生成器的技术堆栈:Handlebars、Sanity.io 和 Netlify

要构建我们的 SSG,我们需要一个模板引擎、一个数据源和一个可以运行我们的 SSG 并构建我们的站点的主机。 许多生成器使用 Markdown 作为数据源,但如果我们更进一步,将 SSG 本地连接到 CMS 会怎样?

  • 数据来源:Sanity.io
  • 数据获取和模板:节点和把手
  • 主机和部署:Netlify。

先决条件

  • 安装了 NodeJS
  • Sanity.io 帐户
  • Git的知识
  • 命令行基础知识
  • 部署到 Netlify 等服务的基本知识。

注意要继续学习,您可以在 GitHub 上的此存储库中找到代码。

跳跃后更多! 继续往下看↓

在 HTML 中设置我们的文档结构

为了开始我们的文档结构,我们将编写纯 HTML。 还没有必要把事情复杂化。

在我们的项目结构中,我们需要为源文件创建一个存放位置。 在这种情况下,我们将创建一个src目录并将我们的index.html放入其中。

index.html中,我们将概述我们想要的内容。 这将是一个相对简单的关于页面。

 <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Title of the page!</title> </head> <body> <h1>The personal homepage of Bryan Robinson</h1> <p>Some pagraph and rich text content next</p> <h2>Bryan is on the internet</h2> <ul> <li><a href="linkURL">List of links</a></li> </ul> </body> </html>

让我们保持简单。 我们将从页面的h1开始。 我们将在此之后添加几段传记信息,并使用链接列表锚定页面以查看更多信息。

将我们的 HTML 转换为接受数据的模板

在我们有了基本结构之后,我们需要建立一个流程来将其与一些数据结合起来。 为此,我们将使用 Handlebars 模板引擎。

Handlebars 的核心是一个类似 HTML 的字符串,通过文档中定义的规则插入数据,然后输出一个编译的 HTML 字符串。

要使用 Handlebars,我们需要初始化一个 package.json 并安装该包。

运行npm init -y以创建带有一些默认内容的 package.json 文件的结构。 一旦我们有了这个,我们就可以安装 Handlebars。

 npm install handlebars

我们的构建脚本将是一个 Node 脚本。 这是我们将在本地用于构建的脚本,也是我们的部署供应商和主机将用于为实时站点构建 HTML 的脚本。

要启动我们的脚本,我们将创建一个index.js文件并在顶部需要两个包。 第一个是 Handlebars,第二个是 Node 中用于访问当前文件系统的默认模块。

 const fs = require('fs'); const Handlebars = require('handlebars');

我们将使用fs模块来访问我们的源文件,以及写入分发文件。 为了开始我们的构建,我们将为我们的文件创建一个在调用时运行的main函数,以及一个用于组合我们的数据和标记的buildHTML函数。

 function buildHTML(filename, data) { const source = fs.readFileSync(filename,'utf8').toString(); const template = Handlebars.compile(source); const output = template(data); return output } async function main(src, dist) { const html = buildHTML(src, { "variableData": "This is variable data"}); fs.writeFile(destination, html, function (err) { if (err) return console.log(err); console.log('index.html created'); }); } main('./src/index.html', './dist/index.html');

main()函数接受两个参数:HTML 模板的路径和我们希望构建的文件存在的路径。 在我们的主函数中,我们使用一些数据在模板源路径上运行buildHTML

build 函数将源文档转换为字符串并将该字符串传递给 Handlebars。 Handlebars 使用该字符串编译模板。 然后我们将数据传递到编译后的模板中,Handlebars 呈现一个新的 HTML 字符串,用数据输出替换任何变量或模板逻辑。

我们将该字符串返回到我们的main函数中,并使用 Node 的文件系统模块提供的writeFile方法将新文件写入我们指定的位置(如果目录存在)。

为防止出现错误,请将dist目录添加到您的项目中,其中包含.gitkeep文件。 我们不想提交我们构建的文件(我们的构建过程会这样做),但我们要确保我们的脚本有这个目录。

在我们创建 CMS 来管理此页面之前,让我们确认它是否正常工作。 为了测试,我们将修改我们的 HTML 文档以使用我们刚刚传递给它的数据。 我们将使用 Handlebars 变量语法来包含variableData内容。

 <h1>{{ variableData }}</h1>

既然我们的 HTML 有一个变量,我们就可以运行我们的节点脚本了。

 node index.js

脚本完成后,我们应该在/dist/index.html有一个文件。 如果我们在浏览器中阅读 open this,我们将看到我们的标记呈现,还有我们的“这是可变数据”字符串。

连接到 CMS

我们有一种将数据与模板放在一起的方法,现在我们需要一个数据源。 此方法适用于任何具有 API 的数据源。 对于这个演示,我们将使用 Sanity.io。

Sanity 是一种 API 优先的数据源,将内容视为结构化数据。 他们有一个开源内容管理系统,可以让编辑和开发人员更方便地管理和添加数据。 CMS 就是通常所说的“无头”CMS。 与您的数据与演示文稿紧密耦合的传统管理系统不同,无头 CMS 创建了一个数据层,该数据层可以被任何前端或服务(可能同时有很多)使用。

Sanity 是一项付费服务​​,但他们有一个“标准”计划,该计划是免费的,并且具有我们对此类网站所需的所有功能。

建立理智

启动和运行新的 Sanity 项目的最快方法是使用 Sanity CLI。 我们将从全局安装开始。

 npm install -g @sanity/cli

CLI 让我们可以访问一组用于管理、部署和创建的助手。 为了让事情开始,我们将运行sanity init 。 这将使我们完成一份问卷调查,以帮助引导我们的工作室(Sanity 称之为他们的开源 CMS)。

 Select a Project to Use: Create new project HTML CMS Use the default dataset configuration? Y // this creates a "Production" dataset Project output path: studio // or whatever directory you'd like this to live in Select project template Clean project with no predefined schemas

此步骤将在您的 Sanity 帐户中创建一个新项目和数据集,创建一个本地版本的 Studio,并为您将数据和 CMS 绑定在一起。 默认情况下, studio目录将创建在我们项目的根目录中。 在大型项目中,您可能希望将其设置为单独的存储库。 对于这个项目,将其捆绑在一起很好。

为了在本地运行我们的 Studio,我们将目录更改为studio目录并运行sanity start 。 这将在localhost:3333运行 Studio。 当您登录时,您会看到一个屏幕,让您知道您有“空架构”。 有了这个,是时候添加我们的模式了,这就是我们的数据的结构和编辑方式。

创建健全模式

在 Sanity Studio 中创建文档和字段的方式是在schemas/schema.js文件中创建模式。

对于我们的网站,我们将创建一个名为“About Details”的模式类型。 我们的模式将来自我们的 HTML。 一般来说,我们可以将大部分网页设置为单个富文本字段,但以解耦方式构建我们的内容是最佳实践。 这为我们将来如何使用这些数据提供了更大的灵活性。

对于我们的网页,我们需要一组数据,包括以下内容:

  • 标题
  • 全名
  • 传记(带有富文本编辑)
  • 具有名称和 URL 的网站列表。

为了在我们的模式中定义它,我们为我们的文档创建一个对象并定义它的字段。 我们的内容的注释列表及其字段type

  • 标题 - 字符串
  • 全名——字符串
  • 传记——“块”数组
  • 网站列表 — 具有名称和 URL 字符串字段的对象数组。
 types: schemaTypes.concat([ /* Your types here! */ { title: "About Details", name: "about", type: "document", fields: [ { name: 'title', type: 'string' }, { name: 'fullName', title: 'Full Name', type: 'string' }, { name: 'bio', title: 'Biography', name: 'content', type: 'array', of: [ { type: 'block' } ] }, { name: 'externalLinks', title: 'Social media and external links', type: 'array', of: [ { type: 'object', fields: [ { name: 'text', title: 'Link text', type: 'string' }, { name: 'href', title: 'Link url', type: 'string' } ] } ] } ] } ])

将此添加到您的架构类型,保存,您的 Studio 将重新编译并向您展示您的第一个文档。 从这里开始,我们将通过创建一个新文档并填写信息将我们的内容添加到 CMS 中。

以可重用的方式构建您的内容

此时,您可能想知道为什么我们有“全名”和“头衔”。 这是因为我们希望我们的内容具有多用途的潜力。 通过包含名称字段而不是仅在标题中包含名称,我们可以更多地使用该数据。 然后,我们可以使用此 CMS 中的信息来支持简历页面或 PDF。 传记字段可以以编程方式用于其他系统或网站。 这使我们能够对大部分内容拥有单一的事实来源,而不是受此特定站点的直接用例所支配。

将我们的数据提取到我们的项目中

现在我们已经通过 API 提供了我们的数据,让我们将其拉入我们的项目中。

安装和配置 Sanity JavaScript 客户端

首先,我们需要访问 Node.js 中的数据。 我们可以使用 Sanity JavaScript 客户端来伪造该连接。

 npm install @sanity/client

这将获取并安装 JavaScript SDK。 从这里开始,我们需要将其配置为从我们之前设置的项目中获取数据。 为此,我们将在/utils/SanityClient.js中设置一个实用程序脚本。 我们为 SDK 提供了我们的项目 ID 和数据集名称,并准备在我们的主脚本中使用它。

 const sanityClient = require('@sanity/client'); const client = sanityClient({ projectId: '4fs6x5jg', dataset: 'production', useCdn: true }) module.exports = client;

使用 GROQ 获取我们的数据

回到我们的index.js文件,我们将创建一个新函数来获取我们的数据。 为此,我们将使用 Sanity 的原生查询语言,即开源 GROQ。

我们将在变量中构建查询,然后使用我们配置的客户端根据查询获取数据。 在这种情况下,我们构建了一个具有名为about的属性的对象。 在此对象中,我们希望返回特定文档的数据。 为此,我们根据创建文档时自动生成的文档_id进行查询。

为了找到文档的_id ,我们导航到 Studio 中的文档,然后从 URL 复制它或进入“检查”模式以查看文档上的所有数据。 要进入 Inspect,请单击右上角的“kabob”菜单或使用快捷键Ctrl + Alt + I 。 该视图将列出该文档上的所有数据,包括我们的_id 。 Sanity 将返回一个文档对象数组,因此为简单起见,我们将返回第0th个条目。

然后我们将查询传递给 Sanity 客户端的fetch方法,它将返回我们文档中所有数据的 JSON 对象。 在这个演示中,返回所有数据并不是什么大问题。 对于更大的实现,GROQ 允许一个可选的“投影”只返回你想要的显式字段。

 const client = require('./utils/SanityClient') // at the top of the file // ... async function getSanityData() { const query = `{ "about": *[_id == 'YOUR-ID-HERE'][0] }` let data = await client.fetch(query); }

将富文本字段转换为 HTML

在返回数据之前,我们需要对富文本字段进行转换。 虽然许多 CMS 使用直接返回 HTML 的富文本编辑器,但 Sanity 使用称为 Portable Text 的开源规范。 Portable Text 返回一个对象数组(将富文本视为段落列表和其他媒体块),其中包含有关富文本样式和属性(如链接、脚注和其他注释)的所有数据。 这允许您在不支持 HTML 的系统中移动和使用您的文本,例如语音助手和本机应用程序。

对于我们的用例,这意味着我们需要将对象转换为 HTML。 有 NPM 模块可用于将可移植文本转换为各种用途。 在我们的例子中,我们将使用一个名为 block-content-to-html 的包。

 npm install @sanity/block-content-to-html

此包将呈现富文本编辑器中的所有默认标记。 可以覆盖每种类型的样式以符合您的用例所需的任何标记。 在这种情况下,我们将让包为我们完成工作。

 const blocksToHtml = require('@sanity/block-content-to-html'); // Added to the top async function getSanityData() { const query = `{ "about": *[_type == 'about'][0] }` let data = await client.fetch(query); data.about.content = blocksToHtml({ blocks: data.about.content }) return await data }

在 Handlebars 中使用 Sanity.io 的内容

现在数据已经形成了我们可以使用的形状,我们将把它作为数据参数传递给我们的buildHTML函数。

 async function main(src, dist) { const data = await getSanityData(); const html = buildHTML(src, data) fs.writeFile(dist, html, function (err) { if (err) return console.log(err); console.log('index.html created'); }); }

现在,我们可以更改 HTML 以使用新数据。 我们将在模板中使用更多变量调用来提取大部分数据。

为了呈现我们的富文本content变量,我们需要在我们的变量中添加一个额外的大括号层。 这将告诉 Handlebars 呈现 HTML 而不是将 HTML 显示为字符串。

对于我们的externalLinks数组,我们需要使用 Handlebars 的内置循环功能来显示我们添加到 Studio 的所有链接。

 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>{{ about.title }}</title> </head> <body> <h1>The personal homepage of {{ about.fullName }}</h1> {{{ about.content }}} <h2>Bryan is on the internet</h2> <ul> {{#each about.externalLinks }} <li><a href="{{ this.href }}">{{ this.text }}</a></li> {{/each}} </ul> </body> </html>

设置部署

让我们直播吧。 我们需要两个组件来完成这项工作。 首先,我们需要一个静态主机来为我们构建文件。 接下来,当我们的 CMS 中的内容发生更改时,我们需要触发我们网站的新构建。

部署到 Netlify

对于托管,我们将使用 Netlify。 Netlify 是一个静态站点主机。 它提供静态资产,但具有使我们的网站顺利运行的附加功能。 他们有一个内置的部署基础设施,可以运行我们的节点脚本、用于触发构建的 webhook,以及一个全球分布的 CDN,以确保我们的 HTML 页面得到快速服务。

Netlify 可以在 GitHub 上查看我们的存储库,并根据我们可以添加到其仪表板中的命令创建构建。

首先,我们需要将此代码推送到 GitHub。 然后,在 Netlify 的 Dashboard 中,我们需要将新存储库连接到 Netlify 中的新站点。

一旦连接好,我们需要告诉 Netlify 如何构建我们的项目。 在仪表板中,我们将前往 Settings > Build & Deploy > Build Settings。 在这方面,我们需要将“构建命令”更改为“node index.js”,将“发布目录”更改为“./dist”。

当 Netlify 构建我们的站点时,它将运行我们的命令,然后检查我们列出的文件夹中的内容并在其中发布内容。

设置 Webhook

当有人更新内容时,我们还需要告诉 Netlify 发布新版本。 为此,我们将设置一个 Webhook 来通知 Netlify 我们需要重建站点。 Webhook 是一个 URL,可以由不同的服务(例如 Sanity)以编程方式访问以在源服务(在本例中为 Netlify)中创建操作。

我们可以在 Netlify 仪表板的 Settings > Build & Deploy > Build hooks 中设置一个特定的“Build hook”。 添加一个钩子,给它一个名字并保存。 这将提供一个 URL,可用于远程触发 Netlify 中的构建。

接下来,我们需要告诉 Sanity 在您发布更改时向此 URL 发出请求。

我们可以使用 Sanity CLI 来完成此操作。 在我们的/studio目录中,我们可以运行sanity hook create来连接。 该命令将要求输入名称、数据集和 URL。 名称可以是您喜欢的任何名称,数据集应该是我们产品的production ,URL 应该是 Netlify 提供的 URL。

现在,每当我们在 Studio 中发布内容时,我们的网站都会自动更新。 不需要框架。

  • 代码可以在这个 GitHub 存储库中找到 →

下一步

这是您创建自己的工具时可以执行的操作的一个非常小的示例。 虽然大多数项目可能需要功能更全面的 SSG,但创建自己的迷你 SSG 可以帮助您更多地了解您选择的生成器中发生的事情。

  • 这个站点只发布一个页面,但是在我们的构建脚本中添加一些额外的内容,我们可以让它发布更多页面。 它甚至可以发布博客文章。
  • 存储库中有点缺乏“开发人员体验”。 我们可以通过实现像 Nodemon 这样的包或使用 BrowserSync 之类的东西添加“热重载”来在任何文件保存上运行我们的 Node 脚本。
  • 存在于 Sanity 中的数据可以为多个站点和服务提供支持。 您可以创建一个使用它并发布 PDF 而不是网页的简历站点。
  • 您可以添加 CSS 并使其看起来像一个真实的站点。