前端请求遇到跨域问题不要再配置Proxy了,试试这款插件一步到位
前言
一直以来,在前端开发的过程中,尤其是现在前后分离的开发模式下,我们想要顺利完成一个项目的落地,首先就有两个问题摆在前端开发人员面前:
-
前后开发步调不一致,往往是前端页面已经做完,可后端接口还没有开发完成。这个时候前端开发人员就不得不转到别的工作上去,从而中断了此项目开发。
-
就算是后端开发人员接口已经成型或者部分成型,在前后接口联调的过程中,总不可避免的就是跨域问题
针对上述问题,当然也有许多解决方案,比如针对第一个问题就是前端开发人员可以使用 MockJs
等第三方数据模拟工具,进行数据模拟。虽然这种方式可以不用阻塞项目的开发,但是会带来别的问题:增加了学习成本以及不能在浏览器控制台中查看实际的请求过程。至于过多 MockJs
知识这里不辍述,感兴趣的同学可以去网上查阅相关的资料。
针对第二个问题,也有两种普遍的解决方式:前端进行代理配置以及后端CORS配置。如果公司的后端人员能进行CORS配置那自然是最好的解决办法,开发环境和生产环境都可以使用。不过很多情况下都是理想状态,更多的情况是因为种种原因无法进行CORS配置。这个时候前端不得不进行代理配置。常见的打包工具如:webpack、vite都已经预设好了配置,我们只需要根据自己实际情况进行配置就好。如果这么简单就好了,可实际是一些开发人员在配置代理的时候不生效,我遇过到问的最多的问题就是:为什么我配置的代理不生效呢?
所以针对上述的问题,vite-plugin-api-serve 插件应运而生。
什么是vite-plugin-api-serve
从名字上不难看出这是一款基于 Vite 开发的插件,遵循了 Vite插件开发的[命令规则](插件 API | Vite 官方中文文档)。而且该插件只在本地开发过程中生效,也就是说在打包之后,该插件就不会再起作用。
说完插件的运行环境之后,再说一下插件的主要作用。
-
解决当前后开发步调不一致时,前端人员可以通过插件自己写接口模拟数据
-
抛弃在打包工具中配置proxy代理,造成配置不生效的问题
其本质也是在本地开了一服务器拦截了请求从而进行数据的处理返回。
插件用法
安装
首先进行插件的安装,支持以下三种方式:
# npm
npm install --save-dev vite-plugin-api-serve
# yarn
yarn add -D vite-plugin-api-serve
# pnpm
pnpm add -D vite-plugin-api-serve
使用
安装好插件之后,我们就可以在项目中引入使用,这里我们假定您已经创建好了一个 vite 项目。可以参见官网进行[创建项目](开始 | Vite 官方中文文档)
我们打开项目中 vite.config.js|ts
,并在项目中引入插件并使用。代码如下:
import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
// 引入插件
import apiServe from "vite-plugin-api-serve"
export default defineConfig({
// 使用插件
plugins: [vue(), apiServe()],
})
// 插件可以接收以下两个参数
apiServe({
// 统一指定请求地址前辍,如 /api,注意不能是以 http:// 或者 https:// 开头
// 最终会和 url 属性指定的路径进行合并,如:/api/get-data
prefix: 'xxx',
//开启日志功能
log: true
})
运行项目
输出以下指令运行项目:
npm run dev
如果是第一次使用插件,插件则会在与vite.config.js|ts
同级目录下创建一个名字为:api.config.js|ts
的文件,具体是 JavaScript 文件还是 TypeScript 文件,则以vite.config.js|ts
文件类型保持一致。
插件初次创建的 api.config
文件内容会根据不同的文件类型而不同,.js
格式的内容如下:
export default [
{
url: "/get-data",
method: "GET",
remote: "http://xxxx/"
handle: ({ query, body, method, headers }, req, res) => {
return {
code: 200,
msg: "msg",
data: query,
}
},
},
]
在项目运行成功之后,会自动加载 api.config
中的内容到本地服务器中。
大家可以根据自己的需求,进行添加或者修改。
我们在项目中添加 axios
网络请求库,也可以使用 fetch
,这里以 axios
为例,我们在页面中创建并调用一个请求:
// template
<button type="button" @click="getData">本地API(get)</button>
// script
async function getData() {
const res = await axios.get("/get-data", {
params: {
name: "qingqingxuan",
job: "dever",
method: "get",
},
})
dataRes.value = JSON.stringify(res.data)
}
效果图:
至此,我们已经成功添加插件并运行。
api.config 文件
该文件支持 HMR,也就是说如果改动了该文件内容会自动加载最新的文件内容。
注意:默认导出的对象最外层必须是一个数组,里面包含着一个个具体对象。该对象中的属性如下:
- url: 该参数是具体的请求路径,完整的地址会以当前启动的服务地址为根路径进行合并如:
http://127.0.0.1:5173/get-data
。当请求的地址为 /get-data
的时候,本地服务器会进行拦截并响应。
如果指定了 remote
属性,则会把两者合并如:http://xxxx/get-data
,这个时候插件则会以 /get-data
路径进行拦截请求从而转发请求到路径 http://xxxx/get-data
中。
-
method: 请求类型方式,与
http
中的method
一致 -
remote: 主要是配合
url
参数拼接完整的请求路径,如果不需要外部路径则此参数不需要指定,一旦指定则本地的服务地址会失效。 -
handle: 该属性为函数类型,用于处理当本地服务器拦截到请求之后的处理逻辑,应该返回一个对象。最终会返回一个
json
数据类型到请求方。注意:如果指定了remote
属性,则该函数也会失效,不会被调用。该函数接受三个参数: -
第一个参数可以解构成以下几个属性:
-
body:请求中带的
body
对象 -
query: 请求中的
?
之后参数 -
method: 请求方法,与上面指定的
method
属性一致 -
headers: 请求头信息
-
req: 原始的请求对象,一般用不到
-
res: 原始的响应对象,一般用不到
关于 TypeScript
前面说过,插件会根据 vite.config
文件的类型创建 api.config
文件的类型。我们看一下如果是 .ts
类型的文件内容:
import { path, method, remote } from "vite-plugin-api-serve/decorator"
export default class DevApiServeController {
@path("/get-data")
@method("GET")
onLogin({ query }: any) {
return {
code: 200,
msg: "success",
data: {
query,
},
}
}
@path("/users/github")
@method("GET")
@remote("https://api.github.com")
getRemoteData() {}
}
可以说 .ts
和 .js
文件类型内容格式完全不同,.ts
采用了类的写法,我更加推荐 .ts
类型的写法,这看起来更像 java
的后端写法。与此同时,插件提供了三个装饰器来代替上面的三个 url、method、remote
三个属性。作用与上面的一模一样,这里不再详解。
注意,如果采用了 .ts
格式的文件,则需要在项目中的 tsconfig.json
文件进行配置:
{
"compilerOptions": {
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"strict": true,
"noEmit": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"lib": ["ES2015", "DOM"]
},
"include": ["vite.config.ts", "api.config.ts"]
}
以下两个配置必须设置
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
并在 include
中添加文件,让 ts
编译器找到它:
"include": ["vite.config.ts", "api.config.ts"]
加载远程数据
最后我们看一下 remote
属性,并演示一下是如何加载远程的数据:
//template
<button type="button" @click="getRemoteData">远程API</button>
//script
async function getRemoteData() {
const res = await axios.get("/users/github")
dataRes.value = res
}
当我们点击按钮就会触发请求:
可以看到我们已经成功请求到了远程的数据。
如果接口有跨域我们也不需要在 vite.config
文件中去配置 proxy
也可以请求到远程数据。如果有多个远程地址可以为每个路径指定不同的 remote
属性。如果是统一的 remote
参数,可以在类中设置 target
属性统一指定远程路径,如:
import { path, method, remote } from "vite-plugin-api-serve/decorator"
export default class DevApiServeController {
static target = 'https://api.github.com'
@path("/get-data")
@method("GET")
onLogin({ query }: any) {
return {
code: 200,
msg: "success",
data: {
query,
},
}
}
@path("/users/github")
@method("GET")
getRemoteData() {}
}
target
作用与 remote
一样,只不过是一个快捷写法,如果每个远程地址都一样可以统一指定,也可以单独为某个请求指定 remote
参数,优先级会高于 target
,覆盖 target
设置的地址。
转载自:https://juejin.cn/post/7399494831668363276