[{"data":1,"prerenderedAt":1040},["ShallowReactive",2],{"page-/post/nuxt/the-best-way-to-set-proxy-in-nuxt":3,"surrounding-page":1031},{"id":4,"title":5,"author":6,"body":7,"date":1020,"description":1021,"extension":1022,"group":6,"lastmod":1023,"meta":1024,"navigation":746,"path":1025,"rawbody":1026,"seo":1027,"showTitle":6,"stem":1028,"tags":1029,"versions":6,"__hash__":1030},"content/post/nuxt/the-best-way-to-set-proxy-in-nuxt.md","Nuxt 中设置代理的正确姿势",null,{"type":8,"value":9,"toc":1013},"minimark",[10,23,31,38,49,52,61,66,76,227,237,243,266,270,278,346,360,377,392,396,407,454,457,464,467,483,490,493,506,516,520,528,537,547,564,567,594,609,615,621,631,764,771,774,796,817,820,863,866,883,889,895,902,909,916,923,933,949,952,958,964,978,989,996,1006,1009],[11,12,13,14,18,19,22],"p",{},"通常在一个前端项目中，后端接口可能不是和前端 ",[15,16,17],"code",{},"Nuxt"," 集成在一起的（",[15,20,21],{},"Nitro",")",[11,24,25,26,30],{},"而前端",[27,28,29],"strong",{},"从浏览器发出","请求时又会遇到跨域问题，为了解决浏览器跨域问题",[11,32,33,34,37],{},"除了能直接设置代理服务器的",[15,35,36],{},"CORS","，就需要用代理服务器转发的方式解决",[11,39,40,41,44,45,48],{},"代理的原理很简单：",[27,42,43],{},"把当前域下的前端往目标服务发送请求，改为向当前域下的代理服务器发送请求","，代理服务器直接转发前端请求到目标服务并把 ",[15,46,47],{},"response"," 返回给前端",[11,50,51],{},"这样，同一个域下不会产生跨域，而服务端之间的请求又不存在跨域问题，就解决了问题。",[11,53,54,55,57,58],{},"在 ",[15,56,17],{}," 中有四种方式可以设置代理，而最后一种则是",[27,59,60],{},"终极解决方案",[62,63,65],"h2",{"id":64},"一vite-sever-proxy","一、Vite sever proxy",[11,67,54,68,71,72,75],{},[15,69,70],{},"nuxt.config.ts"," 可以设置 ",[15,73,74],{},"vite"," 配置，就和 Vue3项目一样",[77,78,83],"pre",{"className":79,"code":80,"language":81,"meta":82,"style":82},"language-typescript shiki shiki-themes github-light","vite : {\n    server: {\n      proxy: {\n        '/api/v1': {\n          target: 'http://localhost:5770',\n          changeOrigin: true,\n          rewrite: (path) => path.replace(/^\\/api\\/v1/, '')\n        }\n      }\n    }\n}\n","typescript","",[15,84,85,97,106,114,123,135,147,203,209,215,221],{"__ignoreMap":82},[86,87,90,93],"span",{"class":88,"line":89},"line",1,[86,91,74],{"class":92},"s7eDp",[86,94,96],{"class":95},"sgsFI"," : {\n",[86,98,100,103],{"class":88,"line":99},2,[86,101,102],{"class":92},"    server",[86,104,105],{"class":95},": {\n",[86,107,109,112],{"class":88,"line":108},3,[86,110,111],{"class":92},"      proxy",[86,113,105],{"class":95},[86,115,117,121],{"class":88,"line":116},4,[86,118,120],{"class":119},"sYBdl","        '/api/v1'",[86,122,105],{"class":95},[86,124,126,129,132],{"class":88,"line":125},5,[86,127,128],{"class":95},"          target: ",[86,130,131],{"class":119},"'http://localhost:5770'",[86,133,134],{"class":95},",\n",[86,136,138,141,145],{"class":88,"line":137},6,[86,139,140],{"class":95},"          changeOrigin: ",[86,142,144],{"class":143},"sYu0t","true",[86,146,134],{"class":95},[86,148,150,153,156,160,163,167,170,173,176,179,182,186,189,191,194,197,200],{"class":88,"line":149},7,[86,151,152],{"class":92},"          rewrite",[86,154,155],{"class":95},": (",[86,157,159],{"class":158},"sqxcx","path",[86,161,162],{"class":95},") ",[86,164,166],{"class":165},"sD7c4","=>",[86,168,169],{"class":95}," path.",[86,171,172],{"class":92},"replace",[86,174,175],{"class":95},"(",[86,177,178],{"class":119},"/",[86,180,181],{"class":165},"^",[86,183,185],{"class":184},"s691h","\\/",[86,187,188],{"class":119},"api",[86,190,185],{"class":184},[86,192,193],{"class":119},"v1/",[86,195,196],{"class":95},", ",[86,198,199],{"class":119},"''",[86,201,202],{"class":95},")\n",[86,204,206],{"class":88,"line":205},8,[86,207,208],{"class":95},"        }\n",[86,210,212],{"class":88,"line":211},9,[86,213,214],{"class":95},"      }\n",[86,216,218],{"class":88,"line":217},10,[86,219,220],{"class":95},"    }\n",[86,222,224],{"class":88,"line":223},11,[86,225,226],{"class":95},"}\n",[11,228,229,230,233,234],{},"此时项目内请求 ",[15,231,232],{},"/api/v1/user/list"," 时，会被转发到 ",[15,235,236],{},"http://localhost:5770/user/list",[11,238,239,242],{},[15,240,241],{},"/api/v1"," 在此处被重写了，所以实际请求到 5770 时是不携带这个前缀的",[11,244,245,246,249,250,253,254,257,258,261,262,265],{},"使用 ",[15,247,248],{},"vite server"," 代理时，和 ",[15,251,252],{},"Vue3"," 项目一样，",[27,255,256],{},"仅对本地开发时有效","，适合项目",[27,259,260],{},"部署","到时和",[27,263,264],{},"实际后端服务的服务器、域名、端口一致","的情况下使用",[62,267,269],{"id":268},"二nitro-devproxy","二、Nitro devProxy",[11,271,54,272,71,274,277],{},[15,273,70],{},[15,275,276],{},"nitro.devProxy"," 进行代理",[77,279,281],{"className":79,"code":280,"language":81,"meta":82,"style":82}," nitro: {\n    devProxy: {\n      '/api/v1': {\n        target: 'http://localhost:5770',\n        changeOrigin: true\n      },\n      '/api/v2': 'http://localhost:5771'\n    }\n }\n",[15,282,283,290,297,304,313,321,326,337,341],{"__ignoreMap":82},[86,284,285,288],{"class":88,"line":89},[86,286,287],{"class":92}," nitro",[86,289,105],{"class":95},[86,291,292,295],{"class":88,"line":99},[86,293,294],{"class":92},"    devProxy",[86,296,105],{"class":95},[86,298,299,302],{"class":88,"line":108},[86,300,301],{"class":119},"      '/api/v1'",[86,303,105],{"class":95},[86,305,306,309,311],{"class":88,"line":116},[86,307,308],{"class":95},"        target: ",[86,310,131],{"class":119},[86,312,134],{"class":95},[86,314,315,318],{"class":88,"line":125},[86,316,317],{"class":95},"        changeOrigin: ",[86,319,320],{"class":143},"true\n",[86,322,323],{"class":88,"line":137},[86,324,325],{"class":95},"      },\n",[86,327,328,331,334],{"class":88,"line":149},[86,329,330],{"class":119},"      '/api/v2'",[86,332,333],{"class":95},": ",[86,335,336],{"class":119},"'http://localhost:5771'\n",[86,338,339],{"class":88,"line":205},[86,340,220],{"class":95},[86,342,343],{"class":88,"line":211},[86,344,345],{"class":95}," }\n",[11,347,348,349,351,352,355,356,359],{},"这样可以借助 ",[15,350,21],{}," ",[27,353,354],{},"在开发时","进行代理 ，原理和 ",[15,357,358],{},"Vite server"," 没有区别",[11,361,362,363,370,371,376],{},"这个选项使用 ",[364,365,369],"a",{"href":366,"rel":367},"https://github.com/unjs/httpxy",[368],"nofollow","unjs/httpxy"," 实现，可以在",[364,372,375],{"href":373,"rel":374},"https://nitro.build/config#devproxy",[368],"官方文档","查看",[11,378,379,380,383,384,387,388,391],{},"以上两种方式，在使用 ",[15,381,382],{},"useFetch"," 可以通过开启 ",[15,385,386],{},"server"," 选项来测试是否在 ",[15,389,390],{},"SSR"," 下生效。",[62,393,395],{"id":394},"三nitro-routerules-proxy","三、Nitro routeRules proxy",[11,397,398,399,402,403,406],{},"比起 ",[15,400,401],{},"devProxy"," ，",[15,404,405],{},"routeRules"," 配置是更好的选择",[77,408,410],{"className":79,"code":409,"language":81,"meta":82,"style":82},"nitro: {\n    routeRules: {\n      '/api/v1/**': {\n        proxy: 'http://localhost:5770/api/v1/**'\n      }\n    },\n}\n",[15,411,412,419,426,433,441,445,450],{"__ignoreMap":82},[86,413,414,417],{"class":88,"line":89},[86,415,416],{"class":92},"nitro",[86,418,105],{"class":95},[86,420,421,424],{"class":88,"line":99},[86,422,423],{"class":92},"    routeRules",[86,425,105],{"class":95},[86,427,428,431],{"class":88,"line":108},[86,429,430],{"class":119},"      '/api/v1/**'",[86,432,105],{"class":95},[86,434,435,438],{"class":88,"line":116},[86,436,437],{"class":95},"        proxy: ",[86,439,440],{"class":119},"'http://localhost:5770/api/v1/**'\n",[86,442,443],{"class":88,"line":125},[86,444,214],{"class":95},[86,446,447],{"class":88,"line":137},[86,448,449],{"class":95},"    },\n",[86,451,452],{"class":88,"line":149},[86,453,226],{"class":95},[11,455,456],{},"既可以在客户端请求时生效， 也可以在 SSR 期间生效",[11,458,459,460,463],{},"唯一的问题是在",[27,461,462],{},"运行时","设置环境变量不太方便",[11,465,466],{},"因为就算把 proxy 换成环境变量里的值",[77,468,470],{"className":79,"code":469,"language":81,"meta":82,"style":82},"proxy: process.env.YOUR_SEVER_URL\n",[15,471,472],{"__ignoreMap":82},[86,473,474,477,480],{"class":88,"line":89},[86,475,476],{"class":92},"proxy",[86,478,479],{"class":95},": process.env.",[86,481,482],{"class":143},"YOUR_SEVER_URL\n",[11,484,485,486,489],{},"也只会在",[27,487,488],{},"构建时","生效，这个值会被硬编码进应用中。",[11,491,492],{},"和直接写在proxy里唯一的区别是：你可以在不同的服务器中构建，再针对不同的构建环境设置不同的环境变量",[11,494,495,496,498,499,501,502,505],{},"所以，",[15,497,17],{}," 中如何在",[27,500,462],{},"读取环境变量？",[15,503,504],{},"runtimeConfig"," !",[11,507,508,509,512,513,515],{},"代理只是一个 ",[15,510,511],{},"node"," 服务，我们当然也可以使用  ",[15,514,21],{}," 自己实现代理行为",[62,517,519],{"id":518},"四nitro-proxtrequest","四、Nitro proxtRequest()",[11,521,54,522,524,525,527],{},[15,523,17],{}," 中，",[15,526,504],{}," 是个比较特殊的配置",[11,529,530,532,533,536],{},[15,531,17],{}," 不在生产环境（运行时）中读取  ",[15,534,535],{},"env"," 文件，只在开发、构建、生成（generate）时读取（因为不同部署环境的兼容性，比如无服务器平台、cloudflare workers 等）",[11,538,539,540,542,543,546],{},"所以才有了 ",[15,541,504],{}," 这个配置，用于在",[27,544,545],{},"运行时读取特定的环境变量","。",[11,548,549,550,552,553,556,557,559,560,563],{},"不同的平台有不同的环境变量设置方式， 在 ",[15,551,17],{}," 中通过读取 ",[15,554,555],{},"NUXT_"," 前缀的环境变量来和 ",[15,558,504],{}," 里的 ",[15,561,562],{},"key"," 对应起来",[11,565,566],{},"比如：",[77,568,570],{"className":79,"code":569,"language":81,"meta":82,"style":82},"runtimeConfig: {\n    proxyUrl: 'http://localhost:5770',\n},\n",[15,571,572,578,589],{"__ignoreMap":82},[86,573,574,576],{"class":88,"line":89},[86,575,504],{"class":92},[86,577,105],{"class":95},[86,579,580,583,585,587],{"class":88,"line":99},[86,581,582],{"class":92},"    proxyUrl",[86,584,333],{"class":95},[86,586,131],{"class":119},[86,588,134],{"class":95},[86,590,591],{"class":88,"line":108},[86,592,593],{"class":95},"},\n",[11,595,596,597,600,601,604,605,608],{},"在运行时，使用 ",[15,598,599],{},"useRuntimeConfig"," 读取 ",[15,602,603],{},"proxyUrl"," 时，会优先去读 ",[15,606,607],{},"NUXT_PROXY_URL"," 这个环境变量，如果没取到就会使用当前字符串",[11,610,611,612,614],{},"所以如果要切换服务，只需要在对应的部署环境中修改 ",[15,613,607],{}," 即可，无需再重新构建",[11,616,617,618,620],{},"理解了这一点，接下来就是借助 ",[15,619,21],{}," 来实现代理行为。",[11,622,623,624,626,627,630],{},"首先需要在 ",[15,625,386],{}," 目录下的新建 ",[15,628,629],{},"api/[...].ts","，然后实现以下内容：",[77,632,634],{"className":79,"code":633,"language":81,"meta":82,"style":82},"import { joinURL } from 'ufo'\nexport default defineEventHandler(async (event) => {\n  const proxyUrl = useRuntimeConfig().proxyUrl\n  \n  const targetApiPrefix = event.path.replace('xxx', '') \n  const targetUrl = joinURL(proxyUrl, targetApiPrefix)\n\n  return proxyRequest(event, targetUrl)\n})\n",[15,635,636,650,679,696,701,727,742,748,759],{"__ignoreMap":82},[86,637,638,641,644,647],{"class":88,"line":89},[86,639,640],{"class":165},"import",[86,642,643],{"class":95}," { joinURL } ",[86,645,646],{"class":165},"from",[86,648,649],{"class":119}," 'ufo'\n",[86,651,652,655,658,661,663,666,669,672,674,676],{"class":88,"line":99},[86,653,654],{"class":165},"export",[86,656,657],{"class":165}," default",[86,659,660],{"class":92}," defineEventHandler",[86,662,175],{"class":95},[86,664,665],{"class":165},"async",[86,667,668],{"class":95}," (",[86,670,671],{"class":158},"event",[86,673,162],{"class":95},[86,675,166],{"class":165},[86,677,678],{"class":95}," {\n",[86,680,681,684,687,690,693],{"class":88,"line":108},[86,682,683],{"class":165},"  const",[86,685,686],{"class":143}," proxyUrl",[86,688,689],{"class":165}," =",[86,691,692],{"class":92}," useRuntimeConfig",[86,694,695],{"class":95},"().proxyUrl\n",[86,697,698],{"class":88,"line":116},[86,699,700],{"class":95},"  \n",[86,702,703,705,708,710,713,715,717,720,722,724],{"class":88,"line":125},[86,704,683],{"class":165},[86,706,707],{"class":143}," targetApiPrefix",[86,709,689],{"class":165},[86,711,712],{"class":95}," event.path.",[86,714,172],{"class":92},[86,716,175],{"class":95},[86,718,719],{"class":119},"'xxx'",[86,721,196],{"class":95},[86,723,199],{"class":119},[86,725,726],{"class":95},") \n",[86,728,729,731,734,736,739],{"class":88,"line":137},[86,730,683],{"class":165},[86,732,733],{"class":143}," targetUrl",[86,735,689],{"class":165},[86,737,738],{"class":92}," joinURL",[86,740,741],{"class":95},"(proxyUrl, targetApiPrefix)\n",[86,743,744],{"class":88,"line":149},[86,745,747],{"emptyLinePlaceholder":746},true,"\n",[86,749,750,753,756],{"class":88,"line":205},[86,751,752],{"class":165},"  return",[86,754,755],{"class":92}," proxyRequest",[86,757,758],{"class":95},"(event, targetUrl)\n",[86,760,761],{"class":88,"line":211},[86,762,763],{"class":95},"})\n",[11,765,766,767,770],{},"思路和配置代理的思路是一样的，因为核心部分 ",[15,768,769],{},"proxyRequest"," 已经实现了",[11,772,773],{},"我们要做的就是：",[775,776,777,784,794],"ol",{},[778,779,780,781,783],"li",{},"拿到实际的服务地址。通过 ",[15,782,504],{}," 可以拿到任意动态的地址",[778,785,786,787,790,791],{},"根据前缀和实际服务地址对应起来。类似配置时的 ",[15,788,789],{},"/api/v1: 'http://localhost:5770'"," 和 ",[15,792,793],{},"pathRewrite",[778,795,769],{},[11,797,798,799,802,803,805,806,809,810,813,814],{},"其中第二条对应的就是 ",[15,800,801],{},"targetApiPrefix"," 的逻辑，假设 ",[15,804,17],{}," 应用发出的请求是",[15,807,808],{},"/api/imgx/xxxx"," ，对应 ",[15,811,812],{},"imgx"," 服务中的 ",[15,815,816],{},"/api/v1/xxxx",[11,818,819],{},"则这样实现：",[77,821,823],{"className":79,"code":822,"language":81,"meta":82,"style":82},"const targetApiPrefix = event.path.replace(/^\\/api\\/imgx\\//, '/api/v1') \n",[15,824,825],{"__ignoreMap":82},[86,826,827,830,832,834,836,838,840,842,844,846,848,850,852,854,856,858,861],{"class":88,"line":89},[86,828,829],{"class":165},"const",[86,831,707],{"class":143},[86,833,689],{"class":165},[86,835,712],{"class":95},[86,837,172],{"class":92},[86,839,175],{"class":95},[86,841,178],{"class":119},[86,843,181],{"class":165},[86,845,185],{"class":184},[86,847,188],{"class":119},[86,849,185],{"class":184},[86,851,812],{"class":119},[86,853,185],{"class":184},[86,855,178],{"class":119},[86,857,196],{"class":95},[86,859,860],{"class":119},"'/api/v1'",[86,862,202],{"class":95},[11,864,865],{},"此时在 Nuxt 中发出请求是这样的",[77,867,869],{"className":79,"code":868,"language":81,"meta":82,"style":82},"$fetch('/api/imgx/img/001/001/哈哈哈')\n",[15,870,871],{"__ignoreMap":82},[86,872,873,876,878,881],{"class":88,"line":89},[86,874,875],{"class":92},"$fetch",[86,877,175],{"class":95},[86,879,880],{"class":119},"'/api/imgx/img/001/001/哈哈哈'",[86,882,202],{"class":95},[11,884,885,886],{},"Nitro 转发时就会变成 ",[15,887,888],{},"http://localhost:5770/api/v1/img/001/001/哈哈哈",[11,890,891,892],{},"由于这个接口我们拥有完全的可控性，所以能做的不只是",[27,893,894],{},"代理",[11,896,897,898,901],{},"在",[27,899,900],{},"隐藏了实际请求地址","的同时",[11,903,904,905,908],{},"还能通过proxyRequest的第三个参数传入 ",[15,906,907],{},"{ headers: { Authorization: 'your token'} }","向目标服务添加身份验证信息",[11,910,911,912,915],{},"如果拥有",[27,913,914],{},"多个后端服务","，还可以自己选择转发到何处，达到类似负载均衡的作用",[11,917,918,919,922],{},"如果想对请求到的数据做进一步处理，又",[27,920,921],{},"何必纠结于代理","？",[11,924,925,926,928,929,932],{},"只需要自己使用 ",[15,927,875],{}," 请求明确的地址 ",[15,930,931],{},"targetUrl","，然后对拿到的数据进行处理，再返回给前端即可！",[11,934,935,936,938,939,941,942,944,945,948],{},"_注意：借助 ",[15,937,21],{}," 实现代理，前提就是你得有 ",[15,940,21],{}," ，如果 ",[15,943,17],{}," 完全",[27,946,947],{},"静态化","了，就无法动态读取环境变量了 _",[62,950,951],{"id":951},"总结",[11,953,954,955,957],{},"以上就是 ",[15,956,17],{}," 中设置代理的四种方式",[11,959,960,961,963],{},"其中第一种和 ",[15,962,252],{}," 项目没有区别，是基于本地服务实现",[11,965,966,967,970,971,974,975],{},"如果你",[27,968,969],{},"只需要在本地开发时使用代理","来解决跨域问题，",[27,972,973],{},"在生产环境中没有这个问题","，那你可以选择",[27,976,977],{},"前两种",[11,979,980,981,984,985,988],{},"如果你的",[27,982,983],{},"代理地址比较固定","，也没有很多不同环境的后端服务可用，那你可以选择第三种 ",[15,986,987],{},"Nitro routeRules proxy"," , 只需要配置，即可实现这个简单的需求",[11,990,991,992,995],{},"如果你的环境比较多，或是需要调用的服务也比较多，或是有很多特殊需求需要实现，那我推荐你使用",[27,993,994],{},"第四种","终极方案",[11,997,998,999,1001,1002,1005],{},"当然，这只是基于 ",[15,1000,17],{}," 中的实现，利用 ",[15,1003,1004],{},"Nginx"," 也能轻松实现代理，可以根据自己的实际情况来抉择",[11,1007,1008],{},"希望对你有所帮助~",[1010,1011,1012],"style",{},"html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .s691h, html code.shiki .s691h{--shiki-default:#22863A;--shiki-default-font-weight:bold}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":82,"searchDepth":99,"depth":99,"links":1014},[1015,1016,1017,1018,1019],{"id":64,"depth":99,"text":65},{"id":268,"depth":99,"text":269},{"id":394,"depth":99,"text":395},{"id":518,"depth":99,"text":519},{"id":951,"depth":99,"text":951},"2025-03-20T00:00:00.000Z","通常在一个前端项目中，后端接口可能不是和前端 Nuxt 集成在一起的（Nitro)","md","2025-08-19T00:00:00.000Z",{},"/post/nuxt/the-best-way-to-set-proxy-in-nuxt","---\ntitle: Nuxt 中设置代理的正确姿势\ndate: 2025-03-20\nlastmod: 2025-08-19\ntags: [\"Nuxt\"]\n\n---\n通常在一个前端项目中，后端接口可能不是和前端 `Nuxt` 集成在一起的（`Nitro`)\n\n而前端**从浏览器发出**请求时又会遇到跨域问题，为了解决浏览器跨域问题\n\n除了能直接设置代理服务器的`CORS`，就需要用代理服务器转发的方式解决\n\n代理的原理很简单：**把当前域下的前端往目标服务发送请求，改为向当前域下的代理服务器发送请求**，代理服务器直接转发前端请求到目标服务并把 `response` 返回给前端\n\n这样，同一个域下不会产生跨域，而服务端之间的请求又不存在跨域问题，就解决了问题。\n\n在 `Nuxt` 中有四种方式可以设置代理，而最后一种则是**终极解决方案**\n## 一、Vite sever proxy\n\n在 `nuxt.config.ts` 可以设置 `vite` 配置，就和 Vue3项目一样\n\n```typescript\nvite : {\n\tserver: {\n      proxy: {\n        '/api/v1': {\n          target: 'http://localhost:5770',\n          changeOrigin: true,\n          rewrite: (path) => path.replace(/^\\/api\\/v1/, '')\n        }\n      }\n    }\n}\n```\n\n此时项目内请求 `/api/v1/user/list` 时，会被转发到 `http://localhost:5770/user/list`\n\n`/api/v1` 在此处被重写了，所以实际请求到 5770 时是不携带这个前缀的\n\n使用 `vite server` 代理时，和 `Vue3` 项目一样，**仅对本地开发时有效**，适合项目**部署**到时和**实际后端服务的服务器、域名、端口一致**的情况下使用\n## 二、Nitro devProxy\n\n在 `nuxt.config.ts` 可以设置 `nitro.devProxy` 进行代理\n\n```typescript\n nitro: {\n\tdevProxy: {\n      '/api/v1': {\n        target: 'http://localhost:5770',\n        changeOrigin: true\n      },\n      '/api/v2': 'http://localhost:5771'\n    }\n }\n```\n\n这样可以借助 `Nitro` **在开发时**进行代理 ，原理和 `Vite server` 没有区别\n\n这个选项使用 [unjs/httpxy](https://github.com/unjs/httpxy) 实现，可以在[官方文档](https://nitro.build/config#devproxy)查看\n\n以上两种方式，在使用 `useFetch` 可以通过开启 `server` 选项来测试是否在 `SSR` 下生效。\n## 三、Nitro routeRules proxy\n\n比起 `devProxy` ，`routeRules` 配置是更好的选择\n\n```typescript\nnitro: {\n\trouteRules: {\n      '/api/v1/**': {\n        proxy: 'http://localhost:5770/api/v1/**'\n      }\n    },\n}\n```\n\n既可以在客户端请求时生效， 也可以在 SSR 期间生效\n\n唯一的问题是在**运行时**设置环境变量不太方便\n\n因为就算把 proxy 换成环境变量里的值\n\n```typescript\nproxy: process.env.YOUR_SEVER_URL\n```\n\n也只会在**构建时**生效，这个值会被硬编码进应用中。\n\n和直接写在proxy里唯一的区别是：你可以在不同的服务器中构建，再针对不同的构建环境设置不同的环境变量\n\n所以，`Nuxt` 中如何在**运行时**读取环境变量？`runtimeConfig` !\n\n代理只是一个 `node` 服务，我们当然也可以使用  `Nitro` 自己实现代理行为\n## 四、Nitro proxtRequest()\n\n在 `Nuxt` 中，`runtimeConfig` 是个比较特殊的配置\n\n`Nuxt` 不在生产环境（运行时）中读取  `env` 文件，只在开发、构建、生成（generate）时读取（因为不同部署环境的兼容性，比如无服务器平台、cloudflare workers 等）\n\n所以才有了 `runtimeConfig` 这个配置，用于在**运行时读取特定的环境变量**。\n\n不同的平台有不同的环境变量设置方式， 在 `Nuxt` 中通过读取 `NUXT_` 前缀的环境变量来和 `runtimeConfig` 里的 `key` 对应起来\n\n比如：\n\n```typescript\nruntimeConfig: {\n    proxyUrl: 'http://localhost:5770',\n},\n```\n\n在运行时，使用 `useRuntimeConfig` 读取 `proxyUrl` 时，会优先去读 `NUXT_PROXY_URL` 这个环境变量，如果没取到就会使用当前字符串\n\n所以如果要切换服务，只需要在对应的部署环境中修改 `NUXT_PROXY_URL` 即可，无需再重新构建\n\n理解了这一点，接下来就是借助 `Nitro` 来实现代理行为。\n\n首先需要在 `server` 目录下的新建 `api/[...].ts`，然后实现以下内容：\n\n```typescript\nimport { joinURL } from 'ufo'\nexport default defineEventHandler(async (event) => {\n  const proxyUrl = useRuntimeConfig().proxyUrl\n  \n  const targetApiPrefix = event.path.replace('xxx', '') \n  const targetUrl = joinURL(proxyUrl, targetApiPrefix)\n\n  return proxyRequest(event, targetUrl)\n})\n```\n\n思路和配置代理的思路是一样的，因为核心部分 `proxyRequest` 已经实现了\n\n我们要做的就是：\n\n1. 拿到实际的服务地址。通过 `runtimeConfig` 可以拿到任意动态的地址\n2. 根据前缀和实际服务地址对应起来。类似配置时的 `/api/v1: 'http://localhost:5770'` 和 `pathRewrite`\n3. proxyRequest\n\n其中第二条对应的就是 `targetApiPrefix` 的逻辑，假设 `Nuxt` 应用发出的请求是`/api/imgx/xxxx` ，对应 `imgx` 服务中的 `/api/v1/xxxx`\n\n则这样实现：\n\n```typescript\nconst targetApiPrefix = event.path.replace(/^\\/api\\/imgx\\//, '/api/v1') \n```\n\n此时在 Nuxt 中发出请求是这样的\n\n```typescript\n$fetch('/api/imgx/img/001/001/哈哈哈')\n```\n\nNitro 转发时就会变成 `http://localhost:5770/api/v1/img/001/001/哈哈哈`\n\n由于这个接口我们拥有完全的可控性，所以能做的不只是**代理**\n\n在**隐藏了实际请求地址**的同时\n\n还能通过proxyRequest的第三个参数传入 `{ headers: { Authorization: 'your token'} }`向目标服务添加身份验证信息\n\n如果拥有**多个后端服务**，还可以自己选择转发到何处，达到类似负载均衡的作用\n\n如果想对请求到的数据做进一步处理，又**何必纠结于代理**？\n\n只需要自己使用 `$fetch` 请求明确的地址 `targetUrl`，然后对拿到的数据进行处理，再返回给前端即可！\n\n_注意：借助 `Nitro` 实现代理，前提就是你得有 `Nitro` ，如果 `Nuxt` 完全**静态化**了，就无法动态读取环境变量了 _\n## 总结\n\n以上就是 `Nuxt` 中设置代理的四种方式\n\n其中第一种和 `Vue3` 项目没有区别，是基于本地服务实现\n\n如果你**只需要在本地开发时使用代理**来解决跨域问题，**在生产环境中没有这个问题**，那你可以选择**前两种**\n\n如果你的**代理地址比较固定**，也没有很多不同环境的后端服务可用，那你可以选择第三种 `Nitro routeRules proxy` , 只需要配置，即可实现这个简单的需求\n\n如果你的环境比较多，或是需要调用的服务也比较多，或是有很多特殊需求需要实现，那我推荐你使用**第四种**终极方案\n\n当然，这只是基于 `Nuxt` 中的实现，利用 `Nginx` 也能轻松实现代理，可以根据自己的实际情况来抉择\n\n希望对你有所帮助~\n\n\n\n\n",{"title":5,"description":1021},"post/nuxt/the-best-way-to-set-proxy-in-nuxt",[17],"b0RkZkQbM45xeRo5K2H88sKaK_c4hNowO5-1y7AwIy4",[1032,1036],{"title":1033,"path":1034,"stem":1035},"OpenClaw 安装入门（Windows）","/post/zzao/openclaw/openclaw-install-windows","post/zzao/openclaw/openclaw-install-windows",{"title":1037,"path":1038,"stem":1039},"假设你是AI，你的Skill应该是什么样的","/post/zzao/ai-skill-structure","post/zzao/ai-skill-structure",1779005085929]