前端性能优化问题?
这个问题无疑是面试常见的问题之一,看了
大神文章以后以为本身以前的回答好次哦,站的角度不够高,那么下边咱们就来看一下,怎样回答好这个问题。
-
文件引入位置: css放在head中引入; js放到script末尾引入,防止阻止页面的加载
-
减小文件体积: 删除冗余代码、 压缩混淆代码、 文件按需加载
-
图片的优化: 在保证分辨率的状况下尽可能压缩图片; 小的图片能够采用多张拼成一张雪碧图,减小图片请求次数; 低于5k图片能够采用base64的图片格式,不须要发请求,浏览器能直接编译;
-
减小http请求,合并请求
-
合理使用缓存: 能够将数据存储到浏览器上,合理利用浏览器的缓存,避免重复发送请求; 若是使用cdn的话,也可利用cdn缓存;(cdn是什么?就相似于京东在各地的仓库,都会从最近的地方查找数据,最快的送到用户手中)
-
能够将首屏加载的文件尽量缩小,
-
资源能够采用懒加载和异步加载的方式,避免堵塞页面渲染
-
利用浏览器提供的preload、prefetch等资源提示,加快文件传输。
-
避免对象嵌套的太深,这样读取也会缓慢,也不利于数据的维护
-
减小循环次数: 一方面能够减小循环的数据量的大小,一方面能够在达到要求的时候尽快结束循环。
-
尽可能避免使用for-in循环,由于他会枚举原型对象
-
条件判断的流程性能:Map>switch>if-else
// 使用if-else
if (type === 1) {
} else if (type === 2) {
} else if (type === 3) {
}
// 使用switch
switch (type) {
case 1:
break;
case 2:
break;
default:
break;
}
// 使用Map
const map = new Map([
[1, () => {} ],
[2, () => {} ],
[3, () => {} ],
]);
map.get(type)();复制代码
5. dom优化: 减小dom的请求,能够将dom进行缓存; 尽可能减小在js中修改样式,减小页面的重绘和回流; 可使用document.fragment,减小页面重绘次数;
减小选择器层级的嵌套; 减小通配符和属性选择器的使用; 7. html优化: 减小dom数量,避免没必要要的嵌套; 避免使用<img src='''/> 空标签,减小服务器压力; 提早定义好 图片的宽高,避免因图片加载致使的浏览器回流; 尽可能使用语义化标签,有利于seo和浏览器解析时间;
浏览器事件循环机制(event-loop)
JS执行是单线程的,可是浏览器执行的时候还配合有一个任务队列;
JS是从上到下执行代码的,当发生一个任务,判断是同步任务仍是异步任务: 若是是同步任务,把该任务放到主线程执行,若是是异步任务放到任务队列执行;
宏任务:script, setTimeOut, setInterval
微任务:Promise.then(),process.nextTick
每次微任务执行完以后,都回去微任务队列里去检查,若是微任务中有操做,就执行微任务的操做,执行完以后再次执行下一个宏任务,一次循环;
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})复制代码
第一次执行宏任务是,遇到console.log 因此打印了2;
遇到setTimeout,把setTimeout的回调放到宏任务中,为setTimeout1;
遇到process.next,把process.next的回调放到微任务中,为process1;
遇到promise时,当即执行console.log('7'),因此打印7,then里边的操做为异步操做,因此将then的回调放到微任务中,为then1;
遇到下一个setTimeout,把setTimeout的回调放到宏任务中,为setTimeout2;
此时,打印了2,7。那么看下此时宏任务和微任务中都有哪些东西
宏任务:setTimeout一、setTimeout2
微任务:process一、then1;复制代码
此时将执行清空微任务的操做,则执行process一、then1,则依次打印六、8。
下边将下一个宏任务即set Ti meout1放到执行栈中,即第二次执行宏任务。
遇到console是,遇到console.log 因此打印了2;
遇到process.nextTick,则把其回调放到微任务中,为process2;
遇到promise,则当即执行console,打印4,并将then放到微任务中,为then2;
宏任务:setTimeout2
微任务:process二、then2;复制代码
这样第二个宏任务执行完毕,接下来清空微任务,则执行process二、then2,依次打印3,5;
接下来将setTimeout2放到执行栈中执行,即第三次宏任务。
遇到console是,遇到console.log 因此打印了9;
遇到process.nextTick,则把其回调放到微任务中,为process3;
遇到promise,则当即执行console,打印11,并将then放到微任务中,为then3;
宏任务:
微任务:process三、then3;复制代码
这样第三个宏任务执行完毕,接下来清空微任务,则执行process三、then3,依次打印10,12;