目录
前言
本篇主要是记录下再开发vue项目时遇到的跨域问题,那么什么是跨域呢,可以简单理解为,我请求A服务的资源,A服务的资源里面又有向B服务发起的情况存在,这就是跨域。这是浏览器的同源策略限制决定的。这里插一句,我以前以为认为跨域请求是浏览器不允许发送请求,但是,实际测试的结果是,请求发出去了,后台服务也收到了请求,但是返回的数据在浏览器端无法获取无法处理,而是直接报的同源策略跨域问题,有意的同学可以自己确认下。只有IP+Port都相同了,才算是同一个域。
1.开发模式下的跨域处理
使用vue-cli脚手架生成的vue项目,在开发的时候,我们使用 npm run dev 命令运行,然后在浏览器中输入http://localhost:8080就能打开项目对应的页面。这是为什么呢?因为默认情况下,我们起了一个nodejs的web服务,node的服务把静态资源进行加载,然后在本地的8080端口提供访问服务。我们在访问本地的localhost:8080时,其实是访问的本地域,但是假如说,页面中有axios的异步请求,请求的测试服务器地址前缀是http://localhost:8007/Service,这时候就会出现跨域问题,尽管请求的都是locahost,但是端口不同。这里提供一个比较通用的处理办法如下:
新增一个pyconfig.js文件,内容如下:
const url = 'http://localhost:8007';
let ROOT;
//这里很重要,必须是production,其实此时的process.env.NODE_ENV是undefined
if(process.env.NODE_ENV === 'production'){
ROOT = url;
}else{
ROOT = "/api/";
}
exports.PROXYROOT = url;
exports.ROOT = ROOT;
然后修改项目中config/index.js这个文件,在dev下面新增代理设置相关代码,代码如下:
const config = require('../src/utils/pyconfig')
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
[config.ROOT]:{
target:config.PROXYROOT,
changeOrigin:true,
pathRewrite:{
[`^${config.ROOT}`]:'/'
}
}
},
可以看到,当npm run dev的时候,默认走的development模式,这些基础配置可以自己看下package.json中的配置说明。此时的ROOT=/api/,而在axios请求时,拼接的请求url格式都是/api/xx/xx。node服务器会自动解析成这种格式的url,把“/api/”变成“/”,而把请求的服务器地址映射为代理服务器地址,即:http://localhost:8007,最后的结果就是:http://localhost:8007/xx/xx。通过代理的方式解决了跨域问题。
需要说明的是,这些配置只在开发模式下运行有用,打包发布的时候,这些代码没用。
注意:
pyconfig.js中定义ROOT和PROXYROOT,是根据process.env.NODE_ENV的模式来的,但是这里是有问题的,因为webpack.dev.conf.js中对于其他js的引入是有顺序的,尤其对于/config/index.js,是早于/config/dev.env.js的,这就造成我们在config/index.js中配置的proxyTable里面的confgi.ROOT的值很容易出问题,index.js中引入pyconfig.js的时候,process.env.NODE_ENV还没有设置,它的值是undefined,所以如果我们在pyconfig中的判断语句根据是否是development模式来的话,代理始终不生效会有问题。这里一定要注意。
另外,[config.ROOT]和[`^${config.ROOT}`],是一个模糊的匹配,具体什么意思呢?就是说,只要请求nodejs的url中有和他们相似的字符串,就会开启代理,是很松散的。
2.打包发布和后台服务一块部署
项目的打包比较简单,直接输入npm build即可。但是在打包之前,要修改前面我们写的pyconfig里面的‘development’为‘production’,同时还要修改url地址为服务的地址前缀。修改为production的话,对外的ROOT就等于url了,请求的服务url就是标准正常的url。至于跨域的处理,是部署时候处理的,如果和服务部署在一起,就不存在跨域的问题,否则跨域问题通过相关配置解决。
一般情况下,前端资源最好是要和后台服务分开部署,这样对系统的架构、负载、吞吐都有好处,而且也便于扩展。但是对于一些小项目,可以把前端资源和服务部署到一块,以tomcat服务器javaweb项目为例。
这里插一句,打包发布的时候,需要修改下config/index.js文件下的bulid对象的assetsPublicPath的值,修改‘/’为‘./’,否则static下的静态资源无法正确打进去。
打包后的项目,就一个html文件和一个static文件夹,把这些直接放置到tomcat的webapp对应的新建一个目录即可,假设新建目录为demo,然后配置下tomcat的server.xml文件,设置默认访问tomcat时直接请求index.html。配置如下:
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
<Context path="" docBase="demo" reloadable="true" />
</Host>
这种发布模式,即前端资源和后端服务公用同一个web服务容器,不存在跨域的问题,但是一般不推荐这样部署,对系统的扩展性、负载能力都有影响。
3.使用nginx发布前端项目
这里要说明下,在打包项目的时候,和之前的打包没有任何区别,而对于章节1中提到的代理相关的代码,和这里没有任何关系,那部分的代码设置主要用来设置node服务的代理解决跨域问题的,打包后的前端项目,就是一个html和对应的js。要想独立部署又要解决跨域问题,就一定要从web容器的设置上下手。
打开nginx的conf/nginx.conf配置文件,修改一下配置:
server {
listen 8007;
server_name localhost;
location / {
root D:/nginx/nginx-1.14.1/vue/demo; #前端项目目录
index index.html; #请求首页
}
location /api {
proxy_pass http://192.168.1.153:8007; #代理的地址
}
.....
}
配置的proxy_pass就是代理的地址,当解析到请求的url中存在/api的时候,nginx会自动把域修改为proxy_pass对应的地址,然后发起请求,进而解决跨域问题。
另:
正常开发中,路径中的/api可以通过再tomcat的server.xml中配置Context的path=“/api”来设置,这样不会污染后台服务的url信息。