下面是全局加載遮罩工具,功能:提供show和showWithDelay/hide方法用于顯示/延時顯示/隱藏遮罩,它還提供loading屬性返回是否正在loading。通常用于耗時較長的操作,比如遠端api調用。
如何用它,下面是個例子,這個是全局的postAction:
import loadingMask from './loadingMask';
...
// 設置延遲顯示加載遮罩(1秒后顯示)loadingMask.showWithDelay('請求處理中,請稍候...', 1000);return axios({url: url,method: 'post',data: parameter,headers: { ...signHeader, ...config.headers },...config}).then(res => {// 請求完成后隱藏加載遮罩loadingMask.hide();return handleResponse(res);}).catch(err => {// 請求出錯后隱藏加載遮罩loadingMask.hide();return handleError(err);});
如果想實時獲得它的loading屬性呢?這時候要訂閱它的狀態變化:
const [loading, setLoading] = useState(loadingMask.loading);
// 訂閱 loadingMask 的 loading 狀態變化useEffect(() => {// 訂閱 loading 狀態變化const unsubscribe = loadingMask.subscribeToLoading(setLoading);// 組件卸載時取消訂閱return unsubscribe;}, []);...<Buttonblocktype='submit'color='primary'loading={loading}>刪除選中記錄</Button>
這時候loadingMask的loading狀態變化會立即返回到setLoading,也就會引導起button的重新渲染。
組件代碼如下:
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Mask, SpinLoading } from 'antd-mobile';
import styled from 'styled-components';const LoadingContainer = styled.div`display: flex;flex-direction: column;align-items: center;justify-content: center;position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);color: #ffffff;
`;const LoadingText = styled.div`margin-top: 12px;font-size: 14px;
`;/*** 全局加載遮罩工具* 用于在長時間請求時顯示加載遮罩*/
class LoadingMask {constructor() {this.container = null;this.root = null;this.visible = false;this.timeoutId = null;this.loadingText = '正在加載,請稍候...';this._loading = false; // 添加內部 loading 狀態this._listeners = []; // 添加監聽器數組this.init();}/*** 初始化加載遮罩容器*/init() {// 創建容器元素this.container = document.createElement('div');this.container.id = 'global-loading-mask-container';document.body.appendChild(this.container);// 創建React 18的rootthis.root = createRoot(this.container);// 初始渲染this.render();}/*** 渲染加載遮罩*/render() {if (!this.root) {this.init();return;}this.root.render(<Mask opacity={0.7} visible={this.visible}><LoadingContainer><SpinLoading color='white' style={{ '--size': '48px' }} /><LoadingText>{this.loadingText}</LoadingText></LoadingContainer></Mask>);}/*** 顯示加載遮罩* @param {string} text - 加載提示文本*/show(text) {this.loadingText = text || '正在加載,請稍候...';this.visible = true;this._setLoading(true); // 使用新方法設置 loading 狀態this.render();}/*** 隱藏加載遮罩*/hide() {this.visible = false;this._setLoading(false); // 使用新方法設置 loading 狀態this.render();// 清除定時器if (this.timeoutId) {clearTimeout(this.timeoutId);this.timeoutId = null;}}/*** 設置延遲顯示加載遮罩* @param {string} text - 加載提示文本* @param {number} delay - 延遲時間(毫秒)*/showWithDelay(text, delay = 1000) {// 清除之前的定時器if (this.timeoutId) {clearTimeout(this.timeoutId);}// 立即設置 loading 狀態為 truethis.loadingText = text || '正在加載,請稍候...';this._setLoading(true); // 使用新方法設置 loading 狀態// 設置新的定時器,只延遲顯示遮罩this.timeoutId = setTimeout(() => {this.visible = true;this.render();}, delay);}// 添加設置 loading 狀態的方法,并通知監聽器_setLoading(value) {if (this._loading !== value) {this._loading = value;// 通知所有監聽器this._listeners.forEach(listener => listener(value));}};// 添加訂閱方法subscribeToLoading(callback) {this._listeners.push(callback);// 立即通知當前狀態callback(this._loading);// 返回取消訂閱的函數return () => {this._listeners = this._listeners.filter(cb => cb !== callback);};};
}// 創建單例實例
const loadingMask = new LoadingMask();// 添加 loading 屬性的 getter
Object.defineProperty(loadingMask, 'loading', {get: function() {return this._loading;}
});export default loadingMask;