运营:别再让你的页面一直loading 了

运营:别再让你的页面一直loading 了

May-17-2024 15-36-38.gif

第一轮 battle

Q: 我想下载一个大文件,界面一直转圈,很耽误时间,我想在下载的时候还做点其他事情

A:一直转圈就一直等呗,反正还能摸会(奈何小姐姐太想做牛马了)

第二轮 battle

Q: 不行,为什么别人的浏览器,下载软件/文件 就能操作界面,你这就一直转圈,什么都做不了

A: 我们js 是单线程,一个时间只能做一件事,你不能在下载文件的时候,还操作界面吧…逐渐语无伦次,行,我给你试着优化优化…

image.png

最终效果

save.gif

无敌.gif

可以看到,下载文件 页面不再转圈,并且可以在界面操作,但是在点击操作1,2,到3的时候,会卡顿一下,下面会说为什么会卡这一下

开始分析

  1. 执行文件下载操作,把转圈逻辑去掉不就行了,

but: 是不转圈了,下载的时候,依然操作不了界面

  1. js 是一个单线程,一个时间只能做一件事,密集的cpu 计算,导致网站反应迟钝,就像卡了一样

resolve: 把下载文件这个耗时操作,放在其他线程操作,等到操作完毕,再通知主线程,执行完了。就像发布订阅模式一样,主线程不用执行密集的计算,也不用特意等密集计算的结果,执行完,告诉我就行了

技术使用 Web Workers

摘自 MDN https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers#web_workers_api

Web Worker 为 Web 内容在后台线程中运行脚本提供了一种简单的方法。线程可以执行任务而不干扰用户界面。此外,它们可以使用 XMLHttpRequest(尽管 responseXMLchannel 属性总是为空)或 fetch(没有这些限制)执行 I/O。一旦创建,一个 worker 可以将消息发送到创建它的 JavaScript 代码,通过将消息发布到该代码指定的事件处理器(反之亦然)。

为什么要用它:worker 的一个优势在于能够执行处理器密集型的运算

不会阻塞 UI 线程

不会阻塞 UI 线程

不会阻塞 UI 线程

不会阻塞 UI 线程

重要的事情说三遍 🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣🤣

基本使用

主线程生成一个专用 worker

const myWorker = new Worker("worker.js"); // worker.js 是一个脚本的 URI 来执行 worker 线程
专用 worker 中消息的接收和发送

就俩主要方法 postMessage onmessage

引入脚本与库

Worker 线程能够访问一个全局函数 importScripts() 来引入脚本,该函数接受 0 个或者多个 URI 作为参数来引入资源;以下例子都是合法的:

importScripts(); /* 什么都不引入 */
importScripts("foo.js"); /* 只引入 "foo.js" */
importScripts("foo.js", "bar.js"); /* 引入两个脚本 */
importScripts("//example.com/hello.js"); /* 你可以从其他来源导入脚本 */

ESModule 模式

const worker = new Worker('worker.js', 
    { type: 'module' // 指定 worker.js 的类型 }
 );

文件下载代码

  • baseCode
import { writeFile, utils } from 'xlsx'
/**模拟生成大文件数据 */
const generateLargeFileData = () => {
	const data = []
	for (let i = 0; i < 10000; i++) {
		data.push({
			id: i + 1,
			name: `User ${i + 1}`,
			email: `user${i + 1}@example.com`,
			age: Math.floor(Math.random() * 100) + 1
		})
	}
	return data
}
  • 一只转圈的代码
/**下载大文件 */
const downloadExcel = async () => {
	// 模拟生成大文件数据
	const data = generateLargeFileData()
	loading.value = true
	// 模拟一段短暂的等待时间,确保状态更新
	await delay(1000)
	// 卡死的罪魁祸者
	// 将数据转换为 Excel 格式
	const ws = utils.json_to_sheet(data)
	const wb = utils.book_new()
	utils.book_append_sheet(wb, ws, 'Sheet1')
	writeFile(wb, 'test.xlsx')
	loading.value = false
}
  • 使用webworker,将耗时计算放到 webworker 线程,解决阻塞ui的问题

主线程


const myWorker = new Worker('downloadWorker.js')
myWorker.onmessage = (event) => {
	let wb = event.data
  // 这里也会占用主线程的ui渲染,所以会卡一下
	writeFile(wb, 'test.xlsx')
   ElMessage.success('下载任务已在后台运行,可以继续操作界面其他任务')
}

/**下载大文件 */
const downloadExcel = async () => {
	const data = generateLargeFileData()
	myWorker.postMessage(data)
}

worker 线程

image.png

// 非模块化文件, public 打包本身就是线上文件了
importScripts("./xlsx.js"); // 线上地址,或者本地地址

self.onmessage = (e) => {
  // 将数据转换为 Excel 格式
  const ws = XLSX.utils.json_to_sheet(e.data)
  const wb = XLSX.utils.book_new()
  XLSX.utils.book_append_sheet(wb, ws, 'Sheet1')
  // writeFile(wb, 'test.xlsx') // 这里会操作dom, 所以将操作dom放到 主线程做
  self.postMessage(wb)
  self.close() 
}  

细节补充

  1. 本文主要介绍了专用worker,其实还有 共享 worker【主要做多页面标签通信】, ServiceWorkers 【主要做网络拦截,可以看一下之前写的pwa文章【https://juejin.cn/post/7062681470116036616】,离线缓存就是使用ServiceWorkers】

  2. 在主线程中使用时,onmessagepostMessage() 必须挂在 worker 对象上,而在 worker 中使用时不用这样做。原因是,在 worker 内部,worker 是有效的全局作用域(就像window.xxx ,window 一般可以不写)

  3. worker的关闭

// main.js(主线程)
const myWorker = new Worker('/worker.js'); // 创建worker 
myWorker.terminate(); // 关闭worker
// worker.js(worker线程) 
self.close(); // 直接执行close方法就ok了
  1. worker 错误监听 messageerror

  2. 关于主线程里的 new Worker(‘downloadWorker.js’)

这个脚本,必须是本地/或者网络地址,这里写的是项目运行地址 匹配相应的worker。这里大家也会发现一个问题,就是这个worker是全局性的,放在public 是一个不错的选择,再者打包后,public 下本身也是会放在服务器上

  1. 用完worker, 要及时关闭,他是不会自己结束的。选择 在worker 关闭,或者主线程关闭,会有区别

  2. 其实小文件下载,用worker 有点画蛇添足,本身使用worker 也是一种消耗

详细的参考资料以及代码地址

MDN

  1. https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Workers_API/Using_web_workers
  2. https://developer.mozilla.org/zh-CN/docs/Web/API/SharedWorker
  3. https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API

MDN code仓库

  1. https://github.com/mdn/dom-examples

可以下载下来直接调试,最好是起一个本地服务: http-server

image.png

掘金

  1. https://juejin.cn/post/7139718200177983524
  2. https://juejin.cn/post/7345672631323115570#heading-15

代码地址

https://gitee.com/Big_Cat-AK-47/vue-project.git

✈️✈️✈️番外 🎉🎉🎉🎉🎉🎉

最近在做一个项目,主要技术栈是v3 + el-plus + axios + pinia + vite5 + UnoCSS, 就是上面的仓库地址,主要是总结平时做的亮点/难点。有志同道合的朋友,想一起code,欢迎滴滴我

目前打算做的模块

  • 首页大屏数据可视化
  • 递归菜单/路由权限
  • 项目基建搭建
  • 大文件上传/断点续传/秒传/进度控制 以及一些优化
  • 手撸实现一个虚拟列表
  • 多语言/多主题 脚本编写《目前是在项目里,后期打算发布一个npm,包括封装常用方法,也会同步到npm》
  • 在线聊天
  • 项目工程化优化
  • 30
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现页面加载loading可以使用CSS和JavaScript结合的方式来实现。 1. CSS实现方式 在HTML中添加一个遮罩层和一个loading动画,通过CSS设置遮罩层透明度为0,当页面加载时,通过JavaScript获取到遮罩层和loading动画的DOM元素,并将遮罩层的透明度设置为1,从而实现页面loading效果。 示例代码如下: ```html <!-- HTML代码 --> <div class="loading-mask"> <div class="loading-animation"></div> </div> ``` ```css /* CSS代码 */ .loading-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: #fff; opacity: 0; z-index: 9999; transition: opacity 0.5s ease-in-out; } .loading-animation { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 50px; height: 50px; border-radius: 50%; border: 5px solid #ccc; border-top-color: #000; animation: spin 1s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } ``` 2. JavaScript实现方式 在HTML中添加一个loading动画,通过JavaScript监听页面的加载事件,当页面加载完成后,将loading动画的DOM元素从页面中移除,从而实现页面loading效果。 示例代码如下: ```html <!-- HTML代码 --> <div class="loading-animation"></div> ``` ```css /* CSS代码 */ .loading-animation { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 50px; height: 50px; border-radius: 50%; border: 5px solid #ccc; border-top-color: #000; animation: spin 1s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } } ``` ```javascript // JavaScript代码 window.addEventListener("load", function() { var loadingAnimation = document.querySelector(".loading-animation"); loadingAnimation.parentNode.removeChild(loadingAnimation); }); ``` 以上两种方式都可以实现页面加载loading效果,根据具体需要选择合适的方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值