发布于 2026-01-06 2 阅读
0

使用 Python 实现 JAMStack:使用 Flask 生成静态网站并部署到 Netlify;JAMStack;使用 Flask 生成静态网站;Netlify;连接打包工具;结语

使用 Python 拥抱 JAMStack:使用 Flask 生成静态网站并部署到 Netlify

JAMStack

使用 Flask 生成静态网站

Netlify

连接捆绑器

最后想说的话

大创意

JAMStack彻底改变了个人和整个组织迭代开发Web应用程序的方式。它将前端和后端工作流程解耦,因此您可以专注于提升最终用户的体验速度,减少对服务方式的担忧,保持独立的迭代周期,最重要的是,它还支持轻松创建功能分支。

我将一步一步地向您展示如何使用 Flask 和 Python 的强大而复杂的工具,而无需使用专门的静态网站生成器。

内容:

重要链接:

开始之前

  • 黑人的命也是命。
  • 我们需要科技行业的多元化和女性人才。
  • 跨性别者的权利是人权。
  • 提醒大家,我们正在迅速破坏地球。

我对这篇内容的唯一要求是,请大家谨慎运用科技。我们并非要解决所有问题,也不必独自承担所有责任,但我们是现代科技的创造者,必须牢记我们的行动至关重要

最后,如果你在学习本教程或理解其中的概念时遇到困难,请记住,我们都会经历这些阶段。技术是一门博大精深的学问,我们每个人(无论处于哪个阶段)都会感到沮丧,并不断自我怀疑。最好的办法就是坚持下去,忽略那些批评的声音,并向他人寻求帮助。

JAMStack

JAMStack 这个名字太糟糕了,听起来像是某种果酱罐装食品的本地活动。但它的概念却很棒:专注于将 JavaScript、API 和 Markup 分离。

前端和后端解耦,Markup 成为静态平台,JavaScript 在其上构建代码。动态数据通过 JavaScript 从 API 传递给客户端。虽然仍然可以在服务器端进行一些动态 Markup 生成,但这已成为一种特殊情况,并且是通过 API 实现的。

由于我们的客户资源现在全部是静态的,因此非常适合直接通过内容分发网络 (CDN) 提供服务。这确保您的网站能够以尽可能快、最高效的方式交付。Netlify 正是为此而生。它让从您的代码库部署到网站的过程变得无比流畅,真正实现了“一劳永逸”的解决方案。稍后我们将详细介绍。

一张展示 JAMStack 元素的示意图。JavaScript 和 Markup 是静态的。JavaScript 增强了 Markup。API 是动态的。API 和 JavaScript 相互发送数据。

一旦设置好前端和后端分离,你会发现它非常棒。绝对物超所值。后端通常需要大量的测试、分析和优化,迭代周期也比前端长得多。前端往往需要快速迭代,并希望立即看到结果。当你想修改按钮的边框半径时,你肯定不想等待后端构建完成,因为后端构建通常需要编译 Docker 镜像、运行测试、部署等等。

无需创建全新的后端环境即可测试前端功能分支,这彻底改变了游戏规则。它革新了从开发人员到业务利益相关者的所有人的体验。Netlify为每次部署提供唯一的 URL ,这意味着您可以尝试新功能、查看旧的部署,并快速测试其是否已准备好投入生产环境。

最后,保持各个部分解耦也使得插入无服务器端点或无后端选项(如 Firebase 或 AWS Amplify)变得更加容易。

模块化……组合……哇!谁能想到它这么棒?

何时不应使用 JAMStack

JAMStack 有一个地方经典标记生成方式更胜一筹,那就是当你需要生成大量动态内容时,即根据用户查看情况或对数据库的其他访问而改变的标记。

对于股票行情显示这类应用来说,这种方法仍然可行,因为它来自 API。但对于内容经常通过数据库更新的 CMS 系统来说,传统的 Jinja 渲染仍然是最佳选择。

不过,数据库驱动型内容管理系统的许多承诺尚未实现,人们越来越发现,直接更改源代码中的内容并重新部署更容易,尤其是在自动快速完成的情况下。

此外,如果您不需要太多内容生成功能,也可以很容易地将其放在 API 端点后面。

使用 Flask 生成静态网站

这项技术可以让你以与创建经典 Flask 应用类似的方式生成静态网站。这个例子与 11ty、Gatsby 和 Jekyll 等生成器提供的示例类似,但我认为它更好,因为它允许我们使用 Python、Flask 以及所有相关的强大工具。

使用 Flask 而不是其他静态网站生成器的一些优势:

  • 在开发过程中,我们只需使用 Flask 服务器,无需编译步骤。
  • 页面数量很多时也不会出现缩放问题。
  • 我们可以使用与构建标准服务器和 API 服务器相同的工具和思维方式。
  • 没有新的领域特定语言来实现诸如 for 循环之类的功能,也没有将数据库查询硬塞进 Markdown 中。
  • 我们仍然可以与 Flask 可以集成的任何数据库、任何远程 API 集成,并且通常可以做 Python 可以做的任何事情,这非常多。

现在,我们可以轻松地使用 Django、FastAPI、Starlette 或任何其他框架来实现这一点,但 Flask 有两个扩展程序使这个过程变得非常简单:Frozen-Flask 和 Flask-FlatPages。

教程

我们将把我们的努力分解为以下几个目标:

  1. 安装/配置我们的依赖项并设置 GitHub
  2. 创建我们的 Flask 应用
  3. 冻住它
  4. 使用 Markdown 添加页面/内容
  5. 添加 JavaScript 并连接到 API

之后,我将向您展示如何使用 Netilify 进行部署。

现在就开始!

目标 1:设置/安装依赖项并设置 GitHub

首先,我们要搭建 Git 仓库。这里用的是GitHub,因为它是一个历史悠久的主流标准。

我们来创建一个新的仓库。我把它命名为“flask-static-tutorial”。我勾选了“使用 README 文件初始化此仓库”,添加了 .gitignore 文件(Python)和许可证(MIT)。你随意发挥吧。

创建完成后,将仓库克隆到本地。

我假设你的系统上安装了 Python 3.6 或更高版本。我喜欢使用Pyenv,它可以管理多个 Python 版本的安装和选择。这里有一个很棒的教程。

本教程中我们还会用到Pipenv,它将用于管理我们的 Python 依赖项。我主要用它是因为 NetlifyPipfile直接支持 Pipenv。所以,请先安装它

现在这些组件都安装好了,接下来我们将安装我们需要的组件:

$ pipenv --python 3.7 install flask frozen-flask flask-flatpages
Enter fullscreen mode Exit fullscreen mode

Pipenv 为我们创建了一个虚拟环境,包括 `<filename>`、`<filename>`Pipfile和 `<filename> Pipfile.Lock`,并安装了我们的软件包。我们还指定它使用 3.7 版本,因为这是 Netlify 的默认版本。

现在让我们提交申请,然后继续前进:

$ git add .
$ git commit -m 'project setup'
$ git push
Enter fullscreen mode Exit fullscreen mode

目标 2:创建我们的 Flask 应用

这部分基本上就是按照 Flask 教程做的。唯一的小改动是我们用的是 Pipenv。

让我们来制作app.py

from flask import Flask

# Create our app object, use this page as our settings (will pick up DEBUG)
app = Flask(__name__)

# For settings, we just use this file itself, very easy to configure
app.config.from_object(__name__)

# We want Flask to allow no slashes after paths, because they get turned into flat files
app.url_map.strict_slashes = False

# Create a route to our index page at the root url, return a simple greeting
@app.route("/")
def index():
    return "Hello, Flask"
Enter fullscreen mode Exit fullscreen mode

这是 Flask 的基础知识。当然,你可以在这里做任何事情,甚至可以访问数据库,但最好不要添加任何基于用户交互或状态的功能。所有操作都应该响应简单的 GET 请求。访问全局变量request在这里是一个危险信号。毕竟,这是静态内容。

现在我们运行服务器,Flask 的优势就在这里:我们可以边开发边更新网站,无需在每次更改后重新构建或运行任何命令。我们只需假装自己在开发一个普通的 Flask 网站。实际上,我们也的确是在开发一个普通的 Flask 网站。

首先搭建环境,然后使用 Pipenv 运行它。

$ export FLASK_DEBUG=True
$ export FLASK_APP=app.py
$ pipenv run flask run
Enter fullscreen mode Exit fullscreen mode

我们告诉pipenv了你run flask如何flask操作run。希望你能明白。或者,你也可以创建一个pipenv 脚本。但那是你自己的事

现在我们可以打开浏览器访问http://127.0.0.1:5000/,然后就会看到欢迎页面。

干得漂亮!但别骄傲自满,我们还有更多工作要做。

目标3:冻结它

你知道吗?你可以在炎热的沙漠里像几千年前的波斯人一样制作冰块。你也知道吗?你甚至可以在沙漠里,用电脑创建一个类似Flask的冰冻网站。

之前我们安装了Frozen-Flask。现在,我们只需要另一个文件freeze.py

from flask_frozen import Freezer
from app import app

freezer = Freezer(app)

if __name__ == '__main__':
    freezer.freeze()
Enter fullscreen mode Exit fullscreen mode

然后运行它:

$ pipenv run python freeze.py
Enter fullscreen mode Exit fullscreen mode

你会看到它创建了一个目录build和一个文件index.html。打开它,你就能看到当我们的 Flask 进程运行时,浏览器访问“ http://127.0.0.1:5000/ ”时实际看到的内容。

Frozen-Flask 非常简单。它运行你的应用,获取所有根端点(没有路径变量的端点),复制其标记,并将其保存到相应的文件中。为了查找其他页面,它会跟踪每个响应url_for()并将其添加到队列中。

要查找根树之外的页面,请点击此处阅读更多关于如何查找 URL 的信息

我们离目标很近了。现在我们只需要,你知道的,内容。

目标 4:使用 Markdown 添加页面/内容

是时候了。是时候审视你那颗冰冷的心,看看你究竟想做什么样的网站了。对我来说,答案很简单:一个宠物侦探社的网站。但你做你自己就好。

其中之一是 11ty、Jekyll 以及基本上所有东西。 博客创建框架静态网站生成器可以轻松地让你使用 Markdown 创建页面。我们也将这样做。

这时 Flask-FlatPages 就派上用场了。它允许我们创建任何格式的页面,处理这些页面,然后将它们作为 HTML 交付。

App.py 的更改

让我们更新一下app.py

from flask import Flask, render_template
from flask_flatpages import FlatPages

# Tell Flatpages to auto reload when a page is changed, and look for .md files
FLATPAGES_AUTO_RELOAD = True
FLATPAGES_EXTENSION = '.md'

# Create our app object, use this page as our settings (will pick up DEBUG)
app = Flask(__name__)

# For settings, we just use this file itself, very easy to configure
app.config.from_object(__name__)

# We want Flask to allow no slashes after paths, because they get turned into flat files
app.url_map.strict_slashes = False

# Create an instance of our extension
pages = FlatPages(app)

# Route to FlatPages at our root, and route any path that ends in ".html"
@app.route("/")
@app.route("/<path:path>.html")
def page(path=None):
    # Look for the page with FlatPages, or find "index" if we have no path
    page = pages.get_or_404(path or 'index')

    # Render the template "page.html" with our page and title
    return render_template("page.html", page=page, title=page.meta['title'])
Enter fullscreen mode Exit fullscreen mode

请注意,我们可以创建其他路由来实现任何我们想要的功能,但目前我们只有一个路由可以与 Flask-FlatPages 配合使用。

Markdown页面

默认情况下,Flask-FlatPages 会在pages指定目录中查找页面。所以,让我们创建一些页面。您可以点击每个页面进行复制,或者创建自己的内容。任何您在此处添加的扩展名为“.md”的文件都会变成一个页面,前提是您在另一个页面中使用 `.` 标签链接到它url_for()

浏览文件

pages/
  content.md
  index.md
  team.md
Enter fullscreen mode Exit fullscreen mode

在每个页面的顶部,您都会注意到它的“meta”部分,它是YAML格式的,看起来像这样:

Title: Rare Pup Detective Agency
Description: We sniff out the clues.
Enter fullscreen mode Exit fullscreen mode

页面中可能会出现一些 HTML 代码。别忘了,Markdown 允许我们嵌入 HTML 代码!请使用它。

Jinja模板

现在让我们创建 Jinja 模板,它将作为我们页面的包装器templates/page.html

<!doctype html>
<html>
  <head>

    <meta charset="utf-8">
    <title>{{ title }}</title>
    <link rel="stylesheet" href="/static/base.css">
    <meta name="viewport" content="width=device-width, initial-scale=1">

  </head>
  <body>

    <header>
      <nav>
        <a href="/" class="logo" aria-hidden="true"></a>
        <a href="/" {% if page.path == "index" %}active{% endif %}>Home</a>
        <a href="{{ url_for('page', path='team') }}" {% if page.path == "team" %}active{% endif %}>Team</a>
        <a href="{{ url_for('page', path='contact') }}" {% if page.path == "contact" %}active{% endif %}>Contact</a>
      </nav>
    </header>

    <main>
      <article>
      {% block content %}
          <h1>{{ page.meta.description }}</h1>
          {{ page.html|safe }}
      {% endblock content %}
      </article>
    </main>

  </body>
</html>

Enter fullscreen mode Exit fullscreen mode

这里主要是一个基础页面。有几点需要注意:

<a href="{{ url_for('page', path='team') }}" {% if page.path == "team" %}active{% endif %}>Team</a>
Enter fullscreen mode Exit fullscreen mode

我们使用 Flask 的url_for()方法来获取“团队”页面的最终 URL,并且<a>为了样式目的,当在该页面上时,我们还向标签添加了“active”属性。

此外,在我们的内容模块中,我们还做了:

<h1>{{ page.meta.description }}</h1>
{{ page.html|safe }}

Enter fullscreen mode Exit fullscreen mode

我们添加了一个<h1>包含页面元数据中描述的元素。最后,我们获取该元素page.htmlsafe对其进行过滤,因为它包含我们希望以未转义形式呈现的 HTML 代码。

静态资源

最后,所有我们链接的静态资源,例如图片、CSS 或 JS 文件,都需要放在 `<head>` 标签内/static。Frozen-Flask 会在需要时自动获取它们。在这个例子中,我只用了几个资源,但你可能会用到更多:

浏览文件

static/
  images/
    logo.png
  base.css
Enter fullscreen mode Exit fullscreen mode

显影与冷冻

好了好了,内容有点多。我们来看看效果如何,做些修改等等。开发过程中,我们只需使用 Flask 服务器,它会自动重新加载。我们可以把它想象成一个普通的 Flask 网站:

$ pipenv run flask run
Enter fullscreen mode Exit fullscreen mode

完成后,我们可以测试冻结功能,它应该会将所有页面和资源放入构建目录中:

build/
  static/
    images/
      logo.png
    base.css
  content.html
  index.html
  team.html
Enter fullscreen mode Exit fullscreen mode

目标 5:添加 JavaScript 并连接到 API

最后一步,我们将添加调用外部 API 的功能。在实际应用中,您需要创建 API 并自行托管,或者使用无服务器方案。这里我们只是调用了Dog CEO 提供的出色服务 Dog API

我们将添加以下链接中的脚本:

static/js/dogs.js

它定义了一个名为“dog-picture”的自定义元素,我们已经将其分布在网站各处。如果用户没有启用 JavaScript,浏览器会忽略所有这些<dog-picture>元素。一旦这段脚本加载完毕,它们就会生效。自定义元素,真棒!

我们templates/page.html用一个简单的方式将其联系起来:

<script type="module" src="/static/js/dogs.js"></script>
Enter fullscreen mode Exit fullscreen mode

当你冻结代码时,你会看到它被添加到build/……如果我们需要使用 rollup、parcel 或 webpack 编译我们的 JavaScript,那么在冻结代码之前运行这些编译命令就非常简单。

还有一件事,我们来部署一下……

Netlify

有了静态内容之后,我们需要找个地方存放它们。由于内容都是静态的,我们基本上可以把它们放在任何地方:nginx 后面、S3 上等等。但最好还是放在内容分发网络 (CDN) 后面,这样你的数据就会存储在全球各地的多台服务器上,这些服务器都经过专门设计,可以快速缓存和提供内容。唯一的难点在于如何管理这个部署过程。

这就是我们使用 Netlify 的原因。他们确实把这套流程优化得非常完善。简单来说,它会接入你的代码仓库,监听更新,然后运行构建命令,将其部署到 CDN 上,并进行路由。

每个构建版本都由一个唯一的 URL提供服务。这一点至关重要。这意味着我们可以毫不费力地创建前端的功能分支。

而且,像这样的小项目,它是免费的,这一点也很棒。

创建网站

首先,我们创建账户:https://app.netlify.com/

接下来,我们创建第一个网站:https://app.netlify.com/start

将其连接到 GitHub,并选择您的代码仓库。在构建选项中,指定命令“python freeze.py”,构建目录为“build”。

注意:Netlify 的基本构建环境会检查我们的配置信息Pipfile以确定 Python 版本和依赖项。由于它已经配置了基础 Python 环境,因此我们无需使用 pipenv 来实际运行它。

屏幕截图

现在点击“部署站点”。您可以点击部署项查看进度,它会显示完整的输出和状态信息。如果出现“部署失败”的提示,请进入部署页面并向下滚动查看原因。

部署失败日志截图。

成功后,您会在“部署”屏幕顶部看到“已发布”字样,并看到类似“ https://infallible-beaver-cf7576.netlify.app ”的链接。点击即可查看。

部署成功后的部署页面截图。

虽然“infallible-beaver-cf7576”这个域名很棒,但您可以通过“设置”>“域名管理”来重命名您的网站。您也可以添加自定义域名。

现在每次提交代码都会生效。你还可以设置只发布特定分支、设置触发器等等。选项多到数不清,尽情尝试吧!

Netlify 的一些额外功能

Netlify 拥有不错的重定向功能,允许您将类似 /api/* 的请求重新映射到任何您想要的位置,包括自定义 API 端点。了解更多关于重定向和重写的信息

我们的contact.md页面上有一个表单,虽然它不会跳转到任何页面,但它包含一个data-netlify="true"由 Netlify 自动处理的标签。该标签会收集所有提交的内容,您可以在网站后台查看这些内容。此外,您还可以设置通知。了解更多关于 Netlify 表单的信息

使用Netlify 的身份验证系统可以非常轻松地实现轻量级身份验证

连接捆绑器

这种方法的优点在于无需构建步骤,但有时需要使用 JavaScript 进行打包,例如运行 Rollup、Webpack、Parcel 等工具。这种情况在两个地方需要用到:一是开发过程中进行更改时,二是发布步骤中。

出版捆绑包

一种方法是将你的 Netlify 构建命令更改为类似这样的命令:

$ npm run build && python freeze.py
Enter fullscreen mode Exit fullscreen mode

让你的包管理器直接将代码构建到你的静态文件夹(例如 `<static_folder>`)/static/build,然后让你的 Flask 模板从那里读取 JavaScript 文件。Flask-Freeze 随后会将其移动到/buildNetlify 可以使用的目录。

开发用捆绑包

我建议你使用open-wc.orgSnowpack之类的资源,这样你就不用自己构建任何东西来进行开发了。鉴于 ES6 的现状,你不再需要自己构建任何东西了。

也就是说,有时候我们别无选择。在这种情况下,我们需要以监听模式运行 Flask 服务器和打包工具。

最简单的方法就是运行两个终端:

    Terminal 1: $ pipenv run flask run
Enter fullscreen mode Exit fullscreen mode
    Terminal 2: $ npm run webpack --watch
Enter fullscreen mode Exit fullscreen mode

使用 Webpack 打包

如果你正在使用 Webpack,Andrew Montalenti ( @amontalenti ) 编写了一个基于 Flask-Static 的 Webpack 和 Flask 之间的桥接程序,你可以在他的 gist 上查看:

https://gist.github.com/amontalenti/ffeca0dce10f29d42a82e80773804355

最后想说的话

我希望我已经展示了 JAMStack 如何成为一种出色的开发方式,以及我们无需放弃 Python 的所有优势就能实现这一点。接下来,您可以考虑使用Zappa 构建无服务器端点,将容器部署到AWS Fargate,或者使用经典的Google App Engine来满足您的 API 需求。就我个人而言,我仍然更喜欢后者,因为它能快速搭建 Python API。

一些建议

请记住,JAMStack 的 JavaScript 部分应该是最后的选择。如今,开发者们常常将庞大的 React 包推送到客户端,而我们的设备根本无法处理它们。

多年来,渐进增强仍然是我们的目标。尽可能地贴近 Markup 的思路,然后根据需要逐步扩展。我们花费了大量时间去实现那些注定失败、过于复杂且令人头疼的 JavaScript 功能。而用户真正需要的可能仅仅是一个网页表单。JAMStack 可以帮助我们解决这个问题,但它也可能让我们陷入单页 React 应用的噩梦之中。

组织应用的一个好方法是区分已认证用户和未认证用户。可以将 Markup 视为始终匿名,然后在用户登录后通过 JavaScript 添加功能。同时,可以将 API 视为私有端点和公共端点。这在处理缓存时会很有帮助。

Netlify的替代方案

Netlify 在这里并没有做什么特别的事情。也没有什么非用不可的,喜欢冒险的人或许会想自己搭建部署系统。静态资源意味着你可以把哈希值放在文件名里,然后无限期地缓存它们。在使用 Netlify 之前,我用的是一个系统,它会把我的大型 JS 文件分割成小块,然后按哈希值命名后放到 S3 上。当我修改代码时,客户端只需要下载更新后的小块文件即可。

您也可以直接发布到GitHub Pages

将其托管在Nginx服务器上,并在其前面部署CloudFare 。

Flask 的替代方案

Django 提供了Django-Freeze来创建静态内容,其方式与你在这里所做的非常相似。

Pelican是一个基于 Python 的静态网站生成器。

如果您想要一个功能齐全、用 Javascript 编写的专用静态生成器,请使用11ty 。

请与我联系!

如果您发现任何问题、有任何疑问或想要请求更多教程,请在项目仓库中创建 issue:https://github.com/DeadWisdom/flask-static-tutorial/issues

或者在推特上联系我:@deadwisdom

此外,我还提供咨询服务,主要帮助组织简化其创新和开发周期,尤其是在产品、设计和开发需要沟通的情况下。

感谢帮助我撰写本文的人,包括@matteasterday@DanielReesLewis@amontalenti

谢谢!

文章来源:https://dev.to/deadwisdom/embracing-jamstack-with-python-generate-a-static-website-with-flask-and-deploying-to-netlify-4bge