likes
comments
collection
share

Vue 项目之 Webpack 中 devServer 的 proxy 配置(2)

作者站长头像
站长
· 阅读数 15

1. 配置 devServer.proxy

下面来配置代理,我们通常会这样做:

...

module.exports = {
  ...
  devServer: {
    ...
    proxy: {
      '/api': 'http://localhost:8000'
    }
  },
  ...
}

'/api': 'http://localhost:8000' 这行代码相当于是一个映射关系,意思是如果对 /api 进行请求,到时候会将其代理到 http://localhost:8000(准确来说其实是被代理去请求 http://localhost:8000/api 这个地址)。那就意味着我们在发送网络请求时需要这样来写:

axios.get('/api/moment?offset=0&size=10').then(res => {
  console.log(res);
}).catch(err => {
  console.log(err);
})

我们用 /api 替换掉了之前的 http://localhost:8000,就是说我们在源代码中使用的是 /api(到时候它会被映射到 http://localhost:8000 中去),让它作为代理帮我们去请求 http://localhost:8000 中的数据,请求到数据后再返回到我们的 response(即上面代码中的 res)里面,我们就能拿到对应的数据了。

但是,目前这样配置还会有一些问题。我们 npm run serve 重新运行代码看下效果:

Vue 项目之 Webpack 中 devServer 的 proxy 配置(2)

可以看到,之前的跨域访问问题已经不见了,但是现在又报了另外一个错误:请求的资源没找到。报错信息显示现在加载的是 http://localhost:8010/api/moment?offset=0&size=10 这个路径的资源,也就是说,我们上面的配置('/api': 'http://localhost:8000')本质上会将 /api 映射成 http://localhost:8010/api,而不是我们希望的 http://localhost:8010 这个映射结果。如果我们希望映射的结果中不带这个 /api,就需要通过 pathRewrite 属性重写路径,因此,'/api' 后面跟得一般不是字符串,而是一个对象:

...

module.exports = {
  ...
  devServer: {
    ...
    proxy: {
      '/api': {
        target: 'http://localhost:8000',
        pathRewrite: {
          '^/api': '' // 将开头的 '/api' 替换成空字符串(^ 是开头的意思,'^/api' 表示以 /api 开头)
        }
      }
    }
  },
  ...
}

因为我们现在发送的网络请求已经改成了 /api 开头的格式,所以就会匹配上 pathRewrite 的规则,那么最终映射的结果中就不会有 /api 了,因此就能请求到我们想要的路径了。我们重新运行 npm run serve 命令看下效果:

Vue 项目之 Webpack 中 devServer 的 proxy 配置(2)

可以看到,这次我们就能成功请求到本地服务器中的数据了。

以上,就是开发阶段在本地解决浏览器跨域问题的方法,即通过配置 devServer.proxy 来解决,当然,这只是关于 proxy 最基本的配置,下面我们再来讲两个 proxy 的配置项:securechangeOrigin

secure

默认情况下,devServer.proxy 不会接受运行在 HTTPS 协议上且证书无效的后端服务器,意思是假如我们代理到的目标地址是 https://localhost:8000,即目标是 HTTPS 协议的服务器,而 HTTPS 协议的服务器是必须要携带对应的安全证书的,但如果没有配置对应的证书,这里就不会代理成功。要想在没有证书的情况下,依然能成功代理到目标是 HTTPS 协议的服务器上,我们就需要设置 secure: false(默认情况下,securetrue):

...

module.exports = {
  ...
  devServer: {
    ...
    proxy: {
      '/api': {
        target: 'https://localhost:8000',
        pathRewrite: {
          '^/api': '' // 将开头的 '/api' 替换成空字符串(^ 是开头的意思,'^/api' 表示以 /api 开头)
        },
        secure: false // 如果目标是 http 协议的地址,则可以不配置 secure
      }
    }
  },
  ...
}

changeOrigin

changeOrigin 表示是否修改源。当前(配置了 devServer.proxy 后),我们在源代码中发送网络请求时是经过了一层代理的(因为直接发送网络请求在浏览器中会出现跨域问题),让代理去帮我们发送网络请求,代理请求到数据后再将数据返回给我们。在这一过程中,我们源代码中是通过 /api 开头的路径去发送请求的,前面说过,默认情况下它其实会加上当前的源(比如 http://localhost:8010),即实际请求的路径是以 http://localhost:8010/api 开头的。虽然这个源(http://localhost:8010)后面经过代理变成了 http://localhost:8000,但请求的目标服务器(服务器会解析请求的 header,有些服务器可能会对 header 进行验证,看下 header 里面的内容对不对,如果不对的话服务器是不会随便返回对应的数据的,因为服务器可能担心请求来自爬虫软件,因为有些爬虫软件会通过代理服务器爬取我们的数据,所以目标服务器可能会对源进行校验)接收到的源其实还是 http://localhost:8010,如果该服务器有对源做校验,可能就不会给我们返回数据了。这时,我们就可以将 changeOrigin 属性设置为 true,让 devServer 服务器内部帮我们将发送网络请求时的源修改掉,改成代理的目标地址对应的源。这样一来,不管目标服务器上有没有对源做校验,就都没有问题了。

我们通过查看源码可以看到 changeOrigin 设置为 true 时,修改的其实是代理请求中的 headers 中的 host 属性的值:

Vue 项目之 Webpack 中 devServer 的 proxy 配置(2)

更多关于 devServer.proxy 的信息可以查阅官方文档:webpack.js.org/configurati…

2. historyApiFallback

  • historyApiFallback 是开发中非常常见的属性,它主要的作用是解决 SPA 页面在路由跳转之后,进行页面刷新时,返回 404 错误的问题。
  • boolean 值:默认是 false
    • 如果设置为 true,那么在刷新时,返回 404 错误时,会自动返回 index.html 的内容;
  • object 类型的值,可以配置 rewrites 属性(了解):
    • 可以配置 from 来匹配路径,决定要跳转到哪一个页面;
  • 事实上 devServer 中的 historyApiFallback 功能是通过 connect-history-api-fallback 库来实现的:

关于 historyApiFallback 的具体配置我们后面再来讲。