Commit 4d8e39857ea59fff99e69832b4a8cabf3a424c24

Authored by Lan
Committed by GitHub
1 parent c4b22a22

perf(pagewrapper): 优化PageWrapper的高度自适应表现使用getViewportOffset替代useContentViewHeight (#792)

Co-authored-by: NorthLan <lan6995@gmail.com>
src/components/Page/src/PageWrapper.vue
1 <template> 1 <template>
2 - <div :class="getClass"> 2 + <div :class="getClass" ref="wrapperRef">
3 <PageHeader 3 <PageHeader
4 :ghost="ghost" 4 :ghost="ghost"
5 :title="title" 5 :title="title"
@@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
18 </template> 18 </template>
19 </PageHeader> 19 </PageHeader>
20 20
21 - <div class="overflow-hidden" :class="getContentClass" :style="getContentStyle"> 21 + <div class="overflow-hidden" :class="getContentClass" :style="getContentStyle" ref="contentRef">
22 <slot></slot> 22 <slot></slot>
23 </div> 23 </div>
24 24
@@ -35,16 +35,16 @@ @@ -35,16 +35,16 @@
35 <script lang="ts"> 35 <script lang="ts">
36 import type { CSSProperties, PropType } from 'vue'; 36 import type { CSSProperties, PropType } from 'vue';
37 37
38 - import { defineComponent, computed, watch, nextTick, ref, unref } from 'vue'; 38 + import { defineComponent, computed, watch, ref, unref } from 'vue';
39 import PageFooter from './PageFooter.vue'; 39 import PageFooter from './PageFooter.vue';
40 - import { usePageContext } from '/@/hooks/component/usePageContext';  
41 40
42 import { useDesign } from '/@/hooks/web/useDesign'; 41 import { useDesign } from '/@/hooks/web/useDesign';
43 import { propTypes } from '/@/utils/propTypes'; 42 import { propTypes } from '/@/utils/propTypes';
44 import { omit } from 'lodash-es'; 43 import { omit } from 'lodash-es';
45 import { PageHeader } from 'ant-design-vue'; 44 import { PageHeader } from 'ant-design-vue';
46 - import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';  
47 import { useLayoutHeight } from '/@/layouts/default/content/useContentViewHeight'; 45 import { useLayoutHeight } from '/@/layouts/default/content/useContentViewHeight';
  46 + import { useContentHeight } from './useContentHeight';
  47 + import { WrapperProps } from './types';
48 48
49 export default defineComponent({ 49 export default defineComponent({
50 name: 'PageWrapper', 50 name: 'PageWrapper',
@@ -64,13 +64,26 @@ @@ -64,13 +64,26 @@
64 fixedHeight: propTypes.bool, 64 fixedHeight: propTypes.bool,
65 }, 65 },
66 setup(props, { slots }) { 66 setup(props, { slots }) {
  67 + const wrapperRef = ref<ElRef>(null);
67 const headerRef = ref<ComponentRef>(null); 68 const headerRef = ref<ComponentRef>(null);
  69 + const contentRef = ref<ElRef>(null);
68 const footerRef = ref<ComponentRef>(null); 70 const footerRef = ref<ComponentRef>(null);
69 - const footerHeight = ref(0);  
70 - const { prefixCls, prefixVar } = useDesign('page-wrapper');  
71 - const { contentHeight, setPageHeight, pageHeight } = usePageContext(); 71 + const { prefixCls } = useDesign('page-wrapper');
72 const { footerHeightRef } = useLayoutHeight(); 72 const { footerHeightRef } = useLayoutHeight();
73 73
  74 + const getProps = computed(() => {
  75 + return props as WrapperProps;
  76 + });
  77 +
  78 + const { redoHeight, contentHeight } = useContentHeight(
  79 + getProps,
  80 + wrapperRef,
  81 + headerRef,
  82 + contentRef,
  83 + footerRef,
  84 + footerHeightRef
  85 + );
  86 +
74 const getClass = computed(() => { 87 const getClass = computed(() => {
75 return [ 88 return [
76 prefixCls, 89 prefixCls,
@@ -91,7 +104,8 @@ @@ -91,7 +104,8 @@
91 if (!contentFullHeight) { 104 if (!contentFullHeight) {
92 return { ...contentStyle }; 105 return { ...contentStyle };
93 } 106 }
94 - const height = `${unref(pageHeight)}px`; 107 +
  108 + const height = `${unref(contentHeight)}px`;
95 return { 109 return {
96 ...contentStyle, 110 ...contentStyle,
97 minHeight: height, 111 minHeight: height,
@@ -111,9 +125,9 @@ @@ -111,9 +125,9 @@
111 }); 125 });
112 126
113 watch( 127 watch(
114 - () => [contentHeight?.value, getShowFooter.value, footerHeightRef.value], 128 + () => [getShowFooter.value, footerHeightRef.value],
115 () => { 129 () => {
116 - calcContentHeight(); 130 + redoHeight();
117 }, 131 },
118 { 132 {
119 flush: 'post', 133 flush: 'post',
@@ -121,91 +135,16 @@ @@ -121,91 +135,16 @@
121 } 135 }
122 ); 136 );
123 137
124 - onMountedOrActivated(() => {  
125 - nextTick(() => {  
126 - calcContentHeight();  
127 - });  
128 - });  
129 -  
130 - function calcContentHeight() {  
131 - if (!props.contentFullHeight) {  
132 - return;  
133 - }  
134 - //fix:in contentHeight mode: delay getting footer and header dom element to get the correct height  
135 - const footer = unref(footerRef);  
136 - const header = unref(headerRef);  
137 - footerHeight.value = 0;  
138 - const footerEl = footer?.$el;  
139 -  
140 - if (footerEl) {  
141 - footerHeight.value += footerEl?.offsetHeight ?? 0;  
142 - }  
143 - let headerHeight = 0;  
144 - const headerEl = header?.$el;  
145 - if (headerEl) {  
146 - headerHeight += headerEl?.offsetHeight ?? 0;  
147 - }  
148 - // fix:subtract content's marginTop and marginBottom value  
149 - let subtractHeight = 0;  
150 - const ZERO_PX = '0px';  
151 - let marginBottom = ZERO_PX;  
152 - let marginTop = ZERO_PX;  
153 - const classElments = document.querySelectorAll(`.${prefixVar}-page-wrapper-content`);  
154 - if (classElments && classElments.length > 0) {  
155 - const contentEl = classElments[0];  
156 - const cssStyle = getComputedStyle(contentEl);  
157 - marginBottom = cssStyle?.marginBottom ?? ZERO_PX;  
158 - marginTop = cssStyle?.marginTop ?? ZERO_PX;  
159 - }  
160 - if (marginBottom) {  
161 - const contentMarginBottom = Number(marginBottom.replace(/[^\d]/g, ''));  
162 - subtractHeight += contentMarginBottom;  
163 - }  
164 - if (marginTop) {  
165 - const contentMarginTop = Number(marginTop.replace(/[^\d]/g, ''));  
166 - subtractHeight += contentMarginTop;  
167 - }  
168 -  
169 - // fix: wrapper marginTop and marginBottom value  
170 - let wrapperSubtractHeight = 0;  
171 - let wrapperMarginBottom = ZERO_PX;  
172 - let wrapperMarginTop = ZERO_PX;  
173 - const wrapperClassElments = document.querySelectorAll(`.${prefixVar}-page-wrapper`);  
174 - if (wrapperClassElments && wrapperClassElments.length > 0) {  
175 - const contentEl = wrapperClassElments[0];  
176 - const cssStyle = getComputedStyle(contentEl);  
177 - wrapperMarginBottom = cssStyle?.marginBottom ?? ZERO_PX;  
178 - wrapperMarginTop = cssStyle?.marginTop ?? ZERO_PX;  
179 - }  
180 - if (wrapperMarginBottom) {  
181 - const contentMarginBottom = Number(wrapperMarginBottom.replace(/[^\d]/g, ''));  
182 - wrapperSubtractHeight += contentMarginBottom;  
183 - }  
184 - if (wrapperMarginTop) {  
185 - const contentMarginTop = Number(wrapperMarginTop.replace(/[^\d]/g, ''));  
186 - wrapperSubtractHeight += contentMarginTop;  
187 - }  
188 - let height =  
189 - unref(contentHeight) -  
190 - unref(footerHeight) -  
191 - headerHeight -  
192 - subtractHeight -  
193 - wrapperSubtractHeight;  
194 - if (unref(getShowFooter)) {  
195 - height -= unref(footerHeightRef);  
196 - }  
197 - setPageHeight?.(height);  
198 - }  
199 -  
200 return { 138 return {
201 getContentStyle, 139 getContentStyle,
202 - footerRef, 140 + wrapperRef,
203 headerRef, 141 headerRef,
  142 + contentRef,
  143 + footerRef,
204 getClass, 144 getClass,
205 getHeaderSlots, 145 getHeaderSlots,
206 prefixCls, 146 prefixCls,
207 getShowFooter, 147 getShowFooter,
208 - pageHeight,  
209 omit, 148 omit,
210 getContentClass, 149 getContentClass,
211 }; 150 };
src/components/Page/src/types.ts 0 → 100644
  1 +import { CSSProperties } from 'vue';
  2 +
  3 +export interface WrapperProps {
  4 + title?: string;
  5 + dense: boolean;
  6 + ghost: boolean;
  7 + content: string;
  8 + contentStyle?: CSSProperties;
  9 + contentBackground: boolean;
  10 + contentFullHeight: boolean;
  11 + contentClass?: string;
  12 + fixedHeight: boolean;
  13 +}
src/components/Page/src/useContentHeight.ts 0 → 100644
  1 +import { ComputedRef, nextTick, Ref, ref, unref } from 'vue';
  2 +import { WrapperProps } from './types';
  3 +import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
  4 +import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
  5 +import { getViewportOffset } from '/@/utils/domUtils';
  6 +
  7 +export function useContentHeight(
  8 + propsRef: ComputedRef<WrapperProps>,
  9 + wrapperRef: Ref<ElRef>,
  10 + headerRef?: Ref<ComponentRef>,
  11 + contentRef?: Ref<ElRef>,
  12 + footerRef?: Ref<ComponentRef>,
  13 + layoutFooterHeightRef: Ref<number> = ref(0),
  14 + offsetHeightRef: Ref<number> = ref(0)
  15 +) {
  16 + const contentHeight: Ref<Nullable<number>> = ref(null);
  17 +
  18 + const redoHeight = () => {
  19 + nextTick(() => {
  20 + calcContentHeight();
  21 + });
  22 + };
  23 +
  24 + const subtractMargin = (element: HTMLElement | null | undefined): number => {
  25 + let subtractHeight = 0;
  26 + const ZERO_PX = '0px';
  27 + let marginBottom = ZERO_PX;
  28 + let marginTop = ZERO_PX;
  29 + if (element) {
  30 + const cssStyle = getComputedStyle(element);
  31 + marginBottom = cssStyle?.marginBottom ?? ZERO_PX;
  32 + marginTop = cssStyle?.marginTop ?? ZERO_PX;
  33 + }
  34 + if (marginBottom) {
  35 + const contentMarginBottom = Number(marginBottom.replace(/[^\d]/g, ''));
  36 + subtractHeight += contentMarginBottom;
  37 + }
  38 + if (marginTop) {
  39 + const contentMarginTop = Number(marginTop.replace(/[^\d]/g, ''));
  40 + subtractHeight += contentMarginTop;
  41 + }
  42 + return subtractHeight;
  43 + };
  44 +
  45 + const calcContentHeight = async () => {
  46 + const { contentFullHeight } = unref(propsRef);
  47 + if (!contentFullHeight) {
  48 + return;
  49 + }
  50 + // Add a delay to get the correct height
  51 + await nextTick();
  52 +
  53 + const wrapperEl = unref(wrapperRef);
  54 + if (!wrapperEl) {
  55 + return;
  56 + }
  57 + const { bottomIncludeBody } = getViewportOffset(wrapperEl);
  58 + const headerHeight = unref(headerRef)?.$el.offsetHeight ?? 0;
  59 + const footerHeight = unref(footerRef)?.$el.offsetHeight ?? 0;
  60 +
  61 + // content's subtract
  62 + const substractHeight = subtractMargin(unref(contentRef));
  63 + let height =
  64 + bottomIncludeBody -
  65 + unref(layoutFooterHeightRef) -
  66 + unref(offsetHeightRef) -
  67 + headerHeight -
  68 + footerHeight -
  69 + substractHeight;
  70 +
  71 + // fix: compensation height both layout's footer and page's footer was shown
  72 + if (unref(layoutFooterHeightRef) > 0 && footerHeight > 0) {
  73 + height += footerHeight;
  74 + }
  75 +
  76 + contentHeight.value = height;
  77 + };
  78 +
  79 + onMountedOrActivated(() => {
  80 + nextTick(() => {
  81 + calcContentHeight();
  82 + });
  83 + });
  84 + useWindowSizeFn(
  85 + () => {
  86 + calcContentHeight();
  87 + },
  88 + 50,
  89 + { immediate: true }
  90 + );
  91 +
  92 + return { redoHeight, contentHeight };
  93 +}
src/layouts/default/content/useContentViewHeight.ts
@@ -19,7 +19,7 @@ export function useContentViewHeight() { @@ -19,7 +19,7 @@ export function useContentViewHeight() {
19 const contentHeight = ref(window.innerHeight); 19 const contentHeight = ref(window.innerHeight);
20 const pageHeight = ref(window.innerHeight); 20 const pageHeight = ref(window.innerHeight);
21 const getViewHeight = computed(() => { 21 const getViewHeight = computed(() => {
22 - return unref(contentHeight) - unref(headerHeightRef) || 0; 22 + return unref(contentHeight) - unref(headerHeightRef) - unref(footerHeightRef) || 0;
23 }); 23 });
24 24
25 useWindowSizeFn( 25 useWindowSizeFn(