一. 安装
pnpm dlx nuxi@latest init <project-name>// ornpx nuxi@latest init <project-name>
- 如遇到报错
手动安装:
-
浏览器访问报错
https
请求地址:
-
点击
tar
(项目初始文件的下载地址)对应地址,下载starter-3.tar.gz
包到本地 -
本地创建项目文件,将压缩包解压到项目文件内
-
安装依赖
pnpm i / npm install
-
启动项目
pnpm dev
二. 服务端和客户端
1. 对比vite
项目运行和nuxt
项目运行`:
-
vite
-
nuxt
-
nuxt
运行在浏览器
-
总结:
-vite
创建项目,浏览器访问,返回模板html
-nuxt
创建项目,浏览器访问,请求返回渲染后的html
, 输出先是服务端渲染的1111,后是客户端的1111
2. 区分server
和client
const runtimeConfig = useRuntimeConfig()
if (runtimeConfig.apiSecret) {console.log('server');
} else {console.log('clint');
}
或者
if (import.meta.server) {console.log('server');
} else {// import.meta.clientconsole.log('clint');
}
三. 基础配置nuxt.config.ts
1. 环境变量和私有令牌
export default defineNuxtConfig({runtimeConfig: {// 只在服务器端可用的私有键apiSecret: '123',// public中的键也可以在客户端使用public: {apiBase: '/api'}}
})
或者
# 这将覆盖apiSecret的值
NUXT_API_SECRET=api_secret_token
这些变量通过useRuntimeConfig()组合函数暴露给应用程序的其余部分。
<script setup lang="ts">
const runtimeConfig = useRuntimeConfig()
</script>
2. 全局样式导入
有一个 sass部分
文件,其中包含颜色变量,供你的Nuxt
页面 和 组件 使用。
$primary: #49240F;
$secondary: #E4A79D;
export default defineNuxtConfig({// css:['~/assets/css/base.scss'],// 或者vite: {css: {preprocessorOptions: {scss: {additionalData: '@use "@/assets/_colors.scss" as *;'}}}}
})
3. 引入element-plus
安装
npm i element-plus @element-plus/nuxt -D
modules: ['@element-plus/nuxt'],
四、路由
1. 创建pages
文件夹,内部的.vue
文件会自动被创建为路由
// 路由入口 相当于 router-link<NuxtLink to="/">首页</NuxtLink>// 路由容器, 相当于 router-view<NuxtPage />
2. 命名路由
[id].vue
一个中括号包裹的文件名,将匹配一个参数化的路由。
<NuxtLink to="/product/123">产品</NuxtLink><NuxtPage />
product/[id].vue
const route = useRoute()
console.log(route.params);
3. 可选路由
[[test]] / myTest.vue
两个中括号包裹文件夹名。内部.vue
路由访问时test
可省略
<NuxtLink to="/test/myTest">可选路由1</NuxtLink><br>
<NuxtLink to="/myTest">可选路由2</NuxtLink>
4. 全局路由
[...404].vue
一个中括号...
加文件名
5. definePageMeta
为你的页面组件定义元数据。
login.vue
definePageMeta({path: '/login1'
})
- 访问
login
会跳转404
,login1
则会跳转login
6. 嵌套路由
user.vue
同级创建user
文件夹,user
文件夹内路由为user.vue
的子路由
7. 编程路由
navigateTo
在服务器端和客户端均可使用。
if (import.meta.server) {navigateTo('/login1')
}
// navigateTo('/login1')
8. 路由中间件
- 创建
middleware
文件夹,内部创建my-middleware.ts
,
export default defineNuxtRouteMiddleware((to, from) => {console.log("my-middleware", to.path);// // 在实际应用中,你可能不会将每个路由重定向到 `/`// // 但是在重定向之前检查 `to.path` 是很重要的,否则可能会导致无限重定向循环if (to.path === "/about") {return navigateTo("/user");}
});
- 页面使用
about.vue
definePageMeta({middleware: ['my-middleware']
})
- 全局中间件:
test.global.ts
,必须global
结尾
export default defineNuxtRouteMiddleware((to, from) => {console.log("全局中间件", to.path);
});
9. 导航守卫
export default defineNuxtRouteMiddleware((to, from) => {// console.log("全局中间件", to.path);const whiteList = ['/index', '/login', '/404', '/']if(!whiteList.includes(to.path)){let token = ''if(import.meta.client){token = localStorage.getItem('token') || ''} if(!token){return navigateTo({path: '/login',query: { code: 401,message: '请先登录'}});}}
});
onMounted(() => {const route = useRoute()if (route.query.code === '401') {ElMessage.error(route.query.message as string)}
})
五. 目录结构
components
目录是你放置所有Vue
组件的地方。
Nuxt
会自动导入该目录中的所有组件
- 使用
composables
目录将你的Vue
组合式函数自动导入到你的应用程序中
export const useFoo = () => {return useState('foo', () => 'bar')
}
<script setup lang="ts">
const foo = useFoo()
</script><template><div>{{ foo }}</div>
</template>
默认导出可使用驼峰文件名进行访问
- 使用
utils
目录在整个应用程序中自动导入你的工具函数。使用当时同composables
六、请求
<script setup lang="ts">
// 在SSR中数据将被获取两次,一次在服务器端,一次在客户端。
const dataTwice = await $fetch('/api/item')// 在SSR中,数据仅在服务器端获取并传递到客户端。
const { data } = await useAsyncData('item', () => $fetch('/api/item'))// 你也可以使用useFetch作为useAsyncData + $fetch的快捷方式
const { data } = await useFetch('/api/item')
</script>
- 完整
request.ts
/*** @description useFetch* */
import type { NitroFetchRequest } from 'nitropack';const apiRequest = <T>(url:NitroFetchRequest, options: any): Promise<ResultData<T>> => {const config = useRuntimeConfig();const nuxtApp = useNuxtApp()const contentType = options.contentType || 'application/json'return new Promise((resolve, reject) => {useFetch<ResultData<T>>(url, {baseURL: config.public.baseURL,onRequest({ options }) {let token = "";if (import.meta.client) {token = useStore().getToken();}options.headers = {'Content-Type': contentType,'Cookies': `token=${token}`,...options.headers,};},onResponse({ response }) {if(response.status >= 200 && response.status < 300){if(response._data.code === 200){resolve(response._data)} else {if(import.meta.client){ElMessage.error(response._data.msg)} else {nuxtApp.runWithContext(()=>{navigateTo({path: '/Error',query:{code: response._data.code,message: response._data.msg}})})}}}},onResponseError({ response }) {if(import.meta.client){ElMessage.error(response._data.msg)} else {nuxtApp.runWithContext(()=>{navigateTo({path: '/Error',query:{code: response._data.code,message: response._data.msg}})})}},...options});});};interface Result {code: string;msg: string;
}interface ResultData<T = any> extends Result {data: T;
}export const getApi = <T>(url:NitroFetchRequest, options: any = {}): Promise<ResultData<T>> => {return apiRequest(url, {method: 'GET',...options})
}export const postApi = <T>(url:NitroFetchRequest, options: any = {}): Promise<ResultData<T>> => {return apiRequest(url, {method: 'POST',...options})
}
[[Error]] / index.vue
<script setup lang="ts">
onMounted(() => {const route = useRoute();switch (route.query.code) {case '401':ElMessage.error('no login')localStorage.removeItem('token')break;case '501':ElMessage.error('Unknown error' + route.query.message)break;default:ElMessage.error(route.query.code + '' + route.query.message)}
})
</script>
- 调用接口
// template
<p v-for="item in list" :key="item.id">{{ item.name }}</p>// script
const list = ref<Info[]>()
interface Info {id: number;name: string
}interface List {list: Info[],page: number;total: number;
}
const handleClick = async () => {const { data } = await getApi<List>('/list')list.value = data.list
}
七、pinia
- 安装
pinia
pnpm install pinia @pinia/nuxt
- 配置
nuxt.config.ts
modules: ['@pinia/nuxt',
],
- 持久化配置
- 安装
pnpm i -D @pinia-plugin-persistedstate/nuxt
modules: ['@pinia-plugin-persistedstate/nuxt',
],// 默认存在cookies
// piniaPersistedstate: {
// storage: 'localStorage'
// },
八、 nuxt
错误处理
app.vue
同级创建error.vue
<script setup lang="ts">defineProps({error: Object
})clearError({ redirect: '/login' })
</script>
index.vue
throw createError({ statusCode: 404, message: '404 not found' })
- 错误只能从服务端触发
九、SEO
优化
useHead
函数用于自定义Nuxt
应用中单个页面的头部属性。
useHead({// title: 'my login',meta: [{name: 'description',content: 'my login description'},{name: 'keywords',content: 'my login keywords'},],// titleTemplate: (titleChunk) => {// return titleChunk ? `${titleChunk} - my login` : 'my login'// }titleTemplate: `%s ${name.value}`}
)
十、layout
布局
layout
目录下创建default.vue
<template><div><p>一些在所有页面之间共享的默认布局内容</p><slot /></div>
</template>
app.vue
页面使用layout
<template><NuxtLayout><NuxtPage /></NuxtLayout>{/* <NuxtLayout :name="其他layout"><NuxtPage /></NuxtLayout> */}
</template>
login.vue
页面不使用layout
definePageMeta({layout: false
})