通常在一个前端项目中,后端接口可能不是和前端 Nuxt
集成在一起的(Nitro
)
而前端从浏览器发出请求时又会遇到跨域问题,为了解决浏览器跨域问题
除了能直接设置代理服务器的CORS
,就需要用代理服务器转发的方式解决
代理的原理很简单:把当前域下的前端往目标服务发送请求,改为向当前域下的代理服务器发送请求,代理服务器直接转发前端请求到目标服务并把 response
返回给前端
这样,同一个域下不会产生跨域,而服务端之间的请求又不存在跨域问题,就解决了问题。
在 Nuxt
中有四种方式可以设置代理,而最后一种则是终极解决方案
在 nuxt.config.ts
可以设置 vite
配置,就和 Vue3项目一样
vite : {
server: {
proxy: {
'/api/v1': {
target: 'http://localhost:5770',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/v1/, '')
}
}
}
}
此时项目内请求 /api/v1/user/list
时,会被转发到 http://localhost:5770/user/list
/api/v1
在此处被重写了,所以实际请求到 5770 时是不携带这个前缀的
使用 vite server
代理时,和 Vue3
项目一样,仅对本地开发时有效,适合项目部署到时和实际后端服务的服务器、域名、端口一致的情况下使用
在 nuxt.config.ts
可以设置 nitro.devProxy
进行代理
nitro: {
devProxy: {
'/api/v1': {
target: 'http://localhost:5770',
changeOrigin: true
},
'/api/v2': 'http://localhost:5771'
}
}
这样可以借助 Nitro
在开发时进行代理 ,原理和 Vite server
没有区别
这个选项使用 unjs/httpxy 实现,可以在官方文档查看
以上两种方式,在使用 useFetch
可以通过开启 server
选项来测试是否在 SSR
下生效。
比起 devProxy
,routeRules
配置是更好的选择
nitro: {
routeRules: {
'/api/v1/**': {
proxy: 'http://localhost:5770/api/v1/**'
}
},
}
既可以在客户端请求时生效, 也可以在 SSR 期间生效
唯一的问题是在运行时设置环境变量不太方便
因为就算把 proxy 换成环境变量里的值
proxy: process.env.YOUR_SEVER_URL
也只会在构建时生效,这个值会被硬编码进应用中。
和直接写在proxy里唯一的区别是:你可以在不同的服务器中构建,再针对不同的构建环境设置不同的环境变量
所以,Nuxt
中如何在运行时读取环境变量?runtimeConfig
!
代理只是一个 node
服务,我们当然也可以使用 Nitro
自己实现代理行为
在 Nuxt
中,runtimeConfig
是个比较特殊的配置
Nuxt
不在生产环境(运行时)中读取 env
文件,只在开发、构建、生成(generate)时读取(因为不同部署环境的兼容性,比如无服务器平台、cloudflare workers 等)
所以才有了 runtimeConfig
这个配置,用于在运行时读取特定的环境变量。
不同的平台有不同的环境变量设置方式, 在 Nuxt
中通过读取 NUXT_
前缀的环境变量来和 runtimeConfig
里的 key
对应起来
比如:
runtimeConfig: {
proxyUrl: 'http://localhost:5770',
},
在运行时,使用 useRuntimeConfig
读取 proxyUrl
时,会优先去读 NUXT_PROXY_URL
这个环境变量,如果没取到就会使用当前字符串
所以如果要切换服务,只需要在对应的部署环境中修改 NUXT_PROXY_URL
即可,无需再重新构建
理解了这一点,接下来就是借助 Nitro
来实现代理行为。
首先需要在 server
目录下的新建 api/[...].ts
,然后实现以下内容:
import { joinURL } from 'ufo'
export default defineEventHandler(async (event) => {
const proxyUrl = useRuntimeConfig().proxyUrl
const targetApiPrefix = event.path.replace('xxx', '')
const targetUrl = joinURL(proxyUrl, targetApiPrefix)
return proxyRequest(event, targetUrl)
})
思路和配置代理的思路是一样的,因为核心部分 proxyRequest
已经实现了
我们要做的就是:
- 拿到实际的服务地址。通过
runtimeConfig
可以拿到任意动态的地址 - 根据前缀和实际服务地址对应起来。类似配置时的
/api/v1: 'http://localhost:5770'
和pathRewrite
- proxyRequest
其中第二条对应的就是 targetApiPrefix
的逻辑,假设 Nuxt
应用发出的请求是/api/imgx/xxxx
,对应 imgx
服务中的 /api/v1/xxxx
则这样实现:
const targetApiPrefix = event.path.replace(/^\/api\/imgx\//, '/api/v1')
此时在 Nuxt 中发出请求是这样的
$fetch('/api/imgx/img/001/001/哈哈哈')
Nitro 转发时就会变成 http://localhost:5770/api/v1/img/001/001/哈哈哈
由于这个接口我们拥有完全的可控性,所以能做的不只是代理
在隐藏了实际请求地址的同时
还能通过proxyRequest的第三个参数传入 { headers: { Authorization: 'your token'} }
向目标服务添加身份验证信息
如果拥有多个后端服务,还可以自己选择转发到何处,达到类似负载均衡的作用
如果想对请求到的数据做进一步处理,又何必纠结于代理?
只需要自己使用 $fetch
请求明确的地址 targetUrl
,然后对拿到的数据进行处理,再返回给前端即可!
_注意:借助 Nitro
实现代理,前提就是你得有 Nitro
,如果 Nuxt
完全静态化了,就无法动态读取环境变量了 _
以上就是 Nuxt
中设置代理的四种方式
其中第一种和 Vue3
项目没有区别,是基于本地服务实现
如果你只需要在本地开发时使用代理来解决跨域问题,在生产环境中没有这个问题,那你可以选择前两种
如果你的代理地址比较固定,也没有很多不同环境的后端服务可用,那你可以选择第三种 Nitro routeRules proxy
, 只需要配置,即可实现这个简单的需求
如果你的环境比较多,或是需要调用的服务也比较多,或是有很多特殊需求需要实现,那我推荐你使用第四种终极方案
当然,这只是基于 Nuxt
中的实现,利用 Nginx
也能轻松实现代理,可以根据自己的实际情况来抉择
希望对你有所帮助~