From 34923c751366c92fe2ab270d373e934d87a858f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=81=E9=87=87?= Date: Thu, 6 Jun 2019 17:44:06 +0800 Subject: [PATCH] [improvement] List: optimization boundary judgment logic (#3450) --- packages/list/index.js | 58 ++++++-------- packages/list/index.less | 5 ++ .../list/test/__snapshots__/demo.spec.js.snap | 4 +- packages/list/test/index.spec.js | 79 +++++++++++++++++-- 4 files changed, 105 insertions(+), 41 deletions(-) diff --git a/packages/list/index.js b/packages/list/index.js index b4cc67d31..186877695 100644 --- a/packages/list/index.js +++ b/packages/list/index.js @@ -1,12 +1,7 @@ import { use } from '../utils'; import Loading from '../loading'; import { BindEventMixin } from '../mixins/bind-event'; -import { - getScrollTop, - getElementTop, - getVisibleHeight, - getScrollEventTarget -} from '../utils/dom/scroll'; +import { getScrollEventTarget } from '../utils/dom/scroll'; const [sfc, bem, t] = use('list'); @@ -68,9 +63,19 @@ export default sfc({ return; } - const el = this.$el; - const { scroller } = this; - const scrollerHeight = getVisibleHeight(scroller); + const { $el: el, scroller, offset, direction } = this; + let scrollerRect; + + if (scroller.getBoundingClientRect) { + scrollerRect = scroller.getBoundingClientRect(); + } else { + scrollerRect = { + top: 0, + bottom: scroller.innerHeight + }; + } + + const scrollerHeight = scrollerRect.bottom - scrollerRect.top; /* istanbul ignore next */ if ( @@ -78,32 +83,19 @@ export default sfc({ window.getComputedStyle(el).display === 'none' || el.offsetParent === null ) { - return; + return false; } - const { offset, direction } = this; + let isReachEdge = false; + const placeholderRect = this.$refs.placeholder.getBoundingClientRect(); - function isReachEdge() { - if (el === scroller) { - const scrollTop = getScrollTop(el); - - if (direction === 'up') { - return scrollTop <= offset; - } - - const targetBottom = scrollTop + scrollerHeight; - return scroller.scrollHeight - targetBottom <= offset; - } - - if (direction === 'up') { - return getScrollTop(scroller) - getElementTop(el) <= offset; - } - - const elBottom = getElementTop(el) + getVisibleHeight(el) - getElementTop(scroller); - return elBottom - scrollerHeight <= offset; + if (direction === 'up') { + isReachEdge = placeholderRect.top - scrollerRect.top <= offset; + } else { + isReachEdge = placeholderRect.bottom - scrollerRect.bottom <= offset; } - if (isReachEdge()) { + if (isReachEdge) { this.$emit('input', true); this.$emit('load'); } @@ -116,9 +108,11 @@ export default sfc({ }, render(h) { + const placeholder =
; + return (
- {this.direction === 'down' && this.slots()} + {this.direction === 'down' ? this.slots() : placeholder} {this.loading && (
{this.slots('loading') || ( @@ -134,7 +128,7 @@ export default sfc({ {this.errorText}
)} - {this.direction === 'up' && this.slots()} + {this.direction === 'up' ? this.slots() : placeholder}
); } diff --git a/packages/list/index.less b/packages/list/index.less index a300f20fd..7bbd55326 100644 --- a/packages/list/index.less +++ b/packages/list/index.less @@ -9,4 +9,9 @@ line-height: @list-text-line-height; text-align: center; } + + &__placeholder { + height: 0; + pointer-events: none; + } } diff --git a/packages/list/test/__snapshots__/demo.spec.js.snap b/packages/list/test/__snapshots__/demo.spec.js.snap index 109fb9d1e..d0b791185 100644 --- a/packages/list/test/__snapshots__/demo.spec.js.snap +++ b/packages/list/test/__snapshots__/demo.spec.js.snap @@ -15,7 +15,9 @@ exports[`renders demo correctly 1`] = `
-
+
+
+
diff --git a/packages/list/test/index.spec.js b/packages/list/test/index.spec.js index e9c5d4df4..dfb5e2f1a 100644 --- a/packages/list/test/index.spec.js +++ b/packages/list/test/index.spec.js @@ -1,5 +1,5 @@ import List from '..'; -import { mount, later } from '../../../test/utils'; +import { mount, later, mockGetBoundingClientRect } from '../../../test/utils'; function mockOffsetParent(el) { Object.defineProperty(el, 'offsetParent', { @@ -33,7 +33,7 @@ test('error loaded, click error-text and reload', async () => { const wrapper = mount(List, { propsData: { errorText: 'Request failed. Click to reload...', - error: true + error: true, } }); @@ -45,11 +45,15 @@ test('error loaded, click error-text and reload', async () => { expect(wrapper.emitted('input')).toBeFalsy(); // 模拟点击error-text的行为 - wrapper.setProps({ - error: false + wrapper.vm.$on('update:error', val => { + wrapper.setProps({ + error: val + }); }); - wrapper.vm.$emit('input', true); - wrapper.vm.$emit('load'); + + wrapper.find('.van-list__error-text').trigger('click'); + + await later(); expect(wrapper.vm.$props.error).toBeFalsy(); expect(wrapper.emitted('load')).toBeTruthy(); @@ -61,7 +65,8 @@ test('error loaded, click error-text and reload', async () => { test('finished', async () => { const wrapper = mount(List, { propsData: { - finished: true + finished: true, + finishedText: 'Finished' } }); @@ -70,11 +75,13 @@ test('finished', async () => { await later(); expect(wrapper.emitted('load')).toBeFalsy(); expect(wrapper.emitted('input')).toBeFalsy(); + expect(wrapper.contains('.van-list__finished-text')).toBeTruthy(); wrapper.vm.finished = false; await later(); expect(wrapper.emitted('load')).toBeTruthy(); expect(wrapper.emitted('input')).toBeTruthy(); + expect(wrapper.contains('.van-list__finished-text')).toBeFalsy(); }); test('immediate check false', async () => { @@ -89,7 +96,7 @@ test('immediate check false', async () => { expect(wrapper.emitted('input')).toBeFalsy(); }); -test('keey-alive live cycle', () => { +test('keep-alive live cycle', () => { const wrapper = mount( { template: ` @@ -111,3 +118,59 @@ test('keey-alive live cycle', () => { wrapper.vm.show = false; expect(wrapper.vm.el).toBeFalsy(); }); + +test('check the case that scroller is not window', async () => { + const restoreMock = mockGetBoundingClientRect({ + top: 0, + bottom: 200 + }); + + const wrapper = mount({ + template: ` +
+ +
+ `, + components: { List } + }); + + const listRef = wrapper.find({ + ref: 'list' + }); + mockOffsetParent(listRef.vm.$el); + + await later(); + expect(listRef.emitted('load')).toBeTruthy(); + expect(listRef.emitted('input')).toBeTruthy(); + + restoreMock(); +}); + +test('check the direction props', async () => { + const wrapper = mount(List, { + slots: { + default: '
list item
' + }, + propsData: { + direction: 'up' + } + }); + + mockOffsetParent(wrapper.vm.$el); + + await later(); + + let children = wrapper.findAll('.van-list > div'); + expect(children.at(0).is('.van-list__placeholder')).toBeTruthy(); + expect(children.at(1).is('.list-item')).toBeTruthy(); + + // change the direction's value + wrapper.setProps({ + direction: 'down' + }); + + await later(); + children = wrapper.findAll('.van-list > div'); + expect(children.at(0).is('.list-item')).toBeTruthy(); + expect(children.at(1).is('.van-list__placeholder')).toBeTruthy(); +});