目录
📖阅读本文,你将:
初步理解什么是 反向代理。理解为什么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,地址:
六、结束语
我是春哥。大龄前端打工仔,依然在努力学习。我的目标是给大家分享最实用、最有用的知识点,希望大家都可以早早下班,并可以飞速完成工作,淡定摸鱼🐟。
你可以在里找到我:前端要摸鱼。