从 Next.js 迁移
这里有一些关键概念和迁移策略可以帮助你入门。利用我们文档的其余部分和我们的 Discord 社区 继续前进!
Next.js 和 Astro 之间的主要相似之处
标题为“Next.js 和 Astro 之间的主要相似之处”的部分Next.js 和 Astro 有一些相似之处,这将有助于你迁移项目
.astro
文件的语法与 JSX 相似。编写 Astro 会感觉很熟悉。- Astro 项目也可以是 SSG 或带有页面级预渲染的 SSR。
- Astro 使用基于文件的路由,并允许特殊命名的页面创建动态路由。
- Astro 是基于组件的,你的标记结构在迁移前后会很相似。
- Astro 为 React、Preact 和 Solid 提供了官方集成,因此你可以使用现有的 JSX 组件。请注意,在 Astro 中,这些文件必须具有
.jsx
或.tsx
扩展名。 - Astro 支持安装 NPM 包,包括 React 库。你现有的许多依赖项都可以在 Astro 中工作。
Next.js 和 Astro 之间的主要区别
标题为“Next.js 和 Astro 之间的主要区别”的部分在 Astro 中重建 Next.js 网站时,你会注意到一些重要的区别
-
Next.js 是一个 React 单页应用,使用
index.js
作为项目的根。Astro 是一个多页网站,index.astro
是你的主页。 -
.astro
组件不是作为返回页面模板的导出函数编写的。相反,你会将代码分成用于 JavaScript 的“代码围栏”和专用于生成 HTML 的主体部分。 -
内容驱动:Astro 旨在展示你的内容,并允许你仅在需要时选择性地加入交互性。现有的 Next.js 应用可能是为高客户端交互性而构建的,可能需要高级的 Astro 技术来包含使用
.astro
组件难以复制的项目,例如仪表盘。
转换你的 Next.js 项目
标题为“转换你的 Next.js 项目”的部分每个项目的迁移过程都会有所不同,但在从 Next.js 转换为 Astro 时,你会执行一些常见的操作。
创建一个新的 Astro 项目
标题为“创建一个新的 Astro 项目”的部分使用你的包管理器的 create astro
命令来启动 Astro 的 CLI 向导,或从 Astro 主题展示中选择一个社区主题。
你可以向 create astro
命令传递一个 --template
参数,以使用我们的官方入门模板(例如 docs
、blog
、portfolio
)之一来启动新的 Astro 项目。或者,你可以从 GitHub 上的任何现有 Astro 仓库开始一个新项目。
# launch the Astro CLI Wizardnpm create astro@latest
# create a new project with an official examplenpm create astro@latest -- --template <example-name>
# launch the Astro CLI Wizardpnpm create astro@latest
# create a new project with an official examplepnpm create astro@latest --template <example-name>
# launch the Astro CLI Wizardyarn create astro@latest
# create a new project with an official exampleyarn create astro@latest --template <example-name>
然后,将你现有的 Next 项目文件复制到新 Astro 项目中 src
之外的一个单独文件夹中。
访问 https://astro.new 查看官方入门模板的完整列表,以及在 IDX、StackBlitz、CodeSandbox 或 Gitpod 中打开新项目的链接。
安装集成(可选)
标题为“安装集成(可选)”的部分在将 Next 项目转换为 Astro 时,安装一些 Astro 的可选集成可能会很有用
-
@astrojs/react:在新 Astro 网站中重用一些现有的 React UI 组件,或继续使用 React 组件编写。
-
@astrojs/mdx:从你的 Next 项目中引入现有的 MDX 文件,或在你的新 Astro 网站中使用 MDX。
将你的源代码放在 src
中
标题为“将你的源代码放在 src 中”的部分遵循 Astro 的项目结构
-
保留 Next 的
public/
文件夹不变。Astro 和 Next 一样,使用
public/
目录存放静态资源。此文件夹及其内容无需任何更改。 -
在重建你的网站时,复制或移动 Next 的其他文件和文件夹(例如
pages
、styles
等)到 Astro 的src/
文件夹中,并遵循 Astro 的项目结构。与 Next 类似,Astro 的
src/pages/
文件夹是一个用于基于文件的路由的特殊文件夹。所有其他文件夹都是可选的,你可以按任何你喜欢的方式组织src/
文件夹的内容。Astro 项目中其他常见的文件夹包括src/layouts/
、src/components
、src/styles
、src/scripts
。
Astro 配置文件
标题为“Astro 配置文件”的部分Astro 在你的项目根目录下有一个名为 astro.config.mjs
的配置文件。它仅用于配置你的 Astro 项目和任何已安装的集成,包括 SSR 适配器。
提示:将 JSX 文件转换为 .astro
文件
标题为“提示:将 JSX 文件转换为 .astro 文件”的部分以下是将 Next .js
组件转换为 .astro
组件的一些提示
-
使用现有 Next.js 组件函数返回的 JSX 作为你 HTML 模板的基础。
-
将任何 Next 或 JSX 语法更改为 Astro 或 HTML Web 标准。例如,这包括
<Link>
、<Script>
、{children}
和className
。 -
将任何必要的 JavaScript,包括导入语句,移动到“代码围栏”(
---
)中。注意:在 Astro 中,用于条件渲染内容的 JavaScript 通常直接写在 HTML 模板内。 -
使用
Astro.props
来访问之前传递给你 Next 函数的任何附加属性。 -
决定是否需要将任何导入的组件也转换为 Astro。安装官方集成后,你可以在 Astro 文件中使用现有的 React 组件。但是,你可能希望将它们转换为
.astro
组件,特别是如果它们不需要交互性! -
用
import
语句或import.meta.glob()
替换getStaticProps()
来查询本地文件。使用fetch()
获取外部数据。
比较:JSX 与 Astro
标题为“比较:JSX 与 Astro”的部分比较以下 Next 组件和相应的 Astro 组件
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;
---import Header from "./header";import Footer from "./footer";import "./layout.css";
const res = await fetch("https://api.github.com/repos/withastro/astro");const json = await res.json();const message = json.message;const stars = json.stargazers_count || 0;---<Header /><p class="banner">Astro has {stars} 🧑🚀</p><Footer />
<style> .banner { background-color: #f4f4f4; padding: 1em 1.5em; text-align: center; margin-bottom: 1em; }<style>
迁移布局文件
标题为“迁移布局文件”的部分你可能会发现,从将 Next.js 的布局和模板转换为 Astro 布局组件开始会很有帮助。
Next 有两种不同的方法来创建布局文件,每种方法处理布局的方式都与 Astro 不同
-
pages
目录
每个 Astro 页面都明确要求存在 <html>
、<head>
和 <body>
标签,因此在不同页面之间重用布局文件是很常见的。Astro 使用<slot />
来放置页面内容,无需导入语句。注意标准的 HTML 模板和对 <head>
的直接访问
------<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 目录迁移”的部分你的 Next 项目可能有一个 pages/_document.jsx
文件,它导入 React 组件来自定义应用的 <head>
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> ); }}
-
仅使用返回的 JSX 创建一个新的 Astro 布局文件。
-
将任何 React 组件替换为
<html>
、<head>
、<slot>
和其他 HTML 标准标签。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 目录迁移”的部分Next.js 的 app/
目录布局文件由两个文件创建:一个 layout.jsx
文件用于自定义 <html>
和 <body>
的内容,以及一个 head.jsx
文件用于自定义 <head>
元素的内容。
export default function Layout({ children }) { return ( <html lang="en"> <body>{children}</body> </html> );}
export default function Head() { return ( <> <title>My Page</title> </> );}
-
仅使用返回的 JSX 创建一个新的 Astro 布局文件。
-
用一个包含页面外壳(
<html>
、<head>
和<body>
标签)和<slot/>
(而不是 React 的{children}
属性)的 Astro 布局文件替换这两个文件src/layouts/Layout.astro <html lang="en"><head><title>My Page</title></head><body><slot/></body></html>
迁移页面和文章
标题为“迁移页面和文章”的部分在 Next.js 中,你的文章要么在 /pages
目录下,要么在 /app/routeName/page.jsx
。
在 Astro 中,除非你使用内容集合,否则所有页面内容都必须位于 src/
内部。
React 页面
标题为“React 页面”的部分你现有的 Next JSX (.js
) 页面需要从 JSX 文件转换为 .astro
页面。你不能在 Astro 中使用现有的 JSX 页面文件。
这些 .astro
页面必须位于 src/pages/
内,并且会根据其文件路径自动生成页面路由。
Markdown 和 MDX 页面
标题为“Markdown 和 MDX 页面”的部分Astro 内置了对 Markdown 的支持,并为 MDX 文件提供了可选的集成。你可以重用任何现有的 Markdown 和 MDX 文件,但它们可能需要对其 frontmatter 进行一些调整,例如添加 Astro 的特殊 layout
frontmatter 属性。你将不再需要为每个 Markdown 生成的路由手动创建页面。这些文件可以放在 src/pages/
中,以利用自动基于文件的路由。
或者,你可以在 Astro 中使用内容集合来存储和管理你的内容。你将自己检索内容并动态生成这些页面。
迁移测试
标题为“迁移测试”的部分由于 Astro 输出的是原始 HTML,因此可以使用构建步骤的输出来编写端到端测试。如果你能够匹配 Next 网站的标记,那么之前编写的任何端到端测试都可能直接可用。像 Jest 和 React Testing Library 这样的测试库可以导入并在 Astro 中使用,以测试你的 React 组件。
更多信息请参阅 Astro 的测试指南。
参考:将 Next.js 语法转换为 Astro
标题为“参考:将 Next.js 语法转换为 Astro”的部分将 Next 的链接转换为 Astro
标题为“将 Next 的链接转换为 Astro”的部分将任何 Next 的 <Link to="">
、<NavLink>
等组件转换为 HTML <a href="">
标签。
<Link to="/blog">Blog</Link><a href="/blog">Blog</a>
Astro 不使用任何特殊的链接组件,但欢迎你构建自己的 <Link>
组件。然后你可以像导入和使用任何其他组件一样使用这个 <Link>
组件。
---const { to } = Astro.props;---<a href={to}><slot /></a>
将 Next 的导入转换为 Astro
标题为“将 Next 的导入转换为 Astro”的部分更新任何文件导入,以精确引用相对文件路径。这可以通过使用导入别名或完整写出相对路径来完成。
请注意,.astro
和其他几种文件类型必须使用其完整的文件扩展名进行导入。
---import Card from "../../components/Card.astro";---<Card />
将 Next 的 Children 属性转换为 Astro
标题为“将 Next 的 Children 属性转换为 Astro”的部分将任何 {children}
实例转换为 Astro 的 <slot />
。Astro 不需要以函数属性的形式接收 {children}
,它会自动在 <slot />
中渲染子内容。
------export default function MyComponent(props) { return ( <div> {props.children} </div> );}
<div> <slot /></div>
传递多组子组件的 React 组件可以使用命名插槽迁移到 Astro 组件。
查看更多关于Astro 中 <slot />
的具体用法。
将 Next 的数据获取转换为 Astro
标题为“将 Next 的数据获取转换为 Astro”的部分将任何 getStaticProps()
实例转换为 import.meta.glob()
或 getCollection()
/getEntry()
,以便从项目源中的其他文件访问数据。要获取远程数据,请使用 fetch()
。
这些数据请求是在 Astro 组件的 frontmatter 中发出的,并使用顶层 await。
---import { getCollection } from 'astro:content';
// Get all `src/content/blog/` entriesconst allBlogPosts = await getCollection('blog');
// Get all `src/pages/posts/` entriesconst 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()
导入本地文件、使用集合 API 查询或获取远程数据的信息。
将 Next 的样式转换为 Astro
标题为“将 Next 的样式转换为 Astro”的部分你可能需要将任何 CSS-in-JS 库(例如 styled-components)替换为 Astro 中其他可用的 CSS 选项。
如有必要,将任何内联样式对象 (style={{ fontWeight: "bold" }}
) 转换为内联 HTML 样式属性 (style="font-weight:bold;"
)。或者,使用 Astro 的 <style>
标签来编写局部作用域的 CSS 样式。
<div style={{backgroundColor: `#f4f4f4`, padding: `1em`}}>{message}</div><div style="background-color: #f4f4f4; padding: 1em;">{message}</div>
安装 Tailwind Vite 插件后即可支持 Tailwind。无需对你现有的 Tailwind 代码进行任何更改!
查看更多关于在 Astro 中设置样式的信息。
将 Next 的图像插件转换为 Astro
标题为“将 Next 的图像插件转换为 Astro”的部分将任何 Next 的 <Image />
组件转换为 Astro 在 .astro
或 .mdx
文件中的自有图像组件,或在你的 React 组件中适当地转换为标准的 HTML <img>
/ JSX <img />
标签。
Astro 的 <Image />
组件仅在 .astro
和 .mdx
文件中有效。请查看其组件属性的完整列表,并注意其中一些属性与 Next 的属性有所不同。
---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 包以获得更大的灵活性。
你可以在图像指南中了解更多关于在 Astro 中使用图像的信息。
指导示例:将 Next 数据获取转换为 Astro
标题为“指导示例:将 Next 数据获取转换为 Astro”的部分这是一个将 Next.js 的 Pokédex 数据获取转换为 Astro 的示例。
pages/index.js
使用 REST PokéAPI 获取并显示前 151 个宝可梦的列表。
以下是如何在 src/pages/index.astro
中重现该功能,用 fetch()
替换 getStaticProps()
。
-
识别 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,},}} -
创建
src/pages/index.astro
使用 Next 函数的返回值。将任何 Next 或 React 语法转换为 Astro,包括更改任何 HTML 全局属性的大小写。
请注意
-
.map
仍然有效! -
className
变为class
。 -
<Link>
变为<a>
。 -
在 Astro 模板中不需要
<> </>
片段。 -
key
是一个 React 属性,在 Astro 中不是li
的属性。
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> -
-
添加任何需要的导入、属性和 JavaScript
请注意
- 不再需要
getStaticProps
函数。API 数据直接在代码围栏中获取。 - 导入一个
<Layout>
组件并包裹页面模板。
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> - 不再需要
社区资源
标题为“社区资源”的部分如果你发现(或制作了!)一个关于将 Next.js 网站转换为 Astro 的有用视频或博客文章,请将其添加到此列表中!