Commit 0f4b847d69e90e5bbb4fb0883fb5ea1dd1daf1e7
1 parent
e79e540b
perf(lazy-container): optimize lazyContainer code
Showing
10 changed files
with
179 additions
and
97 deletions
.browserslistrc deleted
100644 → 0
CHANGELOG.zh_CN.md
@@ -2,7 +2,8 @@ | @@ -2,7 +2,8 @@ | ||
2 | 2 | ||
3 | ### ⚡ Performance Improvements | 3 | ### ⚡ Performance Improvements |
4 | 4 | ||
5 | -- 菜单性能继续优化 | 5 | +- 菜单性能继续优化,更流畅 |
6 | +- 优化懒加载组件及示例 | ||
6 | 7 | ||
7 | ### 🎫 Chores | 8 | ### 🎫 Chores |
8 | 9 | ||
@@ -13,6 +14,7 @@ | @@ -13,6 +14,7 @@ | ||
13 | ### 🐛 Bug Fixes | 14 | ### 🐛 Bug Fixes |
14 | 15 | ||
15 | - 修复升级之后 table 类型问题 | 16 | - 修复升级之后 table 类型问题 |
17 | +- 修复分割菜单且左侧菜单没有数据时候,继续展示上一次子菜单的问题 | ||
16 | 18 | ||
17 | ## 2.0.0-rc.8 (2020-11-2) | 19 | ## 2.0.0-rc.8 (2020-11-2) |
18 | 20 |
src/components/Container/src/LazyContainer.vue
1 | <template> | 1 | <template> |
2 | - <transition-group v-bind="$attrs" ref="elRef" :name="transitionName" :tag="tag"> | 2 | + <transition-group |
3 | + class="lazy-container" | ||
4 | + v-bind="$attrs" | ||
5 | + ref="elRef" | ||
6 | + :name="transitionName" | ||
7 | + :tag="tag" | ||
8 | + mode="out-in" | ||
9 | + > | ||
3 | <div key="component" v-if="isInit"> | 10 | <div key="component" v-if="isInit"> |
4 | <slot :loading="loading" /> | 11 | <slot :loading="loading" /> |
5 | </div> | 12 | </div> |
6 | - <div key="skeleton"> | 13 | + <div key="skeleton" v-else name="lazy-skeleton"> |
7 | <slot name="skeleton" v-if="$slots.skeleton" /> | 14 | <slot name="skeleton" v-if="$slots.skeleton" /> |
8 | <Skeleton v-else /> | 15 | <Skeleton v-else /> |
9 | </div> | 16 | </div> |
@@ -12,19 +19,9 @@ | @@ -12,19 +19,9 @@ | ||
12 | <script lang="ts"> | 19 | <script lang="ts"> |
13 | import type { PropType } from 'vue'; | 20 | import type { PropType } from 'vue'; |
14 | 21 | ||
15 | - import { | ||
16 | - defineComponent, | ||
17 | - reactive, | ||
18 | - onMounted, | ||
19 | - ref, | ||
20 | - unref, | ||
21 | - onUnmounted, | ||
22 | - toRef, | ||
23 | - toRefs, | ||
24 | - } from 'vue'; | 22 | + import { defineComponent, reactive, onMounted, ref, toRef, toRefs } from 'vue'; |
25 | 23 | ||
26 | import { Skeleton } from 'ant-design-vue'; | 24 | import { Skeleton } from 'ant-design-vue'; |
27 | - import { useRaf } from '/@/hooks/event/useRaf'; | ||
28 | import { useTimeout } from '/@/hooks/core/useTimeout'; | 25 | import { useTimeout } from '/@/hooks/core/useTimeout'; |
29 | import { useIntersectionObserver } from '/@/hooks/event/useIntersectionObserver'; | 26 | import { useIntersectionObserver } from '/@/hooks/event/useIntersectionObserver'; |
30 | interface State { | 27 | interface State { |
@@ -36,13 +33,12 @@ | @@ -36,13 +33,12 @@ | ||
36 | name: 'LazyContainer', | 33 | name: 'LazyContainer', |
37 | components: { Skeleton }, | 34 | components: { Skeleton }, |
38 | props: { | 35 | props: { |
39 | - // 等待时间,如果指定了时间,不论可见与否,在指定时间之后自动加载 | 36 | + // Waiting time, if the time is specified, whether visible or not, it will be automatically loaded after the specified time |
40 | timeout: { | 37 | timeout: { |
41 | type: Number as PropType<number>, | 38 | type: Number as PropType<number>, |
42 | - default: 0, | ||
43 | - // default: 8000, | ||
44 | }, | 39 | }, |
45 | - // 组件所在的视口,如果组件是在页面容器内滚动,视口就是该容器 | 40 | + |
41 | + // The viewport where the component is located. If the component is scrolling in the page container, the viewport is the container | ||
46 | viewport: { | 42 | viewport: { |
47 | type: (typeof window !== 'undefined' ? window.HTMLElement : Object) as PropType< | 43 | type: (typeof window !== 'undefined' ? window.HTMLElement : Object) as PropType< |
48 | HTMLElement | 44 | HTMLElement |
@@ -50,19 +46,19 @@ | @@ -50,19 +46,19 @@ | ||
50 | default: () => null, | 46 | default: () => null, |
51 | }, | 47 | }, |
52 | 48 | ||
53 | - // 预加载阈值, css单位 | 49 | + // Preload threshold, css unit |
54 | threshold: { | 50 | threshold: { |
55 | type: String as PropType<string>, | 51 | type: String as PropType<string>, |
56 | default: '0px', | 52 | default: '0px', |
57 | }, | 53 | }, |
58 | 54 | ||
59 | - // 视口的滚动方向, vertical代表垂直方向,horizontal代表水平方向 | 55 | + // The scroll direction of the viewport, vertical represents the vertical direction, horizontal represents the horizontal direction |
60 | direction: { | 56 | direction: { |
61 | type: String as PropType<'vertical' | 'horizontal'>, | 57 | type: String as PropType<'vertical' | 'horizontal'>, |
62 | default: 'vertical', | 58 | default: 'vertical', |
63 | }, | 59 | }, |
64 | 60 | ||
65 | - // 包裹组件的外层容器的标签名 | 61 | + // The label name of the outer container that wraps the component |
66 | tag: { | 62 | tag: { |
67 | type: String as PropType<string>, | 63 | type: String as PropType<string>, |
68 | default: 'div', | 64 | default: 'div', |
@@ -105,23 +101,11 @@ | @@ -105,23 +101,11 @@ | ||
105 | function init() { | 101 | function init() { |
106 | state.loading = true; | 102 | state.loading = true; |
107 | 103 | ||
108 | - requestAnimationFrameFn(() => { | ||
109 | - state.isInit = true; | ||
110 | - emit('init'); | ||
111 | - }); | ||
112 | - } | ||
113 | - | ||
114 | - function requestAnimationFrameFn(callback: () => any) { | ||
115 | - // Prevent waiting too long without executing the callback | ||
116 | - // Set the maximum waiting time | ||
117 | useTimeout(() => { | 104 | useTimeout(() => { |
118 | if (state.isInit) return; | 105 | if (state.isInit) return; |
119 | - callback(); | 106 | + state.isInit = true; |
107 | + emit('init'); | ||
120 | }, props.maxWaitingTime || 80); | 108 | }, props.maxWaitingTime || 80); |
121 | - | ||
122 | - const { requestAnimationFrame } = useRaf(); | ||
123 | - | ||
124 | - return requestAnimationFrame; | ||
125 | } | 109 | } |
126 | 110 | ||
127 | function initIntersectionObserver() { | 111 | function initIntersectionObserver() { |
@@ -165,31 +149,8 @@ | @@ -165,31 +149,8 @@ | ||
165 | }); | 149 | }); |
166 | </script> | 150 | </script> |
167 | <style lang="less"> | 151 | <style lang="less"> |
168 | - .lazy-container-enter { | ||
169 | - opacity: 0; | ||
170 | - } | ||
171 | - | ||
172 | - .lazy-container-enter-to { | ||
173 | - opacity: 1; | ||
174 | - } | ||
175 | - | ||
176 | - .lazy-container-enter-from, | ||
177 | - .lazy-container-enter-active { | ||
178 | - position: absolute; | ||
179 | - top: 0; | 152 | + .lazy-container { |
180 | width: 100%; | 153 | width: 100%; |
181 | - transition: opacity 0.3s 0.2s; | ||
182 | - } | ||
183 | - | ||
184 | - .lazy-container-leave { | ||
185 | - opacity: 1; | ||
186 | - } | ||
187 | - | ||
188 | - .lazy-container-leave-to { | ||
189 | - opacity: 0; | ||
190 | - } | ||
191 | - | ||
192 | - .lazy-container-leave-active { | ||
193 | - transition: opacity 0.5s; | 154 | + height: 100%; |
194 | } | 155 | } |
195 | </style> | 156 | </style> |
src/components/Container/src/collapse/CollapseContainer.vue
@@ -8,13 +8,13 @@ | @@ -8,13 +8,13 @@ | ||
8 | <CollapseTransition :enable="canExpan"> | 8 | <CollapseTransition :enable="canExpan"> |
9 | <Skeleton v-if="loading" /> | 9 | <Skeleton v-if="loading" /> |
10 | <div class="collapse-container__body" v-else v-show="show"> | 10 | <div class="collapse-container__body" v-else v-show="show"> |
11 | - <!-- <LazyContainer :timeout="lazyTime" v-if="lazy"> | 11 | + <LazyContainer :timeout="lazyTime" v-if="lazy"> |
12 | <slot /> | 12 | <slot /> |
13 | <template #skeleton> | 13 | <template #skeleton> |
14 | <slot name="lazySkeleton" /> | 14 | <slot name="lazySkeleton" /> |
15 | </template> | 15 | </template> |
16 | - </LazyContainer> --> | ||
17 | - <slot /> | 16 | + </LazyContainer> |
17 | + <slot v-else /> | ||
18 | </div> | 18 | </div> |
19 | </CollapseTransition> | 19 | </CollapseTransition> |
20 | </div> | 20 | </div> |
@@ -28,7 +28,7 @@ | @@ -28,7 +28,7 @@ | ||
28 | import CollapseHeader from './CollapseHeader.vue'; | 28 | import CollapseHeader from './CollapseHeader.vue'; |
29 | import { Skeleton } from 'ant-design-vue'; | 29 | import { Skeleton } from 'ant-design-vue'; |
30 | 30 | ||
31 | - // import LazyContainer from '../LazyContainer'; | 31 | + import LazyContainer from '../LazyContainer.vue'; |
32 | 32 | ||
33 | import { triggerWindowResize } from '/@/utils/event/triggerWindowResizeEvent'; | 33 | import { triggerWindowResize } from '/@/utils/event/triggerWindowResizeEvent'; |
34 | // hook | 34 | // hook |
@@ -36,7 +36,7 @@ | @@ -36,7 +36,7 @@ | ||
36 | export default defineComponent({ | 36 | export default defineComponent({ |
37 | components: { | 37 | components: { |
38 | Skeleton, | 38 | Skeleton, |
39 | - // LazyContainer, | 39 | + LazyContainer, |
40 | CollapseHeader, | 40 | CollapseHeader, |
41 | CollapseTransition, | 41 | CollapseTransition, |
42 | }, | 42 | }, |
@@ -75,7 +75,7 @@ | @@ -75,7 +75,7 @@ | ||
75 | // 延时加载时间 | 75 | // 延时加载时间 |
76 | lazyTime: { | 76 | lazyTime: { |
77 | type: Number as PropType<number>, | 77 | type: Number as PropType<number>, |
78 | - default: 3000, | 78 | + default: 0, |
79 | }, | 79 | }, |
80 | }, | 80 | }, |
81 | setup(props) { | 81 | setup(props) { |
src/components/ContextMenu/src/index.tsx
@@ -25,11 +25,13 @@ export default defineComponent({ | @@ -25,11 +25,13 @@ export default defineComponent({ | ||
25 | const state = reactive({ | 25 | const state = reactive({ |
26 | show: false, | 26 | show: false, |
27 | }); | 27 | }); |
28 | + | ||
28 | onMounted(() => { | 29 | onMounted(() => { |
29 | nextTick(() => { | 30 | nextTick(() => { |
30 | state.show = true; | 31 | state.show = true; |
31 | }); | 32 | }); |
32 | }); | 33 | }); |
34 | + | ||
33 | onUnmounted(() => { | 35 | onUnmounted(() => { |
34 | const el = unref(wrapRef); | 36 | const el = unref(wrapRef); |
35 | el && document.body.removeChild(el); | 37 | el && document.body.removeChild(el); |
@@ -61,6 +63,7 @@ export default defineComponent({ | @@ -61,6 +63,7 @@ export default defineComponent({ | ||
61 | 63 | ||
62 | handler && handler(); | 64 | handler && handler(); |
63 | } | 65 | } |
66 | + | ||
64 | function renderContent(item: ContextMenuItem) { | 67 | function renderContent(item: ContextMenuItem) { |
65 | const { icon, label } = item; | 68 | const { icon, label } = item; |
66 | 69 | ||
@@ -72,6 +75,7 @@ export default defineComponent({ | @@ -72,6 +75,7 @@ export default defineComponent({ | ||
72 | </span> | 75 | </span> |
73 | ); | 76 | ); |
74 | } | 77 | } |
78 | + | ||
75 | function renderMenuItem(items: ContextMenuItem[]) { | 79 | function renderMenuItem(items: ContextMenuItem[]) { |
76 | return items.map((item, index) => { | 80 | return items.map((item, index) => { |
77 | const { disabled, label, children, divider = false } = item; | 81 | const { disabled, label, children, divider = false } = item; |
src/components/Preview/src/index.tsx
src/router/menus/modules/demo/comp.ts
@@ -19,6 +19,26 @@ const menu: MenuModule = { | @@ -19,6 +19,26 @@ const menu: MenuModule = { | ||
19 | }, | 19 | }, |
20 | 20 | ||
21 | { | 21 | { |
22 | + path: 'modal', | ||
23 | + name: '弹窗扩展', | ||
24 | + }, | ||
25 | + { | ||
26 | + path: 'drawer', | ||
27 | + name: '抽屉扩展', | ||
28 | + }, | ||
29 | + { | ||
30 | + path: 'desc', | ||
31 | + name: '详情组件', | ||
32 | + }, | ||
33 | + { | ||
34 | + path: 'qrcode', | ||
35 | + name: '二维码组件', | ||
36 | + }, | ||
37 | + { | ||
38 | + path: 'strength-meter', | ||
39 | + name: '密码强度组件', | ||
40 | + }, | ||
41 | + { | ||
22 | path: 'scroll', | 42 | path: 'scroll', |
23 | name: '滚动组件', | 43 | name: '滚动组件', |
24 | children: [ | 44 | children: [ |
@@ -37,20 +57,18 @@ const menu: MenuModule = { | @@ -37,20 +57,18 @@ const menu: MenuModule = { | ||
37 | ], | 57 | ], |
38 | }, | 58 | }, |
39 | { | 59 | { |
40 | - path: 'modal', | ||
41 | - name: '弹窗扩展', | ||
42 | - }, | ||
43 | - { | ||
44 | - path: 'drawer', | ||
45 | - name: '抽屉扩展', | ||
46 | - }, | ||
47 | - { | ||
48 | - path: 'desc', | ||
49 | - name: '详情组件', | ||
50 | - }, | ||
51 | - { | ||
52 | path: 'lazy', | 60 | path: 'lazy', |
53 | name: '懒加载组件', | 61 | name: '懒加载组件', |
62 | + children: [ | ||
63 | + { | ||
64 | + path: 'basic', | ||
65 | + name: '基础示例', | ||
66 | + }, | ||
67 | + { | ||
68 | + path: 'transition', | ||
69 | + name: '动画效果', | ||
70 | + }, | ||
71 | + ], | ||
54 | }, | 72 | }, |
55 | { | 73 | { |
56 | path: 'verify', | 74 | path: 'verify', |
@@ -66,14 +84,6 @@ const menu: MenuModule = { | @@ -66,14 +84,6 @@ const menu: MenuModule = { | ||
66 | }, | 84 | }, |
67 | ], | 85 | ], |
68 | }, | 86 | }, |
69 | - { | ||
70 | - path: 'qrcode', | ||
71 | - name: '二维码组件', | ||
72 | - }, | ||
73 | - { | ||
74 | - path: 'strength-meter', | ||
75 | - name: '密码强度组件', | ||
76 | - }, | ||
77 | ], | 87 | ], |
78 | }, | 88 | }, |
79 | }; | 89 | }; |
src/router/routes/modules/demo/comp.ts
@@ -99,13 +99,32 @@ export default { | @@ -99,13 +99,32 @@ export default { | ||
99 | title: '详情组件', | 99 | title: '详情组件', |
100 | }, | 100 | }, |
101 | }, | 101 | }, |
102 | + | ||
102 | { | 103 | { |
103 | path: '/lazy', | 104 | path: '/lazy', |
104 | name: 'lazyDemo', | 105 | name: 'lazyDemo', |
105 | - component: () => import('/@/views/demo/comp/lazy/index.vue'), | 106 | + redirect: '/comp/lazy/basic', |
106 | meta: { | 107 | meta: { |
107 | title: '懒加载组件', | 108 | title: '懒加载组件', |
108 | }, | 109 | }, |
110 | + children: [ | ||
111 | + { | ||
112 | + path: 'basic', | ||
113 | + name: 'BasicLazyDemo', | ||
114 | + component: () => import('/@/views/demo/comp/lazy/index.vue'), | ||
115 | + meta: { | ||
116 | + title: '基础示例', | ||
117 | + }, | ||
118 | + }, | ||
119 | + { | ||
120 | + path: 'transition', | ||
121 | + name: 'BasicTransitionDemo', | ||
122 | + component: () => import('/@/views/demo/comp/lazy/Transition.vue'), | ||
123 | + meta: { | ||
124 | + title: '动画效果', | ||
125 | + }, | ||
126 | + }, | ||
127 | + ], | ||
109 | }, | 128 | }, |
110 | { | 129 | { |
111 | path: '/verify', | 130 | path: '/verify', |
src/views/demo/comp/lazy/Transition.vue
0 → 100644
1 | +<template> | ||
2 | + <div class="p-4 lazy-base-demo"> | ||
3 | + <Alert message="自定义动画" description="懒加载组件显示动画" type="info" show-icon /> | ||
4 | + <div class="lazy-base-demo-wrap"> | ||
5 | + <h1>向下滚动</h1> | ||
6 | + | ||
7 | + <div class="lazy-base-demo-box"> | ||
8 | + <LazyContainer transitionName="custom"> | ||
9 | + <TargetContent /> | ||
10 | + </LazyContainer> | ||
11 | + </div> | ||
12 | + </div> | ||
13 | + </div> | ||
14 | +</template> | ||
15 | +<script lang="ts"> | ||
16 | + import { defineComponent } from 'vue'; | ||
17 | + import { Skeleton, Alert } from 'ant-design-vue'; | ||
18 | + import TargetContent from './TargetContent.vue'; | ||
19 | + import { LazyContainer } from '/@/components/Container/index'; | ||
20 | + export default defineComponent({ | ||
21 | + components: { LazyContainer, TargetContent, Skeleton, Alert }, | ||
22 | + setup() { | ||
23 | + return {}; | ||
24 | + }, | ||
25 | + }); | ||
26 | +</script> | ||
27 | +<style lang="less"> | ||
28 | + .lazy-base-demo { | ||
29 | + &-wrap { | ||
30 | + display: flex; | ||
31 | + width: 50%; | ||
32 | + height: 2000px; | ||
33 | + margin: 20px auto; | ||
34 | + text-align: center; | ||
35 | + background: #fff; | ||
36 | + justify-content: center; | ||
37 | + flex-direction: column; | ||
38 | + align-items: center; | ||
39 | + } | ||
40 | + | ||
41 | + &-box { | ||
42 | + width: 300px; | ||
43 | + height: 300px; | ||
44 | + } | ||
45 | + | ||
46 | + h1 { | ||
47 | + height: 1300px; | ||
48 | + margin: 20px 0; | ||
49 | + } | ||
50 | + } | ||
51 | + | ||
52 | + .custom-enter { | ||
53 | + opacity: 0; | ||
54 | + transform: scale(0.4) translate(100%); | ||
55 | + } | ||
56 | + | ||
57 | + .custom-enter-to { | ||
58 | + opacity: 1; | ||
59 | + } | ||
60 | + | ||
61 | + .custom-enter-active { | ||
62 | + position: absolute; | ||
63 | + top: 0; | ||
64 | + width: 100%; | ||
65 | + transition: all 0.5s; | ||
66 | + } | ||
67 | + | ||
68 | + .custom-leave { | ||
69 | + opacity: 1; | ||
70 | + } | ||
71 | + | ||
72 | + .custom-leave-to { | ||
73 | + opacity: 0; | ||
74 | + transform: scale(0.4) translate(-100%); | ||
75 | + } | ||
76 | + | ||
77 | + .custom-leave-active { | ||
78 | + transition: all 0.5s; | ||
79 | + } | ||
80 | +</style> |
src/views/demo/comp/lazy/index.vue
@@ -3,12 +3,15 @@ | @@ -3,12 +3,15 @@ | ||
3 | <Alert message="基础示例" description="向下滚动到可见区域才会加载组件" type="info" show-icon /> | 3 | <Alert message="基础示例" description="向下滚动到可见区域才会加载组件" type="info" show-icon /> |
4 | <div class="lazy-base-demo-wrap"> | 4 | <div class="lazy-base-demo-wrap"> |
5 | <h1>向下滚动</h1> | 5 | <h1>向下滚动</h1> |
6 | - <LazyContainer @init="() => {}"> | ||
7 | - <TargetContent /> | ||
8 | - <template #skeleton> | ||
9 | - <Skeleton :rows="10" /> | ||
10 | - </template> | ||
11 | - </LazyContainer> | 6 | + |
7 | + <div class="lazy-base-demo-box"> | ||
8 | + <LazyContainer> | ||
9 | + <TargetContent /> | ||
10 | + <template #skeleton> | ||
11 | + <Skeleton :rows="10" /> | ||
12 | + </template> | ||
13 | + </LazyContainer> | ||
14 | + </div> | ||
12 | </div> | 15 | </div> |
13 | </div> | 16 | </div> |
14 | </template> | 17 | </template> |
@@ -24,7 +27,7 @@ | @@ -24,7 +27,7 @@ | ||
24 | }, | 27 | }, |
25 | }); | 28 | }); |
26 | </script> | 29 | </script> |
27 | -<style lang="less" scoped> | 30 | +<style lang="less"> |
28 | .lazy-base-demo { | 31 | .lazy-base-demo { |
29 | &-wrap { | 32 | &-wrap { |
30 | display: flex; | 33 | display: flex; |
@@ -38,6 +41,11 @@ | @@ -38,6 +41,11 @@ | ||
38 | align-items: center; | 41 | align-items: center; |
39 | } | 42 | } |
40 | 43 | ||
44 | + &-box { | ||
45 | + width: 300px; | ||
46 | + height: 300px; | ||
47 | + } | ||
48 | + | ||
41 | h1 { | 49 | h1 { |
42 | height: 1300px; | 50 | height: 1300px; |
43 | margin: 20px 0; | 51 | margin: 20px 0; |