使用 Python 拥抱 JAMStack:使用 Flask 生成静态网站并部署到 Netlify
JAMStack
使用 Flask 生成静态网站
Netlify
连接捆绑器
最后想说的话
大创意
JAMStack彻底改变了个人和整个组织迭代开发Web应用程序的方式。它将前端和后端工作流程解耦,因此您可以专注于提升最终用户的体验速度,减少对服务方式的担忧,保持独立的迭代周期,最重要的是,它还支持轻松创建功能分支。
我将一步一步地向您展示如何使用 Flask 和 Python 的强大而复杂的工具,而无需使用专门的静态网站生成器。
内容:
- JAMStack:它是什么,以及它为何如此出色
- 使用 Flask 生成静态网站:分步教程
- Netlify:它是什么以及如何部署您的网站
- 连接打包工具:可与 Rollup、Webpack、Parcel 等配合使用
- 最后总结:一些建议以及下一步的方向
重要链接:
开始之前
- 黑人的命也是命。
- 我们需要科技行业的多元化和女性人才。
- 跨性别者的权利是人权。
- 提醒大家,我们正在迅速破坏地球。
我对这篇内容的唯一要求是,请大家谨慎运用科技。我们并非要解决所有问题,也不必独自承担所有责任,但我们是现代科技的创造者,必须牢记我们的行动至关重要。
最后,如果你在学习本教程或理解其中的概念时遇到困难,请记住,我们都会经历这些阶段。技术是一门博大精深的学问,我们每个人(无论处于哪个阶段)都会感到沮丧,并不断自我怀疑。最好的办法就是坚持下去,忽略那些批评的声音,并向他人寻求帮助。
JAMStack
JAMStack 这个名字太糟糕了,听起来像是某种果酱罐装食品的本地活动。但它的概念却很棒:专注于将 JavaScript、API 和 Markup 分离。
前端和后端解耦,Markup 成为静态平台,JavaScript 在其上构建代码。动态数据通过 JavaScript 从 API 传递给客户端。虽然仍然可以在服务器端进行一些动态 Markup 生成,但这已成为一种特殊情况,并且是通过 API 实现的。
由于我们的客户资源现在全部是静态的,因此非常适合直接通过内容分发网络 (CDN) 提供服务。这确保您的网站能够以尽可能快、最高效的方式交付。Netlify 正是为此而生。它让从您的代码库部署到网站的过程变得无比流畅,真正实现了“一劳永逸”的解决方案。稍后我们将详细介绍。
一旦设置好前端和后端分离,你会发现它非常棒。绝对物超所值。后端通常需要大量的测试、分析和优化,迭代周期也比前端长得多。前端往往需要快速迭代,并希望立即看到结果。当你想修改按钮的边框半径时,你肯定不想等待后端构建完成,因为后端构建通常需要编译 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。
教程
我们将把我们的努力分解为以下几个目标:
之后,我将向您展示如何使用 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
Pipenv 为我们创建了一个虚拟环境,包括 `<filename>`、`<filename>`Pipfile和 `<filename> Pipfile.Lock`,并安装了我们的软件包。我们还指定它使用 3.7 版本,因为这是 Netlify 的默认版本。
现在让我们提交申请,然后继续前进:
$ git add .
$ git commit -m 'project setup'
$ git push
目标 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"
这是 Flask 的基础知识。当然,你可以在这里做任何事情,甚至可以访问数据库,但最好不要添加任何基于用户交互或状态的功能。所有操作都应该响应简单的 GET 请求。访问全局变量request在这里是一个危险信号。毕竟,这是静态内容。
现在我们运行服务器,Flask 的优势就在这里:我们可以边开发边更新网站,无需在每次更改后重新构建或运行任何命令。我们只需假装自己在开发一个普通的 Flask 网站。实际上,我们也的确是在开发一个普通的 Flask 网站。
首先搭建环境,然后使用 Pipenv 运行它。
$ export FLASK_DEBUG=True
$ export FLASK_APP=app.py
$ pipenv run flask run
我们告诉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()
然后运行它:
$ pipenv run python freeze.py
你会看到它创建了一个目录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'])
请注意,我们可以创建其他路由来实现任何我们想要的功能,但目前我们只有一个路由可以与 Flask-FlatPages 配合使用。
Markdown页面
默认情况下,Flask-FlatPages 会在pages指定目录中查找页面。所以,让我们创建一些页面。您可以点击每个页面进行复制,或者创建自己的内容。任何您在此处添加的扩展名为“.md”的文件都会变成一个页面,前提是您在另一个页面中使用 `.` 标签链接到它url_for()。
pages/
content.md
index.md
team.md
在每个页面的顶部,您都会注意到它的“meta”部分,它是YAML格式的,看起来像这样:
Title: Rare Pup Detective Agency
Description: We sniff out the clues.
页面中可能会出现一些 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>
这里主要是一个基础页面。有几点需要注意:
<a href="{{ url_for('page', path='team') }}" {% if page.path == "team" %}active{% endif %}>Team</a>
我们使用 Flask 的url_for()方法来获取“团队”页面的最终 URL,并且<a>为了样式目的,当在该页面上时,我们还向标签添加了“active”属性。
此外,在我们的内容模块中,我们还做了:
<h1>{{ page.meta.description }}</h1>
{{ page.html|safe }}
我们添加了一个<h1>包含页面元数据中描述的元素。最后,我们获取该元素page.html并safe对其进行过滤,因为它包含我们希望以未转义形式呈现的 HTML 代码。
静态资源
最后,所有我们链接的静态资源,例如图片、CSS 或 JS 文件,都需要放在 `<head>` 标签内/static。Frozen-Flask 会在需要时自动获取它们。在这个例子中,我只用了几个资源,但你可能会用到更多:
static/
images/
logo.png
base.css
显影与冷冻
好了好了,内容有点多。我们来看看效果如何,做些修改等等。开发过程中,我们只需使用 Flask 服务器,它会自动重新加载。我们可以把它想象成一个普通的 Flask 网站:
$ pipenv run flask run
完成后,我们可以测试冻结功能,它应该会将所有页面和资源放入构建目录中:
build/
static/
images/
logo.png
base.css
content.html
index.html
team.html
目标 5:添加 JavaScript 并连接到 API
最后一步,我们将添加调用外部 API 的功能。在实际应用中,您需要创建 API 并自行托管,或者使用无服务器方案。这里我们只是调用了Dog CEO 提供的出色服务 Dog API。
我们将添加以下链接中的脚本:
它定义了一个名为“dog-picture”的自定义元素,我们已经将其分布在网站各处。如果用户没有启用 JavaScript,浏览器会忽略所有这些<dog-picture>元素。一旦这段脚本加载完毕,它们就会生效。自定义元素,真棒!
我们templates/page.html用一个简单的方式将其联系起来:
<script type="module" src="/static/js/dogs.js"></script>
当你冻结代码时,你会看到它被添加到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
让你的包管理器直接将代码构建到你的静态文件夹(例如 `<static_folder>`)/static/build,然后让你的 Flask 模板从那里读取 JavaScript 文件。Flask-Freeze 随后会将其移动到/buildNetlify 可以使用的目录。
开发用捆绑包
我建议你使用open-wc.org或Snowpack之类的资源,这样你就不用自己构建任何东西来进行开发了。鉴于 ES6 的现状,你不再需要自己构建任何东西了。
也就是说,有时候我们别无选择。在这种情况下,我们需要以监听模式运行 Flask 服务器和打包工具。
最简单的方法就是运行两个终端:
Terminal 1: $ pipenv run flask run
Terminal 2: $ npm run webpack --watch
使用 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



