记一次使用PWA(ServiceWorker)后懒加载失效解决

问题所在

不知道是哪方的BUG,首次加载和http/s下加载都正常。

而在ServiceWorker下, IntersectionObserver.entries.isIntersecting返回都是false,导致评论框和lazyload图片不显示。

导致问题的原因是PWA加载太快,导致DOM还没渲染完成就执行了io.observe(document.getElementById(targetId));
并且由于没有获取到元素信息(entries[0].intersectionRect为空),所以就不会有元素可视的回调。
解决方案就是增加失败后 Rollback 到 scroll 模式判断是否可见。详见下面的代码。

函数位置

/source/js/utils.js
waitElementVisible

1
2
3
4
5
6
7
8
9
10
var io = new IntersectionObserver(function(entries, ob) {
if (entries[0].isIntersecting) { //在service worker下,返回的都是false
callback && callback();
ob.disconnect();
}
}, {
threshold : [0],
rootMargin: (window.innerHeight || document.documentElement.clientHeight) + 'px'
});
io.observe(document.getElementById(targetId));

解决方法/思路

评论换成js判断加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  if (!isBot && runningOnBrowser){// && supportsIntersectionObserver) {

if('attachEvent' in window)document.attachEvent("scroll", _callback);
if('addEventListener' in window)document.addEventListener('scroll', _callback);
function _callback() {
const _target = document.getElementById(targetId);
//滚动条高度+视窗高度 = 可见区域底部高度
let visibleBottom = window.scrollY + document.documentElement.clientHeight;
//可见区域顶部高度
let visibleTop = window.scrollY;
let centerY = _target.offsetTop + (_target.offsetHeight / 2);
if (centerY > visibleTop && centerY < visibleBottom) {
if('attachEvent' in window)document.detachEvent('scroll', _callback);
if('addEventListener' in window)document.removeEventListener('scroll', _callback);
callback && callback();
}
}

新的方法

更新 2021年03月17日 19点10分

直接重写整个waitElementVisible函数,正常的情况下使用IntersectionObserver,失败的情况下回退到使用scroll

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
42
43
44
45
waitElementVisible: function(targetId, callback) {
var runningOnBrowser = typeof window !== 'undefined';
var isBot = (runningOnBrowser && !('onscroll' in window)) || (typeof navigator !== 'undefined'
&& /(gle|ing|ro|msn)bot|crawl|spider|yand|duckgo/i.test(navigator.userAgent));
var supportsIntersectionObserver = 'IntersectionObserver' in window;
var attachEvent = 'attachEvent' in window;
var addEventListener = 'addEventListener' in window;
if (!isBot && runningOnBrowser && (attachEvent || addEventListener)) {
var _scroll = function() {
var _callback = function() {
var _target = document.getElementById(targetId);
//滚动条高度+视窗高度 = 可见区域底部高度
var visibleBottom = window.scrollY + document.documentElement.clientHeight;
//可见区域顶部高度
var visibleTop = window.scrollY;
var centerY = _target.offsetTop + (_target.offsetHeight / 2);
if (centerY > visibleTop && centerY < visibleBottom) {
if (attachEvent)document.detachEvent('scroll', _callback);
if (addEventListener)document.removeEventListener('scroll', _callback);
callback && callback();
}
};
if (attachEvent)document.attachEvent('scroll', _callback);
if (addEventListener)document.addEventListener('scroll', _callback);
};
if (supportsIntersectionObserver) {
var io = new IntersectionObserver(function(entries, ob) {
//如果失败,回退到scroll方式
if (entries[0].intersectionRect.x <= 0) { _scroll(); ob.disconnect(); return; }
if (entries[0].isIntersecting) {
callback && callback();
ob.disconnect();
}
}, {
threshold : [0],
rootMargin: (window.innerHeight || document.documentElement.clientHeight) + 'px'
});
io.observe(document.getElementById(targetId));
} else {
_scroll();
}
} else {
callback && callback();
}
},

图片加载我直接换了一个库

使用了lazyloadjs.cn的js加载

scripts/events/lib/lazyload.js第32-35行改为

1
2
3
4
5
if (/data-src=/i.test(str)) {
return str;
}
return str.replace(p1, `${loadingImage}" class="lazyload" data-src="${p1}`);

如果开启了progressbar,相应的/source/js/plugins.js

var images = $('main img:not([srcset])');

也要修改为

var images = $('main img:not([data-src])');


至此修改完成.
题外话。提交了pr到fluid-dev,结果失败,出现了50多个错误,看了一下,竟然全都是语法错误,惭愧惭愧