- 后端
- 前端
- Web框架
- 开发工具
【免费下载链接】redwood
RedwoodGraphQL
本文以 RedwoodJS 为背景,完整讲解如何在不使用 API 层与数据库的前提下,将一个 Redwood 项目改造成纯静态站点(或"本地 JSON 数据 + 静态页面"的混合站点)并部署到 Netlify。读完本文,你将掌握:删除api目录的正确姿势、通过yarn rw deploy netlify --prisma=false --dm=false关闭 Prisma 与数据迁移、在netlify.toml中关闭函数构建,以及利用web/public下的静态 JSON 文件配合浏览器原生fetch()与 Chart.js 渲染数据图表的完整实战方案。
适用场景与前提
RedwoodJS 虽然以"全栈"著称,但并非每个项目都需要 GraphQL API 或数据库。以下两类场景非常适合禁用 API/数据库:
- 纯静态站点:站点不需要任何外部数据,例如个人主页、产品落地页、文档站;
- 低频数据站点:只需要消化一个简单的 JSON 数据结构,且数据变化频率极低——低到修改数据只需编辑一个纯文本文件、然后重新部署站点即可。
本文以Netlify 作为部署目标编写(原文档即以此为前提)。若使用其他托管平台或自定义构建流程,命令与配置可能有所差异,需要结合对应平台的构建说明自行调整。相关平台完整部署流程可参考 Netlify 部署指南 与 部署总览。
第一步:删除 /api 目录
最简单粗暴的方式是:直接把整个/api目录删掉,Redwood 应用在开发模式下依然可以正常工作:
rm -rf api删除后建议再运行一次yarn install,清理掉那些已经不再被引用的依赖包,让node_modules与锁文件保持整洁。
说明:Redwood 的目录约定是
api(服务端)与web(前端)并存,可参考仓库中任意一个完整项目,例如 example-todo-main 项目 的目录结构。删除api后,web侧依旧可以独立开发与构建。
第二步:禁用 Prisma 与数据迁移功能
yarn rw deploy netlify命令中有两个关键参数:--prisma与--dm(--data-migrate的别名),两者默认值均为true,必须显式设置为false。
在netlify.toml的构建命令中关闭它们:
[build] command = "yarn rw deploy netlify --prisma=false --dm=false"为什么必须关闭?在本地开发环境中,即使不带这两个参数也不会影响开发;但一旦部署到生产环境(至少以 Netlify 为部署目标时),如果仍保持默认值true,构建会报出'No Prisma Schema found'错误——因为我们已经删除了api目录,Prisma 无从找到 schema 文件。
源码验证:这两个参数究竟做了什么
从 Redwood CLI 的源码可以看到这两个参数的定义与默认值,位于 deploy 命令的共享 helpers:
.option('prisma', { description: 'Apply database migrations', type: 'boolean', default: 'true', }) .option('data-migrate', { description: 'Migrate the data in your database', type: 'boolean', default: 'true', alias: 'dm', })而 deployHandler 会根据这些开关动态拼接最终执行的命令链:
let commandSet = [] if (build) { commandSet.push('yarn rw build --verbose') } if (prisma) { commandSet.push('yarn rw prisma migrate deploy') } if (dataMigrate) { commandSet.push('yarn rw>[build] command = "yarn rw deploy netlify --prisma=false --dm=false" publish = "web/dist" # functions = "api/dist/functions" [dev] command = "yarn rw dev" [[redirects]] from = "/*" to = "/index.html" status = 200各配置项含义:
command:Netlify 构建时执行的命令,这里已关闭 Prisma 与数据迁移;publish:指定静态站点的发布目录为web/dist(Redwoodweb侧构建产物);# functions = "api/dist/functions":注释掉这一行,Netlify 便不会尝试把api/dist/functions下的代码部署为 Lambda 函数;[dev]/command = "yarn rw dev":Netlify Dev 本地开发启动命令;[[redirects]]:SPA 回退规则,将任意路径请求重定向到index.html(状态码 200),保证前端路由在刷新时可用。
对照默认模板理解差异
Redwood 官方提供的 netlify.toml 模板(yarn rw setup deploy netlify生成)默认包含functions = "api/dist/functions"这一行,见 setup deploy 的 Netlify 模板:
[build] command = "yarn rw deploy netlify" publish = "web/dist" functions = "api/dist/functions" [build.environment] NODE_VERSION = "20"仓库中的完整示例项目(如 example-todo-main 的 netlify.toml)同样采用默认配置。而本文的静态站点方案正是在这个默认模板基础上:① 构建命令追加--prisma=false --dm=false;② 注释掉functions行。
如果只是纯静态站点、完全不需要访问任何数据,做到这一步就已经完成了。继续往下看,如何用"随 web 侧一起部署的本地 JSON 文件"充当数据源,构建一个混合型站点。
第四步:从本地 JSON 文件读取数据
下面演示一个完整场景:展示 2017 年 1 月 30 日当周俄罗斯莫斯科的天气预报折线图。这个场景听起来很"特别",是因为它用的正是 OpenWeather API 的官方示例数据(天气 API 示例数据)。
你可以从 OpenWeather 官方示例接口获取 JSON,也可以直接使用下文给出的完整数据,保存为web/public/forecast.json:
{ "cod": "200", "message": 0, "city": { "geoname_id": 524901, "name": "Moscow", "lat": 55.7522, "lon": 37.6156, "country": "RU", "iso2": "RU", "type": "city", "population": 0 }, "cnt": 7, "list": [ { "dt": 1485766800, "temp": { "day": 262.65, "min": 261.41, "max": 262.65, "night": 261.41, "eve": 262.65, "morn": 262.65 }, "pressure": 1024.53, "humidity": 76, "weather": [ { "id": 800, "main": "Clear", "description": "sky is clear", "icon": "01d" } ], "speed": 4.57, "deg": 225, "clouds": 0, "snow": 0.01 }, { "dt": 1485853200, "temp": { "day": 262.31, "min": 260.98, "max": 265.44, "night": 265.44, "eve": 264.18, "morn": 261.46 }, "pressure": 1018.1, "humidity": 91, "weather": [ { "id": 600, "main": "Snow", "description": "light snow", "icon": "13d" } ], "speed": 4.1, "deg": 249, "clouds": 88, "snow": 1.44 }, { "dt": 1485939600, "temp": { "day": 270.27, "min": 266.9, "max": 270.59, "night": 268.06, "eve": 269.66, "morn": 266.9 }, "pressure": 1010.85, "humidity": 92, "weather": [ { "id": 600, "main": "Snow", "description": "light snow", "icon": "13d" } ], "speed": 4.53, "deg": 298, "clouds": 64, "snow": 0.92 }, { "dt": 1486026000, "temp": { "day": 263.46, "min": 255.19, "max": 264.02, "night": 255.59, "eve": 259.68, "morn": 263.38 }, "pressure": 1019.32, "humidity": 84, "weather": [ { "id": 800, "main": "Clear", "description": "sky is clear", "icon": "01d" } ], "speed": 3.06, "deg": 344, "clouds": 0 }, { "dt": 1486112400, "temp": { "day": 265.69, "min": 256.55, "max": 266, "night": 256.55, "eve": 260.09, "morn": 266 }, "pressure": 1012.2, "humidity": 0, "weather": [ { "id": 600, "main": "Snow", "description": "light snow", "icon": "13d" } ], "speed": 7.35, "deg": 24, "clouds": 45, "snow": 0.21 }, { "dt": 1486198800, "temp": { "day": 259.95, "min": 254.73, "max": 259.95, "night": 257.13, "eve": 254.73, "morn": 257.02 }, "pressure": 1029.5, "humidity": 0, "weather": [ { "id": 800, "main": "Clear", "description": "sky is clear", "icon": "01d" } ], "speed": 2.6, "deg": 331, "clouds": 29 }, { "dt": 1486285200, "temp": { "day": 263.13, "min": 259.11, "max": 263.13, "night": 262.01, "eve": 261.32, "morn": 259.11 }, "pressure": 1023.21, "humidity": 0, "weather": [ { "id": 600, "main": "Snow", "description": "light snow", "icon": "13d" } ], "speed": 5.33, "deg": 234, "clouds": 46, "snow": 0.04 } ] }关键机制:web/public目录下的任何文件都会被 Netlify 原样托管,跳过一切构建过程。也就是说,你把forecast.json放进web/public,它就等同于站点根路径下的一个静态资源/forecast.json。这正是"编辑一个纯文本文件 → 重新部署"这种极简数据更新流程的基础。
第五步:用 React 组件拉取并展示数据
接下来让 React 组件远程获取这份 JSON 并展示在页面上。先生成一个首页:
yarn rw generate page home /然后使用浏览器内置的fetch()获取数据,先把原始 JSON 直接 dump 到屏幕上验证链路是否打通:
import { useState, useEffect } from 'react' const HomePage = () => { const [forecast, setForecast] = useState({}) useEffect(() => { fetch('/forecast.json') .then((response) => response.json()) .then((json) => setForecast(json)) }, []) return <div>{JSON.stringify(forecast)}</div> } export default HomePage这里用useState保存天气数据,用useEffect在组件挂载时触发数据加载。由于fetch请求的是同源静态资源/forecast.json,不存在跨域问题,在本地开发与 Netlify 生产环境都能直接工作。
第六步:用 Chart.js 绘制天气曲线
数据链路打通后,引入 Chart.js 做简单图表。Chart.js 是 Redwood 项目中常用的第三方图表库之一,通过yarn workspace web将其安装到web侧:
yarn workspace web add chart.js先用假数据验证图表渲染
import { useState, useEffect, useRef } from 'react' import Chart from 'chart.js' const HomePage = () => { const chartRef = useRef() const [forecast, setForecast] = useState({}) useEffect(() => { fetch('/forecast.json') .then((response) => response.json()) .then((json) => setForecast(json)) }, []) useEffect(() => { new Chart(chartRef.current.getContext('2d'), { type: 'line', data: { labels: ['Jan', 'Feb', 'March'], datasets: [ { label: 'High', data: [86, 67, 91], }, { label: 'Low', data: [45, 43, 55], }, ], }, }) }, [forecast]) return <canvas ref={chartRef} /> } export default HomePage要点说明:
useRef创建chartRef并绑定到<canvas>元素,Chart.js 通过chartRef.current.getContext('2d')获取 2D 绘图上下文;- 图表创建逻辑放在第二个
useEffect中,依赖数组为[forecast],即等天气数据加载完成后才绘制图表; - 这一段先用写死的假数据(
[86, 67, 91]等)验证 Chart.js 渲染链路是否正常。
把真实 JSON 数据转换成 Chart.js 需要的格式
假数据渲染正常后,剩下的工作就是把天气 JSON 转换成 Chart.js 期望的结构。最终版HomePage增加了两个数据转换函数,并正确处理日期显示:
import { useState, useEffect, useRef } from 'react' import Chart from 'chart.js' const MONTHS = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', ] const getDates = (forecast) => { return forecast.list.map((entry) => { const date = new Date(0) date.setUTCSeconds(entry.dt) return `${MONTHS[date.getMonth()]} ${date.getDate()}` }) } const getTemps = (forecast) => { return [ { label: 'High', data: forecast.list.map((entry) => kelvinToFahrenheit(entry.temp.max)), borderColor: 'red', backgroundColor: 'transparent', }, { label: 'Low', data: forecast.list.map((entry) => kelvinToFahrenheit(entry.temp.min)), borderColor: 'blue', backgroundColor: 'transparent', }, ] } const kelvinToFahrenheit = (temp) => { return ((temp - 273.15) * 9) / 5 + 32 } const HomePage = () => { const chartRef = useRef() const [forecast, setForecast] = useState(null) useEffect(() => { fetch('/forecast.json') .then((response) => response.json()) .then((json) => setForecast(json)) }, []) useEffect(() => { if (forecast) { new Chart(chartRef.current.getContext('2d'), { type: 'line', data: { labels: getDates(forecast), datasets: getTemps(forecast), }, }) } }, [forecast]) return <canvas ref={chartRef} /> } export default HomePage各函数的职责:
MONTHS:月份英文缩写常量表,用于把 Unix 时间戳格式化为"Jan 30"这样的可读标签;getDates(forecast):遍历forecast.list,把每条记录的dt(Unix 秒级时间戳)通过new Date(0)+setUTCSeconds(entry.dt)转换为日期对象,再格式化为"月份 日期",作为折线图的 X 轴标签;getTemps(forecast):分别提取entry.temp.max与entry.temp.min,经kelvinToFahrenheit把开尔文温度换算为华氏度,生成"High"(红线)与"Low"(蓝线)两条数据集;kelvinToFahrenheit:温度单位换算工具函数。
注意forecast的初始值从{}改成了null,并在绘制前增加if (forecast)判断——这样既避免在数据尚未到达时对undefined调用forecast.list.map,也防止重复创建 Chart 实例。渲染成功后,页面会出现一条红色高温曲线与一条蓝色低温曲线。
收尾:部署到生产
至此,一个"无 API、无数据库"的混合型站点就完成了:数据存于web/public/forecast.json,前端通过静态资源请求读取并在浏览器端用 Chart.js 绘制。剩余工作就是提交代码并交给 Netlify 构建部署——注意构建命令必须保持--prisma=false --dm=false,且functions行保持注释状态。
总结
RedwoodJS 的核心价值在于让"需要访问数据库或第三方 API"的全栈应用开发更轻松,但它同样可以用于纯静态站点,以及本文演示的这种混合站点——当你想消化并展示数据,但数据源只是自己 URL 下的一个静态文件时。核心要点回顾:
- 删除
api目录后开发模式依旧可用; yarn rw deploy netlify --prisma=false --dm=false关闭数据库迁移与数据迁移(二者默认均为true,源码见 deploy helpers);- 注释掉
netlify.toml中的functions行,阻止 Netlify 扫描 Lambda 函数代码; web/public下的文件被原样托管,可充当极简数据源;配合浏览器fetch()与 Chart.js 即可渲染数据图表。
这种方案的额外收益是:数据更新成本极低(改一个 JSON 文件重新部署),无需维护数据库、无需关心迁移,部署链路更简单,也省去了 Lambda 函数的冷启动与配额顾虑。
- 后端
- 前端
- Web框架
- 开发工具
【免费下载链接】redwood
RedwoodGraphQL
相关推荐
RedwoodJS 禁用 API 层与数据库:从全栈应用到纯静态站点部署实战指南
RedwoodJS 禁用 API 层与数据库:从全栈应用到纯静态站点部署实战指南 本指南讲解如何在 RedwoodJS 项目中彻底去掉 API 层与数据库依赖,
后端前端Web框架开发工具Redwood 禁用 API 与数据库指南:将应用部署为纯静态站点
Redwood 禁用 API 与数据库指南:将应用部署为纯静态站点 本指南讲解如何在 Redwood 项目中彻底关闭 API 层与数据库依赖,仅保留 Web 前
后端前端Web框架开发工具Redwood 无后端部署实战:禁用 API 与数据库,构建纯静态站点
Redwood 无后端部署实战:禁用 API 与数据库,构建纯静态站点 Redwood 是面向全栈应用的框架,但并不是每个项目都需要 GraphQL API 与
后端前端Web框架开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考