likes
comments
collection
share

超详细的 nuxt3 使用分享

作者站长头像
站长
· 阅读数 122

前言

在接下来的一周中 nuxt3 将迎来 rc 版本。作为喜欢尝鲜的用户,其实早早的就开始使用 nuxt3 了。以下是我在开发过程中的 nuxt3 的使用分享,希望对大家有所帮助。当然也可看线上文档 👉 MShared

介绍

Nuxt3

开源的高性能混合型 Vue 框架。

Featues

  • 开发更快
  • 打包更小
  • 支持 vite
  • 支持 vue3
  • 支持自动引入
  • 支持文件路由
  • 支持布局系统
  • 支持多种渲染模式
  • 支持 typescript
  • 支持 composition-api

安装

基础

  1. 创建 package.json,并补充以下信息 👇
{
    "scripts": {
        "dev": "nuxi dev",
        "build": "nuxi build",
        "start": "nuxi build && node .output/server/index.mjs"
    },
    "devDependencies": {
        "nuxt3": "latest"
    }
}
  1. 在根目录下创建 app.vue
<!-- app.vue -->
<template>
    <div>Hello, World!!</div>
</template>
  1. 安装依赖
npm i
  1. 启动项目 (开发环境)
npm run dev
  1. 打包项目
npm run build
  1. 启动项目 (生产环境)
npm run start

类型声明

如果你想要开一个 ts 项目

请在根目录下创建 tsconfig.json,并补充 👇

{
  "extends": "./.nuxt/tsconfig.json"
}

为了更好的类型提示,还需要在根目录下创建 nuxt.config.ts,并补充 👇

import { defineNuxtConfig } from 'nuxt3'

export default defineNuxtConfig({
	typescript: {
		shim: false
	}
})

插件推荐

安装以下插件可以让你在 vscode 中有更友好的开发体验

自动引入

API

composables 目录下模块的同名导出会被自动引入

例如,composables 下有个 useFoo 模块

// composables/useFoo.ts
export const useFoo = 100
<script setup lang="ts">
// useFoo 将被按需自动引入
console.log(useFoo) 
</script>

嵌套

如果 API 嵌套在目录内,可以通过 index 模块导出

例如,composables/useDark 下有个 index 模块

// composables/useDark/index.ts
export const useDark = 'yes'
<script setup lang="ts">
// useDark 将被按需自动引入
console.log(useDark) 
</script>

内置

vuenuxt3 本身的 api 也支持按需自动引入

<script setup lang="ts">
// ref 将被按需自动引入
const counter = ref(1)
</script>

组件

components 目录下的组件将被自动引入

例如,components 下有个 bar 组件

<!-- components/bar.vue -->
<template>
    Bar
</template>
<!-- app.vue -->
<template>
    Hello, World!
    <Bar /> <!-- bar 组件将被按需自动引入并注册 -->
</template>

当然小写也可以

<!-- app.vue -->
<template>
    Hello, World!
    <bar /> <!-- bar 组件将被按需自动引入并注册 -->
</template>

嵌套

如果组件嵌套在目录内,可以用驼峰式

例如,components/user 下有个 avatar 组件

<!-- components/user/avatar.vue -->
<template>
    user avatar
</template>
<!-- app.vue -->
<template>
    Hello, World!
    <UserAvatar /> <!-- avatar 组件将被按需自动引入并注册 -->
</template>

文件路由

基础

  1. app.vue 中加入 NuxtPage
<template>
    <NuxtPage />
</template>
  1. pages 下的页面都将被自动路由

例如,/ 页面只需创建 pages/index.vue

<!-- pages/index.vue -->
<!-- 路由 / 将渲染 -->
<template> default </template>

又例如,/home 页面只需创建 pages/home.vue

<!-- pages/home.vue -->
<!-- 路由 /home 将渲染 -->
<template> home </template>

再例如,/about 页面只需创建 pages/about.vue

<!-- pages/about.vue -->
<!-- 路由 /about 将渲染 -->
<template> about </template>

嵌套

如果需要子路由,可以才用目录嵌套的方式

例如,/user/details 页面只需创建 pages/user/details.vue

<!-- pages/user/details.vue -->
<!-- 路由 /user/details 将渲染 -->
<template> user/details </template>

当然你还可以创建 pages/user.vue 来做成嵌套路由

<!-- pages/user.vue -->
<template>
    user
    <NuxtChild /> <!-- 子路由出口 -->
</template>

动态

如果需要动态路由,可以使用 [param] 的方式

例如,/user/:id 页面只需创建 /pages/user/[id].vue

同时可以通过 $route.params.id 获取到具体的参数

<!-- pages/user/[id].vue -->
<!-- 路由 /user/:id 将渲染 -->
<template>
    user / {{ $route.params.id }}
</template>

当然父路由也可以是动态的

例如,/user-:group/:id 页面只需创建 /pages/user-[group]/[id].vue

同时可以通过 $route.params.group 获取到具体的参数

<!-- /pages/user-[group]/[id].vue -->
<!-- 路由 /user-:group/:id 将渲染 -->
<template>
    user-{{ $route.params.group }} / {{ $route.params.id }}
</template>

导航

NuxtLink 可以用来导航

<!-- app.vue -->
<template>
    <!-- 点击将跳转到 /about 页面 -->
    <NuxtLink to="/about"> to about </NuxtLink>
</template>

当然还可以跳转外链

<!-- app.vue -->
<template>
    <!-- 点击将跳转到 百度 页面 -->
    <NuxtLink to="https://www.baidu.com/"> to baidu </NuxtLink>
</template>

元信息

我们可以通过 definePageMeta 设置当前路由的元信息

<script setup lang="ts">
// 定义元信息
definePageMeta({
    foo: 'bar'
})

// 获取当前路由
const route = useRoute()

// 获取元信息
const { meta } = route
console.log(
    toRaw(meta) // 输出 { foo: 'bar' }
)
</script>

布局系统

基础

  1. layouts 下创建的布局都可以应用到页面上

例如,创建 layouts/custom.vue 布局

<!-- layouts/custom.vue -->
<template>
    <!-- 根元素是必需的 -->
    <div>
        custom
        <slot /> <!-- 页面出口 -->
    </div>
</template>

紧接着就可以在页面中使用

例如,在 /home 页面中使用

<!-- pages/home.vue -->
<template>
	<NuxtLayout name="custom"> home </NuxtLayout>
</template>

全局

有时我们需要全局的去应用布局,这时对 app.vue 进行修改

<!-- app.vue -->
<template>
	header <!-- 这将应用到全局 -->
	<NuxtPage />
</template>

但是这是一个无法关闭的全局布局,这时可以删除 app.vue,同时创建 layouts/default.vue 布局

<!-- layouts/default.vue -->
<template>
    <div>
        header
        <slot /> <!-- 页面出口 -->
    </div>
</template>

该布局也是全局应用的,但是可以手动关闭

例如 /about 页面不需要该布局

<!-- pages/about.vue -->
<script setup lang="ts">
    definePageMeta({
        layout: false // 通过元信息的方式关闭 default 布局
    })
</script>

<template> about </template>

注意事项

如果你是使用组件的形式去应用其他布局,default 布局将与其他布局重合,那么需要手动关闭。

例如,在 /home 页面中

<script setup lang="ts">
    definePageMeta({
         layout: false // 手动关闭 default 布局
    })
</script>

<template>
    <!-- 使用组件的方式应用 custom 布局 -->
	<NuxtLayout name="custom"> home </NuxtLayout>
</template>

状态共享

基础

  1. 使用 useState 可以定义简单的共享状态

例如在 composables 中定义 useCounter

// composables/useCounter.ts
// composables 下模块的同名导出将被自动按需引入
export const useCounter = () => useState('counter', () => 100)

useState 的第一参数为 key,第二参数为初始化的工厂函数

此时在 index 页面中可以直接使用 useCounter

<!-- pages/index.vue -->
<script lang="ts" setup>
    const conuter = useCounter() // 这是按需自动引入的
</script>

<template>
    <div> <!-- 顶层的 div 包裹是必需的,不然跳转会丢失状态 -->
        <div @click="conuter++">{{ conuter }}</div>
        <NuxtLink to="/home">to home</NuxtLink>
    </div>
</template>

home 页面中也可以直接使用

<!-- pages/home.vue -->
<script lang="ts" setup>
	const conuter = useCounter()
</script>

<template>
	<div @click="conuter++">{{ conuter }}</div>
</template>

你会发现两个页面的状态是共享的 👍

注意

useState 只允许在生命周期中使用。

数据获取

基础

nuxt3 中内置了以下四种请求的方法

  • useFetch
  • useLazyFetch
  • useAsyncData
  • useLazyAsyncData
<script setup lang="ts">
// 参数一是 key
// 参数二是请求 handler,返回请求的 promise
// 解构出来的 data 为数据,error 为错误信息
const { data, error } = await useAsyncData('count', () => {
    return new Promise(resolve => resolve('mock'))
})
</script>

<template>
    <div>
        <div>data: {{ data }}</div>
        <div>error: {{ error }}</div>
    </div>
</template>

内置的 $fetch 可以直接进行请求

<script setup lang="ts">
const { data, error } = await useAsyncData('count', () => {
    return $fetch('http://...') // 请求地址
})
</script>

当然你可以用更简洁的 useFetch,它会在内部自动生成 key

<script setup lang="ts">
const { data, error } = await useFetch('http://...') // 你的请求地址
</script>

useFetch 会在内部自动使用 $fetch 作为请求方法,所以没有 useAsyncData 灵活。

lazy

useFetchuseAsyncData 会阻塞导航的跳转。

例如,index 页面跳转到 home 页面

<template>
	<NuxtLink to="/home"> to home</NuxtLink>
</template>

home 页面有 useFetch 请求

<!-- pages/home.vue -->
<script lang="ts" setup>
// 以下请求将会阻塞导航的跳转
const { data } = await useFetch('http://localhost:4000')
</script>

<template>
	<div>{{ data }}</div>
</template>

而使用对应的 lazy 模式,useLazyFetchuseLazyAsyncData 可以让导航跳转不被阻塞

<!-- pages/home.vue -->
<script lang="ts" setup>
// 使用 useFetch 的 lazy 模式,不会阻塞导航跳转
const { data } = await useLazyFetch('http://localhost:4000')
</script>

<template>
	<div>{{ data }}</div>
</template>

注意

由于 nuxt3 默认是全 SSR 的渲染模式,所以相同的业务代码会在 node 和浏览器都跑一遍。

这意味着大多数 仅支持浏览器的请求库 可能需要被迫在特定生命周期节点中运行。

具体可见 👉 SSR/访问平台特有 API

因为 axios 兼容 node 和浏览器双端的,所以你能够使用它进行请求。

<script lang="ts" setup>
const { data } = await axios.get('http://...') // 你的请求地址
</script>

不过还是推荐你使用内置请求方法。

频道

接下来也会在 哔哩哔哩 中分享,欢迎关注哦!!