代理模式
代理模式
代理模式就是给对象提供一个代用品或占位符,以控制对它的访问
举个🌰
小明暗恋女神小芳很久了,情人节终于鼓起勇气了,买了束花,想向小芳进行表白,就像如下流程:
let Flower = function(){};
let xiaoming = {
sendFlower: function(target){
const flower = new Flower();
target.receiveFlower(flower);
}
};
let xiaofang = {
receiveFlower: function(flower){
console.log('已收到', flower);
}
}
xiaoming.sendFlower(xiaofang)
但在送花途中,小明一直犹豫不决,仍是有点胆怯,正在这时,遇见了小芳的好闺蜜小红,小明仿佛像抓住了救命稻草一般,让小红帮忙代送,此时流程如下:
let Flower = function(){};
let xiaoming = {
sendFlower: function(target){
const flower = new Flower();
target.receiveFlower(flower);
}
};
let xiaohong = {
receiveFlower: function(flower){
xiaofang.receiveFlower(flower);
}
}
let xiaofang = {
receiveFlower: function(flower){
console.log('已收到', flower);
}
}
xiaoming.sendFlower(xiaofang)
这就是代理模式的概念了,我们将一件事交给他人去做,以便达到某种目的/解决某种问题。 然而代理模式实际上又分为两种情况: 虚拟代理和保护代理。
虚拟代理
虚拟代理: 先占个位,将一些开销很大的对象,延迟到真正需要它的时候去进行创建
我们还是继续送花的🌰:小红收到花后想了想,觉得小芳最近心情不好,想到小芳心情好的时候再送花,这就是虚拟代理
保护代理
保护代理 代理时进行保护,将需要代理的方法或者对象处理成想要的结果,然后再传递给最终的对象。
小红觉得花中有一些瑕疵,需要重新包裹下,然后再送给小芳,这就是保护代理
Intersection Observer
Intersection Observer API 提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法,说人话就是可以检测元素是否进入视口,我们常用来做图片的懒加载、进入某个区域时执行任务或播放动画等
属性
-
所监听对象的具体祖先元素 (
element
)。如果未传入值或值为null
,则默认使用顶级文档的视窗。意思就是我们在哪里的根元素内进行监听。比如图片懒加载,当图片进入视口时我们才去进行获取对应的链接,图片进入视口,相当于监听图片是否在视口内,这个视口就是根元素 -
IntersectionObserver.rootMargin
计算交叉时添加到根 (root)边界盒bounding box (en-US)的矩形偏移量,可以有效的缩小或扩大根的判定范围从而满足计算需要。所有的偏移量均可用像素 (px) 或百分比 (%) 来表达,默认值为"0px 0px 0px 0px"。相当于设置root的margin,从而扩大/缩小判定范围
-
IntersectionObserver.thresholds
只读一个包含阈值的列表,按升序排列,列表中的每个阈值都是监听对象的交叉区域与边界区域的比率。当监听对象的任何阈值被越过时,都会生成一个通知 (Notification)。如果构造器未传入值,则默认值为 0。
方法
-
IntersectionObserver.disconnect()
使
IntersectionObserver
对象停止监听工作,停止监听所有的元素 -
IntersectionObserver.observe()
使
IntersectionObserver
开始监听一个目标元素。 -
IntersectionObserver.takeRecords()
返回所有观察目标的
IntersectionObserverEntry
对象数组。 -
IntersectionObserver.unobserve()
使
IntersectionObserver
停止监听指定的目标元素。
图片懒加载总思路
为了遵循单一职责原则,同时也为了复用,我们应将懒加载设置为一个单独的方法(angular中可使用service)实现(代理模式),而当图片进入视口时先加载占位图(虚拟代理),当请求图片返回时,再使用原图片资源覆盖占位图
图片懒加载具体实现
private _qfLazyPlaceholder: string | boolean;
// 是否展示懒加载占位图
@Input()
get qfLazyPlaceholder(): string | boolean {
return this._qfLazyPlaceholder;
}
set qfLazyPlaceholder(value: string | boolean) {
this._qfLazyPlaceholder = value === '' || value;
}
//图片指令初始化
ngOnInit(): void {
this.proceeLazyPlaceHolder();
//propertyChanges可以理解为每次不同图片的加载,
//lazyload就是懒加载服务,intoView是判断元素是否进入视口的方法,元素进入视口,加载完对应的src,
//我们就去更新一遍src
//hostElement就是对应的图片标签元素
this.propertyChanges$
.pipe(
mergeMap(() => (this.lazyLoad.intoView(this.hostElement)),
map(() => this.src),
)
.subscribe(src => {
//这边用到了rxjs,每个propertyChanges都会像水流一样流经pipe的处理再进入更新图片src的方法
this.updateImgSrc(src);
});
}
//先去加载占位图,假如没有就直接跳出
private proceeLazyPlaceHolder() {
if (this.qfLazyPlaceholder === undefined) {
return;
}
let src: string = FALLBACK_IMG;
if (typeof this.qfLazyPlaceholder === 'string' && this.qfLazyPlaceholder !== '') {
src = this.qfLazyPlaceholder;
}
//将占位图传入src,进行虚拟代理
this.updateImgSrc(src);
}
//懒加载服务的方法,写法千人千面,不再陈述细节,总结就是利用intersectionObserver过滤出进入视口的图片
intoView(element: HTMLElement, threshold: number = 0.1): Observable<boolean> {
let observer_: Subscriber<boolean>;
let intersectionObserver: IntersectionObserver;
return new Observable(observer => {
const options = {
root: null,
threshold,
};
observer_ = observer;
intersectionObserver = new IntersectionObserver(entries => {
observer.next(entries);
}, options);
intersectionObserver.observe(element);
}).pipe(
mergeMap((entries: IntersectionObserverEntry[]) => entries),
filter(entry => entry.isIntersecting),
tap(() => intersectionObserver.disconnect()),
map(entry => entry.isIntersecting),
tap(() => observer_.complete()),
);
}
以上就是本文全部内容啦,大家如果觉得有用的话,帮忙点个赞转发一下吧