目前我正在做的这个项目中,有个表格的数据比较多,导致页面加载时间长,体验感大大滴下降了,就在网上各种搜各种Ctrl CV,最后还算是实现了想要的结果把,九成新,微瑕,又不是不能用。


vue自定义指令

众所周知,vue中除了内置的v-ifv-showv-textv-htmlv-model等几个指令之外,还支持注册自定义指令,同时vue中指令的定义对象也拥有和组件相同的生命周期钩子函数,使用起来歪瑞购得。

const myDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {
// 下面会介绍各个参数的细节
},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {}
}

钩子函数

指令的钩子会传递以下几种参数:

  • el:指令绑定到的元素。这可以用于直接操作 DOM。
  • binding:一个对象,包含以下属性。
    • value:传递给指令的值。例如在 v-my-directive="1 + 1" 中,值是 2
    • oldValue:之前的值,仅在 beforeUpdateupdated 中可用。无论值是否更改,它都可用。
    • arg:传递给指令的参数 (如果有的话)。例如在 v-my-directive:foo 中,参数是 "foo"
    • modifiers:一个包含修饰符的对象 (如果有的话)。例如在 v-my-directive.foo.bar 中,修饰符对象是 { foo: true, bar: true }
    • instance:使用该指令的组件实例。
    • dir:指令的定义对象。
  • vnode:代表绑定元素的底层 VNode。
  • prevNode:之前的渲染中代表指令所绑定元素的 VNode。仅在 beforeUpdate 和 updated 钩子中可用。

创建自定义指令

为了方便使用,直接将指令注册到全局使用首先在src目录下新创建directive目录,并在directive目录下创建index.js文件和loadmore目录,loadmore目录下在创建index.js文件;

// 刚好还用到防抖函数
import { throttle } from 'lodash-es';
const loadmore = {
mounted(el, binding) {
// 因为使用的是elementui,所以查找这个元素,根据实际情况而定
const ele = el.querySelector('.el-scrollbar__wrap');
ele.addEventListener(
'scroll',
throttle(function () {
// 1200 可根据实际清空调整
this.scrollHeight - this.scrollTop <= 1200 && binding.value();
}, 150),
);
},
updated(el, binding) {
const ele = el.querySelector('.el-scrollbar__wrap');
if (binding.value?.toTop) {
ele.scrollTop = 0;
delete binding.value.toTop;
}
},
};
export default loadmore;
// 导入自定义指令
import loadmore from './loadmore';
const directives = {
loadmore,
};
export default {
install(app) {
Object.keys(directives).forEach(key => {
app.directive(key, directives[key]);
});
},
};
...
import Directives from './directive/';
...
const app = createApp(App);
// 注册即可
app.use(Directives);
...

使用

<!-- 在组件上使用 v-loadmore="onLoadMore 使用" -->
<el-table v-loadmore="onLoadMore" :data="displayData">
...
</el-table>
<script>
...
const onLoadMore = () => {
let tempLen = unref(displayData).length;
// 每次触发 push 50 条数据
tempLen < unref(resultData).length &&
unref(displayData).push(...unref(searchData).slice(tempLen, tempLen + 50));
}
...
</script>

按钮点击水波纹效果

import './waves.css';
const vueWaves = {};
vueWaves.install = (Vue, options = {}) => {
Vue.directive('waves', {
bind(el, binding) {
el.addEventListener('click', e => {
const customOpts = Object.assign(options, binding.value);
const opts = Object.assign({
ele: el, // 波纹作用元素
type: 'hit', // hit点击位置扩散center中心点扩展
color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, customOpts),
target = opts.ele;
if (target) {
target.style.position = 'relative';
target.style.overflow = 'hidden';
const rect = target.getBoundingClientRect();
let ripple = target.querySelector('.waves-ripple');
if (!ripple) {
ripple = document.createElement('span');
ripple.className = 'waves-ripple';
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px';
target.appendChild(ripple);
} else {
ripple.className = 'waves-ripple';
}
switch (opts.type) {
case 'center':
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px';
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px';
break;
default:
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px';
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px';
}
ripple.style.backgroundColor = opts.color;
ripple.style.overflow = 'hidden';
ripple.className = 'waves-ripple z-active';
return false;
}
}, false);
}
})
};
export default vueWaves;
.waves-ripple {
position: absolute;
border-radius: 100%;
background-color: rgba(0, 0, 0, 0.15);
background-clip: padding-box;
pointer-events: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-transform: scale(0);
-ms-transform: scale(0);
transform: scale(0);
opacity: 1;
}
.waves-ripple.z-active {
opacity: 0;
-webkit-transform: scale(2);
-ms-transform: scale(2);
transform: scale(2);
-webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
transition: opacity 1.2s ease-out, transform 0.6s ease-out;
transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out;
}
-------- 本文结束感谢阅读 --------

这里是评论区,如果你看到这段话,就是没加载出来,刷新一下~


集中精神,以气御剪