next.js v14 升级全步骤|迁移 pages Router 到 App Router

【概括】本文升级整体按照官网文档指引进行,在迁移 pages Router 前先看了官网的实操视频

【注意】文章内对 .babel.ts、next.config.js 进行了多次更改,最终配置可见 报错3: Server Error ReferenceError: React is not defined

一、升级 Next.js 版本

npm install next@latest react@latest react-dom@latest

升级后报错:

报错1:Build Error :You may need an appropriate loader to handle this file type, currently no loaders areconfigured to process this file

file type, currently no loaders areconfigured to process this file.
Failed to compile ●Next.js (14.2.3)
/components/GlobalLoading/index.module.less Module parse failed:
Unexpected token(1:0)You may need an appropriate loader to handle this
file type, currently no loaders areconfigured to process this file.
See https://webpack.js.org/concepts#loaders.container f
pointer-events: none; transition-property: opacity;

原因:next 内部支持 css 而不支持 less,我的项目在 next v12.1.6 时使用 next-plugin-antd-less 来支持 less,但根据官方文档来看,它只实现了对 next v11、v12 的支持,不支持 v14,因此我们选择安装其他包来支持。
在这里插入图片描述
解决方法:

npm install less less-loader next-with-less

配置:

// next.config.js
const withLess = require("next-with-less");

module.exports = withLess({
  lessLoaderOptions: {
    /* 里面的配置根据自己的来,下面是我之前next-plugin-antd-less里的配置 */
     javascriptEnabled: true,
     modifyVars: {
       '@primary-color': '#4169E1',
       '@primary-deep': '#3656F8',
       '@background-color': '#F4F4F4',
     },
     additionalData: `@import "${__dirname}/src/styles/variables.less";`,
  },
});

参考文章:
如何在 next.js 13.4.13 中使用 less?
Next.js 和 Antd 以及 CSS Less 设置
next-with-less
Next13支持less&自定义less-module

报错2:Build Error:Syntax error: Selector “:global .ant-menu-sub” is not pure (pure selectors must contain at least one local class or id)

在这里插入图片描述
原因:scss 不期望在模块样式文件中使用全局选择器,我们最好使用类选择器,然后在类选择器内使用其他选择器。
解决方法:不要在 .module.scss 的第一层选择器使用 :global 或 标签选择器;如果需要全局拦截,考虑添加一个全局 css 文件(global.css)然后在 layout.tsx 里引入

// .module.scss,下面的写法是可以的
.root {
	a {
		color: red;
	}
}

参考文档:Selector “:global .class” is not pure

报错3:Build Error:Global CSS cannot be imported from within node_modules.

在这里插入图片描述
解决办法:我参考了很多文档,但还是没明白根本原因。我这里觉得是 less 文件的问题,因此修改了 webpack 配置,修改后在我本地可以正常运行。(后面觉得 next 对 less 的支持很不友好,于是放弃了 less,采用了 scss, 因此下面这段 webpack 配置最终也被替换)

// next.config.js
  webpack(config, { isServer }) {
    config.module.rules.push({
      test: /\.less$/,
      use: [
        {
          loader: 'style-loader',
        },
        {
          loader: 'css-loader',
        },
        {
          loader: 'less-loader',
          options: {
            lessOptions: {
              javascriptEnabled: true,
            },
          },
        },
      ],
    });
    return config;
  },
// .babelrc.js
module.exports = {
  presets: ['next/babel'],
  // 下面这行注释了,我这次更新就不用 antd 了,所以直接注释,升级完会把和 antd 相关的都删了
  // plugins: [['import', { libraryName: 'antd', style: true }]],
};

二、升级新功能

升级 Image

npx @next/codemod@latest next-image-to-legacy-image .

升级 Link

npx @next/codemod@latest new-link .

升级 Script

我没用到 Script ,升级可看官网

三、迁移 pages 到 app

报错1:TypeError: Property left of AssignmentExpression expected node to be of a type [“LVal”] but instead got “BooleanLiteral”

解决方法:参考下面文章,评论回复 .babelrc.js 文件在不必要时可删除,因此我删除后可以正常运行了。但在我继续升级中,.babelrc.js 还是增加了如下配置:

module.exports = {
  "presets": [ 
    'next/babel',
  ],
  "plugins": []
}

参考文章:Upgrading nextjs from 12 to 14 and receiving Boolean Literal type error on node_modules/process/browser.js when trying to access env variables

报错2:Support for the experimental syntax ‘jsx’ isn’t currently enabled

解决方法: 安装两个包并配置如下内容

npm install --save-dev @babel/preset-env @babel/preset-react
// .babelrc.js
module.exports = {
  "presets": ["@babel/preset-env", "@babel/preset-react"]
}

参考文章:Support for the experimental syntax ‘jsx’ isn’t currently enabled

报错3: Server Error ReferenceError: React is not defined

在这里插入图片描述

总结:
把 less 换成 scss 了

// next.config.js
const path = require('path')
 
const SassLoader = {
  sassOptions: {
    includePaths: [path.join(__dirname, 'styles')],
    prependData: `
      $primary-color: #4169E1;
      $primary-deep: #3656F8;
      $background-color: #F4F4F4;
    `,
  },
}
// babelrc.ts
module.exports = {
  "presets": [ 
    'next/babel',
  ],
}

报错4: app-index.js:33 Warning: A component was suspended by an uncached promise. Creating promises inside a Client Component or hook is not yet supported, except via a Suspense-compatible library or framework.

解决方法:当前文件顶部添加 'use client'
在这里插入图片描述

报错5: Server Error Error: (0 , react__WEBPACK_IMPORTED_MODULE_0__.createContext) is not a function

解决方法:当前文件顶部添加 'use client'
在这里插入图片描述

报错6: Server Error Error: Super expression must either be null or a function

解决方法:当前文件顶部添加 'use client'
在这里插入图片描述

App Router SEO 处理

在未升级前,我通过 url.pathname 为不同页面设置不同的 title、 content:

 <Head>
   <title>{title}</title>
   <meta name="description" content={description} />
   <meta name="keywords" content={keywords} />
 </Head>

但在升级之后,这些内容由 layout.tsx 的 generateMetadata 暴露出去,在 server component 中我无法直接获取 url 的 pathname
解决方法:通过中间件的形式注入 pathname,保证在 server component 里也可以获取到页面路径。采用设置 x-url 而不是直接读取请求头的 referer ,是因为直接输入 url 访问时 headers 没有 referer。

// middleware.ts
// eslint-disable-next-line @next/next/no-server-import-in-page
import { NextRequest, NextResponse } from "next/server";

export function middleware(request: NextRequest) {
  const requestHeaders = new Headers(request.headers)
  requestHeaders.set('x-url', request.url);

  const response = NextResponse.next({
    request: {
      headers: requestHeaders,
    },
  })
 
  response.headers.set('x-url', request.url)
  return response
}
// layout.tsx
export async function generateMetadata(): Promise<Metadata | null> {
  const url = new URL(headers().get("x-url")!);

  let seoData = null
  try {
    seoData = seo(url.pathname)
  } catch (error) {
  }
  return seoData
}

middleware.ts 配置参考:
官方文档
github: How to access pathname in generateMetadata?

相关推荐

  1. 从 PostgreSQL 15 升级 16

    2024-06-15 16:00:04       31 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-06-15 16:00:04       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-06-15 16:00:04       106 阅读
  3. 在Django里面运行非项目文件

    2024-06-15 16:00:04       87 阅读
  4. Python语言-面向对象

    2024-06-15 16:00:04       96 阅读

热门阅读

  1. 深入探索 Spring Boot 自定义启动画面

    2024-06-15 16:00:04       23 阅读
  2. 解析xml文件获得元素并修改最后保存

    2024-06-15 16:00:04       26 阅读
  3. C++ 字符串处理5-手机号邮箱如何脱敏处理

    2024-06-15 16:00:04       34 阅读
  4. MongoDB 正则表达式

    2024-06-15 16:00:04       27 阅读
  5. C#中数组ProtoBuf使用问题

    2024-06-15 16:00:04       27 阅读
  6. js字符串域名把域名前缀剪切掉

    2024-06-15 16:00:04       20 阅读
  7. Html_Css问答集(6)

    2024-06-15 16:00:04       27 阅读
  8. python的random模块三choices和shuffle()

    2024-06-15 16:00:04       28 阅读
  9. 表的删除与更新

    2024-06-15 16:00:04       31 阅读