代理模式

113 阅读6分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情

代理模式的概念

代理模式 (Proxy Pattern)又称委托模式,它为目标对象创造了一个代理对象,以控制对目标对象的访问。

  • 代理模式把代理对象插入到访问者和目标对象之间,从而为访问者对目标对象的访问引入一定的间接性。正是这种间接性,给了代理对象很多操作空间,比如在调用目标对象前和调用后进行一些预操作和后操作,从而实现新的功能或者扩展目标的功能。

  • 代理是为了控制对对象的访问,不让外部直接访问到对象。在现实生活中,也有很多代理的场景。比如你需要买一件国外的产品,这时候你可以通过代购来购买产品。又或者导演/法院(访问者)对明星/当事人(目标)的访问都是通过经纪人/律师(代理)来完成,经纪人/律师对外部访问明星/当事人有过滤的功能。

代理模式中主要有以下几个概念:

  • Target: 目标对象,也是被代理对象,是具体业务的实际执行者;
  • Proxy: 代理对象,负责引用目标对象,以及对访问的过滤和预处理;

ES6 原生提供了 Proxy 构造函数,这个构造函数让我们可以很方便地创建代理对象:

var proxy = new Proxy(target, handler);

在 ES6 之前,一般是使用 Object.defineProperty 来完成相同的功能。

代理模式在实战中的应用

1、事件委托

<ul id="ul">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>
<script>
    let ul = document.querySelector('#ul')
    ul.addEventListener('click', (event) => {
        console.log(event.target);
    })
</script>

在实际代码中其实代理的场景很多,比如事件代理就用到了代理模式:因为存在太多的 li,不可能每个都去绑定事件。这时候可以通过给父节点绑定一个事件,让父节点作为代理去拿到真实点击的节点。

2、拦截器

比如根据业务设置一个拦截器

  • 拦截器的思想在实战中应用非常多,比如我们在项目中经常使用 Axios 的实例来进行 HTTP 的请求,使用拦截器 interceptor 可以提前对 request 请求和 response 返回进行一些预处理,比如:

    • request 请求头的设置,和 Cookie 信息的设置;
    • 权限信息的预处理,常见的比如验权操作或者 Token 验证;
    • 数据格式的格式化,比如对组件绑定的 Date 类型的数据在请求前进行一些格式约定好的序列化操作;
    • 空字段的格式预处理,根据后端进行一些过滤操作;
    • response 的一些通用报错处理,比如使用 Message 控件抛出错误;除了 HTTP 相关的拦截器之外,还有路由跳转的拦截器,可以进行一些路由跳转的预处理等操作。

3、响应式

  • 现在的很多前端框架或者状态管理框架都使用上面介绍的 Object.definePropertyProxy 来实现数据的响应式化,比如 VueMobxAvalonJS 等,Vue 2.xAvalonJS 使用前者,而 Vue 3.xMobx 5.x 使用后者。
  • Vue 2.x 中通过 Object.defineProperty 来劫持各个属性的 setter/getter,在数据变动时,通过发布-订阅模式发布消息给订阅者,触发相应的监听回调,从而实现数据的响应式化,也就是数据到视图的双向绑定。

为什么 Vue 2.x3.x 要从 Object.defineProperty 改用 Proxy 呢,是因为Object.defineProperty的一些局限性:

  • 无法监听利用索引直接设置数组的一个项,例如:vm.items[index] = newValue;
  • 无法监听数组的长度的修改,例如:vm.items.length = newLength;
  • 无法监听 ES6SetWeakSetMapWeakMap 的变化;
  • 无法监听 Class 类型的数据;
  • 无法监听对象属性的新加或者删除;

除此之外还有性能上的差异,基于这些原因,Vue 3.x 改用 Proxy 来实现数据监听了。当然缺点就是对 IE 用户的不友好,兼容性敏感的场景需要做一些取舍。

4、保护代理和虚拟代理

  • 保护代理 :当一个对象可能会收到大量请求时,可以设置保护代理,通过一些条件判断对请求进行过滤;

    保护代理其实就是对访问的过滤,之前的明星/经纪人例子就属于这种类型。

  • 虚拟代理 :在程序中可以能有一些代价昂贵的操作,此时可以设置虚拟代理,虚拟代理会在适合的时候才执行操作。

    虚拟代理是为一个开销很大的操作先占位,之后再执行,比如:一个很大的图片加载前,一般使用loading图、低质量图片等提前占位,优化图片加载导致白屏的情况; 现在很流行的页面加载前使用骨架屏来提前占位,很多 WebAppNativeApp 都采用这种方式来优化用户白屏体验

5、正向代理与反向代理

  • 正向代理: 一般的访问流程是客户端直接向目标服务器发送请求并获取内容,使用正向代理后,客户端改为向代理服务器发送请求,并指定目标服务器(原始服务器),然后由代理服务器和原始服务器通信,转交请求并获得的内容,再返回给客户端。正向代理隐藏了真实的客户端,为客户端收发请求,使真实客户端对服务器不可见;
  • 反向代理: 与一般访问流程相比,使用反向代理后,直接收到请求的服务器是代理服务器,然后将请求转发给内部网络上真正进行处理的服务器,得到的结果返回给客户端。反向代理隐藏了真实的服务器,为服务器收发请求,使真实服务器对客户端不可见。

代理模式的优缺点

优点

  • 代理模式能将代理对象与被调用对象分离,降低了系统的耦合度。代理模式在客户端和目标对象之间起到一个中介作用,这样可以起到保护目标对象的作用
  • 代理对象可以扩展目标对象的功能;通过修改代理对象就可以了,符合开闭原则;

缺点

  • 增加了系统的复杂度,要斟酌当前场景是不是真的需要引入代理模式
  • 处理请求速度可能有差别,非直接访问存在开销问题