undefined
前言 Nuxt.js是一个免费且开放源代码的Web应用框架,基于Vue.js、Nitro以及Vite。Nuxt.js的灵感来源于作用类似但基于React.js的框架Next.js。(维基百科 )
创建项目 npm 1 2 npx nuxi init <project_name> cd <project_name>
pnpm 1 2 pnpm dlx nuxi init <project_name> cd <project_name>
下载依赖 npm
yarn
pnpm 1 pnpm install --shamefully-hoist
项目结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + assets // 资源 + components // 组件 + composables // 组合API + layout // 布局 + pages // 页面 - index.vue // 首页 + plugins // 插件 + public // 静态资源目录 - app.vue // 应用程序入口 - app.config.ts // 应用程序配置 - nuxt.config.ts // Nuxt配置 - package.json - package-lock.json - README.md - tsconfig.json // TS配置
基本命令 打包项目
打包项目并提前渲染路由
打包项目并在本地预览
运行项目
生成预制文件
生成.next/目录和*.tx文件
在npm install之后会自动执行nuxt prepare
配置文件 定义配置
runtimeConfig:运行时配置,可以通过环境变量覆盖
key1:定义在public外部表示仅服务端配置key2:定义在public内部表示客户端和服务端配置
appConfig:应用配置,构建时变量app:应用页面配置
app.head:所有页面的<head></head>配置
ssr:是否是SSR项目
true:缺省值,SSR项目false:SPA项目
router:路由配置
router.options.hashMode:在SPA模式下切换路由模式为Hash模式
nuxt.config.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 export default defineNuxtConfig ({ runtimeConfig : { key1 : "value" , public : { key2 : "value" } }, appConfig : { key3 : "value" }, app : { head : { title : "" , charset : "UTF-8" , viewport : "" , meta : [ { name : "keywords" , content : "" }, { name : "description" , content : "" } ], link : [ { rel : "" , href : "favicon.ico" , type : "image/x-icon" } ], style : [ { children : "body { color: red }" } ], script : [ { src : "" , body : true } ] } }, ssr : false , router : { options : { hashMode : true } } });
通过环境变量覆盖runtimeConfig运行时配置
使用全大写的、以NUXT_为前缀的蛇形命名环境变量覆盖对应的小驼峰命名的运行时配置
.env 1 2 NUXT_KEY1 = "" NUXT_PUBLIC_KEY2 = ""
在页面内修改app配置 通过useHead()修改页面配置 1 2 3 4 5 6 7 8 <script setup> useHead({ title: "", bodyAttrs: { class: "" } }); </script>
通过SEO组件修改页面配置 1 2 3 4 5 6 7 <template> <Head> <Meta name="" content=""> <Style></Style> <Link></Link> </Head> </template>
1 2 3 4 5 <template> <Meta name="" content=""> <Style></Style> <Link></Link> </template>
使用运行时配置 1 2 3 4 5 6 7 8 9 10 11 12 <script setup> const runtimeConfig = useRuntimeConfig(); if (process.server) { console.log(runtimeConfig.key1); console.log(runtimeConfig.public.key2); } if (process.client || typeof window === "object") { console.log(runtimeConfig.public.key2); } </script>
使用应用配置 app.vue 1 2 3 4 5 6 7 <script setup> const appConfig = useAppConfig(); onMounted(function () { console.log(appConfig.key3); // "value" }); </script>
内置组件
SEO组件
<Html>、<Body>、<Head>、<Title>、<Meta>、<Style>、<Link>、<NoScript>、<Base>
欢迎组件,来自@nuxt/ui
布局组件
占位组件
pages/index.vue
app.vue 1 2 3 <template> <NuxtPage></NuxtPage> </template>
fallback-tag:预显示的标签,缺省值为spanfallback:预显示的内容
通过属性 来配置预渲染
1 2 3 <template> <ClientOnly fallback-tag="span" fallback="loading..."></ClientOnly> </template>
通过插槽 来配置预渲染
1 2 3 4 5 <template> <ClientOnly> <span #fallback>loading...</span> </ClientOnly> </template>
内置别名
别名
路径根目录
~、~~、@、@@
项目根目录
assets
项目根目录/assets
/、public
项目根目录/public
CSS样式 全局CSS样式 在app.vue中定义全局CSS 1 2 3 4 5 <style> * { color: red; } </style>
在配置文件引入CSS文件作为全局CSS 定义css文件 assets/styles/main.css
修改配置文件 nuxt.config.ts 1 2 3 export default defineNuxtConfig ({ css : ["~/assets/styles/main.css" ] });
局部CSS样式 1 2 3 4 5 <style scope> * { color: red; } </style>
通过Sass定义CSS样式 下载依赖
全局CSS样式 在配置文件中引入Sass文件作为全局CSS assets/styles/main.scss
nuxt.config.ts 1 2 3 export default defineNuxtConfig ({ css : ["~/assets/styles/main.scss" ] });
局部CSS样式 定义Sass文件 assets/styles/variables.scss
使用Sass文件 手动引入Sass文件 在Sass文件中引入其他Sass文件
1 2 3 4 5 @use "~/assets/styles/variables.scss" as vb;* { color : vb.$mainColor; }
1 2 3 4 5 @use "~/assets/styles/variables.scss" as *;* { color : $mainColor; }
在Vue文件中引入Sass文件
1 2 3 4 5 6 7 <style scope lang="scss"> @use "~/assets/styles/variables.scss" as vb; * { color: vb.$mainColor; } </style>
1 2 3 4 5 6 7 <style scope lang="scss"> @use "~/assets/styles/variables.scss" as *; * { color: $mainColor; } </style>
通过配置文件自动引入Sass文件 nuxt.config.ts 1 2 3 4 5 6 7 8 9 10 11 export default defineNuxtConfig ({ vite : { css : { preprocessorOtions : { scss : { additionalData : "@use '~/assets/styles/main.scss' as *;" } } } } });
资源文件 1 2 3 + assets + images - img.png
在Vue文件中引入资源文件
1 2 3 <template> <img src="assets/images/img.png" /> </template>
1 2 3 4 5 6 <template> <img :src="Img" /> </template> <script setup> import Img from "assets/images/img.png"; </script>
在CSS中引入资源文件 1 2 3 * { background-image : url ("assets/images/img.png" ); }
字体图标 1 2 3 4 + assets + iconfonts - iconfont.css - iconfont.ttf
assets/iconfonts/iconfont.css 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @font-face { font-family : "iconfont" ; src : url ("assets/iconfonts/iconfont.ttf" ) format ("truetype" ); } .iconfont { font-family : "iconfont" !important ; font-size : 16px ; font-size : normal; -webkit-font-smoothing : antialiased; -moz-osx-font-smoothing : grayscale; } .icon-home :before { content : "\e600" ; }
修改配置文件 nuxt.config.ts 1 2 3 export default defineNuxtConfig ({ css : ["~/assets/iconfonts/iconfont.css" ] });
在Vue文件中引入字体图标 1 2 3 <template> <i class="iconfont icon-home"></i> </template>
路由 创建页面
除了手动创建Vue文件实现创建页面,还可以通过脚手架工具创建页面
1 npx nuxi add page <page_name>
1 npx nuxi add page <page_name>/index
静态路由及路由跳转
1 2 3 + pages + home - index.vue
1 2 3 4 + pages - father.vue + father - son.vue
通过template跳转路由
<NuxtPage></NuxtPage>是对router-view的封装
<NuxtLink></NuxtLink>是对router-link的封装
to、href:定义跳转目标replace:是否替换当前路由active-class:链接为激活状态时的Class,缺省值为router-link-activetarget:以何种方式打开新的页面
_self:缺省值,在当前页面打开页面_blank:在新的页面打开页面
replace:改为替换路由而不是跳转路由exteral:标记为外部路由跳转,会为HTML标签自动添加rel="noopener noreferrer"属性。对于href属性值是http://或https://开头的外部链接,这个exteral是缺省值
pages/index.vue 1 2 3 4 5 6 7 <template> <NuxtLink to="/home"> <button>首页</button> </NuxtLink> <NuxtPage></NuxtPage> </template>
query:指定查询字符串
pages/index.vue 1 2 3 4 5 6 7 8 9 10 11 12 <template> <NuxtLink :to="{ path: '/home', query: { key: 'value' } }"> <button>首页</button> </NuxtLink> <NuxtPage></NuxtPage> </template>
通过script跳转路由 有历史记录的跳转 1 2 3 4 5 6 7 8 <script setup> navigateTo({ path: "/home", query: { key: "value" } }); </script>
1 2 3 <script setup> navigateTo("/home"); </script>
无历史记录的跳转 1 2 3 4 5 6 7 8 9 10 <script setup> navigateTo({ path: "/home", query: { key: "value" } }, { replace: true }); </script>
1 2 3 <script setup> navigateTo("/home", { replace: true }); </script>
跳转到外部 1 2 3 <script setup> navigateTo("https://example.com", { external: true }); </script>
useRouter() 传送门
1 2 3 4 <script setup> const router = useRouter(); router.push("/home"); </script>
动态路由及路由跳转 1 2 3 + pages + home - [key].vue
通过script跳转路由 pages/home/[id].vue 1 2 3 4 <script setup> const route = useRoute(); const { key } = route.params; </script>
多级路由 1 2 3 4 + pages - home.vue + home - index.vue
404页面 1 2 + pages - [...slug].vue
pages/[...slug].vue 1 <template>404</template>
查询字符串参数 1 http://example.com/?key=value
1 2 3 4 <script setup> const route = useRoute(); const { key } = route.query; </script>
路由中间件
中间件函数如果返回的是零值,则继续下一个中间件
如果返回的是navigateTo()则跳转到对应路由,不会继续下一个中间件
如果返回的是{ statusCode: 404, statusMessage: "error" }则表示设置响应码和响应描述,响应描述只能是英文字符串
匿名路由中间件(内联路由中间件) 1 2 3 4 5 6 7 8 9 10 11 12 <script setup> definePageMeta({ middleware: [ function (to, from) { ... }, function (to, from) { ... } ] }); </script>
命名路由中间件 middleware/home.ts 1 2 3 export default defineNuxtRouteMiddleware (function (to, from ) { ... });
1 2 3 4 5 <script setup> definePageMeta({ middleware: ["home"] }); </script>
全局中间件
文件名以.global.ts结尾的中间件是全局中间件,每个页面无需手动引入
全局中间件优先级最高
middleware/home.global.ts 1 2 3 export default defineNuxtRouteMiddleware (function (to, from ) { ... });
为不同路由设置不同渲染模式
ssr: true:SSRssr: fale:SPAstatic: true:只会在构建时生成一次静态页面swr: true:自动重新生成静态页面,间隔时间缺省值为1秒
nuxt.config.ts 1 2 3 4 5 6 7 8 export default defineNuxtConfig ({ routeRules : { "/" : { ssr : true }, "/home" : { ssr : fale }, "/page" : { static : true }, "/log" : { swr : true } } });
布局
默认布局的定义和使用 layouts/default.vue 1 2 3 4 5 6 7 <template> <div class="header"></div> <div class="main"> <slot></slot> </div> <div class="footer"></div> </template>
name:定义使用的布局
default:缺省值,使用默认布局
app.vue 1 2 3 4 5 <template> <NuxtLayout name="default"> <NuxtPage></NuxtPage> </NuxtLayout> </template>
当前页面使用其他的布局
app.vue 1 2 3 4 5 <template> <NuxtLayout> <NuxtPage></NuxtPage> </NuxtLayout> </template>
pages/home.vue 1 2 3 4 5 <script setup> definePageMeta({ layout: "" }); </script>
插件 定义插件 在app.vue中定义插件 app.vue 1 2 3 4 5 6 7 <script setup> const nuxtApp = useNuxtApp(); nuxtApp.provider("fn", function () { ... }); nuxtApp.provider("key", "value"); </script>
在plugins目录内定义插件
使用.ts作为插件的文件名后缀时,在服务端渲染和客户端渲染时都可以使用
使用.server.ts作为插件的文件名后缀时,只有在服务端渲染时才可以使用
使用.client.ts作为插件的文件名后缀时,只有在客户端渲染时才可以使用
使用数字.作为插件的文件名前缀时,插件的加载时具有顺序(正序)
plugins/.ts 1 2 3 4 5 6 export default defineNuxtPlugin (function (nuxtApp ) { return { fn : function ( ) {}, key : "value" }; });
使用插件 1 2 3 4 5 <script setup> const nuxtApp = useNuxtApp(); nuxtApp.$fn(); console.log(nuxtApp.$key); </script>
生命周期函数 通过app.vue定义生命周期函数 app.vue 1 2 3 4 5 6 <script setup> const nuxtApp = useNuxtApp(); nuxtApp.hook("app:mounted", function (vueApp) { ... }); </script>
通过插件定义生命周期函数 plugins/lifecycle.ts 1 2 3 4 5 6 7 export default defineNuxtPlugin (function (nuxtApp ) { nuxtApp.hook ("app:created" , function (vueApp ) {}); nuxtApp.hook ("app:beforeMount" , function (vueApp ) {}); nuxtApp.hook ("vue:setup" , function ( ) {}); nuxtApp.hook ("app:rendered" , function (renderContext ) {}); nuxtApp.hook ("app:mounted" , function (vueApp ) {}); });
组件的生命周期函数
在服务端渲染时
如果是OptionsAPI只会回调beforeCreate()函数和created()函数
如果是CompositionAPI只会回调setup()函数
1 2 3 4 5 6 7 8 9 10 11 12 <script> export default { setup: function () {}, onBeforeMount: function () {}, onMounted: function () {}, onBeforeUpdate: function () {}, onUpdated: function () {}, onBeforeUnmount: function () {}, onUnmounted: function () {}, onErrorCaptured: function () {} } </script>
发送请求
<key>:用于解析异步数据的唯一键,如果没有指定,缺省值为文件名+行号
query、params:查询字符串参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <script setup> const { data } = await useAsyncData("<key>", function () { return $fetch("<url>", { method: "GET", baseURL: "https://example.com", query: { key: "value" }, body: { key: "value" }, headers: { key: "value" }, onRequest: function ({ request, options }) {}, onRequestError: function ({ request, options, error }) {}, onResponse: function ({ request, response, options }) {}, onResponseError: function ({ request, response, options, error }) {} }); }); </script>
1 2 3 <script setup> const { data } = await useFetch("<url>", { method: "GET" }); </script>
懒加载
lazy:懒加载
false:缺省值,不开启懒加载,不开启懒加载时会阻塞页面渲染true:开启懒加载,开启懒加载时不会阻塞页面渲染,可以使用watch()监测数据改变
1 2 3 4 5 6 7 8 9 10 11 12 <script setup> const { data } = await useAsyncData("<key>", function () { return $fetch("<url>", { method: "GET", lazy: true }); }); watch(data, function (newData) { ... }); </script>
1 2 3 4 5 6 7 8 9 10 <script setup> const { data } = await useFetch("<url>", { method: "GET", lazy: true }); watch(data, function (newData) { ... }); </script>
1 2 3 4 5 6 7 8 9 <script setup> const { data } = await useLazyFetch("<url>", { method: "GET" }); watch(data, function (newData) { ... }); </script>
刷新请求 1 2 3 4 5 6 7 8 9 <script setup> const { data, refresh } = await useAsyncData("<key>", function () { return $fetch("<url>", { method: "GET" }); }); refresh(); </script>
判断请求状态
pending:请求状态,如果是false则表示请求结束,如果时true表示正在请求
1 2 3 4 5 6 7 8 9 <script setup> const { data, pending } = await useAsyncData("<key>", function () { return $fetch("<url>", { method: "GET" }); }); if (pending) {} </script>
Cookie 设置Cookie
maxAge:定义存活时间,单位秒
1 2 3 4 <script setup> const cookie = useCookie("key", { maxAge: 10 }); cookie.value = "value"; </script>
获取Cookie
1 2 3 4 <script setup> const cookie = useCookie("key"); const value = cookie.value; </script>
服务器
以服务器的身份处理请求,返回响应,可以用于Mock模拟数据
使用.ts作为API的文件名后缀时,能够同时处理所有请求方法
使用.get.ts作为API的文件名后缀时,只能处理GET请求方法
server/api/home.ts 1 2 3 4 5 6 export default defineEventHandle (function (event ) { return { code : 200 , data : "value" } });
request 1 GET http://127.0.0.1:80/api/home
请求对象 获取请求方法 1 const result = event.node .req .method ;
获取请求URL 1 const result = event.node .req .url ;
全局处理方法 获取请求方法 1 const result = getMethod (event);
获取查询字符串 1 const result = getQuery (event);
获取请求体 1 const result = await readBody ();
获取请求体原始数据 1 const result = await readRawBody ();
自定义Hook
定义Hook
如果使用匿名导出自定义Hook,则Hook名为文件名
composables/useMyHook.ts 1 2 3 export default function ( ) { ... }
如果使用命名导出自定义Hook,则Hook名为函数变量名
composables/useMyHook.ts 1 2 3 export const useMyHook = function ( ) { ... }
使用Hook 1 2 3 <script setup> const myHook = useMyHook(); </script>
状态管理 通过自定义Hook实现状态管理
useState中的值函数返回值不支持返回class类、function函数、symbol符号
composables/useMyHook.ts 1 2 3 4 5 export default function ( ) { return useState ("key" , function ( ) { return "value" ; }); }
1 2 3 4 5 6 7 <template> {{ myHook }} </template> <script setup> const myHook = useMyHook(); </script>
Pinia 下载依赖 1 npm install pinia @pinia/nuxt
修改配置文件 nuxt.config.ts 1 2 3 export default defineNuxtConfig ({ modules : ["@pinia/nuxt" ] });
定义Store store/home.ts 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { defineState } from "pinia" ;export default useHomeStore = defineState ("home" , { state : function ( ) { return { key : "default" } }, actions : { fn : function ( ) { this .key = "value" ; } } });
使用状态 1 2 3 4 5 6 7 8 9 10 11 <script setup> import { storeToRefs } from "pinia"; import { useHomeStore } from "~/store/home"; const homeStore = useHomeStore(); const { key } = storeToRefs(homeStore); console.log(key); // "default" homeStore.fn(); console.log(key); // "value" </script>
完成