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

vue自定义指令
众所周知,vue中除了内置的v-if
、v-show
、v-text
、v-html
、v-model
等几个指令之外,还支持注册自定义指令,同时vue中指令的定义对象也拥有和组件相同的生命周期钩子函数,使用起来歪瑞购得。
| const myDirective = { |
| |
| |
| created(el, binding, vnode, prevVnode) { |
| |
| }, |
| |
| 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
:之前的值,仅在 beforeUpdate
和 updated
中可用。无论值是否更改,它都可用。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) { |
| |
| const ele = el.querySelector('.el-scrollbar__wrap'); |
| ele.addEventListener( |
| 'scroll', |
| throttle(function () { |
| |
| 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); |
| ... |
使用
| |
| <el-table v-loadmore="onLoadMore" :data="displayData"> |
| ... |
| </el-table> |
| |
| <script> |
| ... |
| const onLoadMore = () => { |
| let tempLen = unref(displayData).length; |
| |
| 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', |
| 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; |
| } |