跳到主要内容

工具组件提效优化

  • 在项目开发过程中,使用到的方法和组件,有时候需要进行优化

    • 性能优化

    • 使用方式优化,用起来更便捷

    • 用户体验优化

    • 有时候组件,在某些场景下需要让用户用起来更舒服

      • 比如表格,滚动时候

        • 表头可以一直固定在顶部

        • 底部滚动条固定在底部

event​

ts
export function addEventListen(
target: Window | HTMLElement,
event: string,
handler: EventListenerOrEventListenerObject,
capture = false
) {
if (
target.addEventListener &&
typeof target.addEventListener === 'function'
) {
target.addEventListener(event, handler, capture);
}
}

export function removeEventListen(
target: Window | HTMLElement,
event: string,
handler: EventListenerOrEventListenerObject,
capture = false
) {
if (
target.removeEventListener &&
typeof target.removeEventListener === 'function'
) {
target.removeEventListener(event, handler, capture);
}
}

使用例子

ts
onBeforeMount(() => {
addEventListen(window, 'resize', debounceFn);
});
onBeforeUnmount(() => {
removeEventListen(window, 'resize', debounceFn);
});

固定顶部滚动条实现​

问题:

  • 横向滚动的表格,如果表格高度超过了一个屏幕,横向滚动条只有拖到页面底部时才会出现,这样不方便用户试用。

遇到问题:

  • sticky布局 遮挡影响了fixed布局
    • z-index可解决,底部滚动条z-index 高于sticky布局即可

第三方库参考:

滚动条样式

text
::-webkit-scrollbar {
// width: 16px;
// height: 4px;
}

::-webkit-scrollbar-thumb {
border: 4px solid transparent;
background-clip: padding-box;
border-radius: 10px;
background-color: var(--color-text-4);
}

::-webkit-scrollbar-thumb:hover {
background-color: var(--color-text-3);
}

vue组件实现​

jsx
<script lang="jsx">
import { defineComponent } from 'vue';
import { addEventListen, removeEventListen } from '@/utils/event';
import { useAppStore } from '@/store';
import { forEach } from 'lodash';

export default defineComponent({
props: {
// 距离
distance: {
type: Number,
default: 30,
},
// 滚动条高度
barHeight: {
type: Number,
default: 20,
},
delay: {
type: Number,
default: 500,
},
},
setup(props, { slots }) {
const appStore = useAppStore();

const tableContentRef = ref(null);
const scrollbarWidth = ref(null);
const scrollbarRef = ref(null);
const scrollWidth = ref(0);
const scrollbarLeft = ref(0);
const stopObserver = ref([]);
const tableVisible = ref(false);
const headerVisible = ref(false);

const setScrollbar = async () => {
await nextTick();
setTimeout(() => {
const { width, left } = useElementBounding(tableContentRef.value);
scrollbarWidth.value = width.value;
scrollbarLeft.value = left.value;
}, props.delay);
};

const setTableScroll = (left) => {
// table有数据,body滚动
const tableBodyDom =
tableContentRef.value.querySelector('.arco-table-body');
// 兼容table无数据情况,表头滚动
const tableHeaderDom =
tableContentRef.value.querySelector('.arco-table-header');
tableBodyDom.scrollLeft = left;
tableHeaderDom.scrollLeft = left;
};

const getScrollWidth = () => {
const tableElement = tableContentRef.value.querySelector(
'.arco-table-element'
);
const { stop } = useResizeObserver(tableElement, (entries) => {
const entry = entries[0];
const { width } = entry.contentRect;
scrollWidth.value = width;
});
stopObserver.value.push(stop);
};

const setVisible = () => {
const { stopIntersection } = useIntersectionObserver(
tableContentRef,
([{ isIntersecting }]) => {
tableVisible.value = isIntersecting;
}
);
const tableHeaderDom =
tableContentRef.value.querySelector('.arco-table-header');
const { stopResize } = useResizeObserver(tableHeaderDom, (entries) => {
const entry = entries[0];
const { height } = entry.contentRect;
headerVisible.value = Boolean(height);
});
stopObserver.value.push(stopIntersection, stopResize);
};

const onScroll = (event) => {
setTableScroll(event.target.scrollLeft);
};

const debounceFn = useDebounceFn(setScrollbar, 100);

onMounted(async () => {
await nextTick();
addEventListen(window, 'resize', debounceFn);
getScrollWidth();
setScrollbar();
setVisible();
});

onBeforeUnmount(() => {
removeEventListen(window, 'resize', debounceFn);
forEach(stopObserver.value, (stop) => stop?.());
});

const scrollVisible = computed(() => {
return tableVisible.value && headerVisible.value;
});

watch(
() => appStore.menuCollapse,
() => setScrollbar()
);

return () => (
<div class="fixed-scrollbar-wrapper">
<div ref={tableContentRef}>{slots.default?.()}</div>
{scrollVisible.value && (
<div
class="scrollbar-box overflow-x-auto fixed bottom-0 z-[99]"
style={{
'height': `${props.barHeight}px`,
'width': `${scrollbarWidth.value - props.distance}px`,
'left': `${scrollbarLeft.value + props.distance}px`,
'--barHeight': `${props.barHeight}px`,
}}
ref={scrollbarRef}
onScroll={onScroll}
>
<div
class="scrollbar h-[1px]"
style={{ width: `${scrollWidth.value}px` }}
></div>
</div>
)}
</div>
);
},
});
</script>

<style lang="scss" scoped>
.fixed-scrollbar-wrapper {
:deep(.arco-table .arco-scrollbar-track-direction-horizontal) {
.arco-scrollbar-thumb-bar {
height: 0px;
}
}
.scrollbar-box {
&::-webkit-scrollbar {
height: var(--barHeight) !important;
}
}
}
</style>