【前端面试基础】(五)性能优化、Web安全

216 阅读4分钟

前言:大家好,我是忆白,本文根据慕课网双越老师《一天时间迅速准备前端面试 快速构建初级前端知识体系》课程整理的面试常考题目以及相应知识扩展,持续更新。

1. 描述从输入 url 到渲染出页面的整个过程?

1.1 资源的形式

  • html 代码
  • 媒体文件,如图片、视频等
  • javascript css

1.2 加载过程

  • DNS 解析:域名 -> IP 地址
  • 浏览器根据 IP 地址向服务器发起 http 请求
  • 服务器处理 http 请求,并返回给浏览器

1.3 渲染过程

  • 根据 HTML 代码生成 DOM Tree
  • 根据 CSS 代码生成 CSSOM(CSS Object Model,css对象模型)
  • 将 DOM Tree 和 CSSOM 整合形成 Render Tree(渲染树)
  • 浏览器根据 Render Tree 渲染页面
  • 遇到 <script> 则暂停渲染,优先加载并执行 JS 代码,完成再继续(JS线程和渲染线程共用一个线程,因为JS可能会改变DOM结构)
  • 直至把 Render Tree 渲染完成

2. window.onload 和 DOMContentLoaded 的区别

  • window.onload:资源全部加载完之后才能执行,包括图片
  • DOMContentLoaded:DOM渲染完成即可执行,图片可能尚未下载
window.addEventListener('load', function() {
    // 页面的全部资源加载完才会执行,包括图片、视频等
})
document.addEventtListener('DOMContentLoaded', function() {
    // DOM 渲染完即可执行,此时图片、视频可能还没有加载完
})

3. 性能优化

3.1 性能优化原则

  • 多使用内存、缓存或其他方法
  • 减少 CPU 计算量,减少网络加载耗时
  • (适用于所有编程的性能优化 —— 空间换时间)

3.2 让加载更快

  • 减少资源体积:压缩代码
  • 减少访问次数:合并代码、SSR 服务器端渲染、缓存
  • 使用更快的网络:CDN

3.3 让渲染更快

  • CSS放在 head 中,JS放在 body 最下面

  • 尽早开始执行JS,用DOMContentLoaded触发

  • 懒加载(图片懒加载,上滑加载更多)

  • 对 DOM 查询进行缓存(查询的DOM节点用变量保存,避免多次查询)

    // 不缓存 DOM 查询结果
    for (let i=0; i<document.getElementsByTagName('p')..length; i++) {
        // 每次循环都会计算 length,频繁进行 DOM 查询
    }
    ​
    // 缓存 DOM 查询结果
    const pList = document.getElementsByTagName('p')
    const length = pList.length;
    for (let i=0; i<length; i++) {
        // 缓存 length,只进行一次 DOM 查询 
    }
    
  • 频繁的 DOM 操作,合并到一起(使用文档碎片)插入DOM结构

    const listNode = document.getElementById('list');
    // 创建一个文档片段,此时还没有插入到 DOM 树中
    const frag = document.createDocumentFragment()
    // 执行插入
    for(let x=0; x<10; x++) {
        const li = document.createElement('li');
        li.innerHTML = 'List item' + x;
        frag.appendChild(li);
    }
    // 都完成之后,将文档碎片中的内容一次性插入DOM树中
    listNode.appendChild(frag);
    
  • 节流 throttle 防抖 debounce

3.3.1 为什么把css放在head中?

如果放在最后,则拿到html之后,分析生成DOM树,然后渲染,此时渲染div是默认样式的,渲染到后面发现有CSS,然后加载CSS后生成CSSOM,然后与DOM树结合生成render Tree,可能又会重新渲染。

放在head中,把CSS代码在DOM树生成完成之前就加载完,当DOM树生成完之后,可以直接和所有CSS结合生成渲染树,一步渲染完成,不用重复。

3.3.2 为什么把script放在body最后

因为DOM渲染遇到script之后会阻塞渲染,等js执行完成之后才能继续渲染,导致页面渲染过程长,。放在最后可以先让html渲染完让用户看到界面,之后再执行脚本

3.4 缓存

  • 静态文件加hash后缀,根据文件内容计算hash
  • 文件内容不变,则hash不变,则 url 不变
  • url 和文件不变,则会自动触发 http 缓存机制,返回304

3.5 SSR

  • 服务器端渲染:将网页和数据一起加载,一起渲染
  • 非 SSR (前后端分离):先加载网页,再加载数据,再渲染数据

3.6 懒加载

  • 页面内容数据过多,可以做分页器,这样即可以节省某个页面加载时间
  • 滑动懒加载,当滑到底部没数组的时候再加载新的内容
  • 点击懒加载,点击按钮,类似加载更多,触发加载新的内容

3.7 防抖 debouce

概念:防止抖动,以免把一次事件当成多次事件处理。

防抖重在清零。

应用场景:

  • 登陆、发短信按钮,用户点击太快,为避免发送多次请求,需要防抖
  • 文本编辑器的实时保存,无任何操作之后1秒进行保存
  • 用户在输入框输入结束或者暂停时,才会触发change事件(比如搜索框输入信息停下1秒后根据已经输入的内容进行查询)

手写防抖:

// 封装防抖函数
function debounce(fn, delay = 500) {
    // timer 是在闭包中的
    let timer = null;
    return function() {
        if(timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments);
            timer = null;
        }, delay);
    }
}

3.8 节流

概念:控制流量,即控制事件发生的频率。

节流重在加锁

应用场景:

  • scroll事件,控制0.5秒计算一次位置信息
  • 浏览器播放事件,控制每隔1秒计算一次进度信息
  • 拖拽一个元素时,要随时拿到该元素的被拖拽的位置,直接用drag事件,会频繁触发,很容易导致卡顿。此时采用节流,无论拖拽速度多快,都控制在每隔100ms触发一次

手写节流:

// 节流函数封装
function throttle(fn, delay = 100) {
    let timer = null;
​
    return function() {
        if (timer) {
            return;
        }
        timer = setTimeout(() => {
            // 使用apply中arguments接收外部传入的e事件对象参数,赋值给fn,箭头函数没有arguments,这个arguments是return的函数的arguments
            fn.apply(this, arguments);
            timer = null;
        }, delay);
    }
}

4. Web安全

4.1 XSS 跨站请求攻击

【场景】

  • 一个博客网站,我发表一篇博客,其中嵌入
  • 脚本内容:获取cookie,发送到我的服务器(服务器配合跨域)
  • 发布这篇博客,有人查看它,我就能轻松收割访问者的cookie

【XSS预防】

  • 替换特殊字符,如 < 变为 &lt; > 变为 &gt;
  • <sciprt>就会变为 &lt;script&gt;,直接显示,而不会作为脚本执行
  • 前端要替换,后端也要替换,都做总不会有错
  • 主流的前端框架已做好预防

4.2 XSRF 跨站请求伪造

【场景】

  • 你正在购物,看中了某个商品,商品 id 是100
  • 付费接口时 xxx.com/pay?id=100,但没有任何验证
  • 我是攻击者,我看中了一个商品,id 是200
  • 我向你发送一封电子邮件,邮件标题很吸引人
  • 但邮件正文隐藏着<img src="xxx.com/pay?id=200"/>(并附带有别的执行付费脚本)
  • 你一查看邮件,就帮我购买了 id 是 200的商品

【XSRF预防】

  • 使用 post 接口
  • 增加验证,例如密码、短信验证码、指纹等

写在最后

如果你觉得我写的还不错,可以给我点个赞哦,如果有写错的地方,也欢迎大家评论指出,谢谢。