背景
vue的项目在开发到生产上线的初期最容易遇到的问题就是跨域。浏览器安全机制导致的,配置api代理只是本地的,所以经常需要再服务器的nginx做一个后端接口的nginx反向代理
vite.config.js配置代理
简单粗暴的看下vue2的配置
个人项目网站的配置如下
webpack的配置
proxyTable: { "/api/": { target: "https://yongma16.xyz/", //后端接口地址 ws: true, //接受websocket请求 changeOrigin: true, //是否允许跨越 chunkOrigins: true, pathRewrite: { "^/api": "api", //重写, }, }, "/ws_api/": { target: "wss://yongma16.xyz/ws/webchat/", ws: true, //接受websocket请求 changeOrigin: true, //是否允许跨越 chunkOrigins: true, pathRewrite: { "^/ws_api": "", //重写, }, }, }
vue.config.js的配置
proxy: { "^/cloudApi/": { target: "https://yongma16.xyz/back-front", // target: "http://localhost:9090/", changeOrigin: true, ws: true, rewrite: (path) => path.replace(/^\/cloudApi/, ""), }, },
跨域鼻祖 jsonp
经常写html 我们知道拿个src可以跨域
没错,JSONP实现跨域就是通过html的标签的src去获取跨域请求内容
简陋版
// 前端部分 <script> // 1 callback // 2 后端 callbackName(数据) function onResponse(posts) { console.log(posts); } // 前端没有调用 </script> <!-- 后端返回结果 --> <!-- 调用 --> <script src="http://localhost:9090/api"></script>
//后端部分 const http = require('http'); http.createServer((req, res) => { if (req.url === '/api') { let posts = ['js', 'php']; res.end(`onResponse(${JSON.stringify(posts)})`); } }) .listen(9090, () => { console.log(9090) })
前端json p的跨域
// 前端(代码放在body中执行) <script> function jsonp(url, options) { // 超时处理 const { timeout } = options; return new Promise((resolve, reject) => { // 防止函数名冲突 let funcName = `jsonp${Date.now()}`; let time = null, scriptNode; // 定义callback window[funcName] = function(data) { if (timeout) clearTimeout(time); resolve(data); // 很重要的性能优化点 // 清除本次请求产生的回调函数和script标签 delete window[funcName]; document.body.removeChild(scriptNode); } // 创建script标签 scriptNode = document.createElement('script'); // 给script标签添加src属性 scriptNode.src = `${url}?callback=${funcName}`; // 发出请求 document.body.appendChild(scriptNode); time = setTimeout(() => { reject('network err, timeout') }, timeout) // 失败 scriptNode.onerror = function(err) { reject(err); } }) } jsonp('http://localhost:9090/api', { callBack: 'res1', // 超时处理 timeout: 3000 }) // 请求成功 .then(res => { console.log('jsonp->', res); }) // 请求失败 .catch(err => { console.log("network err!") }) </script>
如下是ajax封装之后的调用
$.ajax({ url: "http://127.0.0.1/~chenjiebin/mycode/php/crossdomain/index.php", dataType: "jsonp", jsonp: "callback", context: document.body, success: function(data) { console.log(data); } });
ok,那么axios也可以使用jsonp产生跨域
譬如
axios.jsonp = (url,data)=>{ if(!url) throw new Error('url is necessary') const callback = 'CALLBACK' + Math.random().toString().substr(9,18) const JSONP = document.createElement('script') JSONP.setAttribute('type','text/javascript') const headEle = document.getElementsByTagName('head')[0] let ret = ''; if(data){ if(typeof data === 'string') ret = '&' + data; else if(typeof data === 'object') { for(let key in data) ret += '&' + key + '=' + encodeURIComponent(data[key]); } ret += '&_time=' + Date.now(); } JSONP.src = `${url}?callback=${callback}${ret}`; return new Promise( (resolve,reject) => { window[callback] = r => { resolve(r) headEle.removeChild(JSONP) delete window[callback] } headEle.appendChild(JSONP) }) }
ok,相信到这,我们可能会猜想vue.config的proxy代理是否页用了jsonp,去看看!
vue proxy用了jsonp吗(并不是)
ctrl点击去查看源代码,走你
哦吼,看见只是他的类型定义。
不急,咋们已经看见这是axios的拦截配置项了。
走去axios一探究竟!
axios是基于promise的网络请求库,由node发起请求。
http的Proxy包括协议 域名 端口
我们找到的是http的服务代理,node开一个web服务器去代理请求,所以不是jsonp
vue项目中的node开启了web服务器代理转发了请求
export class Server extends events.EventEmitter { /** * Creates the proxy server with specified options. * @param options - Config object passed to the proxy */ constructor(options?: ServerOptions) /** * Used for proxying regular HTTP(S) requests * @param req - Client request. * @param res - Client response. * @param options - Additional options. */ web( req: http.IncomingMessage, res: http.ServerResponse, options?: ServerOptions, callback?: ErrorCallback, ): void /** * Used for proxying regular HTTP(S) requests * @param req - Client request. * @param socket - Client socket. * @param head - Client head. * @param options - Additional options. */ ws( req: http.IncomingMessage, socket: unknown, head: unknown, options?: ServerOptions, callback?: ErrorCallback, ): void /** * A function that wraps the object in a webserver, for your convenience * @param port - Port to listen on */ listen(port: number): Server /** * A function that closes the inner webserver and stops listening on given port */ close(callback?: () => void): void /** * Creates the proxy server with specified options. * @param options - Config object passed to the proxy * @returns Proxy object with handlers for `ws` and `web` requests */ static createProxyServer(options?: ServerOptions): Server /** * Creates the proxy server with specified options. * @param options - Config object passed to the proxy * @returns Proxy object with handlers for `ws` and `web` requests */ static createServer(options?: ServerOptions): Server /** * Creates the proxy server with specified options. * @param options - Config object passed to the proxy * @returns Proxy object with handlers for `ws` and `web` requests */ static createProxy(options?: ServerOptions): Server addListener(event: string, listener: () => void): this on(event: string, listener: () => void): this on(event: 'error', listener: ErrorCallback): this on( event: 'start', listener: ( req: http.IncomingMessage, res: http.ServerResponse, target: ProxyTargetUrl, ) => void, ): this on( event: 'proxyReq', listener: ( proxyReq: http.ClientRequest, req: http.IncomingMessage, res: http.ServerResponse, options: ServerOptions, ) => void, ): this on( event: 'proxyRes', listener: ( proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse, ) => void, ): this on( event: 'proxyReqWs', listener: ( proxyReq: http.ClientRequest, req: http.IncomingMessage, socket: net.Socket, options: ServerOptions, head: any, ) => void, ): this on( event: 'econnreset', listener: ( err: Error, req: http.IncomingMessage, res: http.ServerResponse, target: ProxyTargetUrl, ) => void, ): this on( event: 'end', listener: ( req: http.IncomingMessage, res: http.ServerResponse, proxyRes: http.IncomingMessage, ) => void, ): this on( event: 'close', listener: ( proxyRes: http.IncomingMessage, proxySocket: net.Socket, proxyHead: any, ) => void, ): this once(event: string, listener: () => void): this removeListener(event: string, listener: () => void): this removeAllListeners(event?: string): this getMaxListeners(): number setMaxListeners(n: number): this listeners(event: string): Array<() => void> emit(event: string, ...args: any[]): boolean listenerCount(type: string): number }
nginx代理
proxy代理请求,proxy_pass。
http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location /api { proxy_pass https://yongma16.xyz/api; } } }
结尾
vue和nginx都对请求做了一次转发
vue是基于客户端如windows
nginx是基于生产服务器的如centos
在打包前端项目到服务器的时候
由于前端是静态文件
产生协议域名端口的跨域时
需要用到nginx反向代理后端端口地址
结束
感谢阅读,如有不足欢迎指出!