背景
为了拓宽自己技术广度,尝试将自己的毕设迁移到nuxt中。
同时也将功能、接口完善一下
nuxt版本:3.17.6
day1
首先做了试验,想着沿用原先的axios去做接口请求,在网上找了nuxt使用axios的文章,结果发现人家nuxt3就不想支持axios了,于是改用nuxt自带的fetch。
创建useRequest
nuxt的composables中以use开头的文件都会被自动导出,在vue文件中并不需要引入即可使用。
useRequest内容,风格上还是跟axios差不多。
创建之后,我们就可以直接在vue文件中使用const request = useRequest()
// composables/useRequest.ts
interface RequestOptions {
baseURL?: string
headers?: Record<string, string>
timeout?: number
}
interface ResponseData<T = any> {
code: number
message: string
result: boolean
data: T
}
const defaultOptions: RequestOptions = {
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
timeout: 10000,
}
// 请求拦截器
const requestInterceptor = (options: any) => {
// 处理token等情况
return options
}
// 响应拦截器
const responseInterceptor = (response: any) => {
// 处理响应数据
const data: ResponseData = response._data
// 根据业务逻辑处理不同状态码
if (data.code !== 200) {
// 处理未登录等特殊情况
if (data.code === 401) {
navigateTo('/login')
}
return Promise.reject(data)
}
return data.data
}
// 错误处理
const errorHandler = (error: any) => {
// 可以特殊处理一些情况
return Promise.reject(error)
}
// 创建请求实例
const createRequest = (options: RequestOptions = {}) => {
const requestOptions = { ...defaultOptions, ...options }
// 创建自定义fetch函数
const fetchInstance = $fetch.create({
baseURL: requestOptions.baseURL,
headers: requestOptions.headers,
timeout: requestOptions.timeout,
onRequest: requestInterceptor,
onResponse: responseInterceptor,
onRequestError: errorHandler,
onResponseError: errorHandler,
})
// 封装GET请求
const get = async <T = any>(url: string, params?: any, config?: any) => {
return fetchInstance<T>(url, {
method: 'GET',
params,
...config
})
}
// 封装POST请求
const post = async <T = any>(url: string, data?: any, config?: any) => {
return fetchInstance<T>(url, {
method: 'POST',
body: data,
...config
})
}
// 封装PUT请求
const put = async <T = any>(url: string, data?: any, config?: any) => {
return fetchInstance<T>(url, {
method: 'PUT',
body: data,
...config
})
}
// 封装DELETE请求
const del = async <T = any>(url: string, params?: any, config?: any) => {
return fetchInstance<T>(url, {
method: 'DELETE',
params,
...config
})
}
return {
fetch: fetchInstance,
get,
post,
put,
del
}
}
const request = createRequest()
export default function useRequest() {
return request
}
跨域配置
// nuxt.config.ts
export default defineNuxtConfig({
compatibilityDate: '2025-05-15',
devtools: { enabled: true },
devServer: {
port: 8002,
},
nitro: {
devProxy: {
'/api': {
target: 'http://127.0.0.1:3000', // Express API 地址
changeOrigin: true,
prependPath: true,
}
}
}
})
接口存放
这里我希望还是沿用原先的风格,然后在使用nuxt的挂载实例的方式来实现
所以这里就用到了nuxt的plugin,存放在plugin的实例都可以挂载到nuxt实例上
// plugins\api\index.ts
import Common from './module/common' // 存放接口
export default defineNuxtPlugin((nuxtApp) => {
const request = {
Common
}
nuxtApp.provide('request', request)
})
day2
接口存放
感觉之前的方式不是很方便,不如直接使用composables,所以做出下面的更改
// useApi.ts
import Common from './api/module/common'
import Role from './api/module/role'
import User from './api/module/user'
export default function() {
return {
Common,
Role,
User
}
}
这样可以直接在组件内使用const $request = useApi()
引入element-plus
在这里用到了自动引入的工具
npm install -D unplugin-vue-components unplugin-auto-import
在nuxt.config.ts中配置样式和自动引入的vite插件
css: [
'element-plus/dist/index.css'
],
vite: {
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
}
配置token
这里遇到点问题,不知道fetch这个的请求头和响应头是一个专门的Headers对象?然后直接用axios的赋值方式拿不到值奇怪了半天,后来发现这里是得用get、set做取值和赋值的操作
// 请求拦截器
const requestInterceptor = ({ options }: { options: any}) => {
if (!options.headers.get('Authorization')) {
options.headers.set('Authorization', window.localStorage.getItem('token') || '')
}
return options
}
// 响应拦截器
const responseInterceptor = ({ response }: { response: any }) => {
if (response.headers.get('Authorization')) {
window.localStorage.setItem('token', response.headers.get('Authorization'))
}
const data: ResponseData = response._data
return data.data
}
引入pinia
npm i pinia @pinia/nuxt
// nuxt.config.ts
modules: ['@pinia/nuxt']
day3
引入富文本编辑器
组件选型:quill富文本编辑器
一开始使用的vue-quill-editor来实现这个功能,新建针对其的插件,在nuxt.config.ts增加vue-quill-editor的ssr配置,但是试了很多次都是报document is not defined
后面才发现,这个版本是适用于vue2.x的,vue3.x需要使用@vueup/vue-quill
全局使用该组件
如果多处使用该组件可以通过plugins去引入
// nuxt-quill-plugin.js
import { defineNuxtPlugin } from "#app"
import { QuillEditor } from '@vueup/vue-quill'
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.component('QuillEditor', QuillEditor)
})
局部使用
<template>
<QuillEditor v-model:content="content" :options="editorOptions" class="quill-editor" />
</template>
<script setup>
import editorOption from './editorOption'
import { QuillEditor } from '@vueup/vue-quill'
// 编辑器内容
const content = ref('<p>初始内容</p>')
// 编辑器配置
const editorOptions = {
theme: 'snow',
...editorOption
}
</script>
<style>
@import '@vueup/vue-quill/dist/vue-quill.snow.css';
.quill-editor {
min-height: 300px;
margin-top: 1rem;
}
</style>
按照局部使用的方式去做,编译完之后出现如下错误
通过查询之后得知,vue-quill的使用需要在浏览器环境、vue环境中使用,nuxt在客户端渲染之前是没有这个环境的
所以需要添加nuxt自带的组件<ClientOnly>,使用这个标签表示,这个组件只有在客户端渲染的时候才会使用
最终的代码就是:
<template>
<ClientOnly>
<QuillEditor v-model:content="content" :options="editorOptions" class="quill-editor" />
</ClientOnly>
</template>
<script setup>
import editorOption from './editorOption'
const QuillEditor = defineAsyncComponent(() => import('@vueup/vue-quill').then(mod => mod.QuillEditor))
// 编辑器内容
const content = ref('<p>初始内容</p>')
// 编辑器配置
const editorOptions = {
theme: 'snow',
...editorOption
}
</script>
<style>
@import '@vueup/vue-quill/dist/vue-quill.snow.css';
.quill-editor {
min-height: 300px;
margin-top: 1rem;
}
</style>