最近給客戶部署的一個項目瞎嬉,客戶反饋說持續(xù)運行一段時間后瀏覽器會崩潰兜蠕。
收到反饋后扰肌,我們使用自己的設(shè)備進(jìn)行測試,持續(xù)運行了48小時熊杨,頁面并沒有崩潰曙旭。后來找到幾臺老舊機型來測試,運行幾小時后確實出現(xiàn)了崩潰的現(xiàn)象晶府。排查原因應(yīng)該是代碼運行中桂躏,在某些低端設(shè)備上內(nèi)存釋放不及時,長時間運行后內(nèi)存積累川陆,導(dǎo)致頁面崩潰剂习。
在不能強制客戶升級硬件設(shè)備的條件下,只能通過優(yōu)化代碼,排查可能存在內(nèi)存泄漏地方鳞绕。
1失仁、頁面中有很多通過svg和Lottie實現(xiàn)的動畫,并通過v-show來控制不同動畫的顯示和隱藏们何。通過調(diào)試發(fā)現(xiàn)萄焦,在v-show值為false,即動畫display:none的情況下冤竹,動畫依然在占用內(nèi)存拂封,所以在優(yōu)化時將v-show改為了v-if。
2鹦蠕、頁面中的動畫多處使用了setInterval定時器冒签,通過封裝公共方法,使用requestAnimationFrame和setTimeout代替setInterval钟病。
3萧恕、對于一些簡單功能,如顯示當(dāng)前時間档悠,去掉插件廊鸥,通過原生js實現(xiàn)望浩。同時對于頁面中外部依賴的處理辖所、第三方插件的按需引入。
因為Network面板記錄了與服務(wù)器交互的具體細(xì)節(jié)磨德,所以可以通過Network面板來查詢當(dāng)前頁面的資源請求缘回。
在這里我們可以看到發(fā)起的請求數(shù)量,傳輸體積以及解壓縮后的體積典挑,同時還可以知道哪些資源是命中了強緩存酥宴,哪些資源命中的協(xié)商緩存。
拓展
-關(guān)于Lottie動畫
Lottie 是 Airbnb 開源的一套跨平臺的完整的動畫效果解決方案您觉,設(shè)計師可以使用 Adobe After Effects 設(shè)計出漂亮的動畫之后拙寡,使用 Lottic 提供的 Bodymovin 插件將設(shè)計好的動畫導(dǎo)出成 JSON 格式,就可以直接運用在 iOS琳水、Android肆糕、Web 和 React Native之上,無需其他額外操作在孝。
關(guān)于Lottie動畫 https://juejin.cn/post/6844903661760413704
-下面解釋一下使用requestAnimationFrame 的原理
- 何為requestAnimationFrame
window.requestAnimationFrame() 告訴瀏覽器——你希望執(zhí)行一個動畫诚啃,并且要求瀏覽器在下次重繪之前調(diào)用指定的回調(diào)函數(shù)更新動畫。該方法需要傳入一個回調(diào)函數(shù)作為參數(shù)私沮,該回調(diào)函數(shù)會在瀏覽器下一次重繪之前執(zhí)行始赎。
注意:若你想在瀏覽器下次重繪之前繼續(xù)更新下一幀動畫,那么回調(diào)函數(shù)自身必須再次調(diào)用window.requestAnimationFrame()。
requestAnimationFrame的優(yōu)勢
requestAnimationFrame 比起 setTimeout造垛、setInterval的優(yōu)勢主要有兩點:
1魔招、requestAnimationFrame 會把每一幀中的所有DOM操作集中起來,在一次重繪或回流中就完成五辽,并且重繪或回流的時間間隔緊緊跟隨瀏覽器的刷新頻率仆百,一般來說,這個頻率為每秒60幀奔脐。
2俄周、在隱藏或不可見的元素中,requestAnimationFrame將不會進(jìn)行重繪或回流髓迎,這當(dāng)然就意味著更少的的cpu峦朗,gpu和內(nèi)存使用量。關(guān)于requestAnimationFrame的使用 - 用js實現(xiàn)一個無限循環(huán)的動畫排龄。
//大家首先想到的肯定是定時器吧~
<!doctype html>
<html lang="en">
<head>
<title>Document</title>
<style>
#e{
width: 100px;
height: 100px;
background: red;
position: absolute;
left: 0;
top: 0;
zoom: 1;
}
</style>
</head>
<body>
<div id="e"></div>
<script>
var e = document.getElementById("e");
var flag = true;
var left = 0;
//es6中字符串拼接用${變量名}的方式引用變量
//ES6中``內(nèi)的所有東西都被解析為字符串波势,相對于ES5的字符串拼接來說更為方便
function render() {
if(flag == true){
if(left>=100){
flag = false
}
e.style.left = ` ${left++}px`;
}else{
if(left<=0){
flag = true
}
e.style.left = ` ${left--}px`
}
}
setInterval(function(){
render()
},1000/60)
</script>
</body>
</html>
那我們使用requestAnimationFrame函數(shù) 試試
<!doctype html>
<html lang="en">
<head>
<title>Document</title>
<style>
#e{
width: 100px;
height: 100px;
background: red;
position: absolute;
left: 0;
top: 0;
zoom: 1;
}
</style>
</head>
<body>
<div id="e"></div>
<script>
var e = document.getElementById("e");
var flag = true;
var left = 0;
function render() {
if(flag == true){
if(left>=100){
flag = false
}
e.style.left = ` ${left++}px`
}else{
if(left<=0){
flag = true
}
e.style.left = ` ${left--}px`
}
}
//requestAnimationFrame效果
(function animloop() {
render();
window.requestAnimationFrame(animloop);
})();
</script>
</body>
</html>
隨之而來的,有個問題:效果是實現(xiàn)了但是怎么停止requestAnimationFrame函數(shù)橄维?是否有類似clearInterval這樣的類似方法尺铣?
答案是確定的 必須有:cancelAnimationFrame()接收一個參數(shù) requestAnimationFrame默認(rèn)返回一個id,cancelAnimationFrame只需要傳入這個id就可以停止了争舞。
<!doctype html>
<html lang="en">
<head>
<title>Document</title>
<style>
#e{
width: 100px;
height: 100px;
background: red;
position: absolute;
left: 0;
top: 0;
zoom: 1;
}
</style>
</head>
<body>
<div id="e"></div>
<script>
var e = document.getElementById("e");
var flag = true;
var left = 0;
var rafId = null
function render() {
if(flag == true){
if(left>=100){
flag = false
}
e.style.left = ` ${left++}px`
}else{
if(left<=0){
flag = true
}
e.style.left = ` ${left--}px`
}
}
//requestAnimationFrame效果
(function animloop(time) {
console.log(time,Date.now())
render();
rafId = requestAnimationFrame(animloop);
//如果left等于50 停止動畫
if(left == 50){
cancelAnimationFrame(rafId); //停止動畫
}
})();
//setInterval效果
// setInterval(function(){
// render()
// },1000/60)
</script>
</body>
</html>
2凛忿、如果我想動畫頻率降低怎么做,默認(rèn)情況下竞川,requestAnimationFrame執(zhí)行頻率是1000/60,大概是16ms多執(zhí)一次店溢。
如果我們想每50ms執(zhí)行一次怎么辦呢?
<!doctype html>
<html lang="en">
<head>
<title>Document</title>
<style>
#e{
width: 100px;
height: 100px;
background: red;
position: absolute;
left: 0;
top: 0;
zoom: 1;
}
</style>
</head>
<body>
<div id="e"></div>
<script>
var e = document.getElementById("e");
var flag = true;
var left = 0;
//當(dāng)前執(zhí)行時間
var nowTime = 0;
//記錄每次動畫執(zhí)行結(jié)束的時間
var lastTime = Date.now();
//我們自己定義的動畫時間差值
var diffTime = 40;
function render() {
if(flag == true){
if(left>=100){
flag = false
}
e.style.left = ` ${left++}px`
}else{
if(left<=0){
flag = true
}
e.style.left = ` ${left--}px`
}
}
//requestAnimationFrame效果
(function animloop() {
//記錄當(dāng)前時間
nowTime = Date.now()
// 當(dāng)前時間-上次執(zhí)行時間如果大于diffTime委乌,那么執(zhí)行動畫床牧,并更新上次執(zhí)行時間
if(nowTime-lastTime > diffTime){
lastTime = nowTime
render();
}
requestAnimationFrame(animloop);
})()
</script>
</body>
</html>
//@我是一個前端