Django Next.js
Django项目的Next.js集成。
如果你想在项目中同时使用Django和Next.js,有两种情况:
-
你正在开始一个新项目,想使用Django作为后端,Next.js作为前端。Django只处理API请求。所有前端代码都在Next.js中,你不需要编写任何Django模板。
在这种情况下,你不需要这个包(虽然你可以使用它)。 你只需启动Django和Next.js服务器,并将公共Web服务器指向Next.js即可。
-
你需要同时使用Django模板和Next.js,并且这些页面应该可以轻松地相互链接。 也许你有一个现有的Django项目,其中有一些由Django模板渲染的页面, 而你想在Next.js中添加一些新页面。 或者你想将前端迁移到Next.js,但由于项目较大,你需要逐步进行。
在这种情况下,这个包就是为你准备的!
它是如何工作的?
来自[StackOverflow上的一条评论]:
在同一服务器上运行2个端口。一个用于Django(面向公众), 另一个用于Next.js(内部)。 让Django处理所有Web请求。 对于每个请求,从Django视图查询Next.js以获取HTML响应。 从Django视图返回该精确的HTML响应。
安装
-
从PyPI安装最新版本。
pip install django-nextjs
-
将
django_nextjs.apps.DjangoNextJSConfig
添加到INSTALLED_APPS
。 -
根据你的环境设置Next.js URL。
设置Next.js URL(开发环境)
如果你在开发期间使用ASGI提供你的网站服务,
使用Django Channels并
将NextJSProxyHttpConsumer
、NextJSProxyWebsocketConsumer
添加到asgi.py
中,如下例所示。
注意: 我们建议使用ASGI和Django Channels, 因为这对于Next.js 12+中的快速刷新(热模块替换)正常工作是必需的。
import os
from django.core.asgi import get_asgi_application
from django.urls import re_path, path
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
django_asgi_app = get_asgi_application()
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django_nextjs.proxy import NextJSProxyHttpConsumer, NextJSProxyWebsocketConsumer
from django.conf import settings
# 如果需要,在这里放置你的自定义路由
http_routes = [re_path(r"", django_asgi_app)]
websocket_routers = []
if settings.DEBUG:
http_routes.insert(0, re_path(r"^(?:_next|__next|next).*", NextJSProxyHttpConsumer.as_asgi()))
websocket_routers.insert(0, path("_next/webpack-hmr", NextJSProxyWebsocketConsumer.as_asgi()))
application = ProtocolTypeRouter(
{
# Django的ASGI应用程序处理传统的HTTP和websocket请求。
"http": URLRouter(http_routes),
"websocket": AuthMiddlewareStack(URLRouter(websocket_routers)),
# ...
}
)
否则(如果在开发期间使用WSGI提供服务),请在urls.py
的开头添加以下内容:
path("", include("django_nextjs.urls"))
警告: 如果你使用ASGI提供服务,请不要将此添加到
你的urls.py
中。这可能会导致死锁。
设置Next.js URL(生产环境)
在生产环境中,使用反向代理如Nginx或Caddy:
URL | 操作 |
---|---|
/_next/static/... | 提供NEXTJS_PATH/.next/static 目录 |
/_next/... | 代理到http://localhost:3000 |
/next/... | 提供NEXTJS_PATH/public/next 目录 |
Nginx配置示例:
location /_next/static/ {
alias NEXTJS_PATH/.next/static/;
expires max;
add_header Cache-Control "public";
}
location /_next/ {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /next/ {
alias NEXTJS_PATH/public/next/;
expires max;
add_header Cache-Control "public";
}
使用
启动Next.js服务器:
# 开发:
$ npm run dev
# 生产:
$ npm run build
$ npm run start
首先在Next.js中开发你的页面,然后为每个Next.js页面定义一个Django URL。以下是一个示例:
from django_nextjs.views import nextjs_page
urlpatterns = [
path("/nextjs/page", nextjs_page(), name="nextjs_page"),
]
虽然不推荐,但有时你可能需要在Django中展示Next.js页面之前添加一些自定义步骤。然而,我们建议将这种逻辑移到Next.js中,以确保它在客户端重定向时也能应用。如果你发现自己处于这种情况,可以为每个页面创建一个异步视图,如下所示:
from django_nextjs.render import render_nextjs_page
async def jobs(request):
# 你的自定义逻辑
return await render_nextjs_page(request)
自定义HTML响应
你可以在Django代码中修改Next.js返回的HTML代码。
如果你同时使用Next.js和Django模板,避免导航栏和页脚代码重复是一个常见的用例。
否则,你将不得不编写和维护两个单独版本的导航栏和页脚(一个Django模板版本和一个Next.js版本)。
然而,你可以简单地为导航栏创建一个Django模板,并将其代码插入到Next.js返回的<body>
标签的开头。
要启用此功能,你需要在Next.js中自定义文档和根布局,并进行以下调整:
- 将
id="__django_nextjs_body"
作为<body>
元素的第一个属性。 - 在
<body>
内部添加<div id="__django_nextjs_body_begin" />
作为第一个元素。 - 在
<body>
内部添加<div id="__django_nextjs_body_end" />
作为最后一个元素。
注意:目前HTML自定义不适用于app router(Next.js 13+)。
阅读此文档 并自定义你的Next.js文档:
// pages/_document.jsx (或 .tsx)
...
<body id="__django_nextjs_body">
<div id="__django_nextjs_body_begin" />
<Main />
<NextScript />
<div id="__django_nextjs_body_end" />
</body>
...
// app/layout.jsx (或 .tsx)
...
<body id="__django_nextjs_body" className={inter.className}>
<div id="__django_nextjs_body_begin" />
{children}
<div id="__django_nextjs_body_end" />
</body>
...
``` -->
编写一个继承自 `django_nextjs/document_base.html` 的 Django 模板:
```django
{% extends "django_nextjs/document_base.html" %}
{% block head %}
<!-- ... 你想放在 "head" 标签开头的内容 ... -->
{{ block.super }}
<!-- ... 你想放在 "head" 标签结尾的内容 ... -->
{% endblock %}
{% block body %}
... 你想放在 "body" 标签开头的内容 ...
... 例如,包含导航栏模板 ...
{{ block.super }}
... 你想放在 "body" 标签结尾的内容 ...
... 例如,包含页脚模板 ...
{% endblock %}
将模板名称传递给 nextjs_page
或 render_nextjs_page
:
from django_nextjs.render import render_nextjs_page
from django_nextjs.views import nextjs_page
async def jobs(request):
return await render_nextjs_page(request, template_name="path/to/template.html")
urlpatterns = [
path("/nextjs/page", nextjs_page(template_name="path/to/template.html"), name="nextjs_page"),
path("/jobs", jobs, name="jobs_page")
]
注意事项
- 如果你想在 Next.js 的
public
目录中添加文件,该文件应该放在public/next
子目录中才能正常工作。 - 如果你使用的是 Django channels,请确保所有中间件都是异步兼容的。
- 为避免"重定向次数过多"错误,你可能需要在 Django 项目的
settings.py
中添加APPEND_SLASH = False
。同时,在urls.py
中不要在 nextjs 路径末尾添加/
。 - 本包不提供从 Django 向 Next.js 传递数据的解决方案。仍应使用 Django Rest Framework、GraphQL 或类似解决方案。
- 本包不会运行 Next.js 服务器。你需要自己运行它。
设置
默认设置:
NEXTJS_SETTINGS = {
"nextjs_server_url": "http://127.0.0.1:3000",
"ensure_csrf_token": True,
}
nextjs_server_url
Next.js 服务器的 URL(通过 npm run dev
或 npm run start
启动)
ensure_csrf_token
如果用户没有 CSRF 令牌,通过调用 Django 的 django.middleware.csrf.get_token
确保生成一个并包含在对 Next.js 服务器的初始请求中。如果安装了 django.middleware.csrf.CsrfViewMiddleware
,初始响应将包含一个 Set-Cookie
头,以在客户端保存 CSRF 令牌值。此行为默认启用。
何时需要 ensure_csrf_token
?
你可能需要在 Next.js 的 getServerSideProps
中发出 GraphQL POST 请求来获取数据。如果这是用户的第一个请求,将没有 CSRF cookie,导致请求失败,因为 GraphQL 即使对数据获取也使用 POST。然而,只要 getServerSideProps
函数是无副作用的(即,它们不使用 HTTP 不安全方法或 GraphQL 突变),从安全角度来看这应该是可以的。更多信息请参阅这里。
开发
- 在你的虚拟环境中用
pip install -e '.[dev]'
安装开发依赖 - 使用
pre-commit install
安装预提交钩子。
参考
许可
MIT