Skip to content

Commit

Permalink
perf: optimize lazy loading components
Browse files Browse the repository at this point in the history
  • Loading branch information
anncwb committed Nov 1, 2020
1 parent 35d2bfc commit 87fcd0d
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 166 deletions.
90 changes: 36 additions & 54 deletions src/components/Container/src/LazyContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,21 @@
<script lang="ts">
import type { PropType } from 'vue';
import { defineComponent, reactive, onMounted, ref, unref, onUnmounted, toRefs } from 'vue';
import {
defineComponent,
reactive,
onMounted,
ref,
unref,
onUnmounted,
toRef,
toRefs,
} from 'vue';
import { Skeleton } from 'ant-design-vue';
import { useRaf } from '/@/hooks/event/useRaf';
import { useTimeout } from '/@/hooks/core/useTimeout';
import { useIntersectionObserver } from '/@/hooks/event/useIntersectionObserver';
interface State {
isInit: boolean;
loading: boolean;
Expand All @@ -30,7 +39,7 @@
// 等待时间,如果指定了时间,不论可见与否,在指定时间之后自动加载
timeout: {
type: Number as PropType<number>,
default: 8000,
default: 0,
// default: 8000,
},
// 组件所在的视口,如果组件是在页面容器内滚动,视口就是该容器
Expand All @@ -40,6 +49,7 @@
>,
default: () => null,
},
// 预加载阈值, css单位
threshold: {
type: String as PropType<string>,
Expand All @@ -51,6 +61,7 @@
type: String as PropType<'vertical' | 'horizontal'>,
default: 'vertical',
},
// 包裹组件的外层容器的标签名
tag: {
type: String as PropType<string>,
Expand All @@ -62,38 +73,25 @@
default: 80,
},
// // 是否在不可见的时候销毁
// autoDestory: {
// type: Boolean as PropType<boolean>,
// default: false,
// },
// transition name
transitionName: {
type: String as PropType<string>,
default: 'lazy-container',
},
},
emits: ['before-init', 'init'],
setup(props, { emit, slots }) {
emits: ['init'],
setup(props, { emit }) {
const elRef = ref<any>(null);
const state = reactive<State>({
isInit: false,
loading: false,
intersectionObserverInstance: null,
});
immediateInit();
onMounted(() => {
immediateInit();
initIntersectionObserver();
});
onUnmounted(() => {
// Cancel the observation before the component is destroyed
if (state.intersectionObserverInstance) {
const el = unref(elRef);
state.intersectionObserverInstance.unobserve(el.$el);
}
});
// If there is a set delay time, it will be executed immediately
function immediateInit() {
Expand All @@ -105,9 +103,6 @@
}
function init() {
// At this point, the skeleton component is about to be switched
emit('before-init');
// At this point you can prepare to load the resources of the lazy-loaded component
state.loading = true;
requestAnimationFrameFn(() => {
Expand All @@ -120,9 +115,7 @@
// Prevent waiting too long without executing the callback
// Set the maximum waiting time
useTimeout(() => {
if (state.isInit) {
return;
}
if (state.isInit) return;
callback();
}, props.maxWaitingTime || 80);
Expand All @@ -132,12 +125,10 @@
}
function initIntersectionObserver() {
const { timeout, direction, threshold, viewport } = props;
if (timeout) {
return;
}
const { timeout, direction, threshold } = props;
if (timeout) return;
// According to the scrolling direction to construct the viewport margin, used to load in advance
let rootMargin;
let rootMargin: string = '0px';
switch (direction) {
case 'vertical':
rootMargin = `${threshold} 0px`;
Expand All @@ -146,35 +137,26 @@
rootMargin = `0px ${threshold}`;
break;
}
try {
// Observe the intersection of the viewport and the component container
state.intersectionObserverInstance = new window.IntersectionObserver(
intersectionHandler,
{
rootMargin,
root: viewport,
threshold: [0, Number.MIN_VALUE, 0.01],
}
);
const el = unref(elRef);
state.intersectionObserverInstance.observe(el.$el);
const { stop, observer } = useIntersectionObserver({
rootMargin,
target: toRef(elRef.value, '$el'),
onIntersect: (entries: any[]) => {
const isIntersecting = entries[0].isIntersecting || entries[0].intersectionRatio;
if (isIntersecting) {
init();
if (observer) {
stop();
}
}
},
root: toRef(props, 'viewport'),
});
} catch (e) {
init();
}
}
// Cross-condition change handling function
function intersectionHandler(entries: any[]) {
const isIntersecting = entries[0].isIntersecting || entries[0].intersectionRatio;
if (isIntersecting) {
init();
if (state.intersectionObserverInstance) {
const el = unref(elRef);
state.intersectionObserverInstance.unobserve(el.$el);
}
}
}
return {
elRef,
...toRefs(state),
Expand Down
92 changes: 0 additions & 92 deletions src/hooks/core/types.ts

This file was deleted.

16 changes: 16 additions & 0 deletions src/hooks/core/useCounter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ref } from 'vue';

export function useCounter(initialValue = 0) {
const count = ref(initialValue);

const inc = (delta = 1) => (count.value += delta);
const dec = (delta = 1) => (count.value -= delta);
const get = () => count.value;
const set = (val: number) => (count.value = val);
const reset = (val = initialValue) => {
initialValue = val;
return set(val);
};

return { count, inc, dec, get, set, reset };
}
23 changes: 18 additions & 5 deletions src/hooks/core/useDebounce.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import type {
DebounceAndThrottleOptions,
DebounceAndThrottleProcedureResult,
DebounceAndThrottleProcedure,
} from './types';
export interface DebounceAndThrottleOptions {
// 立即执行
immediate?: boolean;

// 是否为debounce
debounce?: boolean;
// 只执行一次
once?: boolean;
}
export type CancelFn = () => void;

export type DebounceAndThrottleProcedure<T extends unknown[]> = (...args: T) => unknown;

export type DebounceAndThrottleProcedureResult<T extends unknown[]> = [
DebounceAndThrottleProcedure<T>,
CancelFn
];

import {
// throttle,
useThrottle,
Expand Down
22 changes: 17 additions & 5 deletions src/hooks/core/useThrottle.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import type {
DebounceAndThrottleOptions,
DebounceAndThrottleProcedureResult,
DebounceAndThrottleProcedure,
} from './types';
export interface DebounceAndThrottleOptions {
// 立即执行
immediate?: boolean;

// 是否为debounce
debounce?: boolean;
// 只执行一次
once?: boolean;
}
export type CancelFn = () => void;

export type DebounceAndThrottleProcedure<T extends unknown[]> = (...args: T) => unknown;

export type DebounceAndThrottleProcedureResult<T extends unknown[]> = [
DebounceAndThrottleProcedure<T>,
CancelFn
];

import { isFunction } from '/@/utils/is';
export function throttle<T extends unknown[]>(
Expand Down
6 changes: 3 additions & 3 deletions src/hooks/core/useTimeout.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { TimeoutFnResult, Fn } from './types';

import { isFunction } from '/@/utils/is';
import { watch } from 'vue';
import { Ref, watch } from 'vue';

import { useTimeoutRef } from '/@/hooks/core/useTimeoutRef';

type TimeoutFnResult = [Fn<void>, Fn<void>, Ref<boolean>];

export function useTimeout(handle: Fn<any>, wait: number): TimeoutFnResult {
if (!isFunction(handle)) {
throw new Error('handle is not Function!');
Expand Down
5 changes: 2 additions & 3 deletions src/hooks/core/useTimeoutRef.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { TimeoutResult } from './types';

import { ref } from 'vue';
import { Ref, ref } from 'vue';
import { tryOnUnmounted } from '/@/utils/helper/vueHelper';
export type TimeoutResult = [Ref<boolean>, Fn<void>, Fn<void>];
export function useTimeoutRef(wait: number): TimeoutResult {
const readyRef = ref(false);

Expand Down
1 change: 0 additions & 1 deletion src/hooks/event/types.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/hooks/event/useEventHub.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { tryOnUnmounted } from '/@/utils/helper/vueHelper';
import {} from 'vue';
import EventHub from '/@/utils/eventHub';
const eventHub = new EventHub();
export function useEventHub(): EventHub {
Expand Down
Loading

0 comments on commit 87fcd0d

Please sign in to comment.