003 使用数据库及页面更新
本文对应 Next.js Learn 入门教程的第 6、7 章。采用的是 Vercel 支持的 Postgres 数据库。
目录
vercel 部署
- github
- vercel 部署
创建 Postgres 数据库
创建、连接
在 Vercel 创建数据库
https://nextjs.org/learn/dashboard-app/setting-up-your-database#create-a-postgres-database
步骤:
- 创建数据库
- 在 Vercel 项目中连接
- 修改
.env
文件
在项目的 Storage 的Tab下,在 Quickstart 部分,找到 .env.local。
注意,放在 '.env' 中!
POSTGRES_URL="************"
POSTGRES_PRISMA_URL="************"
POSTGRES_URL_NO_SSL="************"
POSTGRES_URL_NON_POOLING="************"
POSTGRES_USER="************"
POSTGRES_HOST="************"
POSTGRES_PASSWORD="************"
POSTGRES_DATABASE="************"
数据库位置无法更改
Good to know: You cannot change the database region once it has been initalized. If you wish to use a different region, you should set it before creating a database.
为数据库增加内容
/package.json
json
"scripts": {
"build": "next build",
"dev": "next dev",
"start": "next start",
"seed": "node -r dotenv/config ./scripts/seed.js"
},
-r dotenv/config
是 Node.js 的一个选项,它告诉 Node.js 在运行脚本之前先预加载dotenv/config
模块。dotenv
是一个非常流行的 npm 包,它允许你从.env
文件中加载环境变量。这样你就可以在你的应用中使用这些环境变量了。
dotenv 包默认加载的是 .env 文件
bash
npm run seed
可以在 vercel 通过界面查看数据库
获取数据
查询数据的相关知识
- API layer
- Database query
- using SQL
Next.js 采用 Server Components 获取数据
https://nextjs.org/learn/dashboard-app/fetching-data#using-server-components-to-fetch-data
代码示例在: /app/lib/data.ts
Dashboard 页面获取:加入页面元素
修改:/app/dashboard/page.tsx
tsx
import { Card } from '@/app/ui/dashboard/cards';
import RevenueChart from '@/app/ui/dashboard/revenue-chart';
import LatestInvoices from '@/app/ui/dashboard/latest-invoices';
import { lusitana } from '@/app/ui/fonts';
export default async function Page() {
return (
<main>
<h1 className={`${lusitana.className} mb-4 text-xl md:text-2xl`}>
Dashboard
</h1>
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
{/* <Card title="Collected" value={totalPaidInvoices} type="collected" /> */}
{/* <Card title="Pending" value={totalPendingInvoices} type="pending" /> */}
{/* <Card title="Total Invoices" value={numberOfInvoices} type="invoices" /> */}
{/* <Card
title="Total Customers"
value={numberOfCustomers}
type="customers"
/> */}
</div>
<div className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-4 lg:grid-cols-8">
{/* <RevenueChart revenue={revenue} /> */}
{/* <LatestInvoices latestInvoices={latestInvoices} /> */}
</div>
</main>
);
}
Fetching data for <RevenueChart/>
tsx
import { Card } from '@/app/ui/dashboard/cards';
import RevenueChart from '@/app/ui/dashboard/revenue-chart';
import LatestInvoices from '@/app/ui/dashboard/latest-invoices';
import { lusitana } from '@/app/ui/fonts';
import { fetchRevenue } from '@/app/lib/data';
export default async function Page() {
const revenue = await fetchRevenue();
// ...
}
Then, uncomment the component, navigate to the component file (/app/ui/dashboard/revenue-chart.tsx) and uncomment the code inside it. Check your localhost, you should be able to see a chart that uses revenue data.
Fetching data for <LatestInvoices/>
https://nextjs.org/learn/dashboard-app/fetching-data#fetching-data-for-latestinvoices
Fetch data for the <Card>
components
https://nextjs.org/learn/dashboard-app/fetching-data#practice-fetch-data-for-the-card-components
INFO
However... there are two things you need to be aware of:
- The data requests are unintentionally blocking each other, creating a request waterfall.
- By default, Next.js prerenders routes to improve performance, this is called Static Rendering. So if your data changes, it won't be reflected in your dashboard.
Let's discuss number 1 in this chapter, then look into detail at number 2 in the next chapter.
然而... 你需要意识到两件事情:
- 数据请求不经意间相互阻塞,形成了一种请求瀑布现象。
- 默认情况下,Next.js 为了提高性能会对路由进行预渲染,这被称为静态渲染。所以,如果你的数据发生变化,它不会反映在你的仪表板上。
让我们在本章中讨论第1点,然后在下一章详细探讨第2点。
Parallel data fetching
阻塞方式(见注释):
tsx
const revenue = await fetchRevenue();
const latestInvoices = await fetchLatestInvoices(); // wait for fetchRevenue() to finish
const {
numberOfInvoices,
numberOfCustomers,
totalPaidInvoices,
totalPendingInvoices,
} = await fetchCardData(); // wait for fetchLatestInvoices() to finish
问题:如何将 Dashboard 整个页面改成没有 request waterfall
In JavaScript, you can use the Promise.all()
or Promise.allSettled()
functions to initiate all promises at the same time. For example, in data.ts
, we're using Promise.all()
in the fetchCardData()
function:
/app/lib/data.js
tsx
export async function fetchCardData() {
try {
const invoiceCountPromise = sql`SELECT COUNT(*) FROM invoices`;
const customerCountPromise = sql`SELECT COUNT(*) FROM customers`;
const invoiceStatusPromise = sql`SELECT
SUM(CASE WHEN status = 'paid' THEN amount ELSE 0 END) AS "paid",
SUM(CASE WHEN status = 'pending' THEN amount ELSE 0 END) AS "pending"
FROM invoices`;
const data = await Promise.all([
invoiceCountPromise,
customerCountPromise,
invoiceStatusPromise,
]);
// ...
}
}