背景

在工作学习中,发现一个问题,那就是有一个导航网站非常重要!

无意之中,看到一个很棒的导航网站,打算基于此开发一个,记录一下开发的过程。

Demo仓库:https://github.com/wangfengyuan/frontend-nav

记录

tailwindcss的scrollbar

https://adoxography.github.io/tailwind-scrollbar/examples

prisma的env没有读取到dababase_url

参考这个帖子:

https://stackoverflow.com/questions/67796217/prisma-getting-environment-variable-not-found-error-message-when-running-graph

https://www.prisma.io/docs/orm/more/development-environment/environment-variables/managing-env-files-and-setting-variables#manage-env-files-manually

两种解法:

1、安装dotenv

2、直接env.local改名为env

登陆怎么做?

bcrypt-ts到底怎么用?

!!!注意!不能够把加盐的函数直接去比较,因为每次生产的都是不一样的!

「项目」开发一个导航网站 - 图1

  • 如果是Github模式

0、参考https://authjs.dev/getting-started/authentication/oauth

1、先在github申请appid和secret,填写url链接

2、而后在nextjs的env中写上

「项目」开发一个导航网站 - 图2

  • 如果是账号密码模式

https://authjs.dev/getting-started/authentication/credentials

「项目」开发一个导航网站 - 图3

必须这样!!!官方文档是错的!!

「项目」开发一个导航网站 - 图4

from的action类型错误?

不用管···

https://www.reddit.com/r/nextjs/comments/1616bh9/form_action_not_working/

https://github.com/vercel/next.js/discussions/56581

client和server的调用方式不一样··

「项目」开发一个导航网站 - 图5

「项目」开发一个导航网站 - 图6

客户端的组件又报错了CSRF···

「项目」开发一个导航网站 - 图7

发现是环境变量错了,直接注释掉就行:

「项目」开发一个导航网站 - 图8

shadcn/ui的Button没有loading,怎么办?

直接参考这个文章:https://blog.typeart.cc/add-loading-spinner-in-shadcn-ui-button/

  1. + import { Loader2 } from 'lucide-react';
  2. // ... 略過前面 cva 部份
  3. export interface ButtonProps
  4. extends React.ButtonHTMLAttributes<HTMLButtonElement>,
  5. VariantProps<typeof buttonVariants> {
  6. asChild?: boolean;
  7. + loading?: boolean;
  8. }
  9. // 將 loading 與 children 從 props 取出來
  10. const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
  11. + ({ className, variant, size, asChild = false, loading, children, ...props }, ref) => {
  12. const Comp = asChild ? Slot : 'button';
  13. return (
  14. <Comp
  15. className={cn(buttonVariants({ variant, size, className }))}
  16. + disabled={loading}
  17. ref={ref}
  18. {...props}
  19. >
  20. + {loading ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : children}
  21. </Comp>
  22. );
  23. },
  24. );
  25. Button.displayName = 'Button';

怎么使用safe-server-action?

官方文档:https://next-safe-action.dev/

Zod应该怎么使用?比如声明了一个结构体,应该怎么赋值呢?

官方文档:https://zod.dev/?id=basic-usage

这样:

  1. import { z } from "zod";
  2. const User = z.object({
  3. username: z.string(),
  4. });
  5. User.parse({ username: "Ludwig" });
  6. // extract the inferred type
  7. type User = z.infer<typeof User>;
  8. // { username: string }

怎么实现选择Emoji?

「项目」开发一个导航网站 - 图9

如上图,我需要在这个输入框,可以选择表情或者分组地址。

找到一个库,在这里:https://ealush.com/emoji-picker-react/

很好用,可以选择屏蔽某些不需要的Emoji,比如Flag····

当选择表情之后,会有一个callBack,输出的内容是当前的表情信息:

  1. {
  2. "activeSkinTone": "neutral",
  3. "emoji": "😆",
  4. "imageUrl": "https://cdn.jsdelivr.net/npm/emoji-datasource-apple/img/apple/64/1f606.png",
  5. "isCustom": false,
  6. "names": [
  7. "laughing",
  8. "satisfied",
  9. "smiling face with open mouth and tightly-closed eyes"
  10. ],
  11. "unified": "1f606",
  12. "unifiedWithoutSkinTone": "1f606"
  13. }

发现一个问题useAction的操作是异步的!

  1. // 调用Action进行更新
  2. const AddCategoryAction = useAction(AddCategory)
  3. <Button
  4. type="submit"
  5. variant="default"
  6. loading={AddCategoryAction.isPending}
  7. onClick={async () => {
  8. await AddCategoryAction.execute(newCategory)
  9. mylog("AddCategoryAction res", AddCategoryAction.result)
  10. if (AddCategoryAction.hasErrored) {
  11. mylog(
  12. "AddCategory Err",
  13. AddCategoryAction.result.validationErrors
  14. )
  15. toast(
  16. JSON.stringify(AddCategoryAction.result.validationErrors) ??
  17. "分类添加失败,请重试"
  18. )
  19. } else {
  20. toast("分类添加成功")
  21. // window.location.reload()
  22. }
  23. }}
  24. >
  25. 保存
  26. </Button>

由于是hook来的,在onclick函数里是异步的,造成数据不一致,怎么办啊!

两种方法:

1、换成使用const res = await AddCategoryAction.executeAsync(newCategory)

2、使用useEffect来监听

  1. useEffect(() => {
  2. if (!AddCategoryAction.isPending) {
  3. if (AddCategoryAction.hasErrored) {
  4. toast(
  5. JSON.stringify(AddCategoryAction.result.validationErrors) ??
  6. "分类添加失败,请重试"
  7. )
  8. } else {
  9. toast("分类添加成功")
  10. // 你可以在这里执行其他操作,比如关闭对话框或重置表单
  11. }
  12. }
  13. }, [
  14. AddCategoryAction.isPending,
  15. AddCategoryAction.hasErrored,
  16. AddCategoryAction.result,
  17. ])

Icon可以选择的网站~

https://lucide.dev/guide/packages/lucide-react

如何从结构体中去除某个字段?-Omit

在 TypeScript 中没有严格意义上的“结构体”概念,但可以通过对象类型或接口来模拟。要从一个对象中去除某个字段,可以通过类型断言和对象解构的方式来实现。

假设你有一个类型如下:

  1. interface MyType {
  2. field1: string;
  3. field2: number;
  4. field3: boolean;
  5. }

现在要去除 field2 字段,可以这样做:

  1. const obj: MyType = { field1: 'some string', field2: 123, field3: true };
  2. const { field2,...rest } = obj;
  3. const newObj = rest as Omit<MyType, 'field2'>;
  4. console.log(newObj);

这里使用了对象解构和类型断言来创建一个新的对象,不包含指定的字段。

或者可以使用 Omit 类型工具来创建一个新的类型,然后将原对象转换为新类型:

  1. type NewType = Omit<MyType, 'field2'>;
  2. const newObj2: NewType = obj as NewType;
  3. console.log(newObj2);

遇到一个问题form的值没有更新啊!SetValue

有一个issue已经提到了:https://github.com/react-hook-form/react-hook-form/issues/456

其实关键在于SetValue不会导致form的组件重新更新。

如果想实现setValue之后,组件内容更新,需要useEffect进行组件re-render。

diaglog里的form数据没有被清空。

发现新建一个分类之后,再次点开新建,里面的内容还是上一次保存的数据,因此需要做一次清空。

「项目」开发一个导航网站 - 图10

TODO 图片尺寸不对···

「项目」开发一个导航网站 - 图11

源代码如下:

「项目」开发一个导航网站 - 图12

到底什么原因呢?

部署在vercel之后,一直报错Prisma is not defined

谷歌了半天,一直没找到问题出在哪··

发现是没有import····为什么编辑器没有提示啊?

「项目」开发一个导航网站 - 图13

shadcn/ui的Drawer组件在移动端有问题!

会导致移动端输入、下滑出现问题。

直接换成Sheet组件即可!