- 后端
- 前端
- Web框架
- 开发工具
【免费下载链接】redwood
RedwoodGraphQL
Redwood 是一个基于 React 的全栈 Web 应用框架,它把 GraphQL、Prisma、Jest、Storybook、Vite、Babel、TypeScript 等现代前端生态中的成熟工具预先集成并配置完毕,让开发者可以专注业务本身,用最小的心智负担搭建"前端 UI + 后端服务 + 数据库"的完整应用。本文以 Redwood 官方教程第 0 章(What is Redwood?)为骨架,结合当前仓库源码,系统拆解 Redwood 的前后端架构、路由与鉴权、GraphQL 数据流、Cells 组件模式、安全机制、生成器与测试工具链,帮助你理解"一个命令启动全栈应用"背后的设计哲学与实现细节。
核心思想:React 加上让你更省心的那一堆东西
Redwood 的本质可以用一句话概括:Redwood 就是 React,外加一堆让开发者日子更好过的预置能力。这些能力包括:
- GraphQL:前端与后端之间的数据胶水;
- Prisma:类型安全的数据库访问层与迁移工具;
- Jest:前后端通用的测试框架;
- Storybook:独立构建与预览 UI 组件的"前端工作坊";
- Vite:前端打包与开发服务器;
- Babel:后端代码的编译工具;
- TypeScript:全框架严格类型支持。
所谓"全栈 Web 应用",指的就是经典的 Web 应用形态:浏览器中可见的 UI(前端),由服务器与数据库支撑(后端)。在 React Server Components 出现之前,React 本身对服务器和数据库一无所知——把数据送进应用要么靠手动fetch(),要么靠构建步骤把数据"预烤"进组件。Redwood 的核心设计原则之一,就是让"从后端取数据"这件事尽可能简单:围绕它建立约定,使得组件展示数据只需在组件里加几行代码,并且自动处理加载中、出错、空数据三种状态。
一个 Redwood 应用其实是两个应用
一个 Redwood 应用在技术上是 monorepo,包含两个顶层目录:
web:前端(React 部分);api:后端(服务器、数据库访问、与第三方系统通信)。
启动两者只需要一条命令:
yarn redwood dev仓库中的 测试项目 fixture 就是这种标准结构的真实样例:web/src下是页面、布局、组件,api/src下是服务(services)、SDL、指令(directives)与lib,两者共享根目录的redwood.toml配置。
前端:路由、布局与页面
当浏览器打开 Web 应用时,React 负责初始化应用并监听 history 变化以展示新内容。Redwood 提供了一套自定义的声明式 Router,让你直接指定 URL 与对应页面(页面本质就是一个 React 组件)。一个典型的 routes 文件如下:
import { Set, Router, Route } from '@redwoodjs/router' import ApplicationLayout from 'src/layouts/ApplicationLayout' import { useAuth } from './auth' const Routes = () => { return ( <Router useAuth={useAuth}> <Set wrap={ApplicationLayout}> <Route path="/login" page={LoginPage} name="login" /> <Route path="/signup" page={SignupPage} name="signup" /> <Private unauthenticated="login"> <Route path="/dashboard" page={DashboardPage} name="dashboard" /> <Route path="/products/{sku}" page={ProductsPage} name="products" /> </Private> </Set> <Route path="/" page={HomePage} name="home" /> <Route notfound page={NotFoundPage} /> </Router> ) }即使从未见过 Redwood 的路由,也能大致猜到它的语义:Route把 URL 路径映射到页面组件,Set wrap={...}让一组路由共享同一个布局(layout,也是普通 React 组件),Private标记需要登录才能访问的路由,notfound指定 404 页面。路径中的{sku}是动态参数,会在页面组件的 props 中暴露。
<Private>路由守卫的实现在 AuthenticatedRoute.tsx:它通过useAuth()获取isAuthenticated与hasRole,并支持可选的rolesprop 做角色过滤;未授权时重定向到unauthenticated指定的路由,并自动携带?redirectTo=参数以便登录后跳回原页面。
Prerender:Redwood 版的静态站点生成(SSG)
如果页面内容可以完全静态(例如面向公众的营销页),只需给路由加上prerender属性,构建时该页面就会被完整渲染成 HTML(无论内部组件嵌套多深)。这个页面加载极快,同时仍包含激活 React 所需的 JS;React 加载后会执行水合(rehydration)使页面恢复交互。
带 URL 变量的页面同样可以预渲染——例如上面的/products/{sku},Redwood 会遍历所有可用的 sku 并逐个生成页面,详见 prerender 文档。这就是 Redwood 的 SSG 能力。
认证与授权
<Private>路由限制了未登录用户的访问,但用户如何完成认证?Redwood 内置了多个主流第三方认证服务商的集成(Auth0、Supabase、Clerk 等),也支持自建认证(dbAuth,包含登录、注册、重置密码页面,甚至可选 TouchID/FaceID 等生物识别)以及完全自定义的认证方案。
认证之后,如何控制"某用户能做什么、不能做什么"?Redwood 提供了**基于角色的访问控制(RBAC)**辅助能力,可同时作用于前后端。
GraphQL 与 Cells:数据获取的声明式范式
Redwood 用 GraphQL 作为前后端之间的胶水:任何时候需要服务器/数据库的数据,都通过 GraphQL 获取。前端使用 Apollo Client,它提供useQuery()与useMutation()钩子分别取数与写数。但 Redwood 的集成远不止于此。
Cells:自包含的"超级组件"
Redwood 独创了Cells模式:一个组件不仅负责自身展示,还负责自身的数据获取——加载中、出错、空数据的 UI 全部自包含。Cell 仍然是一个 React 组件(也被称为单文件组件),只需遵守几条约定:
- 文件名以
Cell结尾; - 至少导出两个具名导出:一个名为
QUERY(gql 查询字符串),一个名为Success; - 可选导出
Loading、Failure、Empty等组件,用途不言自明。
Cell 的渲染生命周期是:
- 显示
Loading组件; - 用导出的
QUERY触发一次useQuery(); - 数据成功返回后,渲染
Success组件,其中一个 prop 就是useQuery()返回的数据; - 出错则渲染
Failure(接收error与errorCodeprop);查询返回null或空数组则渲染Empty;若未导出Failure/Empty,则渲染Success,由你在内部用条件代码处理异常与空态。
原文档中的 testimonials 例子:
export const QUERY = gql` query GetTestimonials { testimonals { id author quote } } ` export const Loading = () => <div>Loading...</div> export const Failure = ({ error }) => <div>An error occured! {error.message}</div> export const Success = ({ testimonials }) => { return ( <ul> {testimonials.map((test) => { <li key={test.id}>{test.quote} — {test.author}</li> })} </ul> ) }(此例未导出Empty,因此数据为空时页面该区域什么都不渲染。)
从源码看,Cell 的实现位于 createCell.tsx。其工厂函数createCell会依据运行环境在 Suspense 版本与非 Suspense 版本之间切换;非 Suspense 版本的createNonSuspendingCell接收QUERY、beforeQuery、afterQuery、isEmpty以及各生命周期组件,内部通过useQuery(query, options)驱动状态机,按error → data → loading的顺序决定渲染Failure、Empty/Success或Loading。值得注意的实现细节:
beforeQuery默认把组件 props 当作 GraphQL variables,并设置fetchPolicy: 'cache-and-network'与notifyOnNetworkStatusChange: true;isEmpty默认使用isDataEmpty判断空数据;- 预渲染场景(
__REDWOOD__PRERENDERING)下会从 Cell 缓存上下文读取查询结果,支持静态生成。
Apollo Cache:缓存与数据同步
Apollo Client 会智能缓存上述QUERY的结果。用户离开首页再返回时,Success会立即从缓存渲染;与此同时,查询会再次发往服务器以检查数据是否变化,若有变化则合并进缓存并触发组件重渲染。于是你既得到了"缓存数据秒开"的性能收益,又不会一直看到过期数据。你还可以直接操作缓存增删条目,甚至把它用作状态管理。
Cells 与预渲染的协同
预渲染同样适用于 Cells:构建时 Redwood 会启动 GraphQL 服务器并像真实用户一样发起请求,把渲染结果输出为纯 HTML,浏览器即可秒开。
后端:Prisma 与 Services
前端熟悉了,数据从哪来?GraphQL 本身并不懂数据库,它靠 resolver 定义返回结构。Redwood 用 Prisma 承担"数据库对话"工作,提供自动化迁移、类型安全与 IDE 自动补全。
应用中的schema.prisma文件反映当前数据库结构:
datasource db { provider = "postgresql" url = env("DATABASE_URL") } generator client { provider = "prisma-client-js" binaryTargets = "native" } model Testimonial { id Int @id @default(autoincrement()) author String @unique quote String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt }Prisma 的命令行工具会把该文件的变更转换为 SQL DDL 命令并执行,从而更新数据库结构。仓库测试项目中的 schema.prisma 展示了实际样例:provider可配置为sqlite或postgresql,模型(model)定义字段类型、主键、唯一约束、默认值与关系,例如Post.authorId+@relation(fields: [authorId], references: [id])表达外键关联。
Service:GraphQL resolver 的抽象
Redwood 把 GraphQL resolver 的概念抽象为service(服务)。通常一个 GraphQL 查询/变更对应一个 service 函数,函数名与查询名一致,内部用 Prisma 查库:
import { db } from 'src/lib/db' export const testimonials = () => { return db.testimonial.findMany() }GraphQL 如何知道去这里找testimonials的 resolver?答案是SDL 文件,它包含了从 GraphQL 到 service 世界的映射:
export const schema = gql` type Testimonial { id: Int! author: String! quote: String! createdAt: DateTime! updatedAt: DateTime! } type Query { testimonials: [Testimonial!] @skipAuth } `规则很简单:type Query中定义的每个字段,都必须有同名的 service 函数(testimonials→testimonials())。
安全:Secure by Default 与指令机制
Redwood 默认安全:未认证用户的 GraphQL 请求一律不被执行。你可以对特定查询/变更放行,但必须逐个手动开启。看一个更完整的 Testimonials SDL:
export const schema = gql` type Testimonial { id: Int! author: String! quote: String! createdAt: DateTime! updatedAt: DateTime! } type CreateTestimonialInput { author: String! quote: String! } type Query { testimonials: [Testimonial!] @skipAuth } type Mutation { createTestimonal($input: CreateTestimonialInput!): Testimonial! @requireAuth deleteTestimonal($id: Int!): Testimonial! @requireAuth } `testimonials查询被标记为@skipAuth(GraphQL 指令),表示不限制为已登录用户;而关键写操作createTestimonial与deleteTestimonial标记为@requireAuth,只能由登录用户调用。
指令的底层实现在 makeDirectives.ts:createValidatorDirective(schema, directiveFunc)与createTransformerDirective(schema, directiveFunc)分别创建"校验型"与"转换型"指令,它们从 SDL 定义中解析指令名并绑定到onResolvedValue回调。仓库模板中的 requireAuth.ts 与 skipAuth.ts 是开箱即用的范例:@requireAuth支持可选的roles: [String]参数并把校验委托给src/lib/auth中的requireAuth;@skipAuth的校验函数则直接返回(放行)。
细粒度授权:context 与角色
@requireAuth/@skipAuth是围绕整个 GraphQL 查询的"大门",进门之后还能基于"用户到底是谁"做更细的控制。登录用户在任何 service 中都能通过全局的context对象拿到:
import { db } from 'src/lib/db' import { AuthenticationError } from '@redwoodjs/graphql-server' export const createTestimonial = ({ data }) => { if (context.currentUser.roles.includes('admin')) { return db.testimonial.create({ data }) } else { throw new AuthenticationError("You are not authorized to create testimonials") } }Redwood 后端的 GraphQL 服务器由 GraphQL Yoga 驱动,因此天然继承其安全与性能能力:限速与深度限制、日志、指令等。
可访问性:为屏幕阅读器护航
Redwood 提供了几个辅助屏幕阅读器导航应用的组件:<RouteAnnouncement>让屏幕阅读器朗读浏览器中不可见的内容;<RouteFocus>引导阅读器跳过页面顶部的冗长导航直达正文。
Generators:被低估的 CLI 生产力
Redwood 极为重视命令行工具,其中最有威力的是generators(生成器):用于创建文件、搭建集成、执行脚本、启动开发服务器等。生成布局、页面和 Cells 能节省大量时间——Redwood 文件本身样板代码不多,但生成器会连基础功能的测试一起建好。
生成器还提供开发工具的便捷入口:GraphiQL(对服务器执行 GraphQL 查询)与Prisma Studio(数据库的完整 GUI)。Redwood 的setup命令可接入 Tailwind、Mantine 等 UI 库,并可方便地开关实验性特性。此外还有一个交互式控制台,例如直接执行 Prisma 查询取数,方便你确认查询返回的数据是否符合预期,而无需在代码里到处塞console.log()再刷新浏览器。
仓库中 cli/src/commands/generate 下可以看到page、cell、layout、directive、service等生成器的实现,它们通过 yargs 暴露为yarn redwood generate <type>系列命令。
Jest:前后端统一的测试体系
Redwood 搭配 Jest 作为测试框架,并且大多数生成的文件都会自动附带预填了基础断言的测试文件。Redwood 提供了多个 Jest 辅助工具与匹配器,用于 mock GraphQL 请求、数据库数据、登录用户等:
- Scenarios:接收简单的 JSON 对象,预先用这些数据填充数据库,使其处于已知状态供测试断言,详见 testing 文档;
- Mock Service Worker:模拟 API(含 GraphQL)的响应,详见 testing 文档;
mockCurrentUser():在web或api侧模拟当前登录用户,无需真正经过认证提供方。
Jest 测试可以同时写在应用的前端与后端。
Storybook:在隔离环境中构建 UI
Jest 负责测试代码逻辑,Storybook 则用来编目与测试 UI——Redwood 称其为"在隔离环境中构建 UI 组件的前端工作坊"。运行yarn redwood storybook即可启动。Redwood 为 Storybook 增加了数据 mock 能力:可以展示通常需要 GraphQL 数据才能填充的组件,而无需启动服务器。Storybook 严格属于前端代码库的范畴。
Vite、Babel 与 TypeScript:配置已替你完成
注意上述所有能力中,你几乎不需要说"然后我需要为这个包写配置"——Redwood 都已替你完成,并会在每个新版本中持续跟进。你可以从默认配置中"eject"并添加自定义代码,但大多数应用永远不需要这么做。
技术选型上:Vite作为打包器,负责打包前端代码并自动按页面做代码分割,同时承担web目录的开发服务器;后端(api目录)由 Babel 编译,并通过 Fastify 提供 HTTP 服务。整个框架(严格地)采用 TypeScript 全量类型标注,让 IDE 自动补全无处不在。
部署:从开发到上线
Redwood 的职责延伸到部署环节,内置了面向主流托管平台的部署命令与配置,覆盖 serverless 与传统服务器两类基础设施,包括 Coherence(GWC/AWS)、Flightcontrol.dev(AWS)、Edg.io、Netlify、Render、Serverless.com、Vercel 等;也可以通过 SSH 部署到自己的服务器(即 Baremetal 部署)。
生态与演进:路线图、版本策略与社区
Redwood 仍处于活跃开发中,官方列出的实验性方向包括:React Server Components(RSC)与新的非 GraphQL 透明 API、SSR/Streaming、Realtime 与 GraphQL Subscriptions、Redwood Studio(获取项目运行时洞察)、Mailer等。当前仓库中已能看到这些方向的落地痕迹,例如 packages/web/src/components/cell/createSuspendingCell.tsx 对应的 SSR/Streaming 实验分支。
版本策略上,Redwood 严格遵守语义化版本(SemVer):没有主版本号变更就不会出现突如其来的破坏性改动;每次大版本发布都配有详尽的发布说明与升级指南,需要改动应用代码时会尽量附上 codemod 脚本自动完成迁移。Redwood 由 GitHub 联合创始人 Tom Preston-Werner 创建,其社区活跃,官方论坛与 Discord 中常有核心团队成员亲自答疑——框架面向用户构建,社区反馈是它持续演进的重要动力。
小结
从本仓库的源码看,Redwood 的每一项"开箱即用"背后都有扎实的实现支撑:声明式路由与AuthenticatedRoute守卫、Cell 的createCell状态机与预渲染缓存、createValidatorDirective驱动的@requireAuth/@skipAuth安全指令、Prisma schema 到 Service/SDL 的约定式映射,以及贯穿前后端的 Jest 测试与 Storybook 工作流。理解这些约定与实现,你就掌握了"一个命令启动全栈应用"背后的完整脉络——这也是你进入 Chapter 1:动手搭建 之前最好的心智准备。
- 后端
- 前端
- Web框架
- 开发工具
【免费下载链接】redwood
RedwoodGraphQL
相关推荐
Angular 是什么:框架定位、核心特性与开发者生态全景指南
Angular 是什么:框架定位、核心特性与开发者生态全景指南 本文以 Angular 官方文档站(本仓库 adev/src/content/introduct
前端Web框架终极指南:为什么Poem是Rust开发者的最佳Web框架选择
终极指南:为什么Poem是Rust开发者的最佳Web框架选择 Poem是一个功能全面且易于使用的Rust Web框架,它完美结合了易用性和高性能,通过最小化泛型
后端Web框架MCP 服务Redwood 全栈框架入门指南:从 Side Project 到 Startup 的 React + GraphQL 一体化开发体验
Redwood 全栈框架入门指南:从 Side Project 到 Startup 的 React + GraphQL 一体化开发体验 Redwood 是一个为
后端前端Web框架开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考