「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」。
1. devServer 的 proxy 配置
-
proxy是我们开发中非常常用的一个配置选项,它的目的是设置代理来解决跨域访问的问题:-
比如假如我们现在通过
http://localhost:8010请求到了本地服务器中的静态资源,如果在这些静态资源中我们还发送了一个网络请求,请求http://localhost:8000这个地址下的内容,也就是说在当前http://localhost:8010这个地址下去访问http://localhost:8000这个地址,这时就会出现跨域的问题,没办法正常地响应,会报跨域的错误。解决这种跨域问题的方法有很多,比如有以下几种:- 把静态资源和
api部署在一起,即把静态资源和api都部署在同一个服务器(比如Tomcat/express/koa服务器)上的同一端口下,这时就不会存在跨域问题; - 直接在服务器上把跨域关闭掉,但是通常情况下,为了安全起见,服务器上不会关闭跨域;
- 做一个
nginx代理,之后不管是静态资源还是api,都是通过这个nginx进行访问,再由nginx去访问静态资源和api;
但不管怎样,真实开发中当项目上线时,如果要解决跨域问题,一般都需要后端来配合(不管是部署到一块,还是关掉跨域,亦或是通过
nginx来部署,都是和后端有关的)。但是在我们开发阶段遇到这种跨域问题,后端一般是不会帮我们解决的,因为到部署阶段,我们可能用nginx来解决跨域,而在开发阶段,我们可以通过配置devServer中的proxy来解决跨域问题。 - 把静态资源和
-
简单总结上面的例子:
- 比如我们的一个
api请求的是http://localhost:8000,但是本地启动的服务器的域名是http://localhost:8010,这个时候发送网络请求就会出现跨域的问题; - 那么我们可以将请求先发送到一个代理服务器,代理服务器和
API服务器没有跨域的问题,就可以解决我们的跨域问题了;
- 比如我们的一个
-
-
我们可以进行如下的设置:
target:表示的是代理到的目标地址,比如/api-zhj/moment会被代理到http://localhost:8000/api-zhj/moment;pathRewrite:默认情况下,我们的/api-zhj也会被写入到URL中,如果希望它不被写入,则可以通过pathRewrite对路径进行重写;secure:默认情况下不接收转发到https的服务器上,如果希望支持,可以设置为false;changeOrigin:是否修改源,表示是否更新代理后请求的headers中的host地址,我们一般会将其设置为true;- 这个
changeOrigin官方文档中说得有点模糊,通过查看源码可以发现这个changeOrigin其实是要修改代理请求中的headers中的host属性:- 因为我们真实的请求,其实是需要通过比如
http://localhost:8000来请求的; - 但是因为使用了代码,默认情况下它的值是
http://localhost:8010; - 如果我们需要修改,那么就可以将
changeOrigin设置为true。
- 因为我们真实的请求,其实是需要通过比如
- 这个
下面,我们就来演示一下如何配置 devServer.proxy。
为了方便演示,我们先来安装一个常用的网络请求库:axios:
npm install axios
搭建本地接口服务器
这里为了成功发送一个网络请求,就需要一个对应的服务器,我们从 GitHub 上下载一个王红元老师开发好的本地服务器:
git clone https://github.com/coderwhy/coderhub.git
然后,我们进入这个项目的目录下:
cd coderhub/
安装依赖:
npm install
在项目目录下新建文件名为 .env 的文件,在该文件中添加如下内容(注意配置 MYSQL_PASSWORD 的值):
APP_HOST=http://localhost
APP_PORT=8000
MYSQL_HOST = localhost
MYSQL_PORT = 3306
MYSQL_DATABASE = coderhub
MYSQL_USER = root
MYSQL_PASSWORD = 你的MySQL数据库root用户密码
注:本地需要安装
MySQL数据库。并且,你需要创建coderhub数据库:CREATE DATABASE IF NOT EXISTS `coderhub`;然后在
coderhub数据库中创建以下数据表:CREATE TABLE IF NOT EXISTS `user`( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(50) NOT NULL, createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, avatar_url VARCHAR(200) ); CREATE TABLE IF NOT EXISTS `moment`( id INT PRIMARY KEY AUTO_INCREMENT, content VARCHAR(1000) NOT NULL, user_id INT NOT NULL, createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY(user_id) REFERENCES user(id) ); CREATE TABLE IF NOT EXISTS `comment`( id INT PRIMARY KEY AUTO_INCREMENT, content VARCHAR(1000) NOT NULL, moment_id INT NOT NULL, user_id INT NOT NULL, comment_id INT DEFAULT NULL, createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY(moment_id) REFERENCES moment(id) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY(user_id) REFERENCES user(id) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY(comment_id) REFERENCES comment(id) ON DELETE CASCADE ON UPDATE CASCADE ); CREATE TABLE IF NOT EXISTS `label`( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(10) NOT NULL UNIQUE, createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS `moment_label`( moment_id INT NOT NULL, label_id INT NOT NULL, createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY(moment_id, label_id), FOREIGN KEY (moment_id) REFERENCES moment(id) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (label_id) REFERENCES label(id) ON DELETE CASCADE ON UPDATE CASCADE ); CREATE TABLE IF NOT EXISTS `avatar`( id INT PRIMARY KEY AUTO_INCREMENT, filename VARCHAR(255) NOT NULL UNIQUE, mimetype VARCHAR(30), size INT, user_id INT, createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE ON UPDATE CASCADE ); CREATE TABLE IF NOT EXISTS `file`( id INT PRIMARY KEY AUTO_INCREMENT, filename VARCHAR(100) NOT NULL UNIQUE, mimetype VARCHAR(30), size INT, moment_id INT, user_id INT, createAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updateAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (moment_id) REFERENCES moment(id) ON DELETE CASCADE ON UPDATE CASCADE );
启动项目:
npm run start
项目启动成功后,你可以像下面这样通过 POST 方式调用 localhost:8000/users 接口注册一个用户:
然后,你可以用该用户去发布动态了。但在此之前,你需要像下面这样先根据用户名和密码获取 token:
之后,将获取到的 token 值添加到请求头中:
并在 body 中添加你想发布的动态内容:
动态发布成功后,你可以像下面这样通过 localhost:8000/moment?offset=0&size=10 接口查询当前用户发布过的动态(如果动态数超过 10 条,则只查询 10 条):
发送网络请求
有了可以调用接口获取数据的本地服务器后,我们将该服务器保持开启状态,然后在浏览器中访问 http://localhost:8000/moment?offset=0&size=10 这个地址:
可以看到,我们成功从本地服务器中请求下来了 2 条数据,那么我们把这一请求的路径复制下来,来到 src/main.js 中,导入 axios,然后通过它发送一个 get 请求,请求的路径就用我们刚才在浏览器中的请求路径:
import { createApp } from 'vue';
import axios from 'axios';
...
// axios.get() 返回的是一个 Promise,所以我们可以直接 .then()
axios.get('http://localhost:8000/moment?offset=0&size=10').then(res => {
console.log(res);
}).catch(err => {
console.log(err);
})
同时,为了避免端口号冲突,我们再将 devServer 原来的端口号修改掉(比如我们修改为 8010):
devServer: {
...
port: 8010,
...
},
再来重新运行 npm run serve 命令,重新启动当前的项目(到时候打包的代码中就有我们上面添加的发送网络请求的代码了):
然后在浏览器中查看效果:
你会发现,浏览器控制台报错了。这是因为我们当前是在 http://localhost:8010/ 这个源下面请求 http://localhost:8000/moment?offset=0&size=10 这个源中的内容,而这两个源的端口号不同,所以它们不是相同的源,因此会在浏览器中出现跨域访问的问题。
这时,我们可以通过前面提到的 3 种方案来解决这一跨域问题,而它们都需要服务器那边进行相关配置,但我们现在处于开发阶段,服务器那边可能无法配合我们进行相关配置来解决跨域问题,这时我们就可以通过 webpack-dev-server 的 proxy 进行配置来解决开发阶段的浏览器跨域问题。
我们来到 webpack 的配置文件中,对 devServer 中的 proxy 进行配置,配置完 proxy 后,就相当于做了一个代理:到时候项目中发送的网络请求就都会交给 devServer,让它来帮我们发送(相当于原来由浏览器向目标服务器发送网络请求,改为现在由 devServer 这台 express 服务器向目标服务器发送网络请求,一台服务器向另一台服务器发送网络请求时是没有跨域问题的,跨域问题是浏览器同源策略的限制导致的),而等到 devServer 这台服务器请求到数据后,再由它返回给我们代码中真正发送网络请求的地方,最终就可以成功获取数据了。所以我们可以通过 devServer 开启一个本地的代理(当然,这只是开发阶段的代理,真正部署时,还是会有跨域问题,所以到时候还需要和后端配合,一起解决部署时的跨域问题)。
CORS
CORS 是一个 W3C 标准,全称是“跨域资源共享”(Cross-origin resource sharing),它是一种机制,用来让网页的受限资源能够被其它域名的页面访问,以避开浏览器的同源策略(Same-origin policy)。
“源”(
origin)指的是由“协议名”(URI scheme)、“主机名”(host name)和“端口号”(port number)这三部分组成的组合体。当这三个部分都相同时就称为“同源”1。