指令文件:
// 有可能list的el和绑定scroll的el并不是一个,把paddingTop和paddingBottom传出去自己设置到list上
const toUpdateData = function ({ maxDrawCount, itemHeight, updateRealDrawList: fnUpt }, el) {
const allData = el.__allData__ || [];
let dataCount = allData.length;
if (dataCount <= maxDrawCount) {
el.dataset.startindex = 0;
return fnUpt({
data: allData,
paddingTop: 0,
paddingBottom: 0
});
}
const start = el.dataset.startindex * 1;
let drawEndIndex = Math.min(start + maxDrawCount, dataCount);
let paddingTop = start * itemHeight + "px";
let paddingBottom = (dataCount - drawEndIndex) * itemHeight + "px";
let realDrawList = allData.slice(start, drawEndIndex);
fnUpt({
data: realDrawList,
paddingTop,
paddingBottom
});
};
/**
* 指令的binding对象接收:
* {Array} data 列表的总数据
* {Number} maxDrawCount 实际在页面上最多渲染多少条数据
* {Number} itemHeight 每条数据的高度
* {Function} updateRealDrawList 自动计算出要渲染的数据时会将(data, paddingTop, paddingBottom)作为入参调用此更新方法
* 在使用此指令的页面将data更新为要渲染的列表,将paddingTop, paddingBottom绑定在列表上
*/
export default {
bind: function (el, binding) {
// 当滚动时updateRealDrawList
el.addEventListener('scroll', (function () {
let scrollTimer = null;
return function () {
if (scrollTimer) {
return;
}
scrollTimer = setTimeout(() => {
let { itemHeight } = binding.value;
let v = parseInt(el.scrollTop / itemHeight),
startIndex = v > 4 ? v - 4 : 0; // 在上方不可见范围内再渲染4个
el.dataset.startindex = startIndex;
toUpdateData(binding.value, el);
clearTimeout(scrollTimer);
scrollTimer = null;
}, 250);
};
})());
},
update: function (el, binding) { // 当总列表改变时updateRealDrawList
let { data } = binding.value;
if (data === el.__allData__) {
return;
}
// 因为各个页面都有可能要用这个指令,数据不能存在全局变量,会造成各页面都公用这份数据。所以现在挂在el上
el.__allData__ = data;
toUpdateData(binding.value, el);
},
};
使用指令
<div v-virtualscroll="{
data: allData,
maxDrawCount: 20,
itemHeight: 100,
updateRealDrawList: onUptDrawList,
}">
<!-- 渲染drawList列表,并将paddingTop ,paddingBottom 设置到style -->
</div>
import VirtualScroll from "@/utils/VirtualScroll.js";
export default {
directives: {
"virtualscroll": VirtualScroll,
},
methods: {
onUptDrawList({ data, paddingTop, paddingBottom }) {
this.drawList = data;
this.paddingTop = paddingTop;
this.paddingBottom = paddingBottom;
},
}
}
自定义指令一个重要的问题:如何传值
1、指令的这些钩子函数里面如何拿到组件中的数据?
- 一是绑定在dom上,因为钩子里都能拿到el再从el上读取,一般用于静态参数
- 二是在绑定指令的时候传入,例:v-mycopy="billSn",在钩子里使用value参数来获取传入的值,用于动态参数。
2、在哪里监控入参的更新?
- 注意update钩子,所在组件的VNode更新,就会调update。这个VNode更新,可能原因是入参绑定的数据更新了,也可能是其他数据更新导致的,所以指令的入参值是否发生了更新需要自己判断。举个例子:
<template>
<div>
<div v-mycopy="userName">复制</div>
<div>{{ billSn }}</div>
</div>
</template>
当billSn改变时,依然会调mycopy指令的update,但userName并没有变
- 假如template中只有一个
<div v-mycopy="userInfo"></div>
,传给指令的是个引用型的数据,那么只有userInfo这个引用发生改变的时候,才会去调update,只更改userInfo中的属性值是不会触发的;
3、指令的钩子中能改变组件中的数据吗?
网友评论