next.js-学习3
- 6. 设置数据库
- 1. 传代码到github https://github.com/
- 2. github和Vercel链接,用Vercel预览和部署
- 3. 创建数据库
- 4. 初始化数据库
- 7.读取数据
- 1. 在/app/lib/data.ts中这两行是连接postgres的
- 2. 在/app/dashboard/page.tsx中使用
- 3.在/app/dashboard/page.tsx中使用
- 4. /app/dashboard/page.tsx中使用
- 5. card组件使用
- 8.静态和动态渲染
- 9.串流 解决加载缓慢导致白屏的问题
- 10.部分预渲染 **Partial Prerendering (PPR)**
6. 设置数据库
1. 传代码到github https://github.com/
git init
git add README.md
git commit -m "first commit"
git branch -M main
//更改现有的远程仓库 URL,会让你输入密码
git remote set-url origin https://github.com/hexiaoming123/nextjs-dashboard.git
git push -u origin main
2. github和Vercel链接,用Vercel预览和部署
- https://vercel.com/signup
- 自己的推到next.js的项目,Settings->Project Settings->Framework Preset记得选Next.js,点击deploy部署
3. 创建数据库
- Storage中选postgres(好吧没了)请选https://vercel.com/marketplace/neon点击Install
4. 初始化数据库
-
点击Show secret 和 Copy Snippet
-
把生成的.env.local内容写到本地的.env中,在.gitignore添加忽略.env
-
-
pnpm run dev运行项目后请求http://localhost:3000/seed,这里会执行placeholder-data.ts,页面返回Database seeded successfully表示成功,我是失败了,因为没这个模块,无法安装,替代方式:
-
安装bcryptjs
pnpm add bcryptjs
-
app/seed/route.ts下修改
import bcrypt from 'bcryptjs';
如果访问http://localhost:3000/seed成功了,就删除这个文件避免下次再初始化数据。
-
取消app/query/route.ts中注释,删除
return Response.json({message:'Uncomment this file and remove this line. You can delete this file when you are finished.',});
,访问:http://localhost:3000/query这个验证是否能查询出数据
-
-
关联上项目,不然提交github代码后构建会失败
7.读取数据
1. 在/app/lib/data.ts中这两行是连接postgres的
-
import postgres from 'postgres';const sql = postgres(process.env.POSTGRES_URL!, { ssl: 'require' });// ...
2. 在/app/dashboard/page.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" /> */}{/* <Cardtitle="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>); }
3.在/app/dashboard/page.tsx中使用
import { fetchRevenue } from '@/app/lib/data';export default async function Page() {const revenue = await fetchRevenue();// ...
}
取消注释,取消/app/ui/dashboard/revenue-chart.tsx中注释,页面显示出了柱状图
4. /app/dashboard/page.tsx中使用
-
import { fetchRevenue, fetchLatestInvoices } from '@/app/lib/data';const latestInvoices = await fetchLatestInvoices();//在revenue下边加上
-
取消注释,取消/app/ui/dashboard/latest-invoices注释,显示出了Latest Invoices最新的5条消息
5. card组件使用
-
/app/dashboard/page.tsx中
import {fetchRevenue,fetchLatestInvoices,fetchCardData, } from '@/app/lib/data';//latestInvoices下边加上const {numberOfInvoices,numberOfCustomers,totalPaidInvoices,totalPendingInvoices,} = await fetchCardData();
await Promise.all 可以同时加载多个接口
8.静态和动态渲染
静态渲染是页面不会随数据改变而改变,动态渲染是随着数据改变而改变。
-
例如/app/lib/data.ts中打开fetchRevenue函数注释,模拟:一个接口加入个3s的耗时,另一个接口也跟着受了影响
export async function fetchRevenue() {try {// We artificially delay a response for demo purposes.// Don't do this in production :)console.log('Fetching revenue data...');await new Promise((resolve) => setTimeout(resolve, 3000));const data = await sql<Revenue[]>`SELECT * FROM revenue`;console.log('Data fetch completed after 3 seconds.');return data;} catch (error) {console.error('Database Error:', error);throw new Error('Failed to fetch revenue data.');} }
点击home的时候页面会等3s的空白才能出来概览页
9.串流 解决加载缓慢导致白屏的问题
创建/app/dashboard/loading.tsx文件
export default function Loading() {return <div>Loading...</div>;
}
加载的时候右侧空白会变为Loading…,成功后右侧显示页面
/app/dashboard/loading.tsx加载骨架
import DashboardSkeleton from '@/app/ui/skeletons';export default function Loading() {return <DashboardSkeleton />;
}
在/app/dashboard下创建overview文件夹,移动/app/dashboard下的loading.tsx和page.tsx,这样以后访问概念就不会影响其他页面了,http://localhost:3000/dashboard/overview
开始优化加载页面,/dashboard/overview/page.tsx中,
import { fetchLatestInvoices, fetchCardData } from '@/app/lib/data'; // 删除 fetchRevenue
const revenue = await fetchRevenue() // 删除这行
使用Suspense 和RevenueChartSkeleton
import { Suspense } from 'react';
import { RevenueChartSkeleton } from '@/app/ui/skeletons';
包裹
<Suspense fallback={<RevenueChartSkeleton />}><RevenueChart /></Suspense>
修改/app/ui/dashboard/revenue-chart.tsx文件
import { fetchRevenue } from '@/app/lib/data';//添加
import { Revenue } from '@/app/lib/definitions'; //移除
export default async function RevenueChart() { // 组件异步,移除参数const revenue = await fetchRevenue(); // 在组件内部获取数据
这样再访问http://localhost:3000/dashboard/overview会很快加载出来
/dashboard/overview/page.tsx中,
import { fetchCardData } from '@/app/lib/data'; // 删除 fetchLatestInvoices
//更新
import {RevenueChartSkeleton,LatestInvoicesSkeleton,
} from '@/app/ui/skeletons';
// 删除 const latestInvoices = await fetchLatestInvoices()
//更新<LatestInvoices />不要怕报错,下个组件会删除参数就不报错了
<Suspense fallback={<RevenueChartSkeleton />}><RevenueChart /></Suspense><Suspense fallback={<LatestInvoicesSkeleton />}><LatestInvoices /></Suspense>
/app/ui/dashboard/latest-invoices.tsx中
import { LatestInvoice } from '@/app/lib/definitions';//删除
import { fetchLatestInvoices } from '@/app/lib/data';//添加
export default async function LatestInvoices() {//删除了参数const latestInvoices = await fetchLatestInvoices();//在组件中直接获取数据
/app/dashboard/overview/page.tsx中,避免表头的cards不一起刷新,提到一个公共组件
import CardWrapper from '@/app/ui/dashboard/cards';//导入组件
import { Card } from '@/app/ui/dashboard/cards';//删除组件
//导入 CardsSkeleton组件
import {RevenueChartSkeleton,LatestInvoicesSkeleton,CardsSkeleton,
} from '@/app/ui/skeletons';
//使用CardWrapper组件<Suspense fallback={<CardsSkeleton />}><CardWrapper /></Suspense>//删除<Card组件
/app/ui/dashboard/cards.tsx中
import { fetchCardData } from '@/app/lib/data';//导入fetchCardData
export default async function CardWrapper() {
//使用fetchCardData函数const {numberOfInvoices,numberOfCustomers,totalPaidInvoices,totalPendingInvoices,} = await fetchCardData();//打开注释封装cardreturn (<><Card title="Collected" value={totalPaidInvoices} type="collected" /><Card title="Pending" value={totalPendingInvoices} type="pending" /><Card title="Total Invoices" value={numberOfInvoices} type="invoices" /><Cardtitle="Total Customers"value={numberOfCustomers}type="customers"/></>);
}
10.部分预渲染 Partial Prerendering (PPR)
-
PPR 只在 Next.js canary 可用 (
next@canary
),Next.js 14开始引入的这个特性pnpm install next@canary
-
对于今天构建的大多数web应用程序,你要么为整个应用程序选择静态和动态渲染,要么选择特定的路由。在Next.js中,如果你在路由中调用一个动态函数(比如查询数据库),整个路由就会变成动态的
-
然而,大多数路由并不是完全静态或动态的。例如,考虑一个电子商务网站。您可能希望静态地呈现大部分产品信息页面,但也可能希望动态地获取用户的购物车和推荐产品,这允许您向用户显示个性化的内容
-
组件不依赖于数据,也不是针对用户个性化的,所以它可以是静态的。
中的组件依赖于经常更改的数据,并且将针对用户进行个性化处理,因此它们可以是动态的。
-
-
js 14引入了部分预渲染的实验版本——一个新的渲染模型,它允许你在同一条路径上结合静态和动态渲染的优点。例如:
-
当用户访问路由时:
提供一个静态路由shell,其中包括导航栏和产品信息,确保快速初始加载。
外壳留下了一些洞,其中动态内容(如购物车和推荐产品)将异步加载。
异步孔是并行流,减少了页面的总体加载时间
-
next.config.ts中加入PPR选项,为Next.js应用启用PPR
/* config options here */experimental: {ppr: 'incremental',},
/app/dashboard/layout.tsx中加入
export const experimental_ppr = true;
这样在加载的时候会自动去部分预渲染,以后在生产中可能会很受欢迎,目前先稳稳别在生产用。
推送到github上去项目访问:https://nextjs-dashboard-git-main-hes-projects-5f35bd0a.vercel.app/dashboard/overview能正常返回数据就可以了。