前端面试准备---杂类篇

326 阅读5分钟

前端性能优化问题?

这个问题无疑是面试常见的问题之一,看了大神文章之后觉得自己之前的回答好次哦,站的角度不够高,那么下边我们就来看一下,怎样回答好这个问题。
  • 页面基础优化
我们常说的都是这部分的优化。
  1. 文件引入位置: css放在head中引入; js放到script末尾引入,防止阻止页面的加载
  2. 减少文件体积: 删除冗余代码、 压缩混淆代码、 文件按需加载
  3. 图片的优化: 在保证分辨率的情况下尽量压缩图片; 小的图片可以采用多张拼成一张雪碧图,减少图片请求次数; 低于5k图片可以采用base64的图片格式,不需要发请求,浏览器能直接编译;
  4. 减少http请求,合并请求
  5. 合理使用缓存: 可以将数据存储到浏览器上,合理利用浏览器的缓存,避免重复发送请求; 如果使用cdn的话,也可利用cdn缓存;(cdn是什么?就类似于京东在各地的仓库,都会从最近的地方查找数据,最快的送到用户手中)
  • 首屏渲染优化
  1. 可以将首屏加载的文件尽可能缩小,
  2. 资源可以采用懒加载和异步加载的方式,避免堵塞页面渲染
  3. 利用浏览器提供的preload、prefetch等资源提示,加快文件传输。
  • 编码优化
这个方面其实考察是程序员个人编程素养问题。
  1. 避免对象嵌套的太深,这样读取也会缓慢,也不利于数据的维护
  2. 减少循环次数: 一方面可以减少循环的数据量的大小,一方面可以在达到要求的时候尽快结束循环。
  3. 尽量避免使用for-in循环,因为他会枚举原型对象
  4. 条件判断的流程性能: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,减少页面重绘次数;
6. css优化:
减少选择器层级的嵌套; 减少通配符和属性选择器的使用; 7. html优化: 减少dom数量,避免不必要的嵌套; 避免使用<img src='''/> 空标签,减少服务器压力; 提前定义好 图片的宽高,避免因图片加载导致的浏览器回流; 尽量使用语义化标签,有利于seo和浏览器解析时间;

浏览器事件循环机制(event-loop)

为什么JS是单线程的,但是不会被卡住呢?
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')
    })
})
整个script相当于一个宏任务。
第一次执行宏任务是,遇到console.log 所以打印了2;
遇到setTimeout,把setTimeout的回调放到宏任务中,为setTimeout1;
遇到process.next,把process.next的回调放到微任务中,为process1;
遇到promise时,立即执行console.log('7'),所以打印7,then里边的操作为异步操作,所以将then的回调放到微任务中,为then1;
遇到下一个setTimeout,把setTimeout的回调放到宏任务中,为setTimeout2;
此时,打印了2,7。那么看下此时宏任务和微任务中都有哪些东西
宏任务:setTimeout1、setTimeout2
微任务:process1、then1;
此时将执行清空微任务的操作,则执行process1、then1,则依次打印6、8。
此过程中打印顺序为1,7,6,8
下边将下一个宏任务即set Ti meout1放到执行栈中,即第二次执行宏任务。
遇到console是,遇到console.log 所以打印了2;
遇到process.nextTick,则把其回调放到微任务中,为process2;
遇到promise,则立即执行console,打印4,并将then放到微任务中,为then2;
宏任务:setTimeout2
微任务:process2、then2;
这样第二个宏任务执行完毕,接下来清空微任务,则执行process2、then2,依次打印3,5;
此过程中打印顺序为2,4,3,5
接下来将setTimeout2放到执行栈中执行,即第三次宏任务。
遇到console是,遇到console.log 所以打印了9;
遇到process.nextTick,则把其回调放到微任务中,为process3;
遇到promise,则立即执行console,打印11,并将then放到微任务中,为then3;
宏任务:
微任务:process3、then3;
这样第三个宏任务执行完毕,接下来清空微任务,则执行process3、then3,依次打印10,12;
此过程中打印顺序为9,11,10,12