手写vue无限滚动指令的详细过程

  import { checkArriveBottom } from "http://www.jb51.net/article/utils";

  export default {

  install(Vue) {

  Vue.directive("infinite-scroll", {

  // 指令在插入的时候会执行一次

  inserted: function (el, binding, vnode) {

  const fn = binding.value;

  const context = vnode.context;

  let timer = null;

  // 指令的值必须是一个函数,我们好执行回调

  if (typeof fn != "function") {

  throw new Error("指令value必须为函数");

  }

  // 事件处理函数

  function handleScroll() {

  // 判断滚动条到达底部了,才开始执行回调

  if (checkArriveBottom(el)) {

  // 执行回调的时候,要把this指向组件实例

  fn.bind(context)();

  }

  }

  // 将滚动处理函数挂载到对应组件实例上面,便于组件更新的时候,对设置了loading和complate属性进行移除事件绑定

  context.handleScroll = handleScroll;

  // 如果设置有immediate说明立即执行,则立即执行回调,直到将内容撑满内容区

  if (binding?.modifiers?.immediate) {

  timer = setInterval(() => {

  // 子元素的总高度大于设置指令的父级包裹元素就表示填满了可视区,停止加载

  const childScrollHeight = el.firstElementChild.scrollHeight;

  if (childScrollHeight >= el.clientHeight) {

  return clearInterval(timer);

  }

  handleScroll();

  }, 1500);

  }

  // 绑定滚动处理函数

  el.addEventListener("scroll", context.handleScroll);

  },

  // 组件更新的时候,会不断触发(最明显就是data中的响应式数据变化,会继续执行update方法)

  update(el, binding, vnode) {

  const context = vnode.context;

  // 如果加载中或者已经加载完了,就移除滚动事件

  if (

  (binding?.modifiers?.complated && context.complated) ||

  (binding?.modifiers?.loading && context.loading)

  ) {

  el.removeEventListener("scroll", context.handleScroll);

  } else {

  // 当loading和complate都是false的时候,表示可以继续加载

  el.addEventListener("scroll", context.handleScroll);

  }

  },

  });

  },

  };