kasuie page cover

Next.js 踩坑记录

发表于

浏览量1451

评论数0

Next.js 踩坑记录

最近博客前端重新换了技术栈写了一下,改用了支持SSR渲染的next.js前端框架,基于react,前前后后踩了不少的坑,从大概4月份开始的,中途遇到了很多卡手的难题,在网上查阅相关的资料也大多都是国外的,以至于到现在才算是有雏形(其实也有摸鱼)。在这里先记录一些。

Next.js和已有后端项目的结合

首先Next.js中有一个API 路由东西,可以理解为在 Next.js 中可以直接写 node 代码作为后端服务来运行,直接维护一个全栈项目,使用文件路径作为路由地址,开始的时候被这个误导了,一直在用它这个api路由,后来还真的想去用Next.js写全栈了。

不过在那个时候了解了一个叫PrismaPrisma的框架,是一个功能强大的数据库工具和 ORM 框架,它简化了与数据库的交互,并提供了类型安全、高效和直观的方式来管理应用程序的数据层。使用 Prisma,开发人员可以更专注于业务逻辑的开发,而无需过多关注底层数据库的操作 ,百科如是的说。实操了一下,感觉是挺新的,也挺厉害,就是我要是再推翻后端框架,感觉工作量就不是一般的大了。

我有后端服务的,所以我只需要直接在组件里调用即可,不过需要注意的是,Next.js它里面的组件分为服务端组件(页面组件)和客户端组件,在项目根目录下的pages下的都是服务端组件,在服务端组件里面可以使用getInitialProps, getServerSideProps, getStaticProps 。这些都是用于数据获取的方法:

  1. getInitialProps: 这是 Next.js 早期版本中使用的一种方式,用于在服务器端获取数据并将其传递给页面组件。它可以用于服务器渲染和客户端渲染,并且可以在每次页面刷新时获取新数据。然而,自从 Next.js 9.3 版本起,推荐使用 getServerSidePropsgetStaticProps 替代该方法。

  2. getServerSideProps: 这个方法用于在每个请求时在服务器端获取数据,并将其作为 props 传递给页面组件。它适用于需要在每次请求时获取最新数据的情况,比如需要根据用户的登录状态进行动态渲染。getServerSideProps 返回的数据将在每次请求时都被计算,因此它可以实现动态的服务器渲染。

  3. getStaticProps: 这个方法用于在构建时在服务器端获取数据,并将其作为 props 传递给页面组件。它适用于静态生成的页面,即页面内容在构建时就已经确定,并且不会因为用户的请求而变化。getStaticProps 可以在构建时预渲染页面,从而提供更好的性能和缓存效果。

这些方法都是异步函数,而且在方法里调用自己的后端api,要使用全路径,即使是代理转发也不行。

另外在Next.js 13中,新加了一种app目录模式,去掉了page目录,大概了解了一下,app目录模式,默认是服务端组件,要转变成客户端组件可以在文件头部写上"use client",这个组件就变成客户端组件了,当然其下如果有组件也是客户端组件了。新版本也没有了以上三种方法,获取数据直接用异步函数获取即可。

以下是我在服务端获取数据的一个示例:

export default function Whisper({
      data,
      statistics,
    }: InferGetServerSidePropsType<typeof getServerSideProps>) {

    ......

  return (
    ......
  );
}

export async function getServerSideProps() {
  const baseUrl = process.env.BASE_URL;
  let data: any = [];
  await request.get(`${baseUrl}/messages`).then((res: any) => {
    data =
      res && res.success ? res.data.filter((v: any) => v.site !== "-1") : [];
  });
  const res1: any = (await request.get(`${baseUrl}/statistics`)) || null;
  const statistics = (await res1) && res1.data;
  return {
    props: {
      data,
      statistics,
    },
  };
}

这里为了好维护,设置了一个环境变量来拼api的全路径。在getServerSideProps方法里按照我的理解主要是为了预渲染,做SEO,后续的数据获取及交互的一些接口请求,正常在方法里面写就好了。那说到交互,那么就要来说说在Next.js中怎么解决跨域了。

Next.js的一些配置

在根目录会有一个next.config.js文件,就是项目的配置文件,配置这块很多,我只讲讲我用到了的,且觉得大概率都会用到的。

跨域配置

const nextConfig = {
  async rewrites() {
    const ret = [
      {
        source: "/api/:path*",
        destination: "http://localhost:8001/:path*", // 根据实际情况来
      },
      ......
    ];
    return {
      beforeFiles: ret,
    };
  },
};
module.exports = nextConfig;

设置环境变量

const nextConfig = {
  env: {
    BASE_URL: process.env.BASE_URL,
  },
};
module.exports = nextConfig;

这里我是在根目录有.env ,取的它的值。

配置图片解析域名

在Next.js中,使用Next.js的
博客图片
组件来显示图像时,Next.js会自动优化图像并提供一些功能,如自动调整图像大小、延迟加载和响应式图像显示,这是非常好用的一个功能。但是,为了确保安全性和性能,Next.js默认情况下只允许从同一域名下的图像进行加载,所以要用此功能优化其他域名下的图片资源文件,需要在配置文件配置上对应的域名,如下:
const nextConfig = {
  images: {
    domains: [
      "cdn.staticaly.com",
      "s2.loli.net",
      "i.loli.net",
    ],
  },
};
module.exports = nextConfig;

这样配置后,Next.js将允许从这三个域名加载图像。如果你尝试从其他域名加载图像,Next.js将会阻止加载,并在控制台中显示错误信息。

需要注意的是,domains 属性只在使用
博客图片
组件时生效,如果你直接使用
博客图片
标签来显示图像,则不会进行域名检查。

配置cdn

可以在Next.js中,将打包后的static文件,使用cdn来优化网站加载速度,如下需要对配置做一些修改:

const isProd = process.env.NODE_ENV === "production";

const nextConfig = {
  assetPrefix: isProd
    ? "https://cdn.example.com" // 根据自己的cdn服务来定
    : "",
};
module.exports = nextConfig;

如上配置后,在生产环境下,项目加载静态资源文件,会从https://cdn.example.com这个域名下找。

其他配置

如果你采用静态资源打包方式部署自己的项目,可能需要配置一下打包文件的输出目录:

const nextConfig = {
  output: "standalone", // 这个目录名称是不能变的
};
module.exports = nextConfig;

如果需要对项目做体积优化,可能会用到:

  experimental: {
    legacyBrowsers: false,
  },

这样的意思是不支持旧版浏览器。这意味着,Next.js将不会生成针对旧版浏览器的兼容性代码,以便提供更好的性能和较小的文件大小。看你需要对旧版浏览器兼容不。

写法上需要注意的点

  1. 在Next.js中,不要使用P标签下包裹着P标签,p标签下包裹着div标签,这样会导致出现服务端和客户端渲染的结果不同儿出现报错。
  2. 避免在预渲染的时候使用到windowstorage 等客户端才有的功能。
  3. 在引入第三方依赖或者组件的时候,尽量使用dynamic动态导入,只加载需要的,避免打包体积太大。
  4. 在Next.js中如果需要自定义每一个页面组件的head做SEO或者是引入第三方js,css,font等,使用Next.js自带的对应组件,找到适合的模式,可以得到一定的性能提升。
  5. Next.js支持多种CSS样式的处理方式,包括内联样式、CSS模块和CSS-in-JS等。可以根据项目的需求选择合适的方式来处理样式,如果是使用CSS模块,有时候可能会用到动态改变className,可以用如下写法:
      <div
        className={
          styles[`${card.key === active ? "k-info-card-option-active" : ""}`]
        }
        onClick={() => {
          setActive(card.key);
        }}
        key={card.key}
      ></div>

大概有这些吧......

写在最后;

地点: 成都(社畜ing) 时间: 2023-07-23 23:30:00 心情:头晕晕的有点难受

FvMmPGtaUAUJxw25
最后修改:2023年10月18日

留下你的评论吧

http(s)://

回到顶部