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

📖阅读本文,你将:

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

一、什么是反向代理?

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

很好理解吧?

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

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

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

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

为什么呢?

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

2.1 合理规避“同源策略”

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

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

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

例如,你在 页面上请求 就属于跨域请求。(域名不同)

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

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

CORS 是一种解决方案。

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

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

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

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

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

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

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

2.2 SPA应用:有它更灵活

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

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

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

每当你在控制台输入:

npm run dev# ornpm run start

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

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

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

切换服务的成本被提高了。

假想一个场景:

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

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

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

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

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

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

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

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

2.3 微前端:用它补全资源

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

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

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

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

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

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

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

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

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

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

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

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

如上所述:

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

当然可以,现在就写!

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

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

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

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

3.1 认识 express

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

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

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

简单来说:

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

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

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

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

mkdir lightweight-proxy # windows 用户请把 mkdir 换成 mdcd lightweight-proxyyarn initgit inityarn add express

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

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

// index.jsconst 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监听一个端口时,它就可以被访问了

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

[浏览器端]

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

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

3.2 利用 http-proxy-middleware 中间件

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

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

它的npm官网:

官网中的自我介绍:

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

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

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

按以下步骤进行操作:

执行 yarn add http-proxy-middleware在 package.json 的 scripts 下新建一个名为 dev 的脚本,内容为:node src/index.mjs  "scripts": {    "dev": "node src/index.mjs"  }新建 src/index.mjs,并初始化内容:import express from expressimport { createProxyMiddleware } from http-proxy-middleware;const app = express();// --------- 看这里 ↓ -------------------------app.use(/, createProxyMiddleware({  target: ,  changeOrigin: true}));// --------- 看这里 ↑ -------------------------app.listen(3000);

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

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

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

是不是很容易?

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

3.3 在 SPA 应用中实际使用

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

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

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

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

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

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

import express from expressimport { 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: :9527/,  changeOrigin: true}));// --------- 看这里 ↑ -------------------------app.listen(3000);

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

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

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

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

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

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

只需要两步:

在 package.json 文件中定义 bin 属性。执行 npm link3.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

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

四、还能做什么?

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

让我们畅想一下:

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

五、源码

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

六、结束语

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

你可以在里找到我:前端要摸鱼。