magix
magix copied to clipboard
vframe依赖加载插件
在我们做一个复杂的长列表页面时,我们通常要优先加载主功能,或者滚动到可视区域时才加载相应的view,这时候就可以使用该插件。
源码:
let Magix = require('magix');
let $ = require('$');
let { Vframe, Event: MEvent, mix } = Magix;
let WaitObserver = mix({}, MEvent);
let Base = Vframe.prototype;
let OldMountView = Base.mountView;
Vframe.on('add', e => {
WaitObserver.fire(e.vframe.id + ':add', e, 1);
});
Vframe.on('remove', e => {
WaitObserver.off(e.vframe.id + ':add');
});
let WaitVframe = (vfId, callback) => {
let vf = Vframe.get(vfId);
if (vf) {
if (vf.$cr) {
callback();
} else {
let ready = () => {
vf.off('created', ready);
callback();
};
vf.on('created', ready);
}
} else {
WaitObserver.on(vfId + ':add', e => {
let vf = e.vframe;
let ready = () => {
vf.off('created', ready);
callback();
};
vf.on('created', ready);
});
}
};
let Win = $(window);
let ScrollList = [];
let Intersect = (rect1, rect2) => {
let half1Width = rect1.width / 2,
half1Height = rect1.height / 2,
half2Width = rect2.width / 2,
half2Height = rect2.height / 2,
cen1 = {
x: rect1.x + half1Width,
y: rect1.y + half1Height
},
cen2 = {
x: rect2.x + half2Width,
y: rect2.y + half2Height
};
return Math.abs(cen2.x - cen1.x) <= half1Width + half2Width &&
Math.abs(cen2.y - cen1.y) <= half1Height + half2Height;
};
let Scrolled = function () {
let viewport = {
x: Win.scrollLeft(),
y: Win.scrollTop(),
width: Win.width(),
height: Win.height()
};
for (let i = 0; i < ScrollList.length; i++) {
let one = ScrollList[i];
let node = $('#' + one.id);
if (node.length) {
let offset = node.offset();
let rect = {
x: offset.left,
y: offset.top,
width: node.width(),
height: node.height()
};
if (Intersect(viewport, rect)) {
ScrollList.splice(i--, 1);
one.cb();
}
} else {
ScrollList.splice(i--, 1);
}
}
if (!ScrollList.length) {
Win.off('scroll', Scrolled);
}
};
let ScrollWatch = (vfId, callback) => {
if (!ScrollList.length) {
Win.on('scroll', Scrolled);
}
ScrollList.push({
id: vfId,
cb: callback
});
Scrolled();
};
Base.mountView = function (viewPath, viewInitParams) {
let me = this;
let sign = ++me.$s;
let node = $('#' + me.id);
let dep = node.attr('mx-dep');
if (dep) { //有依赖
if (dep | 0) { //延时
setTimeout(() => { //由sign保证正确,所以无须取消setTimeout的执行
if (sign == me.$s) {
OldMountView.call(me, viewPath, viewInitParams);
}
}, dep);
} else { //其它依赖
if (dep == '@viewport') { //以@开头的表示内置的命令:当vframe节点处在可视区域时加载
ScrollWatch(me.id, () => {
if (sign == me.$s) {
OldMountView.call(me, viewPath, viewInitParams);
}
});
} else { //其它vframe依赖
//if (IsVframeCreated(dep)) { //如果依赖的已经加载完成
// oldMountView.call(me, viewPath, viewInitParams, callback);
//} else {
WaitVframe(dep, () => { //等待相应的vframe加载
if (me.$s == sign) {
OldMountView.call(me, viewPath, viewInitParams);
}
});
//}
}
}
} else {
OldMountView.call(me, viewPath, viewInitParams);
}
};
插件使用方式
- 把该文件保存成vfdep.js,放在app目录下
- 在Magix.boot时,在exts中启用该插件,如
Magix.boot({
exts: ['vfdep']
});
插件支持的功能
延时加载
通过mx-dep指定一个延时的时间,毫秒为单位
<div mx-view="path/to/view" mx-dep="2000">loading</div>
滚动到可视区域加载
通过mx-dep值为@viewport来指示view滚动到可视区域时才加载
<div mx-view="path/to/view" mx-dep="@viewport">loading</div>
在其它view加载完成后才加载
通过mx-dep值为其它view的id时,其它view加载渲染完成时才加载该view
<div mx-view="path/to/view-a" mx-dep="@viewport" id="viewa">loading</div>
<div mx-view="path/to/view-b" mx-dep="viewa">loading</div>
👍 很常见的应用场景。目前更多的应用场景@viewport这种模式。滚动到dom节点才去动态mountview,既mx-view 变更为 mx-lazy-view 。滚动到此后开始mount. 需要给一淘的同学推荐一下。
用mx-lazy-view也可以,我这个思路是拦截并改变原来的加载流程。把这个过程放在外部,实现类似img-lazyload的效果也是可行的。