从 NuxtJS 迁移
这里有一些关键概念和迁移策略可以帮助你入门。利用我们文档的其余部分和我们的 Discord 社区 继续前进!
本指南指的是 Nuxt 2,而不是较新的 Nuxt 3。虽然一些概念是相似的,但 Nuxt 3 是该框架的更新版本,可能需要为你的迁移部分采取不同的策略。
Nuxt 和 Astro 之间的主要相似之处
标题为“Nuxt 和 Astro 之间的主要相似之处”的部分Nuxt 和 Astro 有一些相似之处,这将有助于你迁移项目
- Astro 项目也可以是 SSG 或带有页面级预渲染的 SSR。
- Astro 使用基于文件的路由,并允许特殊命名的页面创建动态路由。
- Astro 是基于组件的,你的标记结构在迁移前后会很相似。
- Astro 有一个官方集成用于使用 Vue 组件。
- Astro 支持安装 NPM 包,包括 Vue 库。你也许可以保留部分或全部现有的 Vue 组件和依赖项。
Nuxt 和 Astro 之间的主要区别
标题为“Nuxt 和 Astro 之间的主要区别”的部分当你在 Astro 中重建你的 Nuxt 站点时,你会注意到一些重要的区别
-
Nuxt 是一个基于 Vue 的 SPA(单页应用)。Astro 站点是使用
.astro
组件构建的多页应用,但也支持 React、Preact、Vue.js、Svelte、SolidJS、AlpineJS 和原始 HTML 模板。 -
页面路由:Nuxt 使用
vue-router
进行 SPA 路由,并使用vue-meta
管理<head>
。在 Astro 中,你将创建独立的 HTML 页面路由,并直接控制页面的<head>
,或者在布局组件中进行控制。 -
内容驱动:Astro 的设计初衷是为了展示你的内容,并允许你仅在需要时选择性地添加交互性。现有的 Nuxt 应用可能是为高客户端交互性而构建的。Astro 具有处理内容的内置功能,例如页面生成,但可能需要高级 Astro 技术来包含那些使用
.astro
组件难以复制的项目,例如仪表盘。
转换你的 NuxtJS 项目
标题为“转换你的 NuxtJS 项目”的部分每个项目的迁移过程都会有所不同,但在从 Nuxt 转换为 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>
然后,将你现有的 Nuxt 项目文件复制到新 Astro 项目中 src
之外的一个单独文件夹里。
访问 https://astro.new 查看官方入门模板的完整列表,以及在 IDX、StackBlitz、CodeSandbox 或 Gitpod 中打开新项目的链接。
安装集成(可选)
标题为“安装集成(可选)”的部分在将 Nuxt 项目转换为 Astro 时,安装一些 Astro 的可选集成 可能会很有用
-
@astrojs/vue:在你的新 Astro 站点中重用一些现有的 Vue UI 组件,或者继续使用 Vue 组件编写。
-
@astrojs/mdx:从你的 Nuxt 项目中引入现有的 MDX 文件,或者在你的新 Astro 站点中使用 MDX。
将你的源代码放入 src
标题为“将你的源代码放入 src”的部分-
移动 Nuxt 的
static/
文件夹内容到public/
。Astro 使用
public/
目录存放静态资源,类似于 Nuxt 的static/
文件夹。 -
复制或移动 Nuxt 的其他文件和文件夹(例如
pages
、layouts
等)到 Astro 的src/
文件夹中。与 Nuxt 类似,Astro 的
src/pages/
文件夹是一个用于文件路由的特殊文件夹。所有其他文件夹都是可选的,你可以按任何你喜欢的方式组织src/
文件夹的内容。Astro 项目中其他常见的文件夹包括src/layouts/
、src/components
、src/styles
、src/scripts
。
将 Vue SFC 页面转换为 .astro
文件
标题为“将 Vue SFC 页面转换为 .astro 文件”的部分这里有一些将 Nuxt .vue
组件转换为 .astro
组件的技巧
-
使用现有 NuxtJS 组件函数的
<template>
作为你 HTML 模板的基础。 -
将任何 Nuxt 或 Vue 语法更改为 Astro 语法或 HTML Web 标准。例如,这包括
<NuxtLink>
、:class
、{{variable}}
和v-if
。 -
将
<script>
JavaScript 移入“代码栅栏”(---
)中。将组件的数据获取属性转换为服务器端 JavaScript - 见 Nuxt 数据获取转换为 Astro。 -
使用
Astro.props
来访问之前传递给你 Vue 组件的任何额外 props。 -
决定是否需要将任何导入的组件也转换为 Astro。安装了官方集成后,你可以在 Astro 文件中使用现有的 Vue 组件。但是,你可能希望将它们转换为 Astro,特别是如果它们不需要交互的话!
比较:Vue vs Astro
标题为“比较:Vue vs Astro”的部分比较下面的 Nuxt 组件和对应的 Astro 组件
<template> <div> <p v-if="message === 'Not found'"> The repository you're looking up doesn't exist </p> <div v-else> <Header/> <p class="banner">Astro has {{stars}} 🧑🚀</p> <Footer /> </div> </div></template>
<script>import Vue from 'vue'
export default Vue.extend({ name: 'IndexPage', async asyncData() { const res = await fetch('https://api.github.com/repos/withastro/astro') const json = await res.json(); return { message: json.message, stars: json.stargazers_count || 0, }; }});</script>
<style scoped>.banner { background-color: #f4f4f4; padding: 1em 1.5em; text-align: center; margin-bottom: 1em;}<style>
---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;---
{message === "Not Found" ? <p>The repository you're looking up doesn't exist</p> : <> <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>
迁移布局文件
标题为“迁移布局文件”的部分你可能会发现,从将 Nuxt 布局和模板转换为 Astro 布局组件开始会很有帮助。
每个 Astro 页面都明确要求存在 <html>
、<head>
和 <body>
标签。你的 Nuxt layout.vue
和模板将不包含这些。
注意标准的 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" /> <title>Astro</title> </head> <body> <!-- Wrap the slot element with your existing layout templating --> <slot /> </body></html>
你可能还希望重用来自 Nuxt 页面的 head
属性的代码来包含额外的站点元数据。请注意,Astro 既不使用 vue-meta
也不使用组件的 head
属性,而是直接创建 <head>
。你可以导入和使用组件,甚至在 <head>
内部,来分离和组织你的页面内容。
迁移页面和文章
标题为“迁移页面和文章”的部分在 NuxtJS 中,你的页面位于 /pages
中。在 Astro 中,除非你使用内容集合,否则所有页面内容都必须位于 src/
内。
Vue 页面
标题为“Vue 页面”的部分你现有的 Nuxt Vue (.vue
) 页面需要从 Vue 文件转换为 .astro
页面。你不能在 Astro 中使用现有的 Vue 页面文件。
这些 .astro
页面必须位于 src/pages/
内,并且会根据其文件路径自动生成页面路由。
动态文件路径命名
标题为“动态文件路径命名”的部分在 Nuxt 中,你的动态页面使用下划线来表示一个动态页面属性,然后该属性会传递给页面生成
目录pages/
目录pokemon/
- _name.vue
- index.vue
- nuxt.config.js
要转换为 Astro,请将这个带下划线的动态路径属性(例如 _name.vue
)更改为用一对中括号包裹(例如 [name].astro
)
目录src/
目录pages/
目录pokemon/
- [name].astro
- index.astro
- astro.config.mjs
Markdown 和 MDX 页面
标题为“Markdown 和 MDX 页面”的部分Astro 对 Markdown 有内置支持,并为 MDX 文件提供了可选集成。你可以重用任何现有的 Markdown 和 MDX 页面,但它们可能需要对其 frontmatter 进行一些调整,例如添加 Astro 的特殊 layout
frontmatter 属性。
你将不再需要为每个 Markdown 生成的路由手动创建页面,或使用像 @nuxt/content
这样的外部包。这些文件可以放在 src/pages/
中,以利用自动化的基于文件的路由。
迁移测试
标题为“迁移测试”的部分由于 Astro 输出的是原始 HTML,因此可以使用构建步骤的输出来编写端到端测试。如果你能够匹配 Nuxt 站点的标记,那么之前编写的任何端到端测试都可能开箱即用。像 Jest 和 Vue Testing Library 这样的测试库可以导入并在 Astro 中使用,以测试你的 Vue 组件。
更多信息请参阅 Astro 的测试指南。
参考:将 NuxtJS 语法转换为 Astro
标题为“参考:将 NuxtJS 语法转换为 Astro”的部分Nuxt 局部变量转换为 Astro
标题为“Nuxt 局部变量转换为 Astro”的部分要在 Astro 组件的 HTML 中使用局部变量,请将双大括号改为单大括号
---const message = "Hello!"---<p>{{message}}</p><p>{message}</p>
Nuxt 属性传递转换为 Astro
标题为“Nuxt 属性传递转换为 Astro”的部分要在 Astro 组件中绑定属性或组件属性,请将此语法更改为以下内容
------<p v-bind:aria-label="message">...</p><!-- Or --><p :aria-label="message">...</p><!-- Also support component props --><Header title="Page"/>
<p aria-label={message}>...</p><!-- Also support component props --><Header title={"Page"}/>
Nuxt 链接转换为 Astro
标题为“Nuxt 链接转换为 Astro”的部分将任何 Nuxt <NuxtLink to="">
组件转换为 HTML <a href="">
标签。
<NuxtLink to="/blog">Blog</Link><a href="/blog">Blog</a>
Astro 不使用任何特殊的链接组件,但欢迎你构建自定义链接组件。然后你可以像导入和使用任何其他组件一样导入和使用这个 <Link>
。
---const { to } = Astro.props---<a href={to}><slot /></a>
Nuxt 导入转换为 Astro
标题为“Nuxt 导入转换为 Astro”的部分如有必要,更新任何文件导入,以精确引用相对文件路径。这可以通过使用导入别名或完整地写出相对路径来完成。
请注意,.astro
和其他几种文件类型必须使用其完整的文件扩展名进行导入。
---import Card from `../../components/Card.astro`;---<Card />
Nuxt 动态页面生成转换为 Astro
标题为“Nuxt 动态页面生成转换为 Astro”的部分在 Nuxt 中,要生成动态页面,你必须
- 使用 SSR。
- 在
nuxt.config.js
中使用generate
函数来定义所有可能的静态路由。
在 Astro 中,你同样有两个选择
- 使用 SSR.
- 在 Astro 页面的 frontmatter 中导出一个
getStaticPaths()
函数,以告知框架动态生成哪些静态路由。
将 Nuxt 中的 generate
函数转换为 Astro 中的 getStaticPaths
函数。
标题为“将 Nuxt 中的 generate 函数转换为 Astro 中的 getStaticPaths 函数。”的部分要生成多个页面,请将 nuxt.config.js
中用于创建路由的函数替换为直接在动态路由页面本身内部的 getStaticPaths()
{ // ... generate: { async routes() { // Axios is required here unless you're using Node 18 const res = await axios.get("https://pokeapi.co/api/v2/pokemon?limit=151") const pokemons = res.data.results; return pokemons.map(pokemon => { return '/pokemon/' + pokemon.name }) } }}
---export const getStaticPaths = async () => { const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151") const resJson = await res.json(); const pokemons = resJson.results; return pokemons.map(({ name }) => ({ params: { name }, }))}// ...---<!-- Your template here -->
Nuxt 数据获取转换为 Astro
标题为“Nuxt 数据获取转换为 Astro”的部分Nuxt 有两种获取服务器端数据的方法
在 Astro 中,在页面的代码栅栏内获取数据。
迁移以下内容
{ // ... async asyncData() { const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151") const resJson = await res.json(); const pokemons = resJson.results; return { pokemons, } },}
到一个没有包装函数的代码栅栏
---const res = await fetch("https://pokeapi.co/api/v2/pokemon?limit=151")const resJson = await res.json();const pokemons = resJson.results;---
<!-- Your template here -->
Nuxt 样式转换为 Astro
标题为“Nuxt 样式转换为 Astro”的部分Nuxt 利用 Vue 的组件样式来生成页面的样式。
<template> <!-- Your template here --></template>
<script> // Your server logic here</script>
<style scoped> .class { color: red; }</style>
类似地,在 Astro 中,你可以在页面的模板中放入一个 <style>
元素,为组件提供作用域内的样式。
---// Your server logic here---
<style> .class { color: red; }</style>
全局样式
标题为“全局样式”的部分在 Astro 中,<style>
标签默认是作用域内(scoped)
的。要使 <style>
标签变为全局的,请用 is:global
属性标记它
<style is:global> p { color: red; }</style>
预处理器支持
标题为“预处理器支持”的部分Astro 支持最流行的 CSS 预处理器,只需将它们作为开发依赖项安装即可。例如,要使用 SCSS
npm install -D sass
完成此操作后,你就可以在 Vue 组件中使用 .scss
或 .sass
样式,无需修改。
<p>Hello, world</p><style lang="scss">p { color: black;
&:hover { color: red; }}</style>
查看更多关于在 Astro 中设置样式的信息。
Nuxt 图像插件转换为 Astro
标题为“Nuxt 图像插件转换为 Astro”的部分将任何 Nuxt 的 <nuxt-img/>
或 <nuxt-picture/>
组件转换为Astro 自己的图像组件(在 .astro
或 .mdx
文件中),或在你的 Vue 组件中酌情转换为标准的 HTML <img>
或 <picture>
标签。
Astro 的 <Image />
组件仅在 .astro
和 .mdx
文件中有效。请参阅其组件属性的完整列表,并注意其中有几个属性与 Nuxt 的属性不同。
---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.">
在你的 Astro 应用的 Vue (.vue
) 组件中,使用标准的 JSX 图像语法 (<img />
)。Astro 不会优化这些图像,但你可以安装和使用 NPM 包以获得更大的灵活性。
你可以在图像指南中了解更多关于在 Astro 中使用图像的信息。
引导示例:查看步骤!
标题为“引导示例:查看步骤!”的部分这是一个将 Nuxt Pokédex 数据获取转换为 Astro 的示例。
pages/index.vue
使用 REST PokéAPI 获取并显示前 151 只宝可梦的列表。
下面是如何在 src/pages/index.astro
中重现该功能,用 fetch()
替换 asyncData()
。
-
在 Vue SFC 中识别
<template>
和<style>
。pages/index.vue <template><ul class="plain-list pokeList"><li v-for="pokemon of pokemons" class="pokemonListItem" :key="pokemon.name"><NuxtLink class="pokemonContainer" :to="`/pokemon/${pokemon.name}`"><p class="pokemonId">No. {{pokemon.id}}</p><imgclass="pokemonImage":src="`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${pokemon.id}.png`":alt="`${pokemon.name} picture`"/><h2 class="pokemonName">{{pokemon.name}}</h2></NuxtLink></li></ul></template><script>import Vue from 'vue'export default Vue.extend({name: 'IndexPage',layout: 'default',async asyncData() {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 {pokemons,}},head() {return {title: "Pokedex: Generation 1"}}});</script><style scoped>.pokeList {display: grid;grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) );gap: 1rem;}/* ... */</style> -
创建
src/pages/index.astro
使用 Nuxt SFC 的
<template>
和<style>
标签。将任何 Nuxt 或 Vue 语法转换为 Astro。请注意
-
<template>
被移除 -
<style>
的scoped
属性被移除 -
v-for
变为.map
。 -
:attr="val"
变为attr={val}
-
<NuxtLink>
变为<a>
。 -
在 Astro 模板中不需要
<> </>
片段。
src/pages/index.astro ------<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><style>.pokeList {display: grid;grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) );gap: 1rem;}/* ... */</style> -
-
添加任何需要的导入、props 和 JavaScript
请注意
asyncData
函数不再需要。API 数据直接在代码栅栏中获取。- 导入一个
<Layout>
组件,并用它包裹页面模板。- 我们的
head()
Nuxt 方法被传递给<Layout>
组件,然后作为属性传递给<title>
元素。
- 我们的
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 title="Pokedex: Generation 1"><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><style>.pokeList {display: grid;grid-template-columns: repeat( auto-fit, minmax(250px, 1fr) );gap: 1rem;}/* ... */</style>
社区资源
标题为“社区资源”的部分如果你发现(或制作了!)一个关于将 Nuxt 网站转换为 Astro 的有用视频或博客文章,请将其添加到此列表!