1、可选链操作符
可选链操作符是es2020(ES11)引入的新特性,可以安全的使用嵌套对象的某些属性,即使这些属性未定义(undefined)或者为空(null),也不会报错。
//onAction?.(actionId) 的意思是,如果 onAction 是一个函数,则调用这个函数并传递 actionId 作为参数。如果 onAction 为 null 或 undefined,则表达式求值为 undefined,且不会尝试调用函数(从而避免了可能的错误)。
onAction?.(actionId);
2、date-fns
date-fns
是一个现代的JavaScript日期实用程序库,它提供了最全面、简单和一致的工具集,用于在浏览器和Node.js中操纵JavaScript日期。date-fns
是不可变和纯粹的——它使用纯函数构建,并且总是返回一个新的日期实例而不是改变传递的那一个,这有助于防止错误。
您可以通过访问官方网站来了解更多信息,或者通过访问其NPM包页面来查看如何在项目中开始使用date-fns
。如果你需要具体的关于如何使用date-fns
来格式化日期的例子,可以查看如Stack Overflow上的指导。
此外,date-fns
还为在UTC而非系统时区中执行计算提供了工具,这可以通过date-fns/utc
包来实现。如果您需要处理时区问题,date-fns
还提供了一个专门的包date-fns-tz
。
- npm install date-fns
- 在 date-fns 库中,format 函数用于将日期格式化为字符串。”PP” 是 format 函数的一个格式字符串选项,用来指定输出日期的格式。
- P 代表日期,例如:01/31/2020 (美式格式,月/日/年),对应不同的地区会按照当地习惯显示。
- PP会在P的基础上加上日期的名字,如:Jan 31st, 2020。
import { format } from 'date-fns';
const start = new Date();
const formattedDate = format(start, "PP");
console.log(formattedDate);
3、主要代码

"use client";
import { Listing, Reservation } from "@prisma/client";
import { SafeUser } from "@/app/types";
import { useRouter } from "next/navigation";
import useCountries from "@/app/hooks/useCountries";
import { useCallback, useMemo } from "react";
import { format } from "date-fns";
import Image from "next/image";
import HeartButton from "../HeartButton";
import Button from "../Button";
interface ListingCardProp {
data: Listing;
reservation?: Reservation;
onAction?: (id: string) => void;
disabled?: boolean;
actionLabel?: string;
actionId?: string;
currentUser: SafeUser | null;
}
const ListingCard: React.FC<ListingCardProp> = ({
data,
reservation,
onAction,
disabled,
actionLabel,
actionId = "",
currentUser,
}) => {
const router = useRouter();
const { getByValue } = useCountries();
const location = getByValue(data.locationValue);
const handleCancel = useCallback(
(e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
if (disabled) {
return;
}
onAction?.(actionId);
},
[onAction, actionId, disabled]
);
const price = useMemo(() => {
if (reservation) {
return reservation.totalPrice;
}
return data.price;
}, [reservation, data]);
const reservationDate = useMemo(() => {
if (!reservation) {
return null;
}
const start = new Date(reservation.startDate);
const end = new Date(reservation.endDate);
return `${format(start, "PP")} - ${format(end, "PP")}`;
}, [reservation]);
return (
<div
onClick={() => router.push(`/listings/${data.id}`)}
className="col-span-1 cursor-pointer group"
>
<div className="flex flex-col gap-2 w-full">
<div className=" aspect-square w-full relative overflow-hidden rounded-xl">
<Image
fill
alt="listing"
src={data.imageSrc}
className="object-cover h-full w-full group-hover:scale-110 transition"
></Image>
<div className="absolute top-3 right-3">
<HeartButton
listingId={data.id}
currentUser={currentUser}
></HeartButton>
</div>
</div>
<div className="font-semibold text-lg">
{location?.region},{location?.label}
</div>
<div className="font-light text-neutral-500">
{reservationDate || data.category}
</div>
<div className="flex flex-row items-center gap-1">
<div className="font-semibold">
$ {price}
</div>
</div>
{!reservation && (
<div className="font-light">night</div>
)}
</div>
{onAction && actionLabel && (
<Button
disabled={disabled}
small
label={actionLabel}
onClick={handleCancel}
></Button>
)}
</div>
);
};
export default ListingCard;
4、”use client”
在 Next.js 中,是否在前端还是后端运行取决于是否使用了 "use client"
指令以及代码所在的位置。我们来详细解释一下这两个概念以及它们的应用。
“use client” 指令
- “use client” 指令用于指示模块应在客户端运行。默认情况下,Next.js 中的页面和组件是服务器端渲染的(SSR),除非明确使用
"use client"
指令。 - 客户端组件:当一个文件以
"use client"
开头时,该文件中的所有代码都会在客户端运行。这对于需要在客户端执行的代码,例如事件处理、状态管理等,是必要的。
服务器端渲染 (SSR) 与客户端渲染 (CSR)
- 服务器端渲染 (SSR):页面在服务器端生成 HTML,然后将 HTML 发送到客户端。这可以提高初次加载的速度,并改善 SEO,因为搜索引擎可以索引服务器端生成的内容。
- 客户端渲染 (CSR):页面在客户端生成 HTML,通常是通过 JavaScript。在页面加载时,客户端会执行 JavaScript 代码来生成和显示内容。