跳至內容

程式碼修改工具

程式碼修改器是一種以程式化方式在您的程式碼庫上執行的轉換工具。這允許以程式化的方式套用大量變更,而無需手動檢查每個檔案。

當 API 更新或棄用時,Next.js 提供程式碼修改器轉換來協助您升級 Next.js 程式碼庫。

用法

在您的終端機中,導覽 (cd) 至您的專案資料夾,然後執行

終端機
npx @next/codemod <transform> <path>

<transform><path> 替換為適當的值。

  • transform - 轉換的名稱
  • path - 要轉換的檔案或目錄
  • --dry 進行測試執行,不會修改任何程式碼
  • --print 印出修改後的輸出以便比較

程式碼修改器

15.0

將應用程式路由器路由區段設定檔的 runtime 值從 experimental-edge 轉換為 edge

app-dir-runtime-config-experimental-edge

注意:此程式碼修改器專用於應用程式路由器。

終端機
npx @next/codemod@latest app-dir-runtime-config-experimental-edge .

此程式碼修改器會將 路由區段設定檔 runtime 的值從 experimental-edge 轉換為 edge

例如:

export const runtime = 'experimental-edge'

轉換成:

export const runtime = 'edge'

遷移至非同步動態 API

先前支援同步存取的動態渲染 API 現在已改為非同步。您可以在升級指南中閱讀更多關於此重大變更的資訊。

next-async-request-api
終端機
npx @next/codemod@latest next-async-request-api .

此程式碼修改器將會轉換現已改為非同步的動態 API(來自 next/headerscookies()headers()draftMode()),使其被正確地 await 或在適用的情況下以 React.use() 包裝。當無法自動遷移時,程式碼修改器會新增一個類型轉換(如果是 TypeScript 檔案)或註釋,以告知使用者需要手動檢閱和更新。

例如:

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')
}

轉換成:

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.jslayout.jsroute.jsdefault.js)或 generateMetadata / generateViewport API 中偵測到對 paramssearchParams 屬性的存取時,它會嘗試將呼叫點從同步函式轉換為非同步函式,並 await 屬性存取。如果無法轉換為非同步(例如在客戶端元件中),它將使用 React.use 來解開 Promise。

例如:

// 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}`,
  }
}

轉換成:

// 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}`,
  }
}

注意事項:當此程式碼修改器識別出可能需要手動介入的位置,但我們無法確定確切的修復方法時,它會在程式碼中新增註釋或類型轉換,以告知使用者需要手動更新。這些註釋以 @next/codemod 為前綴,類型轉換以 UnsafeUnwrapped 為前綴。您的建置將會出錯,直到這些註釋被明確移除。了解更多

NextRequestgeoip 屬性替換為 @vercel/functions

next-request-geo-ip
終端機
npx @next/codemod@latest next-request-geo-ip .

此程式碼修改器會安裝 @vercel/functions 並使用對應的 @vercel/functions 功能轉換 NextRequestgeoip 屬性。

例如:

import type { NextRequest } from 'next/server'
 
export function GET(req: NextRequest) {
  const { geo, ip } = req
}

轉換成:

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)
}

將使用具名匯出的 next/dynamic 導入轉換為返回具有 default 屬性的物件 next-dynamic-access-named-export

終端機
npx @next/codemod@latest next-dynamic-access-named-export .

此程式碼修改器會轉換使用 next/dynamic 的動態導入,以確保在存取具名匯出時,它們會返回具有 default 屬性的物件。這與 React.lazy 的行為一致,並解決了從伺服器元件中的客戶端元件存取具名匯出的問題。

例如:

import dynamic from 'next/dynamic'
 
const ComponentA = dynamic(() =>
  import('../components/a').then((mod) => mod.default)
)
 
const ComponentB = dynamic(() =>
  import('../components/b').then((mod) => mod.ComponentB)
)

轉換成:

import dynamic from 'next/dynamic'
 
const ComponentA = dynamic(() =>
  import('../components/a').then((mod) => ({ default: mod.default }))
)
 
const ComponentB = dynamic(() =>
  import('../components/b').then((mod) => ({ default: mod.ComponentB }))
)

注意事項:此程式碼修改器僅影響使用 next/dynamic 並存取具名匯出的動態導入。

14.0

遷移 ImageResponse 導入

next-og-import
終端機
npx @next/codemod@latest next-og-import .

此程式碼修改器會將導入從 next/server 移至 next/og,以使用動態 OG 圖片產生

例如:

import { ImageResponse } from 'next/server'

轉換成:

import { ImageResponse } from 'next/og'

使用 viewport 匯出

metadata-to-viewport-export
終端機
npx @next/codemod@latest metadata-to-viewport-export .

此程式碼修改器會將特定 viewport 中繼資料遷移到 viewport 匯出。

例如:

export const metadata = {
  title: 'My App',
  themeColor: 'dark',
  viewport: {
    width: 1,
  },
}

轉換成:

export const metadata = {
  title: 'My App',
}
 
export const viewport = {
  width: 1,
  themeColor: 'dark',
}

13.2

使用內建字體

built-in-next-font
終端機
npx @next/codemod@latest built-in-next-font .

此程式碼修改器會解除安裝 @next/font 套件,並將 @next/font 導入轉換為內建的 next/font

例如:

import { Inter } from '@next/font/google'

轉換成:

import { Inter } from 'next/font/google'

13.0

重新命名 Next Image 導入

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

例如:

pages/index.js
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>
  )
}

轉換成:

pages/index.js
// '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>
  )
}

遷移至新的圖片元件

next-image-experimental
終端機
npx @next/codemod@latest next-image-experimental .

透過新增內嵌樣式和移除未使用的屬性,將 next/legacy/image 遷移至新的 next/image(此操作具風險性)。

  • 移除 layout 屬性並新增 style 屬性。
  • 移除 objectFit 屬性並新增 style 屬性。
  • 移除 objectPosition 屬性並新增 style 屬性。
  • 移除 lazyBoundary 屬性。
  • 移除 lazyRoot 屬性。

移除連結元件內的 <a> 標籤,或對無法自動修復的連結新增 legacyBehavior 屬性。

例如:

<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 屬性。這讓您的應用程式可以針對特定連結繼續使用舊的行為模式。

const Component = () => <a>About</a>
 
<Link href="/about">
  <Component />
</Link>
// becomes
<Link href="/about" legacyBehavior>
  <Component />
</Link>

11

從 CRA 遷移

cra-to-next
終端機
npx @next/codemod cra-to-next

將 Create React App 專案遷移到 Next.js;建立頁面路由器和必要的設定以符合行為模式。最初會利用僅限客戶端渲染,以防止在 SSR 期間使用 window 造成不相容的狀況,並且可以無縫啟用 SSR 以允許逐步採用 Next.js 特定的功能。

請在此討論串中分享任何與此轉換相關的意見回饋。

10

新增 React 導入

add-missing-react-import
終端機
npx @next/codemod add-missing-react-import

將不匯入 React 的檔案轉換為包含匯入,以便新的 React JSX 轉換 能夠正常運作。

例如:

my-component.js
export default class Home extends React.Component {
  render() {
    return <div>Hello World</div>
  }
}

轉換成:

my-component.js
import React from 'react'
export default class Home extends React.Component {
  render() {
    return <div>Hello World</div>
  }
}

9

將匿名元件轉換為命名元件

name-default-component
終端機
npx @next/codemod name-default-component

版本 9 及以上。

將匿名元件轉換為命名元件,以確保它們能與 快速重新整理 搭配使用。

例如:

my-component.js
export default function () {
  return <div>Hello World</div>
}

轉換成:

my-component.js
export default function MyComponent() {
  return <div>Hello World</div>
}

元件將會根據檔案名稱以駝峰式命名法命名,並且也適用於箭頭函式。

8

將 AMP HOC 轉換為頁面設定

withamp-to-config
終端機
npx @next/codemod withamp-to-config

withAmp 高階元件 (HOC) 轉換為 Next.js 9 頁面配置。

例如:

// 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

url-to-withrouter
終端機
npx @next/codemod url-to-withrouter

將頂層頁面上已棄用的自動注入 url 屬性轉換為使用 withRouter 以及它注入的 router 屬性。 在此處閱讀更多資訊:https://nextjs.dev.org.tw/docs/messages/url-deprecated

例如:

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__ 目錄 中找到。