refactor(List): refactor with composition api

This commit is contained in:
chenjiahan 2020-08-28 09:53:16 +08:00
parent 67f418ab07
commit ecb319625b
2 changed files with 97 additions and 95 deletions

View File

@ -9,7 +9,7 @@ import {
} from 'vue'; } from 'vue';
export function useGlobalEvent( export function useGlobalEvent(
target: EventTarget, target: Ref<EventTarget>,
event: string, event: string,
handler: EventListener, handler: EventListener,
passive = false, passive = false,
@ -18,23 +18,23 @@ export function useGlobalEvent(
let binded: boolean; let binded: boolean;
function add() { function add() {
if (binded || (flag && !flag.value)) { if (binded || (flag && !flag.value) || !target.value) {
return; return;
} }
on(target, event, handler, passive); on(target.value, event, handler, passive);
binded = true; binded = true;
} }
function remove() { function remove() {
if (binded) { if (binded && target.value) {
off(target, event, handler); off(target.value, event, handler);
binded = false; binded = false;
} }
} }
if (flag) { if (flag) {
watch(() => { watch(flag, () => {
flag.value ? add() : remove(); flag.value ? add() : remove();
}); });
} }

View File

@ -1,10 +1,20 @@
import {
ref,
watch,
nextTick,
onUpdated,
onMounted,
getCurrentInstance,
} from 'vue';
// Utils // Utils
import { createNamespace } from '../utils'; import { createNamespace } from '../utils';
import { isHidden } from '../utils/dom/style'; import { isHidden } from '../utils/dom/style';
import { getScroller } from '../utils/dom/scroll'; import { getScroller } from '../utils/dom/scroll';
// Mixins // Composition
import { BindEventMixin } from '../mixins/bind-event'; import { useRect } from '../composition/use-rect';
import { useGlobalEvent } from '../composition/use-global-event';
// Components // Components
import Loading from '../loading'; import Loading from '../loading';
@ -12,16 +22,6 @@ import Loading from '../loading';
const [createComponent, bem, t] = createNamespace('list'); const [createComponent, bem, t] = createNamespace('list');
export default createComponent({ export default createComponent({
mixins: [
BindEventMixin(function (bind) {
if (!this.scroller) {
this.scroller = getScroller(this.$el);
}
bind(this.scroller, 'scroll', this.check);
}),
],
props: { props: {
error: Boolean, error: Boolean,
loading: Boolean, loading: Boolean,
@ -45,37 +45,21 @@ export default createComponent({
emits: ['load', 'update:error', 'update:loading'], emits: ['load', 'update:error', 'update:loading'],
data() { setup(props, { emit, slots }) {
return { // use sync innerLoading state to avoid repeated loading in some edge cases
// use sync innerLoading state to avoid repeated loading in some edge cases const loading = ref(false);
innerLoading: this.loading, const rootRef = ref();
}; const scrollerRef = ref();
}, const placeholderRef = ref();
updated() { const check = () => {
this.innerLoading = this.loading; nextTick(() => {
}, if (loading.value || props.finished || props.error) {
mounted() {
if (this.immediateCheck) {
this.check();
}
},
watch: {
loading: 'check',
finished: 'check',
},
methods: {
// @exposed-api
check() {
this.$nextTick(() => {
if (this.innerLoading || this.finished || this.error) {
return; return;
} }
const { $el: el, scroller, offset, direction } = this; const scroller = scrollerRef.value;
const { offset, direction } = props;
let scrollerRect; let scrollerRect;
if (scroller.getBoundingClientRect) { if (scroller.getBoundingClientRect) {
@ -90,12 +74,12 @@ export default createComponent({
const scrollerHeight = scrollerRect.bottom - scrollerRect.top; const scrollerHeight = scrollerRect.bottom - scrollerRect.top;
/* istanbul ignore next */ /* istanbul ignore next */
if (!scrollerHeight || isHidden(el)) { if (!scrollerHeight || isHidden(rootRef.value)) {
return false; return false;
} }
let isReachEdge = false; let isReachEdge = false;
const placeholderRect = this.$refs.placeholder.getBoundingClientRect(); const placeholderRect = useRect(placeholderRef);
if (direction === 'up') { if (direction === 'up') {
isReachEdge = scrollerRect.top - placeholderRect.top <= offset; isReachEdge = scrollerRect.top - placeholderRect.top <= offset;
@ -104,71 +88,89 @@ export default createComponent({
} }
if (isReachEdge) { if (isReachEdge) {
this.innerLoading = true; loading.value = true;
this.$emit('update:loading', true); emit('update:loading', true);
this.$emit('load'); emit('load');
} }
}); });
}, };
clickErrorText() {
this.$emit('update:error', false);
this.check();
},
genLoading() {
if (this.innerLoading && !this.finished) {
return (
<div class={bem('loading')} key="loading">
{this.$slots.loading ? (
this.$slots.loading()
) : (
<Loading size="16">{this.loadingText || t('loading')}</Loading>
)}
</div>
);
}
},
genFinishedText() {
if (this.finished) {
const text = this.$slots.finished
? this.$slots.finished()
: this.finishedText;
const renderFinishedText = () => {
if (props.finished) {
const text = slots.finished ? slots.finished() : props.finishedText;
if (text) { if (text) {
return <div class={bem('finished-text')}>{text}</div>; return <div class={bem('finished-text')}>{text}</div>;
} }
} }
}, };
genErrorText() { const clickErrorText = () => {
if (this.error) { emit('update:error', false);
const text = this.$slots.error ? this.$slots.error() : this.errorText; check();
};
const renderErrorText = () => {
if (props.error) {
const text = slots.error ? slots.error() : props.errorText;
if (text) { if (text) {
return ( return (
<div onClick={this.clickErrorText} class={bem('error-text')}> <div onClick={clickErrorText} class={bem('error-text')}>
{text} {text}
</div> </div>
); );
} }
} }
}, };
},
render() { const renderLoading = () => {
const Placeholder = <div ref="placeholder" class={bem('placeholder')} />; if (loading.value && !props.finished) {
const Content = this.$slots.default?.(); return (
<div class={bem('loading')} key="loading">
{slots.loading ? (
slots.loading()
) : (
<Loading size="16">{props.loadingText || t('loading')}</Loading>
)}
</div>
);
}
};
return ( watch([() => props.loading, () => props.finished], check);
<div class={bem()} role="feed" aria-busy={this.innerLoading}>
{this.direction === 'down' ? Content : Placeholder} onUpdated(() => {
{this.genLoading()} loading.value = props.loading;
{this.genFinishedText()} });
{this.genErrorText()}
{this.direction === 'up' ? Content : Placeholder} onMounted(() => {
</div> scrollerRef.value = getScroller(rootRef.value);
);
if (props.immediateCheck) {
check();
}
});
useGlobalEvent(scrollerRef, 'scroll', check);
// @exposed-api
const vm = getCurrentInstance().proxy;
vm.check = check;
return () => {
const Content = slots.default?.();
const Placeholder = (
<div ref={placeholderRef} class={bem('placeholder')} />
);
return (
<div ref={rootRef} role="feed" class={bem()} aria-busy={loading.value}>
{props.direction === 'down' ? Content : Placeholder}
{renderLoading()}
{renderFinishedText()}
{renderErrorText()}
{props.direction === 'up' ? Content : Placeholder}
</div>
);
};
}, },
}); });