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 | 2 | |
3 | 3 | ### ⚡ Performance Improvements |
4 | 4 | |
5 | -- 菜单性能继续优化 | |
5 | +- 菜单性能继续优化,更流畅 | |
6 | +- 优化懒加载组件及示例 | |
6 | 7 | |
7 | 8 | ### 🎫 Chores |
8 | 9 | |
... | ... | @@ -13,6 +14,7 @@ |
13 | 14 | ### 🐛 Bug Fixes |
14 | 15 | |
15 | 16 | - 修复升级之后 table 类型问题 |
17 | +- 修复分割菜单且左侧菜单没有数据时候,继续展示上一次子菜单的问题 | |
16 | 18 | |
17 | 19 | ## 2.0.0-rc.8 (2020-11-2) |
18 | 20 | |
... | ... |
src/components/Container/src/LazyContainer.vue
1 | 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 | 10 | <div key="component" v-if="isInit"> |
4 | 11 | <slot :loading="loading" /> |
5 | 12 | </div> |
6 | - <div key="skeleton"> | |
13 | + <div key="skeleton" v-else name="lazy-skeleton"> | |
7 | 14 | <slot name="skeleton" v-if="$slots.skeleton" /> |
8 | 15 | <Skeleton v-else /> |
9 | 16 | </div> |
... | ... | @@ -12,19 +19,9 @@ |
12 | 19 | <script lang="ts"> |
13 | 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 | 24 | import { Skeleton } from 'ant-design-vue'; |
27 | - import { useRaf } from '/@/hooks/event/useRaf'; | |
28 | 25 | import { useTimeout } from '/@/hooks/core/useTimeout'; |
29 | 26 | import { useIntersectionObserver } from '/@/hooks/event/useIntersectionObserver'; |
30 | 27 | interface State { |
... | ... | @@ -36,13 +33,12 @@ |
36 | 33 | name: 'LazyContainer', |
37 | 34 | components: { Skeleton }, |
38 | 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 | 37 | timeout: { |
41 | 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 | 42 | viewport: { |
47 | 43 | type: (typeof window !== 'undefined' ? window.HTMLElement : Object) as PropType< |
48 | 44 | HTMLElement |
... | ... | @@ -50,19 +46,19 @@ |
50 | 46 | default: () => null, |
51 | 47 | }, |
52 | 48 | |
53 | - // 预加载阈值, css单位 | |
49 | + // Preload threshold, css unit | |
54 | 50 | threshold: { |
55 | 51 | type: String as PropType<string>, |
56 | 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 | 56 | direction: { |
61 | 57 | type: String as PropType<'vertical' | 'horizontal'>, |
62 | 58 | default: 'vertical', |
63 | 59 | }, |
64 | 60 | |
65 | - // 包裹组件的外层容器的标签名 | |
61 | + // The label name of the outer container that wraps the component | |
66 | 62 | tag: { |
67 | 63 | type: String as PropType<string>, |
68 | 64 | default: 'div', |
... | ... | @@ -105,23 +101,11 @@ |
105 | 101 | function init() { |
106 | 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 | 104 | useTimeout(() => { |
118 | 105 | if (state.isInit) return; |
119 | - callback(); | |
106 | + state.isInit = true; | |
107 | + emit('init'); | |
120 | 108 | }, props.maxWaitingTime || 80); |
121 | - | |
122 | - const { requestAnimationFrame } = useRaf(); | |
123 | - | |
124 | - return requestAnimationFrame; | |
125 | 109 | } |
126 | 110 | |
127 | 111 | function initIntersectionObserver() { |
... | ... | @@ -165,31 +149,8 @@ |
165 | 149 | }); |
166 | 150 | </script> |
167 | 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 | 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 | 156 | </style> |
... | ... |
src/components/Container/src/collapse/CollapseContainer.vue
... | ... | @@ -8,13 +8,13 @@ |
8 | 8 | <CollapseTransition :enable="canExpan"> |
9 | 9 | <Skeleton v-if="loading" /> |
10 | 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 | 12 | <slot /> |
13 | 13 | <template #skeleton> |
14 | 14 | <slot name="lazySkeleton" /> |
15 | 15 | </template> |
16 | - </LazyContainer> --> | |
17 | - <slot /> | |
16 | + </LazyContainer> | |
17 | + <slot v-else /> | |
18 | 18 | </div> |
19 | 19 | </CollapseTransition> |
20 | 20 | </div> |
... | ... | @@ -28,7 +28,7 @@ |
28 | 28 | import CollapseHeader from './CollapseHeader.vue'; |
29 | 29 | import { Skeleton } from 'ant-design-vue'; |
30 | 30 | |
31 | - // import LazyContainer from '../LazyContainer'; | |
31 | + import LazyContainer from '../LazyContainer.vue'; | |
32 | 32 | |
33 | 33 | import { triggerWindowResize } from '/@/utils/event/triggerWindowResizeEvent'; |
34 | 34 | // hook |
... | ... | @@ -36,7 +36,7 @@ |
36 | 36 | export default defineComponent({ |
37 | 37 | components: { |
38 | 38 | Skeleton, |
39 | - // LazyContainer, | |
39 | + LazyContainer, | |
40 | 40 | CollapseHeader, |
41 | 41 | CollapseTransition, |
42 | 42 | }, |
... | ... | @@ -75,7 +75,7 @@ |
75 | 75 | // 延时加载时间 |
76 | 76 | lazyTime: { |
77 | 77 | type: Number as PropType<number>, |
78 | - default: 3000, | |
78 | + default: 0, | |
79 | 79 | }, |
80 | 80 | }, |
81 | 81 | setup(props) { |
... | ... |
src/components/ContextMenu/src/index.tsx
... | ... | @@ -25,11 +25,13 @@ export default defineComponent({ |
25 | 25 | const state = reactive({ |
26 | 26 | show: false, |
27 | 27 | }); |
28 | + | |
28 | 29 | onMounted(() => { |
29 | 30 | nextTick(() => { |
30 | 31 | state.show = true; |
31 | 32 | }); |
32 | 33 | }); |
34 | + | |
33 | 35 | onUnmounted(() => { |
34 | 36 | const el = unref(wrapRef); |
35 | 37 | el && document.body.removeChild(el); |
... | ... | @@ -61,6 +63,7 @@ export default defineComponent({ |
61 | 63 | |
62 | 64 | handler && handler(); |
63 | 65 | } |
66 | + | |
64 | 67 | function renderContent(item: ContextMenuItem) { |
65 | 68 | const { icon, label } = item; |
66 | 69 | |
... | ... | @@ -72,6 +75,7 @@ export default defineComponent({ |
72 | 75 | </span> |
73 | 76 | ); |
74 | 77 | } |
78 | + | |
75 | 79 | function renderMenuItem(items: ContextMenuItem[]) { |
76 | 80 | return items.map((item, index) => { |
77 | 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 | 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 | 42 | path: 'scroll', |
23 | 43 | name: '滚动组件', |
24 | 44 | children: [ |
... | ... | @@ -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 | 60 | path: 'lazy', |
53 | 61 | name: '懒加载组件', |
62 | + children: [ | |
63 | + { | |
64 | + path: 'basic', | |
65 | + name: '基础示例', | |
66 | + }, | |
67 | + { | |
68 | + path: 'transition', | |
69 | + name: '动画效果', | |
70 | + }, | |
71 | + ], | |
54 | 72 | }, |
55 | 73 | { |
56 | 74 | path: 'verify', |
... | ... | @@ -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 | 99 | title: '详情组件', |
100 | 100 | }, |
101 | 101 | }, |
102 | + | |
102 | 103 | { |
103 | 104 | path: '/lazy', |
104 | 105 | name: 'lazyDemo', |
105 | - component: () => import('/@/views/demo/comp/lazy/index.vue'), | |
106 | + redirect: '/comp/lazy/basic', | |
106 | 107 | meta: { |
107 | 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 | 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 | 3 | <Alert message="基础示例" description="向下滚动到可见区域才会加载组件" type="info" show-icon /> |
4 | 4 | <div class="lazy-base-demo-wrap"> |
5 | 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 | 15 | </div> |
13 | 16 | </div> |
14 | 17 | </template> |
... | ... | @@ -24,7 +27,7 @@ |
24 | 27 | }, |
25 | 28 | }); |
26 | 29 | </script> |
27 | -<style lang="less" scoped> | |
30 | +<style lang="less"> | |
28 | 31 | .lazy-base-demo { |
29 | 32 | &-wrap { |
30 | 33 | display: flex; |
... | ... | @@ -38,6 +41,11 @@ |
38 | 41 | align-items: center; |
39 | 42 | } |
40 | 43 | |
44 | + &-box { | |
45 | + width: 300px; | |
46 | + height: 300px; | |
47 | + } | |
48 | + | |
41 | 49 | h1 { |
42 | 50 | height: 1300px; |
43 | 51 | margin: 20px 0; |
... | ... |