在使用 React 构建下一个静态站点之前,请考虑一下这艘 Dhow

2025-05-24

在使用 React 构建下一个静态站点之前,请考虑这一点

单桅帆船

⚠️警告:前方可能存在争议的观点!如果 Gatsby 是你的救世主,并且你致力于捍卫 React 的教条,那么现在就回头吧。

所以我刚刚完成了我闪亮的新个人网站的构建✨我考虑过使用像Gatsby这样的花哨的、组件驱动的框架来保持在Web 开发的前沿,但经过一番思考后,我心想……

我真的需要所有这些工具来编写一些静态 HTML 吗?

这最终让我学到了如何从零开始构建静态生成的 SPA(如果你好奇我是怎么做到的,可以看看README !)。但它也教会了我一些宝贵的经验,让我明白在抛弃我们熟知和喜爱的组件库时,你能走多远。

一些背景知识:从动态网站到静态网站

好吧,让我们先来现实一下:组件级思维主导着现代 Web 开发。即使新的前端库Ruby 风格的框架层出不穷,它们仍然依赖于相同的基本公式:将标记和 JS 逻辑编写在小型组件中,然后使用导入和导出功能组合这些组件。无论这些组件是基于类的、函数式的,还是 DOM 级别的(你好,Web 组件👋),它们都专注于逻辑隔离代码可复用性的理念。

像 React 和 Vue 这样的热门库已经成为了万能的解决方案,我们甚至不再质疑它们。你是第一次在互联网上构建任何东西吗?赶紧行动npx create-react-app起来吧!

...正确的?

我以前也同意。但在过去几年里,我一直在宣扬 Gatsby 和 JAMStack,也意识到我们做的JS 包真是臃肿不堪。有了这些基于 JS 的组件,我们甚至把整个渲染库都发到了浏览器上,甚至连公司飞机上的静态启动页都用上了!

在愤世嫉俗之前,有必要先回顾一下这些库最初诞生的初衷。React 的诞生并非因为 Facebook 应该成为一个更好的静态网站;而是因为 Facebook 是一个超级动态、极其复杂的 Web应用,包含登录、追踪器、主页动态、设置菜单等等。这涉及大量数据/状态管理,这意味着需要大量的 JavaScript 代码来构建网页。对于这种用例,构建和使用状态驱动(而非标记驱动)的 UI 渲染库是完全合理的。

这就是为什么 Gatsby(一个流行的静态站点生成器)在几年后才出现,比如 Redux 的状态管理。开发者们大多热衷于构建动态的、由 JS 驱动的体验,而这些体验可以通过状态、props 和数据对象来解决。直到后来,开发者们才开始思考如何将这些 JS 密集型的库灵活地应用于静态站点的创建。

如果你问我,这很讽刺,它需要一个 500 MB 的目录来node_modules生成一个包含尽可能少的 JavaScript 的网站。

不过,我也不觉得惊讶。当你使用像 React 这样的库时,它需要JavaScript 来将所有内容渲染到页面上,显然你首先需要更多的 JavaScript 来处理所有渲染逻辑。火真的可以灭火。

那么...为什么要在静态站点上使用 React?

乍一看,这感觉就像用电锯切面包一样。既然几乎不需要担心重新渲染,为什么还要使用像 React 这样的渲染库呢?

简而言之,补水。

闪光舞蹈场景:水桶掉落在坐在椅子上的舞者身上
如果你不明白这个参考,那就自己去文化吧

对于不熟悉 Hydration 的人来说,Hydration 本质上就是让我们编写一个动态的、状态驱动的网页,同时尽可能使用静态 HTML 提前渲染页面的大部分内容。Gatsby博客很好地解释了这个过程,但这里还是提供一个快速的步骤:

  1. 您的应用程序以一大堆组件的形式存在,就像一个create-react-app
  2. 静态站点生成器会在构建时渲染此包。现在,您无需向用户发送空白的 HTML 文件,而是可以发送整个页面的标记,从而加快页面加载速度。
  3. 现在,我们想在刚刚构建的静态 HTML之外,实现一些状态组件的神奇功能。为了实现这一点,我们可以查看已经生成的 HTML 页面,并将其与我们的组件树进行比较。当我们发现某个组件需要进行一些复杂的状态管理时,我们会将其插入到现有的 HTML 中,而无需重新渲染整个页面。换句话说,我们用一些状态组件来补充我们的标记。

看起来很棒!当你想用一些 JS(比如一个用来增添趣味的动画库)但只在静态网站的一小部分区域使用时,这个方法就很方便了。但你可能已经猜到了,我们需要把整个组件库都发到客户端才能与 HTML 代码比较。所以它仍然很臃肿……但至少用户在首页加载时能看到一些东西🤷‍♀️

如果您不需要状态管理怎么办?

现在,React 的意义不大了。如果我们只需要处理一些按钮点击,我们可能应该只写几行原生 JS,而不是把整个 React 库都搬过来 😬

从某些角度来看,以下是构建静态站点时的一些常见开发请求:

  1. 我们希望将静态网站拆分成可复用的 UI 组件,这些组件可以接受一些 JS 对象作为参数(也称为“props”)。这样,我们就可以把博客文章链接列表变成首页上一堆可点击的卡片。
  2. 我们需要在构建时获取一些信息,以便将其嵌入到生产站点中。例如,我们可以在构建时获取一些 Twitter 帖子,并将其嵌入到网站首页,而无需发送任何 API 调用或暴露密钥。
  3. 我们需要从文件目录或内容为 JSON 的胖对象生成一系列 URL 路由。例如,我们有一个包含 Markdown 文件的文件夹,希望将其转换为个人博客,使每个文件在互联网上都有自己的 URL。

这些都是使用静态站点生成器的充分理由。但纵观这份清单,只有第一个要求真正涉及到组件库。即便如此,我们可能也不需要担心重新渲染或组件化状态管理;这些大部分都在构建时完成!要是能有一种方法,让我们的标记可重用且可模板化,而无需传递一堆未使用的 JS 代码就好了……

(重新)进入:哈巴狗

没错,就是那个老掉牙的 Pug(以前叫 Jade)。你知道吗,就是你上次在 CodePen 上用过的那个可爱的小模板库,或者你在 Express 服务器模板上发现的那些看起来很奇怪的 HTML。它是一个强大的小库,诞生于 React 之前的时代,那时组件驱动的状态管理甚至还没有出现。

它还使用了简化的HTML 语法,使其更容易输入/查看,我个人很喜欢它😁

那么,我为什么要提起这个令人厌倦的(双关语)模板库呢?好吧,让我们先来了解一下 Pug 的一些定义特性,看看它能做什么。我饿了,所以我们用一家甜甜圈店来举例吧🍩:

1. 你可以接收一些 JavaScript 数据并将其转换为 HTML 元素

这为各种疯狂的事情打开了大门,比如循环、条件“if”块、定义文本内容......随便你怎么说:



   main.krispy-kreme-menu
    if menuItems.length === 0
        p.alert Sorry! Sold out of gooey deliciousness :(
    else
            dl
            each item in menuItems
                dt #{item.name}
                dd #{item.price}


Enter fullscreen mode Exit fullscreen mode

在 JavaScript 级别:



   const stringOfRenderedHTML = pug.render('/filename.pug', { menuItems: [...donuts] })
   // spit out this string of HTML into a .html file at build time


Enter fullscreen mode Exit fullscreen mode

2. 您可以将多个 HTML 文件(现在的.pug文件)组合成单个页面布局

例如,您可以在一个文件中创建导航栏...



   // nav.pug
   nav.krispy-kreme-navigation
    a(href="/") Home
    a(href="/donuts") Buy some donuts
    a(href="/contact") Somehow complain about your donuts


Enter fullscreen mode Exit fullscreen mode

...并导入到另一个文件:



   // index.pug
   html
       body
           include nav.pug
           main.donut-content
               ul.list-of-tastiness
           ...


Enter fullscreen mode Exit fullscreen mode

我们可以通过在这些文件之间传递参数/“props”来更深入地了解。看看这个mixin语法:



   // nav-mixins.pug
   mixin NavBar(links)
    each link in links
        a(href=link.href) link.text


Enter fullscreen mode Exit fullscreen mode


   // index.pug
   include nav-mixins.pug
   html
    body
        +NavBar(donutLinksPassedDownByJS)
        main.donut-content
            ul.list-of-tastiness


Enter fullscreen mode Exit fullscreen mode

在这里,我们可以将每个 mixin 视为export来自 的一个语句nav-mixins.pug。然后,当我们include将此文件移到其他地方时,这些 mixin 就可以通过+装饰器(也就是我们的“import”语句)使用了。

总而言之...

✅ 我们可以用一行脚本将 JSON 对象转换为静态pug.render(filename, someJSON)HTML( )

✅ 我们可以使用导入将布局拆分为多个文件

✅ 我们可以定义类似组件的“mixins”以实现可重用性和数据传递

...换句话说,我们可以使用组件来制作 UI,但无需向客户端发送一堆库!

但是等等,这个想法并不新鲜!

我知道!后端服务器已经这样做了几十年了。

让我们考虑一下你会在ExpressJS应用中使用的服务器驱动模型。每次你访问 API 端点时,服务器可能会从数据库中查找一些信息,将这些数据打包成 HTML 模板(可能使用 Pug),然后将其返回给用户。PHP、C#、GoLang 或你之前见过的任何奇特的服务器也是如此。

但你猜怎么着?静态网站生成器的功能完全一样!唯一的区别在于,现在我们不再在API 端点中执行所有数据获取和模板化操作,而是在网站实际部署时调用的构建脚本中执行。对于熟悉的人来说,这就是你在首次部署网站时告诉 Netlify 运行的那个脚本。

早在我们开发出疯狂、超动态的 Web 应用之前,服务器就已经使用了这种模板模型。所以我的问题是:当你的网站只有一些静态的落地页、几行 JS 代码,甚至可能还要生成一个博客内容时……为什么要抛弃使用组件库模板的想法呢?

行动号召👉查看 11ty

我刚刚发现了11ty (发音为“eleven-tee”),一个基于此理念构建的静态网站生成器。你可以选择你喜欢的 HTML 模板语言(Markdown、Haml、Pug、Nunjucks 等等),然后让框架为你处理所有复杂的路由和页面生成。如果你想创建一个包含博客的作品集网站、一个公司宣传页,或者任何超级静态的网站,这绝对是我能想到的最佳解决方案。

如果你感兴趣的话,也可以fork 我的个人网站使用的基于 Pug 的框架。目前它缺少一些非常重要的功能(例如嵌套路由和 CMS 集成),所以如果你胆子够大的话,还是谨慎操作吧 😈

话虽如此,我绝对不建议你放弃你漂亮的 Gatsby 网站!如果你需要任何状态管理功能,它们的 hydration 模型确实有很多好处。另外,如果你已经非常熟悉 React 框架,没时间学习新工具,那么 Gatsby 是一个相当便捷的选择,因为它拥有庞大的支持社区。Gridsome 也是如此,它是一个基于 Vue 的静态网站生成器,其工作原理类似。

不管你最终用什么,我希望这篇文章能让你更深入地思考静态网站生成器到底能做什么。最后,我会给你留下一张可爱的哈巴狗照片,让你铭记在心🐶

可爱的哈巴狗坐在床上

学到一点东西吗?

太棒了!万一你错过了,我特意开通了“网络魔法”简报,来探索更多类似的知识!

这东西探讨的是Web 开发的“首要原则”。换句话说,究竟是哪些糟糕的浏览器 API、扭曲的 CSS 规则以及半无障碍的 HTML 支撑着我们所有的 Web 项目?如果你想要超越框架,那么亲爱的 Web 魔法师,这东西就是为你准备的🔮

赶紧在这里订阅吧!我保证永远教书,绝不会发垃圾信息❤️

文章来源:https://dev.to/bholmesdev/before-building-your-next-static-site-with-react-consider-this-2b60
PREV
Git + GitHub 团队最佳实践(个人观点)
NEXT
任何开发人员都应该使用的 16 种免费工具和服务 为任何开发人员提供的免费工具和服务