JS 应用程序介绍
安装
完成本教程需要以下软件,请继续阅读以获取具体的安装说明:
- Python
- 一个 Python 包管理器,例如 pip(通常随 Python 一起提供)或 uv
- 快速HTML
- 网页浏览器
- Railway.app 账户
如果你以前没有使用过Python,我们建议从Miniconda开始。
请注意,您只需在每个环境中按照安装部分的步骤进行一次。如果您创建一个新的代码库,则无需重新执行这些步骤。
安装 FastHTML
对于Mac、Windows和Linux,请输入:
pip install python-fasthtml第一步
到本节结束时,你将拥有自己在 railway.app 部署的 FastHTML 网站和测试。
创建一个hello world
创建一个新文件夹以组织您项目的所有文件。在这个文件夹内,创建一个名为 main.py 的文件,并添加以下代码:
main.py
from fasthtml.common import *
app = FastHTML()
rt = app.route
@rt('/')
def get():
return 'Hello, world!'
serve()最后,在您的终端中运行 python main.py 并打开出现的“链接”在您的浏览器中。
快速绘画:快速HTML冒险 🎨✨
本教程的最终结果将是QuickDraw,一个使用FastHTML的实时协作绘图应用程序。最终网站的样子如下:

绘画房间
绘图空间是我们应用的核心概念。每个房间代表一个独立的绘图空间,用户可以在这里展现他们内心的毕加索。以下是详细说明:
- 房间创建和存储
main.py
db = database('data/drawapp.db')
rooms = db.t.rooms
if rooms not in db.t:
rooms.create(id=int, name=str, created_at=str, pk='id')
Room = rooms.dataclass()
@patch
def __ft__(self:Room):
return Li(A(self.name, href=f"/rooms/{self.id}"))或者你可以使用我们的 fast_app 函数在一行中创建一个带有 SQLite 数据库和数据类的 FastHTML 应用:
main.py
def render(room):
return Li(A(room.name, href=f"/rooms/{room.id}"))
app,rt,rooms,Room = fast_app('data/drawapp.db', render=render, id=int, name=str, created_at=str, pk='id')我们指定一个渲染函数将我们的数据类转换为HTML,这与我们之前使用的patch装饰器中扩展__ft__方法是一样的。我们将在接下来的教程中使用这个方法,因为它更简洁,更易于阅读。
- 我们使用SQLite数据库(通过FastLite)来存储我们的房间。
- 每个房间都有一个 id(整数)、一个 name(字符串)和一个 created_at 时间戳(字符串)。
- Room数据类是基于此结构自动生成的。
- 创建一个房间
main.py
@rt("/")
def get():
# The 'Input' id defaults to the same as the name, so you can omit it if you wish
create_room = Form(Input(id="name", name="name", placeholder="New Room Name"),
Button("Create Room"),
hx_post="/rooms", hx_target="#rooms-list", hx_swap="afterbegin")
rooms_list = Ul(*rooms(order_by='id DESC'), id='rooms-list')
return Titled("DrawCollab",
H1("DrawCollab"),
create_room, rooms_list)
@rt("/rooms")
async def post(room:Room):
room.created_at = datetime.now().isoformat()
return rooms.insert(room)- 当用户提交“创建房间”表单时,将调用此路由。
- 它创建一个新的房间对象,设置创建时间,并将其插入到数据库中。
- 它返回一个带有链接到新房间的HTML列表项,这得益于HTMX,这个链接是动态添加到主页的房间列表中的。
- 让我们给房间设计形状
main.py
@rt("/rooms/{id}")
async def get(id:int):
room = rooms[id]
return Titled(f"Room: {room.name}", H1(f"Welcome to {room.name}"), A(Button("Leave Room"), href="/"))- 此路线渲染特定房间的界面。
- 它从数据库中获取房间并渲染标题、标题和段落。
到目前为止这是完整的代码:
main.py
from fasthtml.common import *
from datetime import datetime
def render(room):
return Li(A(room.name, href=f"/rooms/{room.id}"))
app,rt,rooms,Room = fast_app('data/drawapp.db', render=render, id=int, name=str, created_at=str, pk='id')
@rt("/")
def get():
create_room = Form(Input(id="name", name="name", placeholder="New Room Name"),
Button("Create Room"),
hx_post="/rooms", hx_target="#rooms-list", hx_swap="afterbegin")
rooms_list = Ul(*rooms(order_by='id DESC'), id='rooms-list')
return Titled("DrawCollab", create_room, rooms_list)
@rt("/rooms")
async def post(room:Room):
room.created_at = datetime.now().isoformat()
return rooms.insert(room)
@rt("/rooms/{id}")
async def get(id:int):
room = rooms[id]
return Titled(f"Room: {room.name}", H1(f"Welcome to {room.name}"), A(Button("Leave Room"), href="/"))
serve()现在在您的终端中运行 python main.py 并打开您出现的‘链接’。您应该会看到一个页面,其中有一个创建新房间的表单和一个现有房间的列表。
画布 - 我们开始绘画吧! 🖌️
是时候添加实际的绘图功能了。我们将使用 Fabric.js 来实现这一点:
main.py
# ... (keep the previous imports and database setup)
@rt("/rooms/{id}")
async def get(id:int):
room = rooms[id]
canvas = Canvas(id="canvas", width="800", height="600")
color_picker = Input(type="color", id="color-picker", value="#3CDD8C")
brush_size = Input(type="range", id="brush-size", min="1", max="50", value="10")
js = """
var canvas = new fabric.Canvas('canvas');
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = '#3CDD8C';
canvas.freeDrawingBrush.width = 10;
document.getElementById('color-picker').onchange = function() {
canvas.freeDrawingBrush.color = this.value;
};
document.getElementById('brush-size').oninput = function() {
canvas.freeDrawingBrush.width = parseInt(this.value, 10);
};
"""
return Titled(f"Room: {room.name}",
A(Button("Leave Room"), href="/"),
canvas,
Div(color_picker, brush_size),
Script(src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"),
Script(js))
# ... (keep the serve() part)现在我们有了一个绘图画布!FastHTML 使得包含外部库和添加自定义 JavaScript 变得简单。
保存和加载画布 💾
现在我们有了一个可以使用的绘图画布,让我们添加保存和加载绘图的功能。我们将修改我们的数据库架构,包括一个 canvas_data 字段,并添加用于保存和加载画布数据的新路由。以下是我们将如何更新代码:
- 修改数据库架构:
main.py
app,rt,rooms,Room = fast_app('data/drawapp.db', render=render, id=int, name=str, created_at=str, canvas_data=str, pk='id')- 添加一个保存按钮,将画布的状态抓取并发送到服务器:
main.py
@rt("/rooms/{id}")
async def get(id:int):
room = rooms[id]
canvas = Canvas(id="canvas", width="800", height="600")
color_picker = Input(type="color", id="color-picker", value="#3CDD8C")
brush_size = Input(type="range", id="brush-size", min="1", max="50", value="10")
save_button = Button("Save Canvas", id="save-canvas", hx_post=f"/rooms/{id}/save", hx_vals="js:{canvas_data: JSON.stringify(canvas.toJSON())}")
# ... (rest of the function remains the same)- 添加用于保存和加载画布数据的路线:
main.py
@rt("/rooms/{id}/save")
async def post(id:int, canvas_data:str):
rooms.update({'canvas_data': canvas_data}, id)
return "Canvas saved successfully"
@rt("/rooms/{id}/load")
async def get(id:int):
room = rooms[id]
return room.canvas_data if room.canvas_data else "{}"- 更新JavaScript以加载现有画布数据:
main.py
js = f"""
var canvas = new fabric.Canvas('canvas');
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = '#3CDD8C';
canvas.freeDrawingBrush.width = 10;
// Load existing canvas data
fetch(`/rooms/{id}/load`)
.then(response => response.json())
.then(data => {{
if (data && Object.keys(data).length > 0) {{
canvas.loadFromJSON(data, canvas.renderAll.bind(canvas));
}}
}});
// ... (rest of the JavaScript remains the same)
"""通过这些更改,用户现在可以保存他们的绘图,并在返回房间时加载它们。画布数据以JSON字符串的形式存储在数据库中,从而便于序列化和反序列化。试试看!创建一个新房间,画一个图形,保存它,然后重新加载页面。你应该可以看到你的图形重新出现,准备进行进一步编辑。
这是完成的代码:
main.py
from fasthtml.common import *
from datetime import datetime
def render(room):
return Li(A(room.name, href=f"/rooms/{room.id}"))
app,rt,rooms,Room = fast_app('data/drawapp.db', render=render, id=int, name=str, created_at=str, canvas_data=str, pk='id')
@rt("/")
def get():
create_room = Form(Input(id="name", name="name", placeholder="New Room Name"),
Button("Create Room"),
hx_post="/rooms", hx_target="#rooms-list", hx_swap="afterbegin")
rooms_list = Ul(*rooms(order_by='id DESC'), id='rooms-list')
return Titled("QuickDraw",
create_room, rooms_list)
@rt("/rooms")
async def post(room:Room):
room.created_at = datetime.now().isoformat()
return rooms.insert(room)
@rt("/rooms/{id}")
async def get(id:int):
room = rooms[id]
canvas = Canvas(id="canvas", width="800", height="600")
color_picker = Input(type="color", id="color-picker", value="#000000")
brush_size = Input(type="range", id="brush-size", min="1", max="50", value="10")
save_button = Button("Save Canvas", id="save-canvas", hx_post=f"/rooms/{id}/save", hx_vals="js:{canvas_data: JSON.stringify(canvas.toJSON())}")
js = f"""
var canvas = new fabric.Canvas('canvas');
canvas.isDrawingMode = true;
canvas.freeDrawingBrush.color = '#000000';
canvas.freeDrawingBrush.width = 10;
// Load existing canvas data
fetch(`/rooms/{id}/load`)
.then(response => response.json())
.then(data => {{
if (data && Object.keys(data).length > 0) {{
canvas.loadFromJSON(data, canvas.renderAll.bind(canvas));
}}
}});
document.getElementById('color-picker').onchange = function() {{
canvas.freeDrawingBrush.color = this.value;
}};
document.getElementById('brush-size').oninput = function() {{
canvas.freeDrawingBrush.width = parseInt(this.value, 10);
}};
"""
return Titled(f"Room: {room.name}",
A(Button("Leave Room"), href="/"),
canvas,
Div(color_picker, brush_size, save_button),
Script(src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"),
Script(js))
@rt("/rooms/{id}/save")
async def post(id:int, canvas_data:str):
rooms.update({'canvas_data': canvas_data}, id)
return "Canvas saved successfully"
@rt("/rooms/{id}/load")
async def get(id:int):
room = rooms[id]
return room.canvas_data if room.canvas_data else "{}"
serve()部署到 Railway
您可以将您的网站部署到许多托管服务提供商,在本教程中我们将使用 Railway。要开始,请确保您创建一个 account 并安装 Railway CLI。安装完成后,请确保运行 railway login 以登录到您的帐户。
为了尽可能简化您网站的部署,FastHTMl 配备了一个内置的 CLI 工具,可以为您处理大部分部署过程。要部署您的网站,请在项目的根目录中在终端运行以下命令:
fh_railway_deploy quickdraw您的应用必须位于 main.py 文件中才能正常工作。
结论:你现在是一个FastHTML艺术家!🎨🚀
恭喜!您刚刚使用FastHTML构建了一个时尚、互动的web应用程序。让我们回顾一下我们所学到的内容:
- FastHTML允许您用最少的代码创建动态网页应用。
- 我们使用FastHTML的路由系统来处理不同的页面和操作。
- 我们与SQLite数据库集成,以存储房间信息和画布数据。
- 我们利用 Fabric.js 创建了一个交互式绘图画布。
- 我们实现了颜色选择、画笔大小调整和画布保存等功能。
- 我们使用HTMX实现无缝的部分页面更新,而无需完全重新加载。
- 我们学习了如何将我们的FastHTML应用程序部署到Railway,以便于托管。
您已经迈出了进入FastHTML开发世界的第一步。从这里开始,可能性是无穷无尽的!您可以通过添加以下功能进一步增强绘图应用程序:
- 实现不同的绘图工具(例如,形状,文本)
- 添加用户身份验证
- 创建已保存图画的图库
- 使用WebSockets实现实时协作绘图
无论你选择构建什么,FastHTML 都会支持你。现在去创造一些令人惊叹的东西吧!编码愉快! 🖼️🚀