背景

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反向代理后端端口地址

结束

感谢阅读,如有不足欢迎指出!
在这里插入图片描述

Logo

前往低代码交流专区

更多推荐