wangjing013 / blog

📝 记录

Home Page:https://wangjing013.github.io/blog/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

代理模式

wangjing013 opened this issue · comments

出于某些原因或限制,一个对象不能够直接访问另外一个对象,需要通过第三者牵线搭桥才能访问,这种模式称之为代理模式.

代理模式在日常生活中随处可见,点外卖,委托,中介所等. 他们的关系图如下:

proxy

从上面的图来说,代理对象在用户和实际对象间起的就是桥梁的作用. 当用户发送请求时,代理对象经过一系列的处理后,再将请求转发给实际对象.

在某些情况下用户来说他并不关系他访问的是代理对象还是本体. 他只要能够完成对应的事情即可.

其次代理对象和真实对象之间是存在可替换的关系,那么它们就必须具备接口的一致性.

下面来看看代理模式在开发中常见的应用场景.

图片预加载

在现代的 Web 应用中,图片是网站组成中比较重要的元素. 例如头像、背景、宣传页等等. 当我们弱网络环境访问应用时,可以看到图片位置会出现短暂的空白,常见的做法就是提供一张占位图,等真正的图片加载完成后再去进行替换。 这种场景就很适合虚拟代理.

下面看看具体的实现:

<template>
  <img ref="avatar" alt="" />
</template>

<script>
export default {
  props: {
    avatar: {
      type: String,
      default: ''
    }
  },
  mounted() {
    this.proxyImg(require('./placeholder.png'))
  },
  methods: {
    setSrc(src) {
      this.$refs.avatar.src = src
    },
    proxyImg(src) {
      const img = new Image()
      img.src = this.avatar
      this.setSrc(src)
      img.onload = () => {
        this.setSrc(this.avatar)
      }
      img.onerror = (error) => {
        console.log('avatar load :::error', error)
      }
    }
  }
}
</script>

通过 proxyImg 对象控制 img 对象的展示,在图片未加载成功之前都会展示提供 placeholder.png.

缓存代理

当我们程序涉及大量的计算的时,可以通过缓存方式来提供其计算效率. 例如:

在不使用代理之前 :

var mult = function () {
  console.log("开始计算了");
  var a = 1;
  for (var i = 0, l = arguments.length; i < l; i++) {
    a = a * arguments[i];
  }
  return a;
};

console.log(mult(2,3,4)); 
console.log(mult(2,3,4)); 

输出:

开始计算了
24
开始计算了
24

从结果来看,虽然输入内容一样,但是依然会执行两次重复计算. 现在通过缓存来优化一下.

var mult = function () {
  console.log("开始计算了");
  var a = 1;
  for (var i = 0, l = arguments.length; i < l; i++) {
    a = a * arguments[i];
  }
  return a;
};

var proxyMult = (function () {
  var cache = {};

  return function () {
    const args = Array.prototype.join.call(arguments, ",");
    if (args in cache) {
      return cache[args];
    }
    return (cache[args] = mult.apply(this, arguments));
  };
})();

console.log(proxyMult(2, 3, 4));
console.log(proxyMult(2, 3, 4));

输出:

开始计算了
24
24

其实这种模式,在 Vue 中 computed 属性中就有应用缓存代理.

事件代理

这应该是我们经常会遇到的一个场景,给列表中的每一项都添加一个点击事件。如下

 <ul id="father">
    <li>事项1</li>
    <li>事项2</li>
    <li>事项3</li>
    <li>事项4</li>
    <li>事项5</li>
    <li>事项6</li>
  </ul>

通常有两种做法,第一种就是每个 li 元素都绑定一个事件.

  let lis = document.getElementsByTagName("li");
  for (let i = 0, len = lis.length; i < len; i++) {
    lis[i].addEventListener("click", function (e) {
      e.preventDefault();
      console.log("li=", e.target.innerHTML);
    });
  }

虽然上面方式达到目的,但是当随着节点增多,就会造成性能问题.

第二种利用事件冒泡,仅在父元素绑定一个事件.

let father = document.getElementById("father");
father.addEventListener("click", function (e) {
  e.preventDefault();
  console.log("li:", e.target.innerHTML);
});

相比前一种做法,现在只需要绑定一次而不需要去绑定 N 次 - 这种做法就是事件代理,它可以很大程度上提高我们代码的性能。

上面列举只是一些常见的使用场景,但是远远不止这些.

总结

通过定义来反向推断其使用场合. 当一个对象不能直接去访问某个对象,而需要通过引入另外一个对象(代理对象)时,那么此时就可以考虑代理模式.