React项目转成NextJS问题汇总分享

639 阅读4分钟

周末花了二十多个小时,将自己的博客从React客户端渲染(CSR)升级到了NextJs服务端渲染(SSR),一方面是为了提高自己博客的SEO,也是为了练习学习。

然而第一次做,经验不是很丰富,国内的相关资料比较少,文档版本滞后,自己英文水平也捉急,查资料花了许多时间。这里将我遇到的问题做个汇总分享给大家。

这是我的博客欢迎大家访问留言 Cc技术人生

欢迎大家关注我的公众号:前端进阶攻城狮


Postcss问题/安装Less

首先是我的博客由于为了适配PC端与移动端,所以使用了postcss-px-to-viewport-plugin 随着屏幕宽度的变化,页面尺寸也相应变化。

然而最新版的Next使用的版本是8+,导致没有对css中的px单位进行vw的转换,而对postcss降级又会引发其他问题,于是无奈只能将所有scss文件转成less

这里提一句postcss-px-to-viewport-plugin在postcss 8+版本中会抛出废弃说明,建议更换成postcss-px-to-viewport-8-plugin

在nextjs中使用less

npm i next-with-less -D

在next.config.js中用withless函数包裹你的相关配置

const withLess = require("next-with-less");

module.exports = withLess({
    ...
})

嵌套路由/动态路由

路由规则

常规的单页面应用一般都是通过配置路由表来定义页面组件的渲染,在next中有一套约定成俗的规定:/pages下的文件名,就是我们的路由地址

例:

/pages/index.tsx ---> xxx.com

/pages/about.tsx ---> xxx.com/about

/pages/message/index.tsx ---> xxx.com/message

动态路由

当你需要通过params传参给到下一个页面时,比如访问/message/1 Router.push('/message/1')

这时我们在message目录下新建文件[id].tsx

然后在getServerSideProps函数的参数里获取

嵌套路由

在React或者Vue项目中,都可以通过声明一个路由的children以及在自身页面中提供<Outlet />或者<RouterView/>来实现路由嵌套。

然而在next中,我们通过定义一个layout组件,或者在pages/_app.tsx中定义好导航栏、侧边栏、底部等等

// layout/index.tsx

import { Meta } from '../layout/Meta';
import { AppConfig } from '../utils/AppConfig';
import Footer from './Footer';
import HeadNav from './HeadNav';

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <div className="min-h-screen flex flex-col relative">
      <Meta title={AppConfig.title} description={AppConfig.description} />
      <HeadNav />
      <div
        className=" flex-auto"
        style={{
          background: 'linear-gradient(225deg,#ffdee9,#b5fffc)'
        }}
      >
        {children}
      </div>
      <Footer />
    </div>
  );
}

// pages/_app.tsx

import '../styles/global.css';
import type { AppProps } from 'next/app';
import Layout from '@/layout';

const MyApp = ({ Component, pageProps }: AppProps) => (

  <Layout>
    <Component {...pageProps} />
  </Layout>
);

export default MyApp;

这里<Component />就是我们接下来页面的入口

浅层路由shallow

在使用Router.push路由跳转时,在第三个参数传递shallow:true页面只会改变url参数,而不会重新去进行获取数据。注意,这只适用于当前页面的修改!在我的项目里使用在右侧分类栏的点击。

SSR\SSG\ISR

NextJs很强大的一点在于能够同时支持这三点,根据你页面的实际情况需求来选择你需要怎么样的渲染方式

SSR服务端渲染

能够解决SEO和爬虫的问题,也是一开始我选择NextJs的原因,然而每次渲染都要去服务器请求数据,而一些文章可能几个月都不更新一次,无疑是浪费资源的。

export async function getServerSideProps(context: any) {
    const initData = await request.post(APIS.ARTICLE_PAGE);
    return {
      props: {
        initData
      }
    };
}

SSG静态站点生成

纯纯的静态页面,在构建时请求服务器获取数据进行生产静态页面,所以当数据变动时完全不会更新。当然好处也是显而易见的,快,很快啊,啪的一下就打开了。

export async function getStaticProps() {
  const initData: Article = await request.post(APIS.ARTICLE_DETAIL);
  return {
    props: {
      initData
    },
  };
}

ISR增量静态生成

当配置好重新构建的间隔时间,在间隔之后刷新页面才能看到动态变更后的数据

export async function getStaticProps() {
  const initData: Article = await request.post(APIS.ARTICLE_DETAIL);
  return {
    props: {
      initData
    },
    revalidate: 30
  };
}

与SSG区别就是多了个revalidate间隔时长

环境变量

当我们在根目录下创建定义环境变量的文件例如.env.development或者.env.production文件,其中的值可以直接在getStaticProps、getServerSideProps函数中获取,因为这是定义了服务器端的环境变量,在React函数中是获取不到的,需要在定义变量的名称加上前缀NEXT_PUBLIC_这样浏览器和服务端都可以使用环境变量。

其他

目前遗留了两个问题

  1. 在React组件中的请求用的是相对路径,服务端或者静态站点生成时请求只能使用绝对路径去请求,否则就会报无法请求80端口,而我的后端服务并非是80端口,这点暂时没有很好的解决方案。
  2. 因为使用了md-editor-rt插件去渲染markdown格式的文本,在预览模式下添加mdCatLog组件,可以看到文章内容的大小标题,点击可以跳转,类似掘金右侧的文章导航,而在我项目中无法正常跳转。

还望大佬指点解惑


开发阶段遇到的问题差不多就这些,上线部署就比较简单了,pm2后台挂机运行,nginx做个转发。

访问地址Cc代码人生