最近给客户部署的一个项目,客户反馈说持续运行一段时间后浏览器会崩溃。
收到反馈后,我们使用自己的设备进行测试,持续运行了48小时,页面并没有崩溃。后来找到几台老旧机型来测试,运行几小时后确实出现了崩溃的现象。排查原因应该是代码运行中,在某些低端设备上内存释放不及时,长时间运行后内存积累,导致页面崩溃。
在不能强制客户升级硬件设备的条件下,只能通过优化代码,排查可能存在内存泄漏地方。
1、页面中有很多通过svg和Lottie实现的动画,并通过v-show来控制不同动画的显示和隐藏。通过调试发现,在v-show值为false,即动画display:none的情况下,动画依然在占用内存,所以在优化时将v-show改为了v-if。
2、页面中的动画多处使用了setInterval定时器,通过封装公共方法,使用requestAnimationFrame和setTimeout代替setInterval。
3、对于一些简单功能,如显示当前时间,去掉插件,通过原生js实现。同时对于页面中外部依赖的处理、第三方插件的按需引入。
因为Network面板记录了与服务器交互的具体细节,所以可以通过Network面板来查询当前页面的资源请求。
在这里我们可以看到发起的请求数量,传输体积以及解压缩后的体积,同时还可以知道哪些资源是命中了强缓存,哪些资源命中的协商缓存。
拓展
-关于Lottie动画
Lottie 是 Airbnb 开源的一套跨平台的完整的动画效果解决方案,设计师可以使用 Adobe After Effects 设计出漂亮的动画之后,使用 Lottic 提供的 Bodymovin 插件将设计好的动画导出成 JSON 格式,就可以直接运用在 iOS、Android、Web 和 React Native之上,无需其他额外操作。
关于Lottie动画 https://juejin.cn/post/6844903661760413704
-下面解释一下使用requestAnimationFrame 的原理
- 何为requestAnimationFrame
window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
注意:若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()。
requestAnimationFrame的优势
requestAnimationFrame 比起 setTimeout、setInterval的优势主要有两点:
1、requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
2、在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。关于requestAnimationFrame的使用 - 用js实现一个无限循环的动画。
//大家首先想到的肯定是定时器吧~
<!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中``内的所有东西都被解析为字符串,相对于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函数 试试
<!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>
随之而来的,有个问题:效果是实现了但是怎么停止requestAnimationFrame函数?是否有类似clearInterval这样的类似方法?
答案是确定的 必须有:cancelAnimationFrame()接收一个参数 requestAnimationFrame默认返回一个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、如果我想动画频率降低怎么做,默认情况下,requestAnimationFrame执行频率是1000/60,大概是16ms多执一次。
如果我们想每50ms执行一次怎么办呢?
<!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 nowTime = 0;
//记录每次动画执行结束的时间
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() {
//记录当前时间
nowTime = Date.now()
// 当前时间-上次执行时间如果大于diffTime,那么执行动画,并更新上次执行时间
if(nowTime-lastTime > diffTime){
lastTime = nowTime
render();
}
requestAnimationFrame(animloop);
})()
</script>
</body>
</html>
//@我是一个前端