Skip to content

从 Next.js 迁移

以下是一些可帮助你入门的关键概念和迁移策略。使用我们的其余文档和 Discord 社区 继续前进!

¥Here are some key concepts and migration strategies to help you get started. Use the rest of our docs and our Discord community to keep going!

Next.js 和 Astro 之间的主要相似之处

标题部分 Next.js 和 Astro 之间的主要相似之处

¥Key Similarities between Next.js and Astro

Next.js 和 Astro 有一些相似之处,可以帮助你迁移项目:

¥Next.js and Astro share some similarities that will help you migrate your project:

Next.js 和 Astro 之间的主要区别

标题部分 Next.js 和 Astro 之间的主要区别

¥Key Differences between Next.js and Astro

当你在 Astro 中重建 Next.js 站点时,你会注意到一些重要的差异:

¥When you rebuild your Next.js site in Astro, you will notice some important differences:

  • Next.js 是一个 React 单页应用,并使用 index.js 作为项目的根目录。Astro 是一个多页面网站,index.astro 是你的主页。

  • .astro 组件 未编写为返回页面模板的导出函数。相反,你将把代码拆分为 JavaScript 的 “代码围栏” 和专门用于你生成的 HTML 的正文。

  • content-driven:Astro 旨在展示你的内容并允许你仅根据需要选择加入交互。现有的 Next.js 应用可能是为高客户端交互性而构建的,并且可能需要高级的 Astro 技术来包含使用 .astro 组件复制更具挑战性的项目,例如仪表板。

转换你的 Next.js 项目

标题部分 转换你的 Next.js 项目

¥Convert your Next.js Project

每个项目迁移看起来都会有所不同,但从 Next.js 转换到 Astro 时你将执行一些常见操作。

¥Each project migration will look different, but there are some common actions you will perform when converting from Next.js to Astro.

创建一个新的 Astro 项目

标题部分 创建一个新的 Astro 项目

¥Create a new Astro project

使用包管理器的 create astro 命令来启动 Astro 的 CLI 向导或从 Astro 主题展示 中选择社区主题。

¥Use the create astro command for your package manager to launch Astro’s CLI wizard or choose a community theme from the Astro Theme Showcase.

你可以将 --template 参数传递给 create astro 命令,以使用我们的官方启动器之一启动新的 Astro 项目(例如 docsblogportfolio)。或者,你可以 从 GitHub 上任何现有的 Astro 存储库启动一个新项目

¥You can pass a --template argument to the create astro command to start a new Astro project with one of our official starters (e.g. docs, blog, portfolio). Or, you can start a new project from any existing Astro repository on GitHub.

终端窗口
# launch the Astro CLI Wizard
npm create astro@latest
# create a new project with an official example
npm create astro@latest -- --template <example-name>

然后,将现有的 Next 项目文件复制到 src 之外的单独文件夹中的新 Astro 项目。

¥Then, copy your existing Next project files over to your new Astro project in a separate folder outside of src.

¥Install integrations (optional)

你可能会发现安装一些 Astro 的可选集成 以便在将 Next 项目转换为 Astro 时使用非常有用:

¥You may find it useful to install some of Astro’s optional integrations to use while converting your Next project to Astro:

  • @astrojs/react:在新的 Astro 站点中重用一些现有的 React UI 组件,或者继续使用 React 组件进行编写。

  • @astrojs/mdx:从你的 Next 项目中引入现有的 MDX 文件,或者在你的新 Astro 站点中使用 MDX。

将你的源代码放在 src

标题部分 将你的源代码放在 src 中

¥Put your source code in src

Astro 的项目结构 之后:

¥Following Astro’s project structure:

  1. Keep Next’s public/ folder untouched.

    Astro uses the public/ directory for static assets, just like Next. There is no change needed to this folder, nor its contents.

  2. Copy or Move Next’s other files and folders (e.g. pages, styles etc.) into Astro’s src/ folder as you rebuild your site, following Astro’s project structure.

    Like Next, Astro’s src/pages/ folder is a special folder used for file-based routing. All other folders are optional, and you can organize the contents of your src/ folder any way you like. Other common folders in Astro projects include src/layouts/, src/components, src/styles, src/scripts.

¥The Astro config file

Astro 在项目的根目录下有一个名为 astro.config.mjs.conf 的配置文件。这仅用于配置你的 Astro 项目和任何已安装的集成,包括 SSR 适配器

¥Astro has a configuration file at the root of your project called astro.config.mjs. This is used only for configuring your Astro project and any installed integrations, including SSR adapters.

提示:将 JSX 文件转换为 .astro 文件

标题部分 提示:将 JSX 文件转换为 .astro 文件

¥Tips: Convert JSX files to .astro files

以下是将 Next .js 组件转换为 .astro 组件的一些技巧:

¥Here are some tips for converting a Next .js component into a .astro component:

  1. 使用现有 Next.js 组件函数返回的 JSX 作为 HTML 模板的基础。

  2. 更改任何 Astro 的 Next 或 JSX 语法 或 HTML Web 标准。例如,这包括 <Link><Script>{children}className

  3. 将任何必要的 JavaScript(包括 import 语句)移至 “代码围栏” (---).js 文件中。注意:JavaScript 到 有条件地渲染内容 通常直接在 Astro 中编写在 HTML 模板内。

  4. 使用 Astro.props 访问之前传递给 Next 函数的任何其他 props。

  5. 确定是否有任何导入组件也需要转换为 Astro。安装官方集成后,你可以 使用 Astro 文件中现有的 React 组件。但是,你可能希望将它们转换为 .astro 组件,尤其是当它们不需要交互时!

  6. getStaticProps() 替换为导入语句或 import.meta.glob() 以查询本地文件。使用 fetch() 获取外部数据。

参见 逐步转换的 Next .js 文件的示例

¥See an example of a Next .js file converted step-by-step.

¥Compare: JSX vs Astro

比较以下 Next 组件和相应的 Astro 组件:

¥Compare the following Next component and a corresponding Astro component:

StarCount.jsx
import Header from "./header";
import Footer from "./footer";
import "./layout.css";
export async function getStaticProps() {
const res = await fetch("https://api.github.com/repos/withastro/astro");
const json = await res.json();
return {
props: { message: json.message, stars: json.stargazers_count || 0 },
}
}
const Component = ({ stars, message }) => {
return (
<>
<Header />
<p style={{
backgroundColor: `#f4f4f4`,
padding: `1em 1.5em`,
textAlign: `center`,
marginBottom: `1em`
}}>Astro has {stars} 🧑‍🚀</p>
<Footer />
</>
)
}
export default Component;

¥Migrating Layout Files

你可能会发现从将 Next.js 布局和模板转换为 Astro 布局组件 开始很有帮助。

¥You may find it helpful to start by converting your Next.js layouts and templates into Astro layout components.

Next 有两种不同的方法来创建布局文件,每种方法处理布局的方式都与 Astro 不同:

¥Next has two different methods for creating layout files, each of which handles layouts differently than Astro:

每个 Astro 页面都明确要求存在 <html><head><body> 标签,因此跨页面重用布局文件是很常见的。Astro 使用 <slot /> 作为页面内容,无需导入语句。注意标准的 HTML 模板,并直接访问 <head>

¥Each Astro page explicitly requires <html>, <head>, and <body> tags to be present, so it is common to reuse a layout file across pages. Astro uses a <slot /> for page content, with no import statement required. Note the standard HTML templating, and direct access to <head>:

src/layouts/Layout.astro
---
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<!-- Wrap the slot element with your existing layout templating -->
<slot />
</body>
</html>

从 Next.js 的 pages 目录迁移

标题部分 从 Next.js 的 pages 目录迁移

¥Migrating from Next.js’ pages directory

你的下一个项目可能有一个 pages/_document.jsx 文件,该文件导入 React 组件来自定义应用的 <head>

¥Your Next project may have a pages/_document.jsx file that imports React components to customize your app’s <head>:

pages/_document.jsx
import Document, { Html, Head, Main, NextScript } from "next/document";
export default class MyDocument extends Document {
render() {
return (
<Html lang="en">
<Head>
<link rel="icon" href="/favicon.ico" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
  1. Make a new Astro layout file using only the returned JSX.

  2. Replace any React components with <html>, <head>, <slot>, and other HTML standard tags.

    src/layouts/Document.astro
    <html lang="en">
    <head>
    <link rel="icon" href="/favicon.ico" />
    </head>
    <body>
    <slot/>
    </body>
    </html>

从 Next.js 的 /app 目录迁移

标题部分 从 Next.js 的 /app 目录迁移

¥Migrating from Next.js’ /app directory

Next.js 的 app/ 目录布局文件由两个文件创建:layout.jsx 文件用于自定义 <html><body> 内容,head.jsx 文件用于自定义 <head> 元素内容。

¥Next.js’ app/ directory layout files are created with two files: a layout.jsx file to customize the <html> and <body> contents, and a head.jsx file to customize the <head> element contents.

app/layout.jsx
export default function Layout({ children }) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
app/head.jsx
export default function Head() {
return (
<>
<title>My Page</title>
</>
);
}
  1. Make a new Astro layout file using only the returned JSX.

  2. Replace both these files with a single Astro layout file that contains a page shell (<html>, <head>, and <body> tags) and a <slot/> instead of React’s {children} prop:

    src/layouts/Layout.astro
    <html lang="en">
    <head>
    <title>My Page</title>
    </head>
    <body>
    <slot/>
    </body>
    </html>

¥Migrating Pages and Posts

在 Next.js 中,你的帖子要么位于 /pages,要么位于 /app/routeName/page.jsx

¥In Next.js, your posts either live in /pages or /app/routeName/page.jsx.

在 Astro 中,除非你使用 内容集合,否则所有页面内容都必须位于 src/ 中。

¥In Astro, all your page content must live within src/, unless you are using content collections.

¥React pages

你现有的 Next JSX (.js) 页面需要是 从 JSX 文件转换为 .astro 页面。你不能在 Astro 中使用现有的 JSX 页面文件。

¥Your existing Next JSX (.js) pages will need to be converted from JSX files to .astro pages. You cannot use an existing JSX page file in Astro.

这些 .astro 必须位于 src/pages/ 内,并且将根据其文件路径自动生成页面路由。

¥These .astro pages must be located within src/pages/ and will have page routes generated automatically based on their file path.

¥Markdown and MDX pages

Astro 内置对 Markdown 的支持以及对 MDX 文件的可选集成。你可以重复使用任何现有的 Markdown 和 MDX 文件,但它们可能需要对其 frontmatter 进行一些调整,例如添加 Astro 的特殊 layout frontmatter 属性。你将不再需要为每个 Markdown 生成的路由手动创建页面。这些文件可以放置在 src/pages/ 内,以利用基于文件的自动路由。

¥Astro has built-in support for Markdown and an optional integration for MDX files. You can reuse any existing Markdown and MDX files, but they may require some adjustments to their frontmatter, such as adding Astro’s special layout frontmatter property. You will no longer need to manually create pages for each Markdown-generated route. These files can be placed within src/pages/ to take advantage of automatic file-based routing.

或者,你可以在 Astro 中使用 内容集合 来存储和管理你的内容。你将自己和 动态生成这些页面 检索内容。

¥Alternatively, you can use content collections in Astro to store and manage your content. You will retrieve the content yourself and generate those pages dynamically.

¥Migrating Tests

由于 Astro 输出原始 HTML,因此可以使用构建步骤的输出编写端到端测试。如果你能够匹配 Next 站点的标记,那么之前编写的任何端到端测试都可以开箱即用。可以在 Astro 中导入和使用 Jest 和 React 测试库等测试库来测试你的 React 组件。

¥As Astro outputs raw HTML, it is possible to write end-to-end tests using the output of the build step. Any end-to-end tests written previously might work out-of-the-box if you have been able to match the markup of your Next site. Testing libraries such as Jest and React Testing Library can be imported and used in Astro to test your React components.

有关更多信息,请参阅 Astro 的 测试指南

¥See Astro’s testing guide for more.

参考:将 Next.js 语法转换为 Astro

标题部分 参考:将 Next.js 语法转换为 Astro

¥Reference: Convert Next.js Syntax to Astro

¥Next Links to Astro

将任何 Next <Link to=""><NavLink> 等组件转换为 HTML <a href=""> 标签。

¥Convert any Next <Link to="">, <NavLink> etc. components to HTML <a href=""> tags.

<Link to="/blog">Blog</Link>
<a href="/blog">Blog</a>

Astro 不使用任何特殊的链接组件,但欢迎你构建自己的 <Link> 组件。然后,你可以像导入任何其他组件一样导入并使用该 <Link>

¥Astro does not use any special component for links, although you are welcome to build your own <Link> component. You can then import and use this <Link> just as you would any other component.

src/components/Link.astro
---
const { to } = Astro.props;
---
<a href={to}><slot /></a>

¥Next Imports to Astro

更新任何 file imports 以准确引用相对文件路径。这可以使用 import aliases 或通过完整写出相对路径来完成。

¥Update any file imports to reference relative file paths exactly. This can be done using import aliases, or by writing out a relative path in full.

请注意,.astro 和其他几种文件类型必须以其完整文件扩展名导入。

¥Note that .astro and several other file types must be imported with their full file extension.

src/pages/authors/Fred.astro
---
import Card from "../../components/Card.astro";
---
<Card />

下一个子属性到 Astro

标题部分 下一个子属性到 Astro

¥Next Children Props to Astro

{children} 的任何实例转换为 Astro <slot />。Astro 不需要接收 {children} 作为函数 prop,并且会自动渲染 <slot /> 中的子内容。

¥Convert any instances of {children} to an Astro <slot />. Astro does not need to receive {children} as a function prop and will automatically render child content in a <slot />.

src/components/MyComponent.astro
---
---
export default function MyComponent(props) {
return (
<div>
{props.children}
</div>
);
}
<div>
<slot />
</div>

传递多组子级的 React 组件可以使用 命名槽 迁移到 Astro 组件。

¥React components that pass multiple sets of children can be migrated to an Astro component using named slots.

查看更多有关 Astro 中 <slot /> 的具体用法 的信息。

¥See more about specific <slot /> usage in Astro.

接下来将数据请求到 Astro

标题部分 接下来将数据请求到 Astro

¥Next Data Fetching to Astro

getStaticProps() 的任何实例转换为 import.meta.glob()getCollection()/getEntryBySlug(),以便访问项目源中其他文件的数据。对于 获取远程数据,请使用 fetch()

¥Convert any instances of getStaticProps() to either import.meta.glob() or getCollection()/getEntryBySlug() in order to access data from other files in your project source. To fetch remote data, use fetch().

这些数据请求是在 Astro 组件的 frontmatter 中发出的,并使用顶层等待。

¥These data requests are made in the frontmatter of the Astro component and use top-level await.

src/pages/index.astro
---
import { getCollection } from 'astro:content';
// Get all `src/content/blog/` entries
const allBlogPosts = await getCollection('blog');
// Get all `src/pages/posts/` entries
const allPosts = Object.values(import.meta.glob('../pages/posts/*.md', { eager: true }));
const response = await fetch('https://randomuser.me/api/');
const data = await response.json();
const randomUser = data.results[0];
---

查看有关使用 import.meta.glob()使用 Collections API 进行查询获取远程数据 导入本地文件的更多信息。

¥See more about local files imports with import.meta.glob(), querying using the Collections API or fetching remote data.

¥Next Styling to Astro

你可能需要将任何 CSS-in-JS 库(例如样式组件)替换为 Astro 中其他可用的 CSS 选项。

¥You may need to replace any CSS-in-JS libraries (e.g. styled-components) with other available CSS options in Astro.

如有必要,将任何内联样式对象 (style={{ fontWeight: "bold" }}) 转换为内联 HTML 样式属性 (style="font-weight:bold;")。或者,使用 Astro <style> 标签 作为作用域 CSS 样式。

¥If necessary, convert any inline style objects (style={{ fontWeight: "bold" }}) to inline HTML style attributes (style="font-weight:bold;"). Or, use an Astro <style> tag for scoped CSS styles.

src/components/Card.astro
<div style={{backgroundColor: `#f4f4f4`, padding: `1em`}}>{message}</div>
<div style="background-color: #f4f4f4; padding: 1em;">{message}</div>

安装 Tailwind 整合 后支持 Tailwind。无需更改你现有的 Tailwind 代码!

¥Tailwind is supported after installing the Tailwind integration. No changes to your existing Tailwind code are required!

查看更多有关 Astro 样式 的信息。

¥See more about Styling in Astro.

Astro 的下一个图片插件

标题部分 Astro 的下一个图片插件

¥Next Image Plugin to Astro

将任何 Next <Image /> 组件转换为 .astro.mdx 文件中的 Astro 自带的图片组件,或者转换为 React 组件中适当的 标准 HTML <img> / JSX <img /> 标签。

¥Convert any Next <Image /> components to Astro’s own image component in .astro or .mdx files, or to a standard HTML <img> / JSX <img /> tag as appropriate in your React components.

Astro 的 <Image /> 组件仅适用于 .astro.mdx 文件。查看 其组件属性的完整列表 并注意其中几个属性与 Next 的属性不同。

¥Astro’s <Image /> component works in .astro and .mdx files only. See a full list of its component attributes and note that several will differ from Next’s attributes.

src/pages/index.astro
---
import { Image } from 'astro:assets';
import rocket from '../assets/rocket.png';
---
<Image src={rocket} alt="A rocketship in space." />
<img src={rocket.src} alt="A rocketship in space.">

在 React (.jsx) 组件中,使用标准 JSX 图片语法 (<img />)。Astro 不会优化这些映像,但你可以安装和使用 NPM 包以获得更大的灵活性。

¥In React (.jsx) components, use standard JSX image syntax (<img />). Astro will not optimize these images, but you can install and use NPM packages for more flexibility.

你可以在图片指南中了解有关 在 Astro 中使用图片 的更多信息。

¥You can learn more about using images in Astro in the Images Guide.

指导示例:下一次数据提取到 Astro

标题部分 指导示例:下一次数据提取到 Astro

¥Guided example: Next data fetching to Astro

以下是 Next.js Pokédex 数据获取转换为 Astro 的示例。

¥Here is an example of Next.js Pokédex data fetch converted to Astro.

pages/index.js 使用 REST PokéAPI 获取并显示前 151 个神奇宝贝的列表。

¥pages/index.js fetches and displays a list of the first 151 Pokémon using the REST PokéAPI.

以下是如何在 src/pages/index.astro 中重新创建它,用 fetch() 替换 getStaticProps()

¥Here’s how to recreate that in src/pages/index.astro, replacing getStaticProps() with fetch().

  1. Identify the return() JSX.

    pages/index.js
    import Link from 'next/link'
    import styles from '../styles/poke-list.module.css';
    export default function Home({ pokemons }) {
    return (
    <>
    <ul className={`plain-list ${styles.pokeList}`}>
    {pokemons.map((pokemon) => (
    <li className={styles.pokemonListItem} key={pokemon.name}>
    <Link className={styles.pokemonContainer} as={`/pokemon/${pokemon.name}`} href="/pokemon/[name]">
    <p className={styles.pokemonId}>No. {pokemon.id}</p>
    <img className={styles.pokemonImage} src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}></img>
    <h2 className={styles.pokemonName}>{pokemon.name}</h2>
    </Link>
    </li>
    ))}
    </ul>
    </>
    )
    }
    export const getStaticProps = async () => {
    const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")
    const resJson = await res.json();
    const pokemons = resJson.results.map(pokemon => {
    const name = pokemon.name;
    // https://pokeapi.co/api/v2/pokemon/1/
    const url = pokemon.url;
    const id = url.split("/")[url.split("/").length - 2];
    return {
    name,
    url,
    id
    }
    });
    return {
    props: {
    pokemons,
    },
    }
    }
  2. Create src/pages/index.astro

    Use the return value of the Next function. Convert any Next or React syntax to Astro, including changing the case of any HTML global attributes.

    Note that:

    • .map just works!

    • className becomes class.

    • <Link> becomes <a>.

    • The <> </> fragment is not required in Astro templating.

    • key is a React attribute, and is not an attribute of li in Astro.

    src/pages/index.astro
    ---
    ---
    <ul class="plain-list pokeList">
    {pokemons.map((pokemon) => (
    <li class="pokemonListItem">
    <a class="pokemonContainer" href={`/pokemon/${pokemon.name}`}>
    <p class="pokemonId">No. {pokemon.id}</p>
    <img class="pokemonImage" src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}/>
    <h2 class="pokemonName">{pokemon.name}</h2>
    </a>
    </li>
    ))}
    </ul>
  3. Add any needed imports, props, and JavaScript

    Note that:

    • the getStaticProps function is no longer needed. Data from the API is fetched directly in the code fence.
    • A <Layout> component is imported and wraps the page templating.
    src/pages/index.astro
    ---
    import Layout from '../layouts/layout.astro';
    const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151");
    const resJson = await res.json();
    const pokemons = resJson.results.map(pokemon => {
    const name = pokemon.name;
    // https://pokeapi.co/api/v2/pokemon/1/
    const url = pokemon.url;
    const id = url.split("/")[url.split("/").length - 2];
    return {
    name,
    url,
    id
    }
    });
    ---
    <Layout>
    <ul class="plain-list pokeList">
    {pokemons.map((pokemon) => (
    <li class="pokemonListItem" key={pokemon.name}>
    <a class="pokemonContainer" href={`/pokemon/${pokemon.name}`}>
    <p class="pokemonId">No. {pokemon.id}</p>
    <img class="pokemonImage" src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`} alt={`${pokemon.name} picture`}/>
    <h2 class="pokemonName">{pokemon.name}</h2>
    </a>
    </li>
    ))}
    </ul>
    </Layout>

¥Community Resources

更多迁移指南

Astro 中文网 - 粤ICP备13048890号