news 2026/9/23 19:46:29

Redwood 集成 Supabase Auth 实战:配置、注册、登录、登出与认证状态导航完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redwood 集成 Supabase Auth 实战:配置、注册、登录、登出与认证状态导航完整指南
  • 后端
  • 前端
  • Web框架
  • 开发工具

【免费下载链接】redwood

RedwoodGraphQL

项目地址:https://gitcode.com/gh_mirrors/re/redwood
点击查看免费下载

本篇技术指南以 Redwood 项目中的官方实战手册(docs/versioned_docs/version-6.x/how-to/supabase-auth.md)为核心,完整讲解如何将 Supabase 认证(Auth)接入 Redwood 应用:从一条 CLI 命令完成配置、在管理控制台获取密钥,到亲手实现注册(Sign Up)页、登录(Sign In)页、登出(Sign Out)按钮,以及根据认证状态自动切换的导航链接。读完后,你将掌握 Redwood Auth 抽象层与 Supabase GoTrueJS 客户端之间的协作关系,并能独立为任意 Redwood 页面接入完整的邮箱密码认证闭环。

为什么选择 Supabase Auth

这篇 How-to 本质上可视为 Redwood 官方 GoTrue Auth 实战手册 的 Supabase 移植版。原因很简单:Supabase 的认证服务建立在 Netlify GoTrue 这一开源基础之上——它是一个专门处理用户注册与认证的 API 服务。当你理顺这条技术脉络后,GoTrue 系的实战手册就变成了攀登认证学习曲线的现成阶梯。

对 Redwood 来说,最终使用的是 Redwood Auth 包装层,它提供了一层抽象与统一、简洁的 API 风格。不过,先直接与 Supabase Client 打交道一两个小时(例如跟随 Supabase 的 React Quick Start 走一遍),会对理解 Redwood 包装层所隐藏的细节大有帮助。

前置准备

开始之前,请依次完成以下步骤:

  • 创建 Redwood 应用
  • 注册 Supabase 账户并创建组织与项目
  • 完成 Supabase 的 React Quick Start(先直连 Supabase Client,建立直观感受)
  • 完成 Supabase 的 Redwood Quick Start(观察与纯 React 用法的差异)
  • 启动开发服务器:yarn redwood dev

认证术语梳理

初次接触时,认证领域的名词容易让人头晕。这里帮你理清一条主线:Redwood 的 Supabase 认证客户端包装了 Supabase 的 GoTrueJS 客户端,而后者是 Netlify GoTrueJS 客户端的分支(它与 Netlify Identity 不同);dbAuth 则是 Redwood 完全独立的一种认证方案。本教程暂不涉及 RBAC(基于角色的访问控制)。

安装与初始化:一条命令搞定配置

有了 Supabase Quick Start 的知识储备,回到我们创建好的 Redwood 应用。安装所需包并生成 Redwood Auth 的样板代码,只需一条 CLI 命令:

yarn redwood setup auth supabase

指定supabase作为 provider 后,Redwood 会自动向应用写入必要的 Supabase 配置。在 CLI 源码中可以看到,该命令在 packages/cli/src/commands/setup/auth/auth.js 中注册为setup auth <provider>的子命令,执行时会动态加载@redwoodjs/auth-supabase-setup包(若未安装则自动以与框架一致的版本yarn add -D安装),再调用其 handler 完成文件写入与代码修改。

打开web/src/App.[js/tsx]检查,你应该看到:

import { AuthProvider } from '@redwoodjs/auth' import { createClient } from '@supabase/supabase-js' import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web' import { RedwoodApolloProvider } from '@redwoodjs/web/apollo' import FatalErrorPage from 'src/pages/FatalErrorPage' import Routes from 'src/Routes' import './index.css' const supabaseClient = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY) const App = () => ( <FatalErrorBoundary page={FatalErrorPage}> <RedwoodProvider titleTemplate="%PageTitle | %AppTitle"> <AuthProvider client={supabaseClient} type="supabase"> <RedwoodApolloProvider> <Routes /> </RedwoodApolloProvider> </AuthProvider> </RedwoodProvider> </FatalErrorBoundary> ) export default App

核心动作是第 12 行的<AuthProvider client={supabaseClient} type="supabase">:把用SUPABASE_URLSUPABASE_KEY创建的 Supabase 客户端实例注入到整个组件树中,供所有子组件通过useAuth钩子访问。

在较新版本的 Redwood 中(对应仓库当前的 auth.ts.template),初始化方式稍有演进:setup 会在web/src/auth.ts生成如下代码,把客户端创建与AuthProvider/useAuth的装配封装在createAuth中,页面侧则统一从'src/auth'导入:

import { createClient } from '@supabase/supabase-js' import { createAuth } from '@redwoodjs/auth-supabase-web' const supabaseClient = createClient( process.env.SUPABASE_URL || '', process.env.SUPABASE_KEY || '' ) export const { AuthProvider, useAuth } = createAuth(supabaseClient)

两种写法的本质相同:最终都通过createAuth把 Supabase 客户端包装成 Redwood Auth 统一接口(loginlogoutsignupgetToken等),实现见 packages/auth-providers/supabase/web/src/supabase.ts。

环境变量配置

现在把 Supabase 的 URL、公共 API Key 和 JWT 密钥(SUPABASE_URLSUPABASE_KEYSUPABASE_JWT_SECRET)写入.env文件。这些信息可在 Supabase 管理控制台获取:

  • Project URL 与 API key(anon / public):新建项目后控制台展示的项目 URL 和 API Key;若页面刷新错过,可在「Connecting to your new project」区域找到。
  • JWT Secret:位于左侧导航底部的「Project Settings(齿轮图标)」→「API」→ 向下滚动至「JWT Settings」下的「JWT Secret」。

.env示例如下:

# .env (in your root project directory) SUPABASE_URL=https://replacewithyoursupabaseurl.supabase.co SUPABASE_KEY=eyJhb_replace_VCJ9.eyJy_with_your_wfQ.0Abb_anon_key_teLJs SUPABASE_JWT_SECRET=eyJh_replace_CJ9.eyJy_with_your_NTQwOTB9.MGNZN_JWT_secret_JgErqxj4

此外,还要在redwood.toml中把SUPABASE_URLSUPABASE_KEY加入 web 侧可见的环境变量白名单(JWT Secret 仅用于 api 侧校验,不需要暴露给浏览器):

[web] # ... includeEnvironmentVariables = ["SUPABASE_URL", "SUPABASE_KEY"]

至此配置基本完成。还有一个可选项:若注册流程不需要邮箱确认,可在 Supabase 管理控制台Authentication > Settings中关闭「Enable email confirmations」。

源码视角:Redwood Auth 如何包装 Supabase

理解底层实现有助于排查问题。在 supabase.ts 中,createAuthImplementation返回的 auth 实现把 Supabase SDK 的多种登录方式统一收敛到login方法,通过credentials.authMethod分发到不同底层调用:

  • passwordsupabaseClient.auth.signInWithPassword(credentials):邮箱/手机号 + 密码登录
  • oauthsupabaseClient.auth.signInWithOAuth(credentials):GitHub、Google 等第三方登录
  • otpsupabaseClient.auth.signInWithOtp(credentials):魔法链接或一次性密码(支持邮箱与手机)
  • ssosupabaseClient.auth.signInWithSSO(credentials):企业级单点登录(实验性)
  • id_tokensupabaseClient.auth.signInWithIdToken(credentials):用受支持厂商签发的 ID Token 登录(实验性)
  • 其他情况返回携带AuthError('Unsupported authentication method')的结果

signup直接调用supabaseClient.auth.signUp(credentials),且若返回结果包含session(即服务端开启了自动确认),会同步写入auth-providercookie;logout调用supabaseClient.auth.signOut()getTokengetUserMetadata都基于supabaseClient.auth.getSession()返回会话与用户元数据;restoreAuthState则在 OAuth 或魔法链接回调重定向回站点时,通过refreshSession()恢复会话并清理 URL 中的access_tokenrefresh_token等认证参数。

setup 同时会生成 api 侧的 api/src/lib/auth.ts:getCurrentUser解析并解码 JWT 返回用户信息与可选角色,requireAuth/hasRole/isAuthenticated供 service 层做登录与角色校验(未登录抛AuthenticationError,角色不符抛ForbiddenError)。注意getCurrentUser的返回值会暴露给 web 端,务必只返回安全的字段。

注册页面(Sign Up)

先生成注册页面:

yarn redwood generate page signup

这会向路由文件添加一条注册路由,并创建SignupPage组件。在web/src/pages/SignupPage/SignupPage.[js/tsx]中导入 Redwood Form 组件,做一个最基础的表单:

import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' const SignupPage = () => { return ( <> <h1>Sign Up</h1> <Form> <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </> ) } export default SignupPage

想要更精致的表单样式,可参考 Redwood Form 文档与教程的表单章节。这里聚焦功能本身——为表单添加onSubmit

// ... const SignupPage = () => { const onSubmit = (data) => { // do something here } return ( <> <h1>Sign Up</h1> <Form onSubmit={onSubmit}> <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </> ) } //...

要做的事是——注册。为此需要与<AuthProvider />及其内部包装的 Supabase GoTrue-JS 客户端通信,这就要用到useAuth钩子。导入useAuth并从中解构出client(即我们传入的 Supabase GoTrue-JS 实例,可调用其全部方法):

import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' const SignupPage = () => { const { client } = useAuth() const onSubmit = (data) => { // do something here } return ( <> <h1>Sign Up</h1> <Form onSubmit={onSubmit}> <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </> ) } export default SignupPage

onSubmit中通过client.auth.signUp()传入表单捕获的emailpassword来创建新用户:

import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' const SignupPage = () => { const { client } = useAuth() const onSubmit = async (data) => { try { const response = await client.auth.signUp({ email: data.email, password: data.password }) console.log('response: ', response) } catch(error) { console.log('error: ', error) } } return ( <> <h1>Sign Up</h1> <Form onSubmit={onSubmit}> <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </> ) } export default SignupPage

目前注册可用,但仅把响应打印到控制台并不实用。接下来用React.useState()管理错误状态,条件渲染错误消息,并在每次提交前用setError(null)重置:

import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' const SignupPage = () => { const { client } = useAuth() const [error, setError] = React.useState(null) const onSubmit = async (data) => { setError(null) try { const response = await client.auth.signUp({ email: data.email, password: data.password }) console.log('response: ', response) response?.error?.message && setError(response.error.message) } catch(error) { setError(error.message) } } return ( <> <h1>Sign Up</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </> ) } export default SignupPage

错误有两种返回方式:

  1. promise 正常兑现(fulfillment)时,错误位于返回对象内的error属性中;
  2. promise 被拒绝(rejection)时,错误通过 promise 本身抛出(由catch块捕获处理)。

再处理成功提交的情况:若关闭了邮箱确认,注册成功即等同于登录成功,此时应把用户重定向回应用首页。先生成首页(若还没有):

yarn redwood generate page home /

然后从 Redwood Router 导入routesnavigate,在注册成功后跳转:

import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' import { routes, navigate } from '@redwoodjs/router' const SignupPage = () => { const { client } = useAuth() const [error, setError] = React.useState(null) const onSubmit = async (data) => { setError(null) try { const response = await client.auth.signUp({ email: data.email, password: data.password }) response?.error?.message ? setError(response.error.message) : navigate(routes.home()) } catch(error) { setError(error.message) } } return ( <> <h1>Sign Up</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign Up</Submit> </Form> </> ) } export default SignupPage

注册页完成:注册表单、错误展示、成功后跳转首页一应俱全。

登录页面(Sign In)

先生成登录页:

yarn redwood generate page signin

添加含emailpassword字段的基础表单、错误提示与空的onSubmit

import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' const SigninPage = () => { const [error, setError] = React.useState(null) const onSubmit = (data) => { // do sign in here } return ( <> <h1>Sign In</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign In</Submit> </Form> </> ) } export default SigninPage

导入useAuth并解构出logIn

import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' const SigninPage = () => { const { logIn } = useAuth() const [error, setError] = React.useState(null) const onSubmit = (data) => { setError(null) // do sign in here } return ( <> <h1>Sign In</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign In</Submit> </Form> </> ) } export default SigninPage

与注册不同,这次直接调用 Redwood Auth 的logIn函数(而非经由client),向其传入包含 email 与 password 的对象:

import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' const SigninPage = () => { const { logIn } = useAuth() const [error, setError] = React.useState(null) const onSubmit = async (data) => { setError(null) try { const response = await logIn({ email: data.email, password: data.password }) // do something } catch(error) { setError(error.message) } } return ( <> <h1>Sign In</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign In</Submit> </Form> </> ) } export default SigninPage

登录成功后重定向回首页。从@redwoodjs/router导入navigateroutes,在await logIn之后使用:

import { Form, TextField, PasswordField, Submit } from '@redwoodjs/forms' import { useAuth } from '@redwoodjs/auth' import { navigate, routes } from '@redwoodjs/router' const SigninPage = () => { const { logIn } = useAuth() const [error, setError] = React.useState(null) const onSubmit = async (data) => { setError(null) try { const response = await logIn({ email: data.email, password: data.password }) response?.error?.message ? setError(response.error.message) : navigate(routes.home()) } catch(error) { setError(error.message) } } return ( <> <h1>Sign In</h1> <Form onSubmit={onSubmit}> {error && <p>{error}</p>} <TextField name="email" placeholder="email" /> <PasswordField name="password" placeholder="password" /> <Submit>Sign In</Submit> </Form> </> ) } export default SigninPage

手册剩余部分与 Netlify GoTrue Auth 版本一致,这正体现了 Redwood Auth 包装层的乐趣:特定认证方案相关的代码只集中在上面几个位置,其余部分可统一使用通用的 Redwood Auth 函数。

在 Redwood 中,logIn是统一入口:除密码登录外,它同样支持 OAuth、OTP、ID Token 与 SSO 等方式,只需传入不同的authMethod(对应源码中的credentials.authMethod分发逻辑)。例如 GitHub OAuth 登录可写作logIn({ authMethod: 'oauth', provider: 'github' })

登出按钮(Sign Out)

登出是最容易实现的——只需调用useAuthlogOut方法。先生成组件:

yarn redwood generate component signoutBtn

web/src/components/SignoutBtn/SignoutBtn.js中渲染按钮并添加点击处理器:

const SignoutBtn = () => { const onClick = () => { // do sign out here. } return <button onClick={() => onClick()}>Sign Out</button> } export default SignoutBtn

导入useAuth,解构logOut并在onClick中调用:

import { useAuth } from '@redwoodjs/auth' const SignoutBtn = () => { const { logOut } = useAuth() const onClick = () => { logOut() } return <button onClick={() => onClick()}>Sign Out</button> } export default SignoutBtn

这已经能工作,但用户可能正处在受保护的页面,登出后应将其导航离开当前页:

import { useAuth } from '@redwoodjs/auth' import { navigate, routes } from '@redwoodjs/router' const SignoutBtn = () => { const { logOut } = useAuth() const onClick = async () => { await logOut() navigate(routes.home()) } return <button onClick={() => onClick()}>Sign Out</button> } export default SignoutBtn

在底层,logOut调用supabaseClient.auth.signOut():清除浏览器会话与 localStorage,并触发SIGNED_OUT事件(见 supabase.ts 中logout的实现)。目前这个按钮还没有渲染到任何地方——下一步就把它接进根据认证状态切换的导航。

认证状态导航(Auth Links)

实现一段根据用户认证状态条件渲染的导航:

  • 用户未登录时:显示Sign UpSign In
  • 用户已登录时:显示Log Out

先生成导航组件:

yarn redwood generate component navigation

web/src/components/Navigation/Navigation.js中导入 Router 的Link组件与routes对象,以及用于订阅认证状态的useAuth

import { Link, routes } from '@redwoodjs/router' import { useAuth } from '@redwoodjs/auth' const Navigation = () => { return <nav></nav> } export default Navigation

useAuth解构isAuthenticated并在条件语句中使用:

import { Link, routes } from '@redwoodjs/router' import { useAuth } from '@redwoodjs/auth' const Navigation = () => { const { isAuthenticated } = useAuth() return ( <nav> {isAuthenticated ? ( // signed in - show the Sign Out button ) : ( // signed out - show the Sign Up and Sign In links )} </nav> ) } export default Navigation

Redwood Auth 基于 React 的 Context API 管理与广播认证状态,因此可以放心:只要组件位于<AuthProvider />之下,isAuthenticated就会始终是最新的——即使认证状态在组件树中的其他组件里发生变化,React 也会自动重渲染出正确的组件。

把登出按钮以及登录、注册链接放进条件块的对应分支:

import { Link, routes } from '@redwoodjs/router' import { useAuth } from '@redwoodjs/auth' import SignoutBtn from 'src/components/SignoutBtn/SignoutBtn' const Navigation = () => { const { isAuthenticated } = useAuth() return ( <nav> {isAuthenticated ? ( <SignoutBtn /> ) : ( <> <Link to={routes.signup()}>Sign Up</Link> <Link to={routes.signin()}>Sign In</Link> </> )} </nav> ) } export default Navigation

导航组件已经可用,还需要把它渲染到页面中。先生成一个名为 GlobalLayout 的布局:

yarn redwood generate layout global

在生成的web/src/layouts/GlobalLayout/GlobalLayout中导入并渲染导航组件:

import Navigation from 'src/components/Navigation/Navigation' const GlobalLayout = ({ children }) => { return ( <> <header> <Navigation /> </header> <main>{children}</main> </> ) } export default GlobalLayout

最后,用 Router 的Set组件把每个页面高效地包裹进GlobalLayout,更新web/src/Routes.[js/tsx]

import { Router, Route, Set } from '@redwoodjs/router' import GlobalLayout from 'src/layouts/GlobalLayout/GlobalLayout' const Routes = () => { return ( <Router> <Set wrap={GlobalLayout}> <Route path="/" page={HomePage} name="home" /> <Route path="/signup" page={SignUpPage} name="signup" /> <Route path="/signin" page={SignInPage} name="signin" /> </Set> <Route notfound page={NotFoundPage} /> </Router> ) } export default Routes

现在导航会根据认证状态渲染正确的链接与按钮:登录后看到Sign Out按钮,登出后看到Sign UpSign In链接。

测试与验证:认证实现的正确性保障

仓库为 Supabase 认证实现提供了完整的单元测试,见 packages/auth-providers/supabase/web/src/tests/supabase.test.tsx。这些用例覆盖了本手册涉及的全部核心场景,可作为自测清单:

  • 登录前isAuthenticatedfalse
  • 邮箱 + 密码注册(含附加元数据、重定向 URL 两种变体)后认证状态为true,且currentUser.emailuserMetadata正确;
  • 密码登录后isAuthenticatedtrue,登出后恢复为false
  • hasRole支持从 Supabase 的app_metadata.roles读取角色,也支持通过useHasRole/useCurrentUser自定义钩子覆写默认行为;
  • OAuth、OTP、ID Token、SSO 四种logIn方式均能正确建立认证状态。

此外,supabase.middleware.test.tsx 验证了流式 SSR 场景下中间件认证(middleware auth)的行为。

收尾与延伸

至此,我们已经完成了:用 Redwood Auth 配置 Supabase GoTrue Auth,创建注册页、登录页与登出按钮,并把认证链接接入布局。配置、注册、登录、登出、状态化导航的完整闭环已经跑通。

后续深入时值得留意的特性(未来的实战手册可以逐个深挖):

  • 认证状态变更监听:可通过 Supabase 客户端的onAuthStateChange事件监听SIGNED_INSIGNED_OUTTOKEN_REFRESHEDUSER_UPDATEDPASSWORD_RECOVERY等事件(需在useEffect中调用并记得退订)。
  • 更多认证方式
    • Passwordless(输入邮箱获取魔法确认链接,对应authMethod: 'otp'
    • 第三方登录(GitHub、Google 等,对应authMethod: 'oauth'
    • 手机一次性密码(OTP)
    • 使用刷新令牌(refresh token)登录——JWT 是认证实现的关键组成部分
  • 受保护页面:在 api 侧通过requireAuth保护 service,未认证用户将收到AuthenticationError

相关参考资源:

  • Redwood Supabase Auth 安装与设置文档
  • 版本 6.x 的 Supabase 认证专章
  • Redwood Supabase Auth 客户端实现:packages/auth-providers/supabase/web/src/supabase.ts
  • Supabase 认证客户端使用与配置说明:packages/auth-providers/supabase/web/README.md
  • setup 命令注册源码:packages/cli/src/commands/setup/auth/auth.js
  • 后端
  • 前端
  • Web框架
  • 开发工具

【免费下载链接】redwood

RedwoodGraphQL

项目地址:https://gitcode.com/gh_mirrors/re/redwood
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 19:45:18

Video2X:视频超分辨率与补帧,把 360P 老片免费拉到 4K

Video2X&#xff1a;视频超分辨率与补帧&#xff0c;把 360P 老片免费拉到 4K 【免费下载链接】video2x A machine learning-based video super resolution and frame interpolation framework. Est. Hack the Valley II, 2018. 项目地址: https://gitcode.com/GitHub_Trendi…

作者头像 李华
网站建设 2026/9/23 19:42:52

指数与对数:从逆向思维到运算规律,一次讲透核心概念与应用

我第一次在课堂上和学生们聊对数&#xff0c;总会有人问一个让教室安静三秒钟的问题&#xff1a;"老师&#xff0c;指数我们已经学会了&#xff0c;为什么还要专门发明一个log符号&#xff0c;去问2的几次方等于8这种问题&#xff1f;"这个问题其实问得非常好。它背后…

作者头像 李华
网站建设 2026/9/23 19:36:08

Skywalking与SpringBoot集成实战指南

1. Skywalking与SpringBoot集成全攻略 作为一名长期奋战在微服务监控一线的开发者&#xff0c;我深知分布式系统链路追踪的重要性。今天我将分享如何将Skywalking这一强大工具与SpringBoot项目深度集成&#xff0c;从基础配置到高级功能实现&#xff0c;带你全面掌握这套监控方…

作者头像 李华
网站建设 2026/9/23 19:33:01

sgcWebSockets实战指南:Delphi实时通信从安装到wss压测

简介&#xff1a;sgcWebSockets-Enterprise-V2023.5-FS是一套面向企业环境的WebSocket服务器软件包&#xff0c;适用于在线游戏、实时分析仪表板、金融交易应用、聊天服务等需要高并发双向低延迟通信的场景&#xff0c;帮助开发者在自有系统中快速构建稳定可靠的实时消息通道。…

作者头像 李华
网站建设 2026/9/23 19:22:55

中小冷库的隐形分水岭:数字化和电力

一、价格相差五六倍的冷库&#xff0c;差在哪郑州的冷库市场&#xff0c;正在经历一轮明显的价格分化。一边是远郊的个人冷库&#xff0c;租金已经低到每天每平方米0.34到0.51元&#xff1b;另一边&#xff0c;正规冷链园区仍在每平方米2.2到3元的区间。同样是一间冷库&#xf…

作者头像 李华