Skip to content

Nuxt3简介

1、简介

Nuxt是一个基于Vue.js的通用应用框架,主要有以下特点:

  • 服务端渲染(SSR)。Nuxt 可以将 Vue 组件渲染成 HTML,提高首屏加载速度。
  • 自动代码分层。Nuxt 将代码分为页面、组件、布局、插件等层,结构清晰。
  • 丰富的生态系统。可同时使用 Vue 生态的类库和模块。
  • 集成开发工具。提供包括代码热加载、TypeScript、ESLint等在内的开发工具。
  • 通用应用架构。可用于构建网站、web应用、移动应用等不同用途。

2、官网

英文官网: https://nuxt.com/

中文官网: https://www.nuxt.com.cn/

3、项目配置

3.1、创建项目

TIP

要求:

  • Node.js 16.10版本及更高版本
npx
pnpm
$ npx nuxi@latest init "project-name"

3.2、运行项目

npm
yarn
pnpm
$ npm run dev -- -o

3.3、项目结构

shell
nuxt3-learn          
├─ public // 静态资源           
  └─ favicon.ico  
├─ pages // 路由页面 
  ├─ books.vue
  ├─ details.vue
  └─ index.vue    
├─ server // 服务端目录           
  └─ tsconfig.json  
├─ README.md         
├─ app.vue // app入口
├─ nuxt.config.ts // nuxt配置
├─ package.json      
└─ tsconfig.json

4、Nuxt3 API

4.1、组件

4.1.1、系统组件

  • NuxtWelcome: 欢迎页面
vue
<NuxtWelcome />
  • NuxtPage: 页面
vue
// app.vue
<template>
  <div>
    <h1>app page</h1>
    <!-- pages路由入口 -->
    <NuxtPage></NuxtPage>
  </div>
</template>
  • NuxtLink: 链接,路由跳转
vue
<NuxtLink to="/details">book details</NuxtLink>
  • NuxtLayout: 布局
vue
<template>
  <!-- 默认布局 -->
  <NuxtLayout>
    <h1>app page</h1>
    <!-- 路由入口 -->
    <NuxtPage></NuxtPage>
  </NuxtLayout>
</template>
  • Head: 页面头部
vue
<Head>
  <title>app page</title>
</Head>
  • Title: 页面标题
vue
<Head>
  <Title>app page</Title>
</Head>

4.1.2、自定义组件

TIP

  • 在项目根目录下创建components目录,创建的组件会自动注册
  • 组件名以小写或驼峰命名直接导入,以目录组织使用驼峰命名加载
shell
components        
├─ modal          
  └─ dialog.vue  
└─ AppAlert.vue
vue
// AppAlert.vue
<template>
  <h1>app alert</h1>
  <span>
    <slot />
  </span>
</template>
vue
// dialog.vue
<template>
  <h1>modal dialog</h1>
  <span>
    <slot />
  </span>
</template>
app.vue
<AppAlert>
  this is a app alert
</AppAlert>
<ModalDialog>
  this is a app alert
</ModalDialog>

4.1.3、组件懒加载

TIP

组件懒加载直接在组件前加Lazy

vue
<template>
  <LazyAppAlert></LazyAppAlert>
</template>

4.2、路由

TIP

在根目录下创建pages目录,Nuxt会自动创建路由映射

4.2.1、静态路由

1、在app.vue添加入口

vue
<template>
  <div>
    <h1>app page</h1>
    <!-- pages路由入口 -->
    <NuxtPage></NuxtPage>
  </div>
</template>

2、在pages目录下创建books.vue

vue
<template>
  <div>
    <h1>book page index</h1>
  </div>
</template>

3、访问localhost:3000/books

4.2.2、动态路由

1、在pages新增页面

shell
pages            
├─ user-[group]  
  └─ [id].vue   
├─ books.vue     
├─ details.vue   
└─ index.vue

2、在user-[group]/[id].vue中添加

vue
<template>
  <div>
    <h1>user group id</h1>
    <p>{{ $route.params.group }}-{{ $route.params.id }}</p>
  </div>
</template>

3、在books.vue中添加

vue
<template>
  <div>
    <h1>book page index</h1>
    <NuxtLink to="/user-admin/1">to group user id</NuxtLink>
  </div>
</template>

4.2.3、路由跳转

books.vue中添加

vue
<template>
  <div>
    <h1>book page index</h1>
    <NuxtLink to="/details">book details</NuxtLink>
  </div>
</template>

4.2.4、嵌套路由

TIP

  • 存在与parent目录同名的文件parent.vue,访问目录下页面会默认先加载同名文件parent.vue,再加载parent/index.vue
  • 存在与parent目录同名的文件,会默认加载parent目录下的index.vue
  • 加载parent目录下的child.vue需要在parent.vue添加<NuxtPage></NuxtPage>

1、路由目录

shell
pages            
├─ parent        
  ├─ child.vue  
  └─ index.vue  
├─ books.vue     
└─ parent.vue

2、在parent.vue中添加

vue
<template>
  <div>
    <h1>parent page</h1>
    <NuxtPage></NuxtPage>
  </div>
</template>

3、在child.vue中添加

vue
<template>
  <div>
    <h1>child page</h1>
  </div>
</template>

4.2.5、编程式路由导航

vue
navigateTo({
  path: '/details'
})

4.3、布局

4.3.1、通用布局

TIP

默认使用的是layouts/default.vue布局文件

vue
// app.vue
<template>
  <!-- 默认布局 -->
  <NuxtLayout>
    <h1>app page</h1>
    <!-- 路由入口 -->
    <NuxtPage></NuxtPage>
  </NuxtLayout>
</template>

4.3.2、自定义布局

vue
// layouts/custom-layout.vue
<template>
  <div>
    <h1>自定义布局文件</h1>
    <slot></slot>
  </div>
</template>

全局自定义

vue
// parent.vue
<script setup>
definePageMeta({
  layout: 'custom-layout' 
})
</script>

局部自定义

vue
// parent.vue
<NuxtLayout name="custom-layout">
  <h1>parent page</h1>
  <NuxtPage></NuxtPage>
</NuxtLayout>

4.3.3、自定义布局模板

修改custom-layout.vue

vue
<template>
  <div>
    <h1>自定义布局文件</h1>
    <slot name="header"></slot>
    <slot></slot>
    <slot name="footer"></slot>
  </div>
</template>

修改parent.vue

vue
<template>
  <div>
    <NuxtLayout name="custom-layout">
      <template #header>
        <h1>header</h1>
      </template>
      <h1>parent page</h1>
      <NuxtPage></NuxtPage>
      <template #footer>
        <h1>footer</h1>
      </template>
    </NuxtLayout>
  </div>
</template>

4.3.4、不使用布局

vue
<script setup>
definePageMeta({
  layout: false
})
</script>

4.4、错误页面

在项目根目录下创建error.vue

vue
<template>
  error page
</template>

4.5、server

TIP

Nuxt自动扫描~/server/api, ~/server/routes, 和 ~/server/middleware目录中的文件,以注册具有HMR支持的API和服务器处理程序。

每个文件都应该导出一个用defineEventHandler()定义的默认函数。

处理程序可以直接返回JSON数据,一个Promise或使用event.node.res.end()发送响应。

4.5.1、api接口

TIP

  • 句柄文件名可以用.get, .post, .put, .delete作为后缀
  • 默认请求类型为GET,文件命名xx.get.ts等价于xx.ts
  • POST请求文件命名xx.post.ts

在项目根目录下创建server/api目录, 新建hello.ts

ts
export default defineEventHandler((event) => {
  return {
    api: 'works'
  }
})

在浏览器访问http://localhost:3000/api/hello

server/api目录下新建user.post.ts

ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  return { body }
})

使用postman发送post请求

4.5.2、middleware

TIP

每次执行请求前都会先执行middleware函数 middleware的执行顺序按命名先后排序执行

在项目根目录下创建server/middleware目录, 新建auth.ts

ts
export default defineEventHandler((event) => {
  console.log('$---', event.node.req.url);
  // $--- /api/hello
  event.context.auth = { user: 123 }
})

修改server/api/hello.ts

ts
export default defineEventHandler((event) => {
  console.log('$---', event.context.auth);
  // $--- { user: 123 } 
  return {
    api: 'works'
  }
})

在浏览器访问http://localhost:3000/api/hello

4.5.3、请求接口

vue
<template>
  <div>
    <h1>book page index</h1>
    <NuxtLink to="/details">book details</NuxtLink> |
    <NuxtLink to="/user-admin/1">to group user id</NuxtLink>

    <ul>
      <li v-for="(item, index) in data.books" :key="index">{{ item.title }}</li>
    </ul>
  </div>
</template>

<script setup>
// ref 
const { data } = await useFetch('/api/books', { method: 'GET' })
console.log(data.value.books)
</script>

4.5.4、server插件

TIP

  • Nuxt将自动读取~/server/plugins目录中的任何文件,并将它们注册为Nitro插件
  • 启动服务/编译时执行插件
ts
export default defineNitroPlugin((nitroApp) => {
  console.log('Nitro plugin', nitroApp)
})

4.6、composables

TIP

composables是一种在组件外封装和重用逻辑的方式。

  • 常见的使用场景包括:
    • 封装通用逻辑:如数据获取,验证,格式化等
    • 跨组件共享状态逻辑
    • 访问组件外的状态和功能
  • composables的常见特征:
    • 是一个纯函数,没有副作用
    • 通过返回值导出逻辑
    • 可以用在 setup() 或其他组件中
    • 存放在单独的文件中,便于重用

4.6.1、useState

TIP

useState是一个在Nuxt组件中声明状态的简单方式:

  • Nuxt3中,可以通过useState()来在组件内声明状态:
  • useState会在服务器端渲染(SSR)时自动序列化状态,在客户端保持同步。

WARNING

因为useState中的数据将被序列化为JSON, 所以它不包含任何不能序列化的内容,例如类、函数或符号,这一点很重要。

vue
// app.vue
<template>
  <div>
    <h1>app page</h1>
    <NuxtPage></NuxtPage>
  </div>
</template>

4.6.2、自定义composables

在项目根目录下创建composables目录, 新建foo.ts

ts
export const useFoo = () => {
  return useState('foo', () => 'bar')
}
vue
// 无需注册,引入直接使用
<template>
  <div>
    {{ foo }}
  </div>
</template>
<script setup>
const foo = useFoo()

4.6.3、useFetch

TIP

Nuxt3中,可以通过useFetch函数来发起数据请求

pick选取指定数据

vue
const { data } = await useFetch('/api/books', { method: 'GET', pick: ['books'] })

4.6.4、useHead

vue
<script setup lang="ts">
useHead({
  title: 'My App',
  meta: [
    { name: 'description', content: 'My amazing site.' }
  ],
  bodyAttrs: {
    class: 'test'
  },
  titleTemplate: (titleChunk) => {
    return titleChunk ? `${titleChunk} - Site Title` : 'Site Title';
  },
  script: [ { innerHTML: 'console.log(\'Hello world\')' } ]
})
</script>

4.7、插件

TIP

  • 插件现在有不同的格式,并且只接受一个参数(nuxtApp)
  • 插件会在前端页面加载时访问plugins目录中的插件

在项目根目录下创建plugins目录, 新建a.ts

shell
export default defineNuxtPlugin(nuxtApp => {
  console.log('$---global plugin a');
  // nuxtApp.provide('injected', () => 'my injected function')
  return {
    provide: {
      injected: () => 'my injected function'
    }
  }
})
vue
// app.vue
<script setup>
const { $injected } = useNuxtApp()

console.log('$---', $injected());
</script>

4.8、middleware

在项目根目录下创建middleware目录, 新建auth.ts

shell
export default defineNuxtRouteMiddleware((to, from) => {
  if (to.params.user_id === '1') {
    return abortNavigation()
  }
  return navigateTo('/')
})
vue
// app.vue
<script setup>
const { $injected } = useNuxtApp()
console.log('$---', $injected());
</script>

4.9、runtimeConfig

修改nuxt.config.ts

shell
export default defineNuxtConfig({
  runtimeConfig: {
    apiSecret: '',
    public: {
      apiBase: ''
    },
    api: {
      url: ''
    }
  }
})

创建.env文件

shell
NUXT_API_SECRET=123
NUXT_PUBLIC_API_BASE=http://localhost:3000/api

使用

vue
// app.vue
<script setup>
const config = useRuntimeConfig()
console.log('$---', config.public.apiBase);
</script>

Released under the MIT License.