代码模组
Codemod 是以编程方式在代码库上运行的转换。这允许以编程方式应用大量更改,而无需手动检查每个文件。
¥Codemods are transformations that run on your codebase programmatically. This allows a large number of changes to be programmatically applied without having to manually go through every file.
Next.js 提供 Codemod 转换,以帮助在 API 更新或弃用时升级 Next.js 代码库。
¥Next.js provides Codemod transformations to help upgrade your Next.js codebase when an API is updated or deprecated.
用法
¥Usage
在终端中,导航 (cd
) 到项目文件夹,然后运行:
¥In your terminal, navigate (cd
) into your project's folder, then run:
npx @next/codemod <transform> <path>
将 <transform>
和 <path>
替换为适当的值。
¥Replacing <transform>
and <path>
with appropriate values.
-
transform
- 变换名称¥
transform
- name of transform -
path
- 要转换的文件或目录¥
path
- files or directory to transform -
--dry
进行试运行,不会编辑任何代码¥
--dry
Do a dry-run, no code will be edited -
--print
打印更改后的输出以进行比较¥
--print
Prints the changed output for comparison
代码模组
¥Codemods
15.0
将应用路由路由段配置 runtime
值从 experimental-edge
转换为 edge
¥Transform App Router Route Segment Config runtime
value from experimental-edge
to edge
app-dir-runtime-config-experimental-edge
注意:此 codemod 特定于 App Router。
¥Note: This codemod is App Router specific.
npx @next/codemod@latest app-dir-runtime-config-experimental-edge .
此 codemod 将 路由段配置 runtime
值 experimental-edge
转换为 edge
。
¥This codemod transforms Route Segment Config runtime
value experimental-edge
to edge
.
例如:
¥For example:
export const runtime = 'experimental-edge'
转变为:
¥Transforms into:
export const runtime = 'edge'
迁移到异步动态 API
¥Migrate to async Dynamic APIs
选择动态渲染的 API 以前支持同步访问,现在是异步的。你可以在 升级指南 中阅读有关此重大更改的更多信息。
¥APIs that opted into dynamic rendering that previously supported synchronous access are now asynchronous. You can read more about this breaking change in the upgrade guide.
next-async-request-api
npx @next/codemod@latest next-async-request-api .
此 codemod 将转换现在异步的动态 API(cookies()
、headers()
和 draftMode()
,来自 next/headers
),以便正确等待或在适用的情况下用 React.use()
封装。当无法自动迁移时,codemod 将添加类型转换(如果是 TypeScript 文件)或注释以通知用户需要手动检查和更新。
¥This codemod will transform dynamic APIs (cookies()
, headers()
and draftMode()
from next/headers
) that are now asynchronous to be properly awaited or wrapped with React.use()
if applicable.
When an automatic migration isn't possible, the codemod will either add a typecast (if a TypeScript file) or a comment to inform the user that it needs to be manually reviewed & updated.
例如:
¥For example:
import { cookies, headers } from 'next/headers'
const token = cookies().get('token')
function useToken() {
const token = cookies().get('token')
return token
}
export default function Page() {
const name = cookies().get('name')
}
function getHeader() {
return headers().get('x-foo')
}
转变为:
¥Transforms into:
import { use } from 'react'
import { cookies, headers, type UnsafeUnwrappedCookies } from 'next/headers'
const token = (await cookies()).get('token')
function useToken() {
const token = use(cookies()).get('token')
return token
}
export default function Page() {
const name = (await cookies()).get('name')
}
function getHeader() {
return (headers() as UnsafeUnwrappedCookies).get('x-foo')
}
当我们在页面/路由条目(page.js
、layout.js
、route.js
或 default.js
)或 generateMetadata
/generateViewport
API 中检测到对 params
或 searchParams
属性的属性访问时,它将尝试将调用站点从同步转换为异步函数,并等待属性访问。如果不能使其异步(例如使用客户端组件),它将使用 React.use
来解开 promise。
¥When we detect property access on the params
or searchParams
props in the page / route entries (page.js
, layout.js
, route.js
, or default.js
) or the generateMetadata
/ generateViewport
APIs,
it will attempt to transform the callsite from a sync to an async function, and await the property access. If it can't be made async (such as with a client component), it will use React.use
to unwrap the promise .
例如:
¥For example:
// page.tsx
export default function Page({
params,
searchParams,
}: {
params: { slug: string }
searchParams: { [key: string]: string | string[] | undefined }
}) {
const { value } = searchParams
if (value === 'foo') {
// ...
}
}
export function generateMetadata({ params }: { params: { slug: string } }) {
return {
title: `My Page - ${slug}`,
}
}
转变为:
¥Transforms into:
// page.tsx
export default function Page(props: {
params: { slug: string }
searchParams: { [key: string]: string | string[] | undefined }
}) {
const { value } = await props.searchParams
if (value === 'foo') {
// ...
}
}
export function generateMetadata(props: { params: { slug: string } }) {
const { slug } = await props.params
return {
title: `My Page - ${slug}`,
}
}
很高兴知道:当此 codemod 识别出可能需要手动干预的位置,但我们无法确定确切的修复方法时,它将向代码添加注释或类型转换,以通知用户需要手动更新。这些注释以 @next/codemod 为前缀,类型转换以
UnsafeUnwrapped
为前缀。你的构建将出错,直到明确删除这些注释。阅读更多。¥Good to know: When this codemod identifies a spot that might require manual intervention, but we aren't able to determine the exact fix, it will add a comment or typecast to the code to inform the user that it needs to be manually updated. These comments are prefixed with @next/codemod, and typecasts are prefixed with
UnsafeUnwrapped
. Your build will error until these comments are explicitly removed. Read more.
用 @vercel/functions
替换 NextRequest
的 geo
和 ip
属性
¥Replace geo
and ip
properties of NextRequest
with @vercel/functions
next-request-geo-ip
npx @next/codemod@latest next-request-geo-ip .
此 codemod 安装 @vercel/functions
并使用相应的 @vercel/functions
功能转换 NextRequest
的 geo
和 ip
属性。
¥This codemod installs @vercel/functions
and transforms geo
and ip
properties of NextRequest
with corresponding @vercel/functions
features.
例如:
¥For example:
import type { NextRequest } from 'next/server'
export function GET(req: NextRequest) {
const { geo, ip } = req
}
转变为:
¥Transforms into:
import type { NextRequest } from 'next/server'
import { geolocation, ipAddress } from '@vercel/functions'
export function GET(req: NextRequest) {
const geo = geolocation(req)
const ip = ipAddress(req)
}
14.0
迁移 ImageResponse
导入
¥Migrate ImageResponse
imports
next-og-import
npx @next/codemod@latest next-og-import .
此 codemod 将转换导入从 next/server
移动到 next/og
以使用 动态 OG 图片生成。
¥This codemod moves transforms imports from next/server
to next/og
for usage of Dynamic OG Image Generation.
例如:
¥For example:
import { ImageResponse } from 'next/server'
转变为:
¥Transforms into:
import { ImageResponse } from 'next/og'
使用 viewport
导出
¥Use viewport
export
metadata-to-viewport-export
npx @next/codemod@latest metadata-to-viewport-export .
此 codemod 将某些视口元数据迁移到 viewport
导出。
¥This codemod migrates certain viewport metadata to viewport
export.
例如:
¥For example:
export const metadata = {
title: 'My App',
themeColor: 'dark',
viewport: {
width: 1,
},
}
转变为:
¥Transforms into:
export const metadata = {
title: 'My App',
}
export const viewport = {
width: 1,
themeColor: 'dark',
}
13.2
使用内置字体
¥Use Built-in Font
built-in-next-font
npx @next/codemod@latest built-in-next-font .
此 codemod 卸载 @next/font
包并将 @next/font
导入转换为内置 next/font
。
¥This codemod uninstalls the @next/font
package and transforms @next/font
imports into the built-in next/font
.
例如:
¥For example:
import { Inter } from '@next/font/google'
转变为:
¥Transforms into:
import { Inter } from 'next/font/google'
13.0
重命名下一个图片导入
¥Rename Next Image Imports
next-image-to-legacy-image
npx @next/codemod@latest next-image-to-legacy-image .
将现有 Next.js 10、11 或 12 应用中的 next/image
导入安全地重命名为 Next.js 13 中的 next/legacy/image
。还将 next/future/image
重命名为 next/image
。
¥Safely renames next/image
imports in existing Next.js 10, 11, or 12 applications to next/legacy/image
in Next.js 13. Also renames next/future/image
to next/image
.
例如:
¥For example:
import Image1 from 'next/image'
import Image2 from 'next/future/image'
export default function Home() {
return (
<div>
<Image1 src="/test.jpg" width="200" height="300" />
<Image2 src="/test.png" width="500" height="400" />
</div>
)
}
转变为:
¥Transforms into:
// 'next/image' becomes 'next/legacy/image'
import Image1 from 'next/legacy/image'
// 'next/future/image' becomes 'next/image'
import Image2 from 'next/image'
export default function Home() {
return (
<div>
<Image1 src="/test.jpg" width="200" height="300" />
<Image2 src="/test.png" width="500" height="400" />
</div>
)
}
迁移到新的图片组件
¥Migrate to the New Image Component
next-image-experimental
npx @next/codemod@latest next-image-experimental .
通过添加内联样式并删除未使用的 props,危险地从 next/legacy/image
迁移到新的 next/image
。
¥Dangerously migrates from next/legacy/image
to the new next/image
by adding inline styles and removing unused props.
-
删除
layout
属性并添加style
。¥Removes
layout
prop and addsstyle
. -
删除
objectFit
属性并添加style
。¥Removes
objectFit
prop and addsstyle
. -
删除
objectPosition
属性并添加style
。¥Removes
objectPosition
prop and addsstyle
. -
移除
lazyBoundary
属性。¥Removes
lazyBoundary
prop. -
移除
lazyRoot
属性。¥Removes
lazyRoot
prop.
从链接组件中删除 <a>
标签
¥Remove <a>
Tags From Link Components
new-link
npx @next/codemod@latest new-link .
删除 链接组件 内的 <a>
标签,或向无法自动修复的链接添加 legacyBehavior
属性。
¥Remove <a>
tags inside Link Components, or add a legacyBehavior
prop to Links that cannot be auto-fixed.
例如:
¥For example:
<Link href="/about">
<a>About</a>
</Link>
// transforms into
<Link href="/about">
About
</Link>
<Link href="/about">
<a onClick={() => console.log('clicked')}>About</a>
</Link>
// transforms into
<Link href="/about" onClick={() => console.log('clicked')}>
About
</Link>
如果无法应用自动修复,则会添加 legacyBehavior
属性。这允许你的应用使用该特定链接的旧行为继续运行。
¥In cases where auto-fixing can't be applied, the legacyBehavior
prop is added. This allows your app to keep functioning using the old behavior for that particular link.
const Component = () => <a>About</a>
<Link href="/about">
<Component />
</Link>
// becomes
<Link href="/about" legacyBehavior>
<Component />
</Link>
11
从 CRA 迁移
¥Migrate from CRA
cra-to-next
npx @next/codemod cra-to-next
将 Create React App 项目迁移到 Next.js;创建页面路由和必要的配置来匹配行为。最初仅利用客户端渲染来防止由于 SSR 期间使用 window
而破坏兼容性,并且可以无缝启用以允许逐步采用 Next.js 特定功能。
¥Migrates a Create React App project to Next.js; creating a Pages Router and necessary config to match behavior. Client-side only rendering is leveraged initially to prevent breaking compatibility due to window
usage during SSR and can be enabled seamlessly to allow the gradual adoption of Next.js specific features.
请分享与此转换 在这次讨论中 相关的任何反馈。
¥Please share any feedback related to this transform in this discussion.
10
添加 React 导入
¥Add React imports
add-missing-react-import
npx @next/codemod add-missing-react-import
将不导入 React
的文件转换为包含导入内容,以便新的 React JSX 转换 能够工作。
¥Transforms files that do not import React
to include the import in order for the new React JSX transform to work.
例如:
¥For example:
export default class Home extends React.Component {
render() {
return <div>Hello World</div>
}
}
转变为:
¥Transforms into:
import React from 'react'
export default class Home extends React.Component {
render() {
return <div>Hello World</div>
}
}
9
将匿名组件转换为命名组件
¥Transform Anonymous Components into Named Components
name-default-component
npx @next/codemod name-default-component
版本 9 及以上。
¥Versions 9 and above.
将匿名组件转换为命名组件,以确保它们与 快速刷新 一起工作。
¥Transforms anonymous components into named components to make sure they work with Fast Refresh.
例如:
¥For example:
export default function () {
return <div>Hello World</div>
}
转变为:
¥Transforms into:
export default function MyComponent() {
return <div>Hello World</div>
}
该组件将具有基于文件名的驼峰式名称,并且它还可以与箭头函数一起使用。
¥The component will have a camel-cased name based on the name of the file, and it also works with arrow functions.
8
将 AMP HOC 转换为页面配置
¥Transform AMP HOC into page config
withamp-to-config
npx @next/codemod withamp-to-config
将 withAmp
HOC 转换为 Next.js 9 页面配置。
¥Transforms the withAmp
HOC into Next.js 9 page configuration.
例如:
¥For example:
// Before
import { withAmp } from 'next/amp'
function Home() {
return <h1>My AMP Page</h1>
}
export default withAmp(Home)
// After
export default function Home() {
return <h1>My AMP Page</h1>
}
export const config = {
amp: true,
}
6
使用 withRouter
¥Use withRouter
url-to-withrouter
npx @next/codemod url-to-withrouter
将顶层页面上已弃用的自动注入 url
属性转换为使用 withRouter
及其注入的 router
属性。在这里阅读更多内容:[https://next.nodejs.cn/docs/messages/url-deprecated](/docs/messages/url-deprecated)
¥Transforms the deprecated automatically injected url
property on top level pages to using withRouter
and the router
property it injects. Read more here: https://next.nodejs.cn/docs/messages/url-deprecated
例如:
¥For example:
import React from 'react'
export default class extends React.Component {
render() {
const { pathname } = this.props.url
return <div>Current pathname: {pathname}</div>
}
}
import React from 'react'
import { withRouter } from 'next/router'
export default withRouter(
class extends React.Component {
render() {
const { pathname } = this.props.router
return <div>Current pathname: {pathname}</div>
}
}
)
这是一个案例。所有经过改造(和测试)的案例都可以在 __testfixtures__
目录 中找到。
¥This is one case. All the cases that are transformed (and tested) can be found in the __testfixtures__
directory.