我正在做一个小的学习项目,它从API获取一个硬币列表,然后为从API接收的数组的长度构建一个充满引导卡的页面。在开发过程中,我只使用了100个第一个硬币来避免漫长的等待时间,但现在我已经完成了,当我尝试整个3900多个硬币时,它需要不切实际的长时间。
我很确定我添加字符串的方式是问题的根源,我会添加我的代码,我相信这一切都会更有意义。
我试着构建整个字符串,然后附加它——速度仍然很慢。我试着每次都更改它的innerHTML,然后追加它(追加在for循环中),但它只是覆盖了所有旧的硬币,只追加最后一个。
我想让它做的是将每个盒子单独附加,并在合理的时间内完成,现在需要30多分钟才能完成,这显然不好。
我添加的当前版本的代码需要很长时间,但最终还是做对了(尽管for循环中只有50次迭代,所以如果你现在尝试它,它不会被卡住,它需要超过3900次迭代)
function homeStart(coins: any[]): void {
var divcreate = document.createElement("div");
for (var i = 0; i < 50; i++) {
// console.log(coins[i]);
// ******This is where the string addup starts
divcreate.innerHTML += `
<div class="card text-dark bg-dark m-auto makeinline"
id="${coins[i].id}${i}" style="max-width: 18rem;">
<div class="card-header">
<div class="flexalign">
<span class="coinsymbol"
id="${coins[i].symbol.toUpperCase() +
"a1"}">${coins[i].symbol.toUpperCase()}
</span>
<label class="switch">
<input type="checkbox"
id="${coins[i].id}${coins[i].symbol}"
onchange="selectedCoinUpdate(this,'${coins[i].symbol}')">
<span class="slider round"></span>
</label>
</div>
</div>
<div class="card-body">
<div class="">
<h5 class="card-title coinname">${coins[i].name}</h5>
<button type="button" class="btn btn-secondary" data-
toggle="collapse" href="#collapseIdentity${i}" role="button"
aria-expanded="false" aria-
controls="collapseIdentity${i}"
onclick="moreInfo('${coins[i].id}')">More info</button>
</div>
<div class="collapse" id="collapseIdentity${i}">
<div class="card-body" id="${coins[i].id}">
</div>
</div>
</div>
</div>`;
}
// ******This is where the string addup ends
$("#pagecont").append(divcreate);
}
为了快速加载DOM元素,您可以看到这个链接->javascript中的内存泄漏,函数在循环中
在尽可能快地获取内容后,它可能仍然太慢,并可能导致浏览器冻结/崩溃。
JS中长时间运行的同步操作会锁定UI,。
为了防止这种情况,您可以将长时间运行的sync
操作转换为async
操作,这样做的好处是给UI一些喘息的空间。然后你可以创建一个装载指示器等
下面是一个非常简单的例子,我正在做一个大约100毫秒的同步操作,并做了100次。这总共需要10秒,如果你在不使用async
的情况下这样做,那将不是一个很好的用户体验。
为了证明UI没有被锁定,我只是将Loading指示器闪烁为粉红色,当然你可以做一个旋转的圆圈等。完成后,它还会控制台日志all done
,。。
希望这个小例子能对长期运行的sync
操作有所帮助。
ps:我一直认为async/await
是为了进行下一次勾选,对我来说,尽管在没有sleep(0)
的情况下进行循环,仍然会导致UI锁定10秒。
const loaddiv = document.querySelector(".loading");
var i = setInterval(() => {
loaddiv.classList.toggle("loading");
}, 300);
const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));
async function blockUI() {
let now = new Date().getTime();
const desiredTime = now + 100;
while (now < desiredTime) {
now = new Date().getTime();
}
await sleep(0); //give the UI some breathing space
}
async function longLoop() {
for (let l = 0; l < 100; l += 1)
await blockUI();
clearInterval(i);
console.log("all done");
}
longLoop();
.loading {
background-color: pink;
}
<div class="loading">
Loading.
</div>
正如Keith所说,您可以在多个任务中转换独特的同步操作,并以异步方式运行这些任务(在他的示例中为setTimeout)。
这样做而不是一个大的操作可以提高UI性能,但它并不是很好,因为你接触了DOM。
触摸DOM很慢,因为浏览器可能有很多操作要做(绘制、回流等)。因此,最好让浏览器在他接触DOM及其DOM工作的最佳时间完成任务。
为此,浏览器主要有两种方法可以使用:
- requestAnimationFrame:通常用于执行动画,但现在是要求浏览器执行DOM操作的最佳时机
- requestIdleCallback:执行后台和低优先级工作
下面是一个将这两种方法结合在一起的示例:https://codesandbox.io/s/7jz3yjow0q
它是基于这篇谷歌开发者的文章。
这里有一些关于网络性能的好资源:
- https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/
- https://developers.google.com/web/fundamentals/performance/rendering/
- https://developers.google.com/web/fundamentals/performance/rail