介绍
代理模式在注重交互体验的前端应用环境中是一个十分重要的思想,有些时候,当调用方不适合直接访问一个对象的时候,代理模式可以提供出来一个替身对象来控制对这个对象的访问,当替身对象对请求做出一些处理后,再转交给目标对象。
根据上述定义,我们可以将代理模式分解为三种类型:虚拟代理、保护代理以及缓存代理。其中缓存代理能够为计算开销大的一些运算函数提供一层缓存,避免重复计算,多用于一些算法优化的实现,本文暂且不论,重点介绍其他两种代理模式在前端项目中的应用。
虚拟代理
虚拟代理的应用范围非常广泛,它常用于对创建或使用时开支较大的目标对象进行代理,以达到推迟与减少资源消耗的目的。例如异步请求常用的函数节流、函数防抖、以及懒加载都可以使用此种思想。
我们以实现某图片懒加载来举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> body { height: 2000px; }
#others { width: 100%; height: 1000px; } </style> </head>
<body> <div id="others"></div> <img id="img" /> <script> function lazyloadImage(node, src) { const intersectionObserver = new IntersectionObserver(function(entries) { if (entries[0].isIntersecting) { node.src = src; intersectionObserver.unobserve(node); } }); intersectionObserver.observe(node); }
const imgNode = document.querySelector("#img"); lazyloadImage(imgNode, "https://github.com/realDuang/blog-storage/blob/master/avatar.jpg?raw=true"); </script> </body> </html>
|
又或者通过代理模式实现函数的防抖:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| const debounce = ( fn, time = 17, options = { leading: true, context: null } ) => { let timer; const _debounce = function(...args) { timer && clearTimeout(timer); if (options.leading && !timer) { fn.apply(options.context, args); timer = setTimeout(null, time); } else { timer = setTimeout(() => { fn.apply(options.context, args); timer = null; }, time); } }; return _debounce; };
const scrollFunc = () => { let scrollTop = document.body.scrollTop || document.documentElement.scrollTop; console.log("滚动条位置:" + scrollTop); }
const proxyScrollFunc = debounce( scrollFunc, 200, { leading: false } );
window.onscroll = proxyScrollFunc
|
保护代理
保护代理适用于目标对象安全要求较高,需要鉴权功能时,将所有请求汇聚到代理对象中统一进行授权与控制,再将符合要求的请求转发给目标对象。这种代理解耦了复杂鉴权控制与实际业务处理之间的联系,并且负责鉴权控制的代理模块能做到可复用,从而进一步优化代码架构。
能使用保护代理的情况也有很多,这里以最常见的登录验证为例。(为了更明显体现代理模式的存在,使用了 es6 规范下的 Proxy 对象特性)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| function getValidatorProxy(target) { return new Proxy(target, { validator: { account: value => { const re = /^\d+$/; return { isValid: re.test(value), error: "账号组成必须全为数字", }; }, password: value => ({ isValid: value.length >= 6, error: "密码长度不能小于 6", }), }, set(target, prop, value) { const checkVar = this.validator[prop](value); if (checkVar.isValid) { console.log(`${prop}参数校验通过`); return Reflect.set(target, prop, value); } console.log(`${prop}参数校验不通过,错误原因:${checkVar.error}`); return Reflect.set(target, prop, ""); }, }); }
const loginProxy = getValidatorProxy({ account: "", password: "", });
loginProxy.account = "123"; loginProxy.password = "123"; console.log(loginProxy);
|