📖阅读本文,你将:

  1. 初步理解什么是"反向代理"
  2. 理解为什么2022年,前端离不开"反向代理"
  3. 跟随作者一步步摸清楚如何开发一个简单而实用的轻量代理工具
  4. 学会如何把本地代码发布到​​npm​​ 全局命令中。
  5. 收货一份少得可怜的源码。

一、什么是反向代理?


度娘说:“反向代理位于用户与目标服务器之间,对用户而言,反向代理相当于目标服务。用户直接访问反向代理服务器就可以获得目标服务器的资源。”


很好理解吧?

还不理解的话,我用更通俗的语法解释下:


你朝反向代理发请求,它将请求转发给其他服务,并将结果返回给你。


充分且简单!打造专属“轻量代理神器”?_反向代理

如上图所示,你找代理服务器请求 ​​业务接口/本地资源/对象存储资源​​,代理服务器分别向不同的资源发出请求,获得资源后,再返还给你。

对于2022年的前端开发而言,反向代理已经是工作中不可或缺的一部分了。

为什么呢?

二、前端为啥需要代理层?

2.1 合理规避“同源策略”


同源策略:浏览器的护城河。


浏览器的 “同源策略” 声明:默认情况下,“跨域” 的请求返回会被拦截下来。


啥是跨域?
当一个请求 ​​​url​​​ 的协议、域名、端口三者之间任意一个与当前页面 ​​url​​ 不同即为跨域。


例如,你在 ​​https://baidu.com​​​ 页面上请求 ​​https://google.com/user/abc​​ 就属于跨域请求。(域名不同)

至于为啥浏览器要有这样一条 “同源策略”,那又是一个可以单独开篇的话题了,本文不进行赘述。

总之,因为 “同源策略” 的存在,前端在开发时就会面临很多的问题,比如:“如何跨域去调用服务端的接口?”

充分且简单!打造专属“轻量代理神器”?_Vue.js_02

​CORS​​ 是一种解决方案。


CORS (Cross-Origin Resource Sharing),中文名为 “跨域资源共享”,通过服务端在“响应头”、“OPTIONS” 方法上的配合,可以让浏览器允许页面请求跨域资源。


但这其实也会在一定程度上增加安全风险,并需要服务端同学的配合。

“反向代理” 则是一种成本更低,也被更为泛用技术手段。

充分且简单!打造专属“轻量代理神器”?_前端_03

如上图所示,注意以下几个关键点:

  1. 当前页面在​​http://localhost:8080​​ 域。
  2. 浏览器向​​http://localhost:8080​​ 发起请求时,完全符合“同源策略”。
  3. 反向代理将接收到的请求转发给真实服务器,获得了请求的数据
  4. 反向代理将得到的数据返回给浏览器页面。
  5. 浏览器获得到了它请求的数据。

在整个过程中,既符合浏览器的 “同源策略”, 又不需要服务端介入增加额外的配置,从而不会因为这些增加的配置导致生产环境增加危险系数。

反向代理,让前端研发处理跨域时,随心所欲

2.2 SPA应用:有它更灵活


SPA ( Single Page Application ),中文名为 “单页应用”,是目前市场上前端工程最普遍、最常见的存在形态。


自从 ​​React​​​、 ​​Angular​​​、 ​​Vue​​​ 等一批前端框架开始占据市场,​​SPA​​ 便成为了几乎所有前端都无法绕开的一个关键名词。

​SPA​​​ 通常情况下,和 ​​webpack​​​、​​vite​​​ 等构建工具是相生相伴的好搭档,得益于社区的兴隆,​​webpack​​​ 提供了 ​​devServer​​ 这一特性,能让前端开发者无感地用上了 “反向代理” 的特性。

每当你在控制台输入:

npm run dev
# or
npm run start

的时候,其实前端框架里的 ​​webpack​​ 已经悄悄给你启动了一个 反向代理,你可以在配置文件里轻松地让符合某些规则的请求 被代理 到服务器上。

它不仅提供反向代理,也和 ​​webpack​​​ 本身的 ​​hmr​​ (热更新) 绑定在一起,每当资源更新后,你很快就能在页面上看到最新代码所呈现的效果。

充分且简单!打造专属“轻量代理神器”?_服务端_04

这当然是极佳的开发体验,但它也 带来了一个小小的问题

切换服务的成本被提高了

假想一个场景:


当你的服务连着 [环境A] 进行开发时,突然发现这个环境的服务宕机了,于是你熟练地切换到了 [环境B],因为你修改了代理配置,你的 ​​webpack​​ 需要重启,对于一些巨石项目或低配电脑,这可能需要5分钟或者更久。



终于,服务起好了,你在 [环境B] 快乐的开发着,此时被告知 [环境A] 修复了,为了联调,你得切回去。好家伙,又是一个 ​​5分钟​​……


开发过大型 ​​SPA​​ 项目的小伙伴,想必对这种场景并不陌生。

此时,我们就难免会有一种疑问:


反向代理一定要和 ​​开发服务​​​ 绑定吗?我切个环境,关您 ​​开发服务​​ 啥事?您重启个什么劲呢?


在这个疑问的驱动下,一个想法出现了:

为什么我们不增加一层 轻量代理层 呢?它只从事代理相关的工作,让 代理构建 彻底解耦呢?

因为专一,所以轻量,从而 切换贼快

充分且简单!打造专属“轻量代理神器”?_服务端_05

2.3 微前端:用它补全资源

随着 ​​SPA​​ 的日渐发展,相关配套逐渐成熟。

但是在某些巨型企业级应用日益庞大臃肿之后,开始暴露出一些让人头疼的问题。

  • 代码结构日渐复杂,难以维护和管理
  • 开发工具启动、热更新效率捉急
  • 模块与模块之间依赖变高,复用成本增加

这类应用,被业内人士起了一个形象的名字——“巨石应用”。

于是有了 “微前端” 的概念,比如 ​​micro.js​​​ 和 ​​qiankun.js​​,都提供了相应的解决方案。

微前端固然是 大型企业级应用 开发的利器,形成了一种全新的前端开发方式,提供了前端微应用 "制品级" 复用的能力,但它同样引入了新的问题:

开发期可能需要起一堆应用🤣。(如图)

充分且简单!打造专属“轻量代理神器”?_前端_06

当然,不必惊慌,方法总比困难多。

当你需要 “容器” 或 “其他微应用” 的资源时,完全不必局限在本地端口中获取,也可以在某个开发环境上直接拉取。

此时,一个独立轻量的 “代理层” 可能就会显得 恰如其分

充分且简单!打造专属“轻量代理神器”?_反向代理_07

虽然也可以在每个微应用内设置代理,但是很显然,一个轻量的代理层会让整个结构变得更加便捷,灵活。


比如,此时我想在本地新起一个微应用,想让它进入我的联调范围,相比于重启某个微应的开销,重启代理层的开销就会显得更小。



如上所述:

一个 “轻量代理层” 的存在,对于前端开发而言,真的可以带来非常多的便捷,那么,我们是否可以手写订制一个好用的 “轻量代理层” 呢?

当然可以,现在就写!

跟着春哥一起,手把手带你实现一个 “代理层神器”!

三、手把手!简单实用的自制工具

前端制作开发工具,首选的技术当然是毫无疑问的 ​​nodejs​​,语言上的熟悉,可以降低非常多不必要的麻烦。

在 ​​nodejs​​​ 生态里,被运用最广的 ​​web​​​ 开发框架无疑正是 ​​express​​,这也是我们接下来要选择使用的框架。

3.1 认识 ​​express​


​express​​​:
"基于 ​​​Node.js​​​,快速、开放、极简的 ​​Web​​ 开发框架。"


可以说,它是 ​​nodejs​​ 生态里,最具影响力的明星框架之一。

通过 ​​express​​​,你可以快速构建一个 ​​web​​​ 应用,这个所谓的 ​​web​​ 应用,可以是一个网站、可以是一个接口提供方、可以是一个定时发送邮件的工具,当然,它也可以是我们想要实现的 “轻量代理层”。

简单来说:


你使用 ​​express​​ 可以创建一个应用,该应用可以监听端口,提供 http、https、websocket 等多种形态的网络服务。


关于 ​​express​​ 的一切,如果你有兴趣,可以访问其官网学习:

www.expressjs.com.cn/starter/ins…

在 ​​express​​ 使用过程中,我们需要理解许多的概念,包括:什么是应用、什么是路由、什么是中间件。

虽然这些在文档中都有官方解释,但为了你能快速理解,我们先写一个简单例子:

mkdir 'lightweight-proxy' # windows 用户请把 mkdir 换成 md
cd lightweight-proxy
yarn init
git init
yarn add express

完成以上步骤,就成功创建了一个项目,并安装了对 ​​express​​ 的依赖。

在项目根目录新建 index.js。

// index.js
const express = require('express')
const app = express() // app 就是一个应用

app.use(function (req, res, next) { // 此方法就是一个最基本的中间件,它在每次请求之前打印时间
console.log('Time:', Date.now())
next()
})

app.get('/hello', (req, res) => { // "GET /hello" 就是一个路由
res.send('Hello World!')
})
app.listen(3000) // 当app监听一个端口时,它就可以被访问了

在浏览器里访问 ​​http://localhost:3000/hello​​,你可以立刻看到如下效果:

充分且简单!打造专属“轻量代理神器”?_前端_08 [浏览器端]

并且,控制台上也会出现当次访问的时间戳打印:

充分且简单!打造专属“轻量代理神器”?_反向代理_09

很好,你已经学会了 ​​express​​ 最简单的用法,你又掌握了一项技能,接下来,我们就需要用到一款来自社区的 “中间件” 了。

3.2 利用 ​​http-proxy-middleware​​ 中间件

​express​​​ 的生态无比繁荣,在 ​​github​​​ 上,你可以找到各种场景需要用到的 ​​demo​​ 或者中间件。

​http-proxy-middleware​​ 正是这样一个中间件。

它的npm官网:www.npmjs.com/package/htt…

官网中的自我介绍:


让 node.js 代理变得容易;作为中间件,它支持 ​​connect​​​, ​​express​​​、​​browser-sync​​ 等诸多框架。


很显然,​​http-proxy-middleware​​​ 是为 “请求代理” 而生的插件,它不仅仅支持 ​​express​​​,还支持更多场景,但本文我们只需要知道它在 ​​epxress​​ 中如何使用即可。


在刚才的工程基础上,先删掉根目录下的 index.js


按以下步骤进行操作:

  1. 执行​​yarn add http-proxy-middleware​
  2. 在​​package.json​​​ 的​​scripts​​​ 下新建一个名为​​dev​​​ 的脚本,内容为:​​node src/index.mjs​
"scripts": {
"dev": "node src/index.mjs"
}
  1. 新建 src/index.mjs,并初始化内容:
import express from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware';
const app = express();

// --------- 看这里 ↓ -------------------------
app.use('/', createProxyMiddleware({
target: 'https://***.cn/',
changeOrigin: true
}));
// --------- 看这里 ↑ -------------------------
app.listen(3000);

以上代码的关键就在注释中着重强调的区域,为路由 '/' 下的所有请求指定使用一个中间件,代理到 ​​'https://***.cn/'​​ 地址上去。

因此,当你在浏览器中输入 ​​http://localhost:3000​​ 时,你会惊奇地发现,你打开了掘金的主页:(虽然,因为掘金官网 “防盗链” 机制的存在,你无法刷出文章列表。)

充分且简单!打造专属“轻量代理神器”?_反向代理_10

ok,你已经学会了如何使用 ​​http-proxy-middleware​​​ 在 ​​express​​ 应用创建一个简单的代理!

是不是很容易?

接下来,我们要结合 实际开发场景 让它接管我们的 请求代理

3.3 在 ​​SPA​​ 应用中实际使用


我先假设,你的项目中有一个 ​​vue.js​​​ or ​​react.js​​​ 的项目,它占用了 ​​9527​​ 端口。


通常情况下,​​SPA​​​ 应用中都会采用这样的方法区分 “​​xhr​​ 请求” 和 “前端资源请求”。

  1. 设置一个常量字符串,比如​​/prod-api​
  2. 在你的所有​​xhr​​​ 请求中,配置前缀为​​/prod-api​​​,比如你想请求​​/user/list​​​,那就将其统一定义为​​/prod-api/user/list​
  3. 在​​webpack devServer​​​ 中,识别到以​​/prod-api​​​ 开头,就认定为​​xhr​​ 请求,代理到服务端;否则就认定为前端资源请求。
  4. 在所有​​/prod-api​​​ 请求代理到服务端之前,通过​​rewrite​​​ 方法去除​​/prod-api​​​部分,向服务器发送​​http://www.dev-server.com/user/list​​ 请求。(此处可根据服务端情况配置灵活调整)

只要你想明白了以上过程,你也就能清晰地撰写 “轻量代理层” 中间的逻辑了。

  1. 以​​/prod-api​​ 开头的请求,将其代理到服务器上。
  2. 其他请求,代理到​​http://localhost:9527​​ 上。

修改 ​​src/index.mjs​​ 中的代码为:

import express from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware';
const app = express();

// --------- 看这里 ↓ -------------------------

// 凡是以 '/prod-api' 开头的都认定为 xhr 请求
app.use('/prod-api', createProxyMiddleware({
target: 'https://dev.my-server.com/', // 这一行是你的服务端地址
changeOrigin: true,
pathRewrite: {
'^/prod-api': '' // 在向服务端发起请求时,去掉标识xhr的前缀
}
}));

// 其他请求则认定为前端资源请求,如:html/js/css 等
app.use('/', createProxyMiddleware({
target: 'http://localhost:9527/',
changeOrigin: true
}));
// --------- 看这里 ↑ -------------------------

app.listen(3000);

以上配置已经可以顺利运行我手中的几个项目了,你要不要赶紧上手制作一个,看看你手头的项目是否可以顺利运行起来?

当我们想切换接口服务器时,只需要修改 => 停止 => 重启。

切换顺滑如丝,再也不用等待漫长的构建过程了!

3.3 让其在本机全局命令生效

当我们完成上面的步骤后,实际上 “轻量代理层” 就已经可以在日常开发中用起来了,但每次都要找到 ​​lightweight-proxy​​ 的文件夹,运行命令行,属实麻烦,有啥更好的办法吗?

当然!我们把它注册到全局命令中去!

只需要两步:

  1. 在​​package.json​​​ 文件中定义​​bin​​ 属性。
  2. 执行​​npm link​

3.3.1 增加 ​​bin​​ 属性:

​package.json​

"bin": {
"cc-proxy": "./src/index.mjs"
}

其中 ​​cc-proxy​​​ 是我随便乱取的,你也可以根据自己的喜好,将其改为 ​​lol-proxy​​​、​​i-love-study​​ 等各种奇形怪状的名称,都行🤣。

3.3.2 执行 ​​npm link​

在项目的根目录下执行如下指令:

npm link
# 不知道为啥,yarn link无法产生正确的指令,所以必须使用npm link

执行完以上两步之后,尝试在任何其他位置打开一个全新的 ​​cmd​​​ 或者 ​​powershell​​​ 命令行(对于 ​​windows​​​ 用户); ​​mac​​ 用户应该也需要打开一个新命令行。

然后执行以下命令:

cc-proxy

充分且简单!打造专属“轻量代理神器”?_前端_11

嘿!它生效了!
现在,我们的电脑上正式多了一个专属的 “轻量代理层”!

四、还能做什么?

当然,本文到这里,已经实现了我们初步的目标,那么,我们还可以在这个基础上做些什么呢?

让我们畅想一下:

  1. 支持 https
  2. 将工具打包成​​npm​​​ 包,发布到私有​​npm​​ 源上让同事也能使用?
  3. 提供配置化的能力,在不同的项目通过配置实现不同的效果?
  4. more...

五、源码

细心的春哥,已经把少的可怜的源码上传到了 ​​github​​,地址:

github.com/zhangshichu…

六、结束语

我是​​春哥​​​。
大龄前端打工仔,依然在努力学习。
我的目标是给大家分享最实用、最有用的知识点,希望大家都可以早早下班,并可以飞速完成工作,淡定摸鱼🐟。

你可以在公众号里找到我:​​前端要摸鱼​​。