前端面试知识点梳理——运行环境

193 阅读6分钟

网页加载过程

  • 加载资源的形式
    • html代码
    • 媒体文件,如图片、视频等
    • JavaScript css
  • 加载资源的过程
    • DNS解析:域名 -> IP地址
    • 浏览器根据IP地址向服务器发起http请求,三次握手建立TCP连接(包含三次握手、四次挥手)
      • 三次握手
        1. 客户端发送请求到服务器,服务器知道客户端发送,自己接收正常。SYN=1,seq=x
        2. 服务器发给客户端,客户端知道自己发送、接收正常,服务器接收、发送正常。ACK=1,ack=x+1,SYN=1,seq=y
        3. 客户端发给服务器:服务器知道客户端发送,接收正常,自己接收,发送也正常。seq=x+1,ACK=1,ack=y+1
      • 三次握手图 三次握手图
      • 四次挥手
        1. 客户端请求断开FIN,seq=u
        2. 服务器确认客户端的断开请求ACK,ack=u+1,seq=v
        3. 服务器请求断开FIN,seq=w,ACK,ack=u+1
        4. 客户端确认服务器的断开ACK,ack=w+1,seq=u+1
      • 四次挥手图
        1344250-20190402114059390-716421818.png
    • 服务器处理http请求,并返回给浏览器
  • 渲染页面的过程
    • 根据 HTML 代码生成 DOM Tree
    • 根据 CSS 代码生成 CSSOM
    • 将 DOM Tree 和 CSSOM 整合形成 Render Tree
    • 根据 Render Tree渲染页面
    • 遇到<script>则暂停渲染,优先加载并执行JS代码,完成再继续
    • Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
    • Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
    • Display:将像素发送给GPU,展示在页面上。
  • window.onload 和 DOMContentLoaded
    window.addEventListener('load',function(){
        // 页面的全部资源加载完才会执行,包括图片、视频等
    })
    document.addEventListener('DOMContentLoaded',function(){
        // DOM 渲染完即可执行,此时图片、视频还可能没有加载完
    })
    
  • 回流和重绘
    • 何时发生回流
      • 添加或删除可见的DOM元素
      • 元素的位置发生变化
      • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
      • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
      • 页面一开始渲染的时候(这肯定避免不了)
      • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)
    • 何时发生重绘(回流一定会触发重绘):
      • 当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
      • 有时即使仅仅回流一个单一的元素,它的父元素以及任何跟随它的元素也会产生回流。
      • 以下属性和方法都需要返回最新的布局信息,因此浏览器不得不清空队列,触发回流重绘来返回正确的值。因此,我们在修改样式的时候,**最好避免使用上面列出的属性,他们都会刷新渲染队列。如果要使用它们,最好将值缓存起来。
        • clientWidthclientHeightclientTopclientLeft
        • offsetWidthoffsetHeightoffsetTopoffsetLeft
        • scrollWidthscrollHeightscrollTopscrollLeft
        • widthheight
        • getComputedStyle()
        • getBoundingClientRect()
    • 如何避免触发回流和重绘
      • CSS:
        • 避免使用table布局。
        • 尽可能在DOM树的最末端改变class。
        • 避免设置多层内联样式。
        • 将动画效果应用到position属性为absolutefixed的元素上
        • 避免使用CSS表达式(例如:calc()
        • CSS3硬件加速(GPU加速)
      • JavaScript:
        • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性
        • 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中
        • 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘
        • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来
        • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流

性能优化

  • 性能优化原则
    • 多使用内存、缓存或其他方法
    • 减少CPU计算量,减少网络加载耗时
    • (适用于所有编程的性能优化 —— 空间换时间)
  • 让加载更快
    • 减少资源体积:压缩代码
    • 减少访问次数:合并代码、合并图片(雪碧图)、SSR服务器端渲染、缓存
    • 使用更快的网络:CDN
  • 让渲染更快
    • CSS放在head中,JS放在body最下面
    • 尽早开始执行JS,用DOMContentLoaded触发
    • 懒加载(图片懒加载,上滑加载更多)
    • 对DOM查询进行缓存
    • 频繁DOM操作,合并到一起插入DOM结构
    • 节流throttle 防抖debounce (让渲染更加流畅)

体验优化

防抖debounce

  • 触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间;
  • 多应用于 用户输入,防止频繁触发change事件
  • 手写debounce函数
    // 防抖
    function debounce(fn, delay = 500){
        // timer 是闭包中
        let timer = null
        return function(){
            if(timer){
                clearTimeout(timer)
            }
            timer = setTimeout(()=>{
                console.log('this',this)
                fn.apply(this,arguments)
                timer = null
            },delay)
        }
    }
    input1.addEventListener('keyup',debounce(function(e){
        console.log(e.target)
        console.log(input1.value)
    },600))
    

节流throttle

  • 高频事件触发,但在 n 秒内只会执行一次,所以节流会稀释函数的执行频率。
  • 多应用于 拖拽元素 获取位置
  • 手写throttle函数
    const div1 = document.getElementById('div1')
    function throttle(fn,delay=100){
        let timer = null
        return function(){
            if(timer) return
            timer = setTimeout(() => {
                fn.apply(this,arguments)
                timer = null
            }, delay);
        }
    
    }
    div1.addEventListener('drag',throttle(function(e){
        console.log(e.offsetX,e.offsetY);
    }))
    

常见的web前端攻击方式

XSS 跨站请求攻击

  • 模拟个XSS攻击场景
    一个博客网站,我发表了一篇博客,其中嵌入<script>脚本
    脚本内容:获取cookie,发送到我的服务器(服务器配合跨域)
    发布这篇博客,有人查看他,可以轻松收割访问者的cookie
    
  • 如何预防XSS攻击
    • 替换特殊字符,如<变为 < >变为 >
    • <script> 变为 &ltscript&gt,直接显示,而不会不作为脚本执行
    • 前端要替换,后端也要替换,都做总不会有错 &ltscript&gt alert(111) &lt/script&gt

XSRF 跨站请求伪造

  • 模拟个XSRF攻击场景
    你正在购物,看中了某个商品,商品id是100,
    付费接口是xxx.com/pay?id=100 ,但没有任何验证
    我是攻击者,我看中了一个商品,id是200
    我向你发送了一封电子邮件,邮件标题很吸引人
    但邮件正文隐藏着<img src=xxx.com/pay?id=200 />
    你一查看邮件,就帮我买了id是200的商品
    
  • 如何预防XSRF攻击
    • 使用post接口
    • 增加验证,例如密码、短信验证码、指纹等