你好呀!
如果您和我一样,您可能会对当今实时 Web 应用程序的无缝交互性感到惊叹——那些即时响应的聊天机器人、无需刷新页面即可弹出的实时通知,以及无需刷新即可更新的协作工具。眨眼间。在数字时代,实时功能不再是一种奢侈,而是一种期望。
现在,如果您一直在跟踪 Next.js 领域的发展,您可能已经听说过 13.4 版本中值得关注的功能,尤其是游戏规则改变者:服务器操作。您是否好奇这将如何重新定义我们打造实时体验的方式?
嗯,我也是!
和我一起深入研究这个案例研究,我们将踏上利用Next.js服务器操作的强大功能和技巧构建实时应用程序的旅程。无论您是经验丰富的开发人员还是刚刚涉足实时应用程序领域,都有大量的见解等着您。
让我们开始吧,好吗?
在当今快节奏的数字环境中,“实时”一词经常出现在从游戏和金融到通信和社交媒体的各种环境中。但在 Web 应用程序世界中“实时”到底意味着什么? L
让我们揭开这一点。
实时应用程序是立即响应用户输入或外部事件的系统或程序,提供即时反馈,没有明显的延迟。简而言之,将它们视为“实时”发展的实时动态平台,反映了现代数字生态系统中不断流动的信息。
为了正确理解这一点,请考虑一些普遍存在的例子:
即时通讯应用程序: WhatsApp和 Telegram 等平台可以毫不延迟地发送、接收和查看消息。
协作工具:想想 Google Docs,多个用户可以同时编辑文档,实时观察彼此的更改。
实时股票代码:显示随市场波动即时更新的股票价格的平台。
在线多人游戏:玩家之间和环境之间的交互零延迟,确保无缝的游戏体验。
那么,为什么实时功能如此受追捧呢?
构建实时应用程序并非没有障碍:
可扩展性问题:实时应用程序通常需要处理大量并发连接,需要强大的基础设施。
数据完整性:确保实时数据在各种用户界面之间保持一致可能是一个挑战,特别是在多个同时编辑或交互的情况下。
延迟:实时应用程序的好坏取决于其最慢的组件。确保最小的延迟需要仔细优化和有效利用资源。
现在我们已经对实时应用程序有了基本的了解,我们将深入研究 Next.js 13.4 及其服务器操作如何成为渴望打造这种沉浸式体验的开发人员的关键工具。
在不断发展的 Web 开发领域,Next.js 始终走在最前沿,推出的功能重新定义了我们构建应用程序的方式。版本 13.4 也不例外,特别是它强调服务器操作。但在我们深入研究之前,让我们先澄清一些术语:
React 生态系统中的操作虽然仍处于实验阶段,但通过允许开发人员执行异步代码来响应用户交互,已经带来了范式转变。
有趣的是,虽然它们并不是 Next.js 或React Server Components 所独有的,但通过 Next.js 使用它们意味着您处于 React 实验通道中。
对于熟悉 HTML 表单的人来说,您可能还记得将 URL 传递给action
属性。现在,通过 Actions,您可以直接传递函数,使交互更加动态和集成。
<button action={() => { /* async function logic here */ }}>Click me!</button>
React 与 Actions 的集成还为乐观更新提供了内置解决方案。这强调了虽然操作是开创性的,但模式仍在不断发展,并且可能会添加更新的 API 以进一步丰富它们。
表单操作代表了 React 操作与标准<form>
API 的巧妙结合。它们与 HTML 中的原始formaction
属性产生共鸣,使开发人员能够增强渐进式加载状态和其他开箱即用的功能。
<!-- Traditional HTML approach --> <form action="/submit-url"> <!-- form elements --> </form> <!-- With Next.js 13.4 Form Actions --> <form action={asyncFunctionForSubmission}> <!-- form elements --> </form>
服务器函数本质上是在服务器端运行但可以从客户端调用的函数。这些将 Next.js 的服务器端渲染能力提升到一个全新的水平。
过渡到服务器操作,它们可以理解为服务器功能,但专门作为操作触发。它们与表单元素的集成,尤其是通过action
属性,确保表单即使在客户端 JavaScript 加载之前也保持交互性。这意味着更流畅的用户体验,React 水合不是表单提交的先决条件。
// A simple Server Action in Next.js 13.4 <form action={serverActionFunction}> <!-- form elements --> </form>
最后,我们有服务器突变,它是服务器操作的子集。当您需要修改服务器上的数据然后执行特定响应(例如redirect
、 revalidatePath
或revalidateTag
)时,这些功能特别强大。
const serverMutationFunction = async () => { // Modify data logic here... // ... return { revalidatePath: '/updated-path' }; } <form action={serverMutationFunction}> <!-- form elements --> </form>
注释:总而言之,Next.js 13.4 的服务器操作框架以操作、表单操作、服务器功能和服务器突变为基础,体现了实时 Web 应用程序的变革性方法。
当我们继续进行案例研究时,您将亲眼目睹这些功能所带来的强大功能。
那么,让我们为即将到来的激动人心的旅程做好准备吧!
在构建实时应用程序的背景下,Next.js 13.4 的服务器操作发挥着至关重要的作用。这些 alpha 功能可以轻松管理服务器端数据突变、减少客户端 JavaScript 并逐步增强表单。
首先,您需要在 Next.js 项目中启用服务器操作。只需将以下代码添加到您的next.config.js
文件中:
module.exports = { experimental: { serverActions: true, }, }
服务器操作可以在使用它的服务器组件中定义,也可以在单独的文件中定义,以便在客户端和服务器组件之间重用。
以下是创建和调用服务器操作的方法:
在服务器组件内:可以在服务器组件内轻松定义服务器操作,如下所示:
export default function ServerComponent() { async function myAction() { 'use server' // ... } }
使用客户端组件:在客户端组件内使用服务器操作时,在单独的文件中创建操作,然后导入它。
// app/actions.js 'use server' export async function myAction() { // ... }
在客户端组件中导入并使用:
// app/client-component.js import { myAction } from './actions' export default function ClientComponent() { return ( <form action={myAction}> <button type="submit">Add to Cart</button> </form> ) }
自定义调用:您可以使用startTransition
等自定义方法来调用表单、按钮或输入之外的服务器操作。
// Example using startTransition 'use client' import { useTransition } from 'react' import { addItem } from '../actions' function ExampleClientComponent({ id }) { let [isPending, startTransition] = useTransition() return ( <button onClick={() => startTransition(() => addItem(id))}> Add To Cart </button> ) }
Next.js 13.4 还提供渐进式增强功能,允许<form>
在没有 JavaScript 的情况下运行。服务器操作可以直接传递到<form>
,即使 JavaScript 被禁用,也使表单具有交互性。
// app/components/example-client-component.js 'use client' import { handleSubmit } from './actions.js' export default function ExampleClientComponent({ myAction }) { return ( <form action={handleSubmit}> {/* ... */} </form> ) }
默认情况下,发送到服务器操作的最大请求正文为 1MB。如果需要,您可以使用serverActionsBodySizeLimit
选项配置此限制:
module.exports = { experimental: { serverActions: true, serverActionsBodySizeLimit: '2mb', }, }
要开始使用 Next.js 13.4 构建实时应用程序,第一步是创建一个新项目。您可以使用标准 Next.js CLI 命令来初始化您的项目:
npx create-next-app@latest my-real-time-app
将my-real-time-app
替换为您的项目所需的名称。此命令使用默认配置设置一个新的 Next.js 项目。
对于实时功能,您可能需要某些包和依赖项。根据应用程序的具体情况,这些范围可能从 WebSockets 库到 GraphQL 订阅等等。
确保您已查看项目要求并添加了必要的依赖项。
然而,随着 Next.js 13.4 对服务器操作的支持,已经有一个支持服务器端处理的内置设置,这可以帮助实现一些实时功能。
随着 Next.js 13.4 的推出, App Router成为一项重要功能,允许开发人员利用共享布局、嵌套路由、错误处理等。它被设计为与现有的pages
目录结合使用,但它位于一个名为app
的新目录中。
要开始使用 App Router:
在项目的根目录中创建一个app
目录。
在此目录中添加您的路由或组件。
默认情况下, app
目录中的组件是Server Components ,提供最佳性能并允许开发人员轻松采用它们。
这是一个示例结构:
my-real-time-app/ │ ├── app/ # Main directory for App Router components │ ├── _error.js # Custom error page │ ├── _layout.js # Shared layout for the app │ │ │ ├── dashboard/ # Nested route example │ │ ├── index.js # Dashboard main view │ │ └── settings.js # Dashboard settings view │ │ │ ├── index.js # Landing/Home page │ ├── profile.js # User profile page │ ├── login.js # Login page │ └── register.js # Registration page │ ├── public/ # Static assets go here (images, fonts, etc.) │ ├── images/ │ └── favicon.ico │ ├── styles/ # Global styles or variables │ └── global.css │ ├── package.json # Dependencies and scripts ├── next.config.js # Next.js configuration └── README.md # Project documentation
考虑组件如何呈现至关重要。在传统的 SPA(单页应用程序)中,React 在客户端呈现整个应用程序。使用服务器组件,大部分应用程序都在服务器上呈现,从而带来性能优势。这是一个指南:
服务器组件:非常适合应用程序的非交互式部分。这些组件在服务器上呈现并以 HTML 形式发送到客户端。这里的优点是提高了性能,减少了客户端 JavaScript,以及直接获取数据或访问后端资源的能力。
客户端组件:用于交互式 UI 元素。它们在服务器上预渲染,然后在客户端上“水合”以添加交互性。
为了区分这些组件,Next.js 引入了"use client"
指令。该指令指示组件应被视为客户端组件。它应该放在组件文件的顶部,在任何导入之前。
例如,如果您有一个交互式计数器(如提供的代码中所示),您将使用"use client"
指令来指示它是客户端组件。
在构建您的应用程序时,请遵循以下一些准则:
默认情况下使用服务器组件(因为它们位于app
目录中)。
仅当您有特定的用例(例如添加交互性、利用仅限浏览器的 API 或利用依赖于状态或浏览器功能的 React 挂钩)时才选择客户端组件。
注意:按照此结构和设置,您将能够很好地使用 Next.js 13.4 的服务器操作构建高性能的实时应用程序。
当将实时后端功能集成到我们的项目中时,Next.js 13.4 的力量大放异彩。让我们使用my-real-time-app
的相关代码示例来逐步完成这些步骤。
对于我们的my-real-time-app
,服务器操作充当前端和后端之间的主要桥梁,允许高效的数据交易,而不需要单独的 API。
// my-real-time-app/app/actions/index.js export * from './auth-action'; export * from './chat-action';
在my-real-time-app
中,我们利用服务器操作来简化身份验证过程。
// my-real-time-app/app/actions/auth-action.js export const login = async (credentials) => { // Logic for authenticating user with credentials // Return user details or error message }; export const logout = async (userId) => { // Logic for logging out the user // Return success or error message }; export const register = async (userInfo) => { // Logic for registering a new user // Store user in database and return success or error message };
对于聊天功能:
// my-real-time-app/app/actions/chat-action.js export const sendMessage = async (messageDetails) => { // Logic to send a new message // Store message in database and inform other users via WebSocket or similar }; export const receiveMessage = async () => { // Logic to receive a message in real-time // Return the message details }; export const getRecentMessages = async (userId) => { // Logic to fetch recent messages for the user // Retrieve messages from the database };
使用 MongoDB 作为我们的主要数据存储:
// Initialize MongoDB connection const { MongoClient } = require('mongodb'); const client = new MongoClient(process.env.MONGODB_URI); await client.connect(); // Now, use this connection in server actions to interact with the database.
在我们的聊天操作中:
// my-real-time-app/app/actions/chat-action.js export const sendMessage = async (messageDetails) => { const messagesCollection = client.db('chatDB').collection('messages'); await messagesCollection.insertOne(messageDetails); // Inform other users via WebSocket or similar };
为了安全:
// Middleware for validating request data const validateRequest = (req) => { // Validation logic here return isValid; }; export const sendMessage = async (messageDetails) => { if (!validateRequest(messageDetails)) { throw new Error("Invalid request data"); } // Remaining logic... };
在本节中,我们将为my-real-time-app
构建一个直观且响应灵敏的聊天界面。 Next.js 13.4 的服务器组件的集成将实现实时更新,带来流畅的用户体验。
首先,我们来创建主聊天界面:
// my-real-time-app/app/chat-interface.js import { useEffect, useState } from 'react'; import { getRecentMessages } from './actions/chat-action'; export default function ChatInterface() { const [messages, setMessages] = useState([]); useEffect(() => { async function loadMessages() { const recentMessages = await getRecentMessages(); setMessages(recentMessages); } loadMessages(); }, []); return ( <div className="chatBox"> {messages.map(msg => ( <p key={msg.id}>{msg.content}</p> ))} </div> ); }
该组件在加载时获取最近的消息并将其呈现在聊天框中。
现在,我们将使用 WebSocket 的基本示例来设置实时更新:
// my-real-time-app/app/chat-interface.js const [socket, setSocket] = useState(null); useEffect(() => { const ws = new WebSocket("ws://your-backend-url/ws"); ws.onmessage = (event) => { const newMessage = JSON.parse(event.data); setMessages(prevMessages => [...prevMessages, newMessage]); }; setSocket(ws); return () => { ws.close(); }; }, []);
该挂钩建立 WebSocket 连接并在收到新消息时更新消息列表。
为了获得更好的用户体验,让我们通知用户新消息:
// my-real-time-app/app/chat-interface.js useEffect(() => { if (messages.length && "Notification" in window && Notification.permission === "granted") { const lastMessage = messages[messages.length - 1]; new Notification(`New message from ${lastMessage.sender}: ${lastMessage.content}`); } }, [messages]);
每次消息列表更新为新消息时,此效果都会发送浏览器通知。
为了确保流畅的体验:
const HeavyComponent = React.lazy(() => import('./HeavyComponent')); function Chat() { return ( <React.Suspense fallback={<div>Loading...</div>}> <HeavyComponent /> </React.Suspense> ); }
React Server Components
来拆分逻辑:
请记住,从早期的文档来看,服务器组件可用于非交互部分,而客户端组件可以处理交互部分,从而减少发送到客户端的 JavaScript 量。
例如,在我们的聊天中,消息历史记录可以是服务器组件,而需要客户端交互的输入字段和发送按钮可以是客户端组件。
随着我们的实时应用程序的核心组件就位,必须确保它们按预期运行,并且具有高性能、可扩展性和健壮性。本节阐述了为实时系统(例如我们的my-real-time-app
量身定制的各种测试方法。
对于实时应用程序,端到端测试至关重要。让我们用 Cypress 设置一个示例:
// cypress/integration/chat.spec.js describe('Chat functionality', () => { it('should send and receive messages in real-time', () => { cy.visit('/chat'); cy.get('[data-cy=messageInput]').type('Hello, World!'); cy.get('[data-cy=sendButton]').click(); cy.contains('Hello, World!').should('exist'); }); });
这将有助于理解系统在大量用户或消息下的行为方式:
# artillery-config.yml config: target: '//my-real-time-app.com' phases: - duration: 300 arrivalRate: 20 scenarios: - flow: - emit: channel: 'chat' payload: message: 'Hello, World!'
$ artillery run artillery-config.yml
Node.js 提供用于分析的内置工具,并且--inspect
标志可以与 Next.js 开发服务器一起使用来启用 Node.js 检查器。通过使用 Chrome 的 DevTools,人们可以深入了解性能瓶颈。
对于客户端,Chrome DevTools 中的Performance
选项卡等工具可以帮助识别渲染瓶颈。尤其是在实时更新的情况下,确保不会发生不必要的渲染。
实时应用程序通常涉及保持客户端状态与服务器同步。 SWR 或 React Query 等库通过提供自动重新获取、缓存和实时同步等功能来帮助简化这一过程。
SWR 示例:
// my-real-time-app/app/chat-interface.js import useSWR from 'swr'; function ChatInterface() { const { data: messages } = useSWR('/api/messages', fetcher); // ... rest of the component }
对于后端可扩展性,尤其是 WebSocket,请考虑使用 Redis 等解决方案来管理服务器多个实例的状态。这样,如果一个服务器实例收到一条消息,它可以将其广播到连接到其他服务器实例的客户端。
确保您的数据库查询(尤其是那些在实时应用程序中频繁运行的查询)得到优化。对重要列建立索引,并考虑对经常访问的数据使用数据库缓存解决方案。
注意:测试实时应用程序需要结合标准软件测试技术和一些专门针对实时系统的挑战和特性而定制的技术。确保对my-real-time-app
严格的测试制度,无论用户流量或数据流的规模如何,我们都可以保证流畅且响应迅速的用户体验。
随着我们的实时应用程序的基础架构稳固到位,我们的注意力现在转向完善其功能和性能。以下是一些增强用户体验和优化my-real-time-app
策略:
当收件人阅读了用户的消息后,向用户提供视觉反馈。这增强了实时聊天的交互性。
// my-real-time-app/app/components/Message.js function Message({ content, status }) { return ( <div> <p>{content}</p> {status === 'read' && <span>✓ Read</span>} </div> ); }
当用户在线时,在用户名或头像旁边显示一个指示符。
// my-real-time-app/app/components/UserStatus.js function UserStatus({ isOnline }) { return ( <div> {isOnline ? <span className="online-indicator"></span> : <span className="offline-indicator"></span>} </div> ); }
在可行的情况下批量进行服务器端更新,以减少发送到客户端的消息数量。
对于更新频率较高的应用程序,可以考虑压缩 WebSocket 消息以减少传输的数据量并提高速度。
// Example: Setting up compression with a WebSocket server const WebSocket = require('ws'); const wss = new WebSocket.Server({ perMessageDeflate: { zlibDeflateOptions: { // Add compression options here } } });
如果您注意到客户端的快速连续更新,请考虑消除这些更新,将它们合并为更少、更有意义的更新。
对于应用程序中数据完整性至关重要的关键部分,请考虑采用事件溯源模式。这可确保应用程序状态的每次更改都被捕获为一个事件,从而实现事件的可靠恢复和重放。
确保如果由于网络问题导致消息发送失败或更新未完成,则有重试机制。
// Example: Simple retry logic with fetch let retries = 3; function fetchData(url) { fetch(url) .then(response => response.json()) .catch(error => { if (retries > 0) { retries--; fetchData(url); } else { console.error('Failed to fetch data after 3 retries'); } }); }
定期备份数据并确保您有清晰的计划和流程来在发生故障时恢复数据。使用数据库复制或 Cassandra 等分布式数据库来实现容错。
注: my-real-time-app
的持续成功不仅取决于其核心功能,还取决于确保流畅的用户体验的细微增强和持续优化。通过结合上面列出的策略,我们准备为用户提供可靠且令人愉悦的卓越聊天体验。
我们的my-real-time-app
之旅从 Next.js 13.4 的初始设置开始,一直到使用服务器操作构建后端、设计无缝的前端体验,并确保实时功能经过测试和优化。我们深入研究了服务器和客户端组件的细微差别,确保服务器端渲染和客户端交互之间的有效平衡。
Next.js 13.4 中服务器操作的引入彻底改变了我们处理实时应用程序的方法。它使我们能够构建一个高度交互式的聊天应用程序,利用服务器和客户端渲染的优势。这不仅优化了性能,而且还促进了无缝用户交互,而不会影响安全性或效率。
虽然my-real-time-app
已经取得了长足的进步,但未来增强的潜力仍然巨大:
当您踏上实时应用程序之旅并深入了解 Next.js 的功能和复杂性时,这里有一个精选的资源列表,可以为您提供指导、启发和进一步教育:
首先,非常感谢您与我一起穿越Next.js世界的这个错综复杂的迷宫。如果您已经做到了这一步,那么恭喜您!如果你浏览了某些部分,我不会责怪你——有时候我想跳过写它们!
从很多方面来说,构建实时应用程序就像坐过山车一样。有时候,我感觉自己像个编码天才,而另一些时候,我质疑每一个导致我成为开发人员的人生选择。
你是否有过这样的经历:你花了几个小时调试一个问题,却发现你漏掉了一个分号?或者当您不小心删除了代码的重要部分并希望生活有一个 Ctrl + Z 时?哦,编程的乐趣!
但事情是这样的:在所有的捂脸和偶尔的拉扯头发的过程中,看到你的创作实时变为现实,有一种难以形容的魔力。当你的代码运行没有错误时,这是一种微小的喜悦火花,当用户喜欢你的应用程序时的满足感,以及当你从头开始构建一些东西时的自豪感。
对于每一位正在阅读本文的初露头角的开发人员:挫折、挫败感以及“为什么这不起作用!?”时刻是我们旅程的重要组成部分。它们并不是你失败的迹象,而是变得更好的垫脚石。
因此,下次当你的代码拒绝配合时,深吸一口气,喝点咖啡(或茶,我不评判,我自己就是matecocido 的粉丝),并记住你并不孤单。
不断突破界限,不断学习,并记住每一行代码,无论它是否有效,都会为您的开发者故事增添一章。
如果你需要一个笑声或一个肩膀来哭泣(当然,实际上是这样),请知道我曾经在那里,做过这些,并且已经足够沮丧,以至于考虑将我的笔记本电脑扔出窗外!
这是为了更多的编码冒险和更少的分号引起的错误!
干杯,祝编码愉快!