一直都是用的第三方庫的
loading
元件(ant-design/antd-mobil
),最近一個專案需要用到loading,自己開發了下,總結如下:
Loading
元件的兩種形式:全域性loading
、區域性loading
- 全域性loading:一般覆蓋全屏居中顯示,這種情況的呼叫方式一般是程式設計式呼叫即
Loading.show()
- 區域性loading:只對某個區塊進行loading,這種情況一般是元件包裹形式使用
// 使用方式同 Spin 元件 <Spin visible={true}> <div>區塊內容</div> </Spin> 複製程式碼
- 兩種情況都做了請求速度過快時的
防閃爍
處理
意外收穫 portals
- 在開發全域性loading時,再想怎麼把loading元件掛在body頂層時,用了
ReactDom.render
這個方法一般是一些腳手架幫我們生成專案程式碼時自動生成的把頂層App元件放在root節點上時才會用到,這裡我們利用它把 loading元件掛在我們指定的頂層dom節點上 ReactDOM.createPortal(child, container)
官方文件、 知乎學習文章portals這個東西就是可以把元件放到指定的dom節點,而使用時依舊可以像普通使用元件那樣使用,只不過生成的dom樹不是在一起的 官方話術:Portal 提供了一種將子節點渲染到存在於父元件以外的 DOM 節點的優秀的方案 而且支援合成事件冒泡 利用這個方法開發一些 Dialog、Modal 元件就非常方便、非常簡潔了 antd的 Modal 元件也是用的這個方法 複製程式碼
兩種loading程式碼如下
- 全域性loading,這裡用到了
ReactDom.render(<Comps/>, domNode)
這個頂層apiimport React from 'react'; import ReactDOM from 'react-dom'; import './style/loading'; class Loading { domNode: HTMLElement isExistNode: boolean timer: any constructor() { this.domNode = document.createElement('div'); this.isExistNode = false; } private render(visible: boolean) { if (!this.isExistNode && visible) { document.body.appendChild(this.domNode); const children = this.createNode(); ReactDOM.render(children, this.domNode); this.isExistNode = true } if (visible) { this.domNode.className = 'hp-loading-wrap'; } else { this.domNode.className = 'hp-loading-wrap hide'; // ReactDOM.unmountComponentAtNode(this.domNode) } } createNode() { const node = <div className="loading-content"><div className="loading-img"></div></div>; return node; } show(isDelay=true, delay=300) { this.timer && clearTimeout(this.timer) if (!isDelay) { this.render(true); } else { // 防閃爍 this.timer = setTimeout(() => this.render(true), delay); } } hide() { this.timer && clearTimeout(this.timer) this.render(false) } } export default new Loading() // 樣式 .hp-loading-wrap { position: fixed; z-index: 99999; width: 100%; height: 100%; top: 0; left: 0; display: flex; align-items: center; &.hide { display: none; } .loading-content { width: 37.5px;/*no*/ height: 18px;/*no*/ margin: 0 auto; text-align: center; } .loading-img { width: 100%; height: 100%; margin: 0 auto; background-repeat: no-repeat; background-size: contain; background-position: center; background-image: url(../image/loading.gif) } } 複製程式碼
- 區域性loading,這裡用了
hooks
,使用方式同antd Spin
元件import React, { useState, useEffect } from "react"; import './style/spin.less' interface propsType { visible: boolean; children?: any; tips?: any; delay?: number; } let timer:any; export default function Spin(props: propsType) { const [visible, setVisible] = useState(props.visible); useEffect(() => { if (props.delay) { // 防閃爍 timer && clearTimeout(timer); if (props.visible) { timer = setTimeout(() => setVisible(true), props.delay); } else { setVisible(false); } } else { setVisible(props.visible); } }, [props.visible]); return ( <div className={visible ? "hp-spin" : "hp-spin hide"}> {props.children} <div className={props.children ? "spin-content" : ''}> <div className="spin-img"></div> </div> </div> ); } // 樣式 .hp-spin { position: relative; .spin-content { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 999; } &.hide .spin-content { display: none; } .spin-img { display: inline-block; width: 37.5px;/*no*/ height: 18px;/*no*/ background-repeat: no-repeat; background-size: contain; background-image: url('../image/loading.gif'); } } 複製程式碼