djust-org/djust

GitHub: djust-org/djust

djust 是一个基于 Rust 高性能的 Django 响应式服务端渲染框架,实现无 JavaScript 的实时 Web 交互。

Stars: 66 | Forks: 4

djust

基于 Rust 的 Django 超快速响应式服务端渲染

djust 为 Django 带来了 Phoenix LiveView 风格的响应式组件,其性能犹如原生。编写服务端 Python 代码,即可实现自动、即时的客户端更新——无需 JavaScript 打包、无需构建步骤、无复杂性。 🌐 **[djust.org](https://djust.org)** | 📚 **[docs.djust.org](https://docs.djust.org)** | 🚀 **[快速入门](https://docs.djust.org/getting-started/)** | 📝 **[示例](https://djust.org/examples/)** [![PyPI 版本](https://img.shields.io/pypi/v/djust.svg)](https://pypi.org/project/djust/) [![CI](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/a709ff9561072714.svg)](https://github.com/djust-org/djust/actions/workflows/test.yml) [![MIT 许可证](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) [![Django 4.2+](https://img.shields.io/badge/django-4.2+-green.svg)](https://www.djangoproject.com/) [![PyPI 下载量](https://img.shields.io/pypi/dm/djust.svg)](https://pypi.org/project/djust/) ## ✨ 功能特性 - ⚡ **快 10-100 倍** - Rust 驱动的模板引擎和虚拟 DOM 差异对比 - 🔄 **响应式组件** - Phoenix LiveView 风格的服务端响应性 - 🔌 **Django 兼容** - 与现有 Django 模板和组件协同工作 - 📦 **零构建步骤** - 约 53 KB 压缩混淆的客户端 JavaScript,无需打包 - 🌐 **WebSocket 更新** - 通过 WebSocket 实时 DOM 补丁(含 HTTP 回退) - 🎯 **最少客户端代码** - 智能差异对比仅发送变化部分 - 🔒 **类型安全** - Rust 保障核心性能关键代码 - 🐞 **开发者调试面板** - 交互式调试,包含事件历史和 VDOM 检查 - 💤 **延迟 Hydration** - 为视口下方内容延迟 WebSocket 连接(节省 20-40% 内存) - 🚀 **TurboNav 兼容** - 与 Turbo 风格的客户端导航无缝协作 - 📱 **PWA 支持** - 离线优先的渐进式 Web 应用,带自动同步 - 🏢 **多租户就绪** - 生产级 SaaS 架构,具备租户隔离 - 🔐 **认证与授权** - 视图级和处理器级授权,集成 Django 权限系统 ## 🎯 快速示例 ``` from djust import LiveView, event_handler class CounterView(LiveView): template_string = """

Count: {{ count }}

""" def mount(self, request, **kwargs): self.count = 0 @event_handler def increment(self): self.count += 1 # Automatically updates client! @event_handler def decrement(self): self.count -= 1 ``` 就这样!无需 JavaScript。状态变更自动触发最小化的 DOM 更新。 ## 🔄 响应性工作原理 djust 使用 Rust 驱动的虚拟 DOM (VDOM) 来对比服务端渲染的 HTML,并仅通过 WebSocket 发送变化的补丁。理解几个核心属性就能让一切豁然开朗。 ### 模板结构 ``` {% load djust_tags %} {% djust_scripts %} {# Loads the client runtime #} {# Identifies the WebSocket session #}
{# Reactive boundary — only this is diffed #}

Count: {{ count }}

{# Static content outside dj-root is never touched by VDOM patching #} ``` | 属性 | 位置 | 用途 | |---|---|---| | `{% djust_scripts %}` | `` | 注入客户端 JavaScript | | `dj-view="{{ dj_view_id }}"` | `` | 将页面连接到 WebSocket 会话 | | `dj-root` | 内部 `
` | 标记响应式区域;仅其内部的 HTML 会被对比和补丁 | ### 稳定的列表标识 对于可能重排或插入/删除项的列表,在每一项上添加 `data-key` 或 `dj-key`。djust 使用它来发出 `MoveChild` 补丁,而不是先删除再插入——保留 DOM 状态(焦点、滚动位置、动画): ``` {% for item in items %}
{{ item.name }}
{% endfor %} ``` 没有 key 时,djust 按位置进行差异对比——这虽然正确,但对于重排会产生更多的 DOM 变更。 ### 常见陷阱:类属性中的单侧 `{% if %}` 在 HTML 属性值中使用不带 `{% else %}` 的 `{% if %}` 可能会导致 VDOM 补丁错位,因为 djust 的分支感知深度计数: ``` {# WRONG: one-sided if inside class attribute #}
{# CORRECT: use full if/else #}
{# ALSO CORRECT: move conditional outside the tag #} {% if active %}
{% else %}
{% endif %} ...
``` 这仅适用于属性值——元素内容中的 `{% if %}` 块工作正常。 详情请参见 [VDOM 架构指南](docs/website/advanced/vdom-architecture.md) 和 [模板速查表](docs/website/guides/template-cheatsheet.md)。 ## 🚀 入门指南 这是一个从零开始,通过 5 个步骤实现一个可工作的响应式计数器的完整演练。 ### 步骤 1 — 安装 ``` pip install djust django-channels ``` ### 步骤 2 — 添加到 `INSTALLED_APPS` 并配置设置 在 `myproject/settings.py` 中: ``` INSTALLED_APPS = [ # ... your existing apps ... 'channels', # WebSocket support 'djust', ] ASGI_APPLICATION = 'myproject.asgi.application' CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels.layers.InMemoryChannelLayer', } } ``` ### 步骤 3 — 配置 `asgi.py` 将 `myproject/asgi.py` 替换为: ``` import os from django.core.asgi import get_asgi_application from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack from djust.websocket import LiveViewConsumer from django.urls import path os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings') application = ProtocolTypeRouter({ "http": get_asgi_application(), "websocket": AuthMiddlewareStack( URLRouter([ path('ws/live/', LiveViewConsumer.as_asgi()), ]) ), }) ``` ### 步骤 4 — 添加 URL 路由 在 `myproject/urls.py` 中: ``` from django.urls import path from myapp.views import CounterView urlpatterns = [ path('counter/', CounterView.as_view(), name='counter'), ] ``` ### 步骤 5 — 编写视图和模板 `myapp/views.py`: ``` from djust import LiveView, event_handler class CounterView(LiveView): template_name = 'counter.html' def mount(self, request, **kwargs): self.count = 0 @event_handler def increment(self): self.count += 1 @event_handler def decrement(self): self.count -= 1 ``` `myapp/templates/counter.html`: ``` {% load djust_tags %} Counter {% djust_scripts %}

Count: {{ count }}

``` 使用 `uvicorn myproject.asgi:application --reload` 运行并打开 `/counter/`。点击按钮**无需页面刷新**即可更新计数——无需编写 JavaScript,无需构建步骤。 **下一步:** - [模板速查表](docs/website/guides/template-cheatsheet.md) — 一览所有指令和过滤器 - [组件指南](docs/website/guides/components.md) — 构建带有主题功能的可复用组件 - [CSS 框架指南](docs/website/guides/css-frameworks.md) — Tailwind 和 Bootstrap 集成 - [部署指南](docs/website/guides/deployment.md) — 使用 uvicorn、Redis 和 Nginx 进行生产部署 ## 📊 性能 在 M1 MacBook Pro (2021) 上的基准测试: | 操作 | Django | djust | 加速比 | |-----------|---------|-------|---------| | 模板渲染(100 个项目) | 2.5 ms | 0.15 ms | **16.7x** | | 大型列表(10k 个项目) | 450 ms | 12 ms | **37.5x** | | 虚拟 DOM 差异对比 | 不适用 | 0.08 ms | **亚毫秒** | | 往返更新 | 50 ms | 5 ms | **10x** | 自行运行基准测试: ``` cd benchmarks python benchmark.py ``` ## 🚀 安装 ### 前置条件 - Python 3.10+ - Rust 1.70+(用于从源代码构建) - Django 4.2+ ### 从 PyPI 安装 ``` pip install djust ``` ### 从源代码构建 #### 使用 Make(最简单 - 推荐用于开发) ``` # 克隆仓库 git clone https://github.com/djust-org/djust.git cd djust # 安装 Rust(如需要) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # 安装所有依赖并构建 make install # 启动开发服务器 make start # 查看所有可用命令 make help ``` **常用 Make 命令:** - `make start` - 启动开发服务器(带热重载) - `make stop` - 停止开发服务器 - `make status` - 检查服务器是否运行 - `make test` - 运行所有测试 - `make clean` - 清理构建产物 - `make help` - 显示所有可用命令 #### 使用 uv(快速) ``` # 克隆仓库 git clone https://github.com/djust-org/djust.git cd djust # 安装 Rust(如需要) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # 安装 uv(如需要) curl -LsSf https://astral.sh/uv/install.sh | sh # 创建虚拟环境并安装依赖 uv venv source .venv/bin/activate # On Windows: .venv\Scripts\activate # 安装 maturin 并构建 uv pip install maturin maturin develop --release ``` #### 使用 pip ``` # 克隆仓库 git clone https://github.com/djust-org/djust.git cd djust # 安装 Rust(如需要) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # 创建虚拟环境 python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate # 安装 maturin pip install maturin # 构建并安装 maturin develop --release # 或构建 wheel 文件 maturin build --release pip install target/wheels/djust-*.whl ``` ## 📖 文档 ### 设置 1. 添加到 `INSTALLED_APPS`: ``` INSTALLED_APPS = [ # ... 'channels', # Required for WebSocket support 'djust', # ... ] ``` 2. 配置 ASGI 应用 (`asgi.py`): ``` import os from django.core.asgi import get_asgi_application from channels.routing import ProtocolTypeRouter, URLRouter from channels.auth import AuthMiddlewareStack from djust.websocket import LiveViewConsumer from django.urls import path os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings') application = ProtocolTypeRouter({ "http": get_asgi_application(), "websocket": AuthMiddlewareStack( URLRouter([ path('ws/live/', LiveViewConsumer.as_asgi()), ]) ), }) ``` 3. 添加到 `settings.py`: ``` ASGI_APPLICATION = 'myproject.asgi.application' CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels.layers.InMemoryChannelLayer' } } ``` ### 创建 LiveView #### 基于类的 LiveView ``` from djust import LiveView, event_handler class TodoListView(LiveView): template_name = 'todos.html' # Or use template_string def mount(self, request, **kwargs): """Called when view is first loaded""" self.todos = [] @event_handler def add_todo(self, text): """Event handler - called from client""" self.todos.append({'text': text, 'done': False}) @event_handler def toggle_todo(self, index): self.todos[index]['done'] = not self.todos[index]['done'] ``` #### 基于函数的 LiveView ``` from djust import live_view @live_view(template_name='counter.html') def counter_view(request): count = 0 def increment(): nonlocal count count += 1 return locals() # Returns all local variables as context ``` ### 模板语法 djust 支持带有事件绑定的 Django 模板语法: ```

{{ title }}

{{ text|upper }}

{{ description|truncatewords:20 }}

Search {{ body|urlize }} {# No |safe needed — djust's Rust engine auto-marks urlize output as safe via safe_output_filters. Unlike standard Django where you'd add |safe, djust handles this automatically. #} {% if show %}
Visible
{% endif %} {% if count > 10 %}
Many items!
{% endif %} {% for item in items %}
  • {{ item }}
  • {% endfor %} View {% include "partials/header.html" %}
    ``` ### 支持的事件 - `dj-click` - 点击事件 - `dj-input` - 输入事件(传递 `value`) - `dj-change` - 变更事件(传递 `value`) - `dj-submit` - 表单提交(以字典形式传递表单数据) ### 可复用组件 djust 提供了一个强大的组件系统,具有自动状态管理和稳定的组件 ID。 #### 基础组件示例 ``` from djust.components import AlertComponent class MyView(LiveView): def mount(self, request): # Components get automatic IDs based on attribute names self.alert_success = AlertComponent( message="Operation successful!", type="success", dismissible=True ) # component_id automatically becomes "alert_success" ``` #### 组件 ID 管理 组件会根据您视图中的**属性名称**自动接收一个稳定的 `component_id`。这消除了手动 ID 管理: ``` # 当你编写时: self.alert_success = AlertComponent(message="Success!") # 框架会自动: # 1. 设置 component.component_id = "alert_success" # 2. 在渲染和事件间持久化此 ID # 3. 在 HTML 中使用:data-component-id="alert_success" # 4. 将事件路由回正确的组件 ``` **为何有效:** - 属性名称 (`alert_success`) 在您的视图中已经是唯一的 - 在重新渲染和 WebSocket 重连期间保持稳定 - 事件处理器可以通过属性名称引用组件 - 无需手动同步 ID 字符串 **事件路由示例:** ``` class MyView(LiveView): def mount(self, request): self.alert_warning = AlertComponent( message="Warning message", dismissible=True ) @event_handler def dismiss(self, component_id: str = None): """Handle dismissal - automatically routes to correct component""" if component_id and hasattr(self, component_id): component = getattr(self, component_id) if hasattr(component, 'dismiss'): component.dismiss() # component_id="alert_warning" ``` 当点击关闭按钮时,客户端发送 `component_id="alert_warning"`,处理器使用 `getattr(self, "alert_warning")` 来查找组件。 #### 创建自定义组件 ``` from djust import LiveComponent, event_handler from djust.components import register_component class ButtonComponent(LiveComponent): template = '' def mount(self, **kwargs): self.label = kwargs.get("label", "Click") self.clicks = 0 @event_handler() def on_click(self, **kwargs): self.clicks += 1 self.trigger_update() def get_context_data(self): return {"label": self.label, "clicks": self.clicks} # register_component 接受 LiveComponent 子类(有状态、事件驱动) register_component('my-button', ButtonComponent) ``` ### 装饰器 ``` from djust import LiveView, event_handler, reactive class MyView(LiveView): @event_handler def handle_click(self): """Marks method as event handler""" pass @reactive def count(self): """Reactive property - auto-triggers updates""" return self._count @count.setter def count(self, value): self._count = value ``` ### 配置 在您的 Django `settings.py` 中配置 djust: ``` LIVEVIEW_CONFIG = { # Transport mode 'use_websocket': True, # Set to False for HTTP-only mode (no WebSocket dependency) # Debug settings 'debug_vdom': False, # Enable detailed VDOM patch logging (for troubleshooting) # Serialization (issue #292) 'strict_serialization': False, # Raise TypeError for non-serializable state values (recommended in development) # CSS Framework 'css_framework': 'bootstrap5', # Options: 'bootstrap5', 'tailwind', None } ``` **常用配置选项:** | 选项 | 默认值 | 描述 | |--------|---------|-------------| | `use_websocket` | `True` | 使用 WebSocket 传输(需要 Django Channels) | | `debug_vdom` | `False` | 启用详细的 VDOM 调试日志 | | `strict_serialization` | `False` | 对不可序列化的状态引发 TypeError(建议在开发中使用) | | `css_framework` | `'bootstrap5'` | 用于组件的 CSS 框架 | **CSS 框架设置:** 对于 Tailwind CSS(推荐),使用单命令设置: ``` python manage.py djust_setup_css tailwind ``` 这会自动检测模板目录、创建配置文件并构建您的 CSS。用于生产: ``` python manage.py djust_setup_css tailwind --minify ``` 详细设置说明、Bootstrap 配置和 CI/CD 集成,请参见 [CSS 框架指南](docs/website/guides/css-frameworks.md)。 **调试模式:** 排查 VDOM 问题时,启用调试日志: ``` # 在 settings.py 中 LIVEVIEW_CONFIG = { 'debug_vdom': True, } # 或通过编程方式设置 from djust.config import config config.set('debug_vdom', True) ``` 这将记录: - 服务端:补丁生成详情(标准错误) - 客户端:补丁应用和 DOM 遍历(浏览器控制台) ### 状态管理 djust 提供了纯 Python 的状态管理装饰器,消除了手动编写 JavaScript 的需要。 #### 🚀 快速入门(5 分钟) 用 **8 行 Python** 构建一个防抖搜索(无 JavaScript): ``` from djust import LiveView from djust.decorators import debounce class ProductSearchView(LiveView): template_string = """
    {% for p in results %}
    {{ p.name }}
    {% endfor %}
    """ def mount(self, request): self.results = [] @debounce(wait=0.5) # Wait 500ms after typing stops def search(self, query: str = "", **kwargs): self.results = Product.objects.filter(name__icontains=query)[:10] ``` **就这样!** 服务器仅在您停止输入后才查询。添加 `@optimistic` 实现即时 UI 更新,`@cache(ttl=300)` 缓存响应 5 分钟。 **👉 [完整快速入门指南(5 分钟)](docs/STATE_MANAGEMENT_QUICKSTART.md)** #### 主要特性 - ✅ **无需 JavaScript** - 常见模式无需编写任何 JS - ✅ **减少 87% 代码** - 装饰器替代数百行手动 JavaScript - ✅ **轻量级包** - 压缩混淆的 client.js 约 53 KB,无 npm 依赖 - ✅ **有竞争力的开发体验** - 匹配 Phoenix LiveView 和 Laravel Livewire 的开发体验 #### 可用装饰器 | 装饰器 | 使用场景 | 示例 | |-----------|----------|---------| | `@debounce(wait)` | 用户输入中 | 搜索、自动保存 | | `@throttle(interval)` | 快速事件 | 滚动、缩放 | | `@optimistic` | 即时反馈 | 计数器、切换 | | `@cache(ttl, key_params)` | 重复查询 | 自动完成 | | `@client_state(keys)` | 多组件 | 仪表盘过滤器 | | `@background` | 长时间操作 | AI 生成、文件处理 | | `DraftModeMixin` | 自动保存表单 | 联系表单 | **快速决策矩阵:** - 在输入框中输入?→ `@debounce(0.5)` - 滚动/缩放?→ `@throttle(0.1)` - 需要即时 UI 更新?→ `@optimistic` - 同一查询多次?→ `@cache(ttl)` - 多个组件?→ `@client_state([keys])` - 长时间运行的工作?→ `@background` 或 `self.start_async(callback)` - 自动保存表单?→ `DraftModeMixin` #### 了解更多 - 🚀 **[快速入门(5 分钟)](docs/STATE_MANAGEMENT_QUICKSTART.md)** - 快速上手 - 📚 **[完整教程(20 分钟)](docs/STATE_MANAGEMENT_TUTORIAL.md)** - 逐步构建产品搜索 - 📖 **[API 参考](docs/STATE_MANAGEMENT_API.md)** - 完整的装饰器文档和速查表 - 🎯 **[示例](docs/STATE_MANAGEMENT_EXAMPLES.md)** - 即用型代码 - 🔄 **[迁移指南](docs/STATE_MANAGEMENT_MIGRATION.md)** - 将 JavaScript 转换为 Python - ⚖️ **[框架对比](docs/STATE_MANAGEMENT_COMPARISON.md)** - 与 Phoenix LiveView 和 Laravel Livewire 对比 ### 🧭 导航模式 #### 何时使用何种方式 | 场景 | 使用 | 原因 | |----------|-----|-----| | 在同一视图内筛选/排序/分页 | `dj-patch` / `live_patch()` | 无需重新挂载,URL 保持可收藏 | | 导航到不同的 LiveView | `dj-navigate` / `live_redirect()` | 使用同一 WebSocket,无页面刷新 | | 链接到非 LiveView 页面 | 标准 `` | 需要完整页面加载 | #### 快速决策树 选择导航方法时使用此流程图: ``` Is this a direct user click on a link? ├─ Yes → Is it the same view (filter/sort)? │ ├─ Yes → Use dj-patch │ └─ No → Use dj-navigate │ └─ No → Is navigation conditional on server logic? ├─ Yes → Use live_redirect() in @event_handler │ Examples: form validation, auth checks, async operations └─ No → You probably need dj-navigate (see anti-pattern below) ``` #### ⚠️ 反模式:不要使用 `dj-click` 进行导航 这是构建多视图 djust 应用时**最常见的错误**。使用 `dj-click` 触发一个处理器,该处理器立即调用 `live_redirect()`,这会创建不必要的往返。 **❌ 错误** — 使用 `dj-click` 触发调用 `live_redirect()` 的处理器: ``` # 反模式:处理程序仅执行导航操作 @event_handler() def go_to_item(self, item_id, **kwargs): self.live_redirect(f"/items/{item_id}/") # Wasteful round-trip! ``` ``` ``` **实际发生了什么:** 1. 用户点击按钮 → 客户端发送 WebSocket 消息(50-100ms) 2. 服务器接收消息,处理处理器(10-50ms) 3. 服务器以 `live_redirect` 命令响应(50-100ms) 4. 客户端最终导航到新视图 **总计:110-250ms** + 处理器处理时间 **✅ 正确** — 直接使用 `dj-navigate`: ``` View Item ``` **发生了什么:** 1. 用户点击链接 → 客户端直接导航 **总计:~10ms**(仅 DOM 更新) **为何重要:** - **性能:** 导航速度快 10-20 倍 - **网络效率:** 节省 WebSocket 带宽 - **用户体验:** 即时响应,无需加载指示器 - **简单性:** 更少的代码,更少的活动部件 #### 何时在处理器中使用 `live_redirect()` 仅当导航依赖于**服务端逻辑或验证**时,才在处理器中进行导航: **✅ 表单验证后的条件导航:** ``` @event_handler() def submit_form(self, **kwargs): if self.form.is_valid(): self.form.save() self.live_redirect("/success/") # OK: Conditional on validation else: # Stay on form to show errors pass ``` **✅ 基于认证/权限的导航:** ``` @event_handler() def view_sensitive_data(self, **kwargs): if not self.request.user.has_perm('app.view_sensitive'): self.live_redirect("/access-denied/") # OK: Auth check required return self.show_sensitive = True ``` **✅ 异步操作后的导航:** ``` @event_handler() async def create_and_view_item(self, name, **kwargs): item = await Item.objects.acreate(name=name, owner=self.request.user) self.live_redirect(f"/items/{item.id}/") # OK: Navigate to newly created item ``` **✅ 多步骤向导逻辑:** ``` @event_handler() def next_step(self, **kwargs): if self.current_step == "payment" and not self.payment_valid: # Stay on payment step if invalid return self.current_step = self.get_next_step() self.live_patch(params={"step": self.current_step}) # OK: Conditional flow ``` **共同主题:** 处理器在导航前执行**有意义的工作**。如果您的处理器仅调用 `live_redirect()`,请改用 `dj-navigate`。 #### 快速示例:多视图应用 ``` from djust import LiveView from djust.mixins.navigation import NavigationMixin from djust.decorators import event_handler class ProductListView(NavigationMixin, LiveView): template_string = """ Electronics Books
    {% for product in products %} {{ product.name }} {% endfor %}
    """ def mount(self, request, **kwargs): self.category = "all" self.products = [] def handle_params(self, params, uri): """Called when URL changes via dj-patch or browser back/forward""" self.category = params.get("category", "all") self.products = Product.objects.filter(category=self.category) ``` **了解更多:** - 📖 **[导航指南](docs/guides/navigation.md)** - 完整的 API 参考 (`live_patch()`, `live_redirect()`, `handle_params()`) ### 开发者工具 #### 调试面板 LiveView 开发的交互式调试工具(仅限 DEBUG 模式): ``` # 在 settings.py 中 DEBUG = True # Debug panel automatically enabled ``` **打开**:按 `Ctrl+Shift+D`(Windows/Linux)或 `Cmd+Shift+D`(Mac),或点击 🐞 浮动按钮 **功能**: - 🔍 **事件处理器** - 发现所有处理器及其参数、类型和描述 - 📊 **事件历史** - 实时日志,带时间指标(例如 `search • 45.2ms`) - ⚡ **VDOM 补丁** - 以亚毫秒精度监控 DOM 更新 - 🔬 **变量** - 检查当前视图状态 **了解更多**: - 📖 **[调试面板指南](docs/DEBUG_PANEL.md)** - 完整用户指南 - 📝 **[事件处理器最佳实践](docs/EVENT_HANDLERS.md)** - 模式和约定 #### 事件处理器 始终使用 `@event_handler` 装饰器以实现自动发现和验证: ``` from djust.decorators import event_handler @event_handler() def search(self, value: str = "", **kwargs): """Search handler - description shown in debug panel""" self.search_query = value ``` **参数约定**:对于表单输入 (`dj-input`, `dj-change` 事件),使用 `value`: ``` # ✅ 正确 - 与表单事件发送的内容匹配 @event_handler() def search(self, value: str = "", **kwargs): self.search_query = value # ❌ 错误 - 无法接收输入值 @event_handler() def search(self, query: str = "", **kwargs): self.search_query = query # Always "" (default) ``` ## 🏗️ 架构 ``` ┌─────────────────────────────────────────────┐ │ Browser │ │ ├── Client.js (~53 KB gz) - Events & DOM │ │ └── WebSocket Connection │ └─────────────────────────────────────────────┘ ↕️ WebSocket (Binary/JSON) ┌─────────────────────────────────────────────┐ │ Django + Channels (Python) │ │ ├── LiveView Classes │ │ ├── Event Handlers │ │ └── State Management │ └─────────────────────────────────────────────┘ ↕️ Python/Rust FFI (PyO3) ┌─────────────────────────────────────────────┐ │ Rust Core (Native Speed) │ │ ├── Template Engine (<1ms) │ │ ├── Virtual DOM Diffing (<100μs) │ │ ├── HTML Parser │ │ └── Binary Serialization (MessagePack) │ └─────────────────────────────────────────────┘ ``` ## 🎨 示例 完整的可工作示例,请参见 [examples/demo_project](examples/demo_project) 目录: - **计数器** - 简单的响应式计数器 - **待办事项列表** - 带列表的 CRUD 操作 - **聊天** - 实时消息传递 运行演示: ``` cd examples/demo_project pip install -r requirements.txt python manage.py migrate python manage.py runserver ``` 访问 http://localhost:8000 ## 🔧 开发 ### 项目结构 ``` djust/ ├── crates/ │ ├── djust_core/ # Core types & utilities │ ├── djust_templates/ # Template engine │ ├── djust_vdom/ # Virtual DOM & diffing │ ├── djust_components/ # Reusable component library │ └── djust_live/ # Main PyO3 bindings ├── python/ │ └── djust/ # Python package │ ├── live_view.py # LiveView base class │ ├── component.py # Component system │ ├── websocket.py # WebSocket consumer │ └── static/ │ └── client.js # Client runtime ├── branding/ # Logo and brand assets ├── examples/ # Example projects ├── benchmarks/ # Performance benchmarks └── tests/ # Tests ``` ### 运行测试 ``` # 所有测试(Python + Rust + JavaScript) make test # 单独的测试套件 make test-python # Python tests make test-rust # Rust tests make test-js # JavaScript tests # 特定测试 pytest tests/unit/test_live_view.py cargo test --workspace --exclude djust_live ``` 全面的测试文档,请参见**[测试指南](docs/TESTING.md)**。 ### 构建文档 ``` cargo doc --open ``` ## 💰 支持 djust ## 📝 路线图 - [x] 模板继承 (`{% extends %}`) - [x] `{% url %}` 和 `{% include %}` 标签 - [x] `{% if %}` 标签中的比较运算符 - [x] 所有 57 个 Django 内置模板过滤器 - [x] 安全加固(WebSocket 源验证、HMAC 签名、速率限制) - [x] 开发者调试面板,带事件历史和 VDOM 检查 - [x] 可复用组件库 (`djust_components` crate) - [x] JIT 管道改进和过时闭包修复 - [x] 认证与授权(视图级 + 处理器级) - [x] 文件上传处理 - [x] 服务器发送事件 (SSE) 回退 - [ ] React/Vue 组件兼容性 - [x] TypeScript 定义 (`djust.d.ts` 随包附带) - [x] Redis 支持的会话存储 - [x] 水平扩展支持 ## 🔒 安全 - 通过 Django 中间件提供 CSRF 保护 - 通过自动模板转义提供 XSS 保护(Rust 引擎默认转义所有变量) - 产生 HTML 的过滤器 (`urlize`, `urlizetrunc`, `unordered_list`) 内部自行处理转义——Rust 引擎的 `safe_output_filters` 白名单防止了双重转义,因此这些过滤器无需使用 `|safe` - 通过 Django 会话进行 WebSocket 认证 - WebSocket 源验证和 HMAC 消息签名 (v0.2.1) - 支持按视图和全局速率限制 - 可配置的 WebSocket 连接允许源 - 在 `mount()` 之前强制执行视图级认证 (`login_required`, `permission_required`) - 用于保护单个事件处理器的处理器级 `@permission_required` - `djust_audit` 命令和 `djust.S005` 系统检查,用于认证状况可见性 安全问题报告至:security@djust.org ## 📄 许可证 MIT 许可证 - 详情请参见 [LICENSE](LICENSE) 文件。 ## 🙏 致谢 - 灵感来源于 [Phoenix LiveView](https://hexdocs.pm/phoenix_live_view/) - 使用 [PyO3](https://pyo3.rs/) 构建 Python/Rust 互操作 - 使用 [html5ever](https://github.com/servo/html5ever) 进行 HTML 解析 - 由出色的 Rust 和 Django 社区驱动
    标签:Django, Phoenix LiveView风格, Python, Rust, SOC Prime, Syscall, Virtual DOM, WebSocket, Web开发, 依赖分析, 兼容性, 反应式组件, 反应式编程, 可视化界面, 后端框架, 实时更新, 开发工具, 性能优化, 懒加载, 无后门, 最小客户端代码, 服务端渲染, 检测绕过, 模板引擎, 现代Web开发, 类型安全, 网络流量审计, 调试面板, 轻量级客户端, 逆向工具, 零构建步骤, 高并发