10.如何实现网页加载进度条

1. 背景

为了提升整站用户加载等待体验,考虑使用加载进度条反馈给用户加载过程

  • 怎么拿到进度?
    • 使用什么方法拿,或者什么库?兼容性如何
    • fetch、ajax使用哪一种
  • 怎么绘制进度?
    • dom
    • svg
    • canvas

2. 方案

2.1 页面加载

监听页面加载的事件,来控制进度条的绘制

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
			#progress-bar{
				position: fixed;
				top: 0;
				left: 0;
				width: 100%;
				height: 4px;
				background-color: #29d;
				z-index: 9999;
				transition: width 0.5s ease;
			}
		</style>
	</head>
	<body>
		<div id="progress-bar"></div>
		<script>
			function simulateLoadingProgress(){
				const progressBar=document.getElementById('progress-bar')
				let width=0
				const interval=setInterval(()=>{
					if(width>=100){
						clearInterval(interval)
						progressBar.style.width='100%'
						setTimeout(()=>{
							progressBar.style.display='none'
						},100)
					}else{
						width+=10
						progressBar.style.width=100+'%'
					}
				},200)
			}
			window.addEventListener('load',()=>{
				simulateLoadingProgress()
			})
		</script>
	</body>
</html>

2.2 请求场景实现

ajax拿到进度,svg/dom绘制

function loadResource(url) {
	const xhr = new XMLHttpRequest()
	xhr.open('GET', url, true)
	xhr.onprogress = function(event) {
		if (event.lengthComputable) {
			const percentComplete = (event.loaded / event.total) * 100
			document.getElementById('progress-bar').style.width = percentComplete + '%'
		}
	}
	xhr.onload = function() {
		if (xhr.status === 200) {
			document.getElementById('progress-bar').style.width = '100%'
			setTimeout(() => {
				document.getElementById('progress-bar').style.display = 'none'
			}, 500)
		}
	}
}

2.3 框架中页面切换的进度条

在React中我们可以通过nprogress这个库来实现

import NProgress from 'nprogress'

const App=()=>{
	const history=useHistory()
	
	useEffect(()=>{
		NProgress.configure({
			showSpinner:false
		})
		const handleStart=()=>{
			Nprogress.start()
		}
		const handleStop=()=>{
			NProgress.done()
		}
		history.listen(()=>{
			handleStart()
			handleStop()
		})
	})
}

在Vue中可以使用导航守卫来实现:

router.beforeEach((_to, _from, next) => {
    window.NProgress?.start?.();
    next();
  });
  router.afterEach(_to => {
    window.NProgress?.done?.();
  });