浏览器工作原理和实践(三)——页面 (下)

简介:   《浏览器工作原理与实践》是极客时间上的一个浏览器学习系列,在学习之后特在此做记录和总结。

  执行 JavaScript 添加元素是在一个任务中执行的,重新计算样式布局是在另外一个任务中执行。

  所谓强制同步布局,是指 JavaScript 强制将计算样式和布局操作提前到当前的任务中。


function foo() {
    let main_div = document.getElementById("mian_div")
    let new_node = document.createElement("li")
    let textnode = document.createTextNode("time.geekbang")
    new_node.appendChild(textnode);
    document.getElementById("mian_div").appendChild(new_node);
    //由于要获取到offsetHeight,
    //但是此时的offsetHeight还是老的数据,
    //所以需要立即执行布局操作
    console.log(main_div.offsetHeight)
}


55.png


  为了避免强制同步布局,可以调整策略,在修改 DOM 之前查询相关值。


function foo() {
    let main_div = document.getElementById("mian_div")
    //为了避免强制同步布局,在修改DOM之前查询相关值
    console.log(main_div.offsetHeight)
    let new_node = document.createElement("li")
    let textnode = document.createTextNode("time.geekbang")
    new_node.appendChild(textnode);
    document.getElementById("mian_div").appendChild(new_node);   
}


  (3)避免布局抖动。

  所谓布局抖动,是指在一次 JavaScript 执行过程中,多次执行强制布局和抖动操作。


function foo() {
    let time_li = document.getElementById("time_li")
    for (let i = 0; i < 100; i++) {
        let main_div = document.getElementById("mian_div")
        let new_node = document.createElement("li")
        let textnode = document.createTextNode("time.geekbang")
        new_node.appendChild(textnode);
        new_node.offsetHeight = time_li.offsetHeight;
        document.getElementById("mian_div").appendChild(new_node);
    }
}


56.png

  (4)合理利用 CSS 合成动画。

  合成动画是直接在合成线程上执行的,这和在主线程上执行的布局、绘制等操作不同,如果主线程被 JavaScript 或者一些布局任务占用,CSS 动画依然能继续执行。

  另外,如果能提前知道对某个元素执行动画操作,那就最好将其标记为 will-change,这是告诉渲染引擎需要将该元素单独生成一个图层。

  (5)避免频繁的垃圾回收。

  要尽量避免产生那些临时垃圾数据。尽可能优化储存结构,尽可能避免小颗粒对象的产生。

3)虚拟DOM

  虚拟 DOM 解决的事情。

  (1)将页面改变的内容应用到虚拟 DOM 上,而不是直接应用到 DOM 上。

  (2)变化被应用到虚拟 DOM 上时,虚拟 DOM 并不急着去渲染页面,而仅仅是调整虚拟 DOM 的内部状态,这样操作虚拟 DOM 的代价就变得非常轻了。

  (3)在虚拟 DOM 收集到足够的改变时,再把这些变化一次性应用到真实的 DOM 上。

  把虚拟 DOM 看成是 DOM 的一个 buffer,和图形显示一样,它会在完成一次完整的操作之后,再把结果应用到 DOM 上,这样就能减少一些不必要的更新,同时还能保证 DOM 的稳定输出。


57.png


八、WebComponent


  WebComponent能提供给开发者组件化开发的能力。

  对内高内聚,对外低耦合。对内各个元素彼此紧密结合、相互依赖,对外和其他组件的联系最少且接口简单。

  WebComponent提供了对局部视图的封装能力,可以让 DOM、CSSOM 和 JavaScript 运行在局部环境中,这样就使得局部的 CSS 和 DOM 不会影响到全局。

1)使用

  WebComponent 是一套技术的组合,具体涉及到了 Custom elements(自定义元素)、Shadow DOM(影子 DOM)和HTML templates(HTML 模板)。


<!DOCTYPE html>
<html>
<body>
    <!--
            一:定义模板
            二:定义内部CSS样式
            三:定义JavaScript行为
    -->
    <template id="geekbang-t">
        <style>
            p {
                background-color: brown;
                color: cornsilk
            }
            div {
                width: 200px;
                background-color: bisque;
                border: 3px solid chocolate;
                border-radius: 10px;
            }
        </style>
        <div>
            <p>time.geekbang.org</p>
            <p>time1.geekbang.org</p>
        </div>
        <script>
            function foo() {
                console.log('inner log')
            }
        </script>
    </template>
    <script>
        class GeekBang extends HTMLElement {
            constructor() {
                super()
                //获取组件模板
                const content = document.querySelector('#geekbang-t').content
                //创建影子DOM节点
                const shadowDOM = this.attachShadow({ mode: 'open' })
                //将模板添加到影子DOM上
                shadowDOM.appendChild(content.cloneNode(true))
            }
        }
        customElements.define('geek-bang', GeekBang)
    </script>
    <geek-bang></geek-bang>
    <div>
        <p>time.geekbang.org</p>
        <p>time1.geekbang.org</p>
    </div>
    <geek-bang></geek-bang>
</body>
</html>


  要使用 WebComponent,通常要实现下面三个步骤。

  (1)首先,使用 template 属性来创建模板。

  (2)其次,需要创建一个 GeekBang 的类。查找模板内容;创建影子 DOM;再将模板添加到影子 DOM 上。

  (3)最后,可以像正常使用 HTML 元素一样使用该元素。

2)影子 DOM

  WebComponent的核心就是影子 DOM。

  (1)影子 DOM 中的元素对于整个网页是不可见的;

  (2)影子 DOM 的 CSS 不会影响到整个网页的 CSSOM,影子 DOM 内部的 CSS 只对内部的元素起作用。


58.png


  从图中可以看出,使用了两次 geek-bang 属性,那么就会生成两个影子 DOM,并且每个影子 DOM 都有一个 shadow root 的根节点。

  可以将要展示的样式或者元素添加到影子 DOM 的根节点上,每个影子 DOM 都可以看成是一个独立的 DOM,它有自己的样式、自己的属性,内部样式不会影响到外部样式,外部样式也不会影响到内部样式。


九、安全沙箱


  在渲染进程和操作系统之间建一道墙,即便渲染进程由于存在漏洞被黑客攻击,但由于这道墙,黑客就获取不到渲染进程之外的任何操作权限。将渲染进程和操作系统隔离的这道墙就是安全沙箱。

  浏览器中的安全沙箱是利用操作系统提供的安全技术,让渲染进程在执行过程中无法访问或者修改操作系统中的数据,在渲染进程需要访问系统资源的时候,得通过浏览器内核来实现,然后将访问的结果通过 IPC 转发给渲染进程。

  了解了被安全沙箱保护的进程会有一系列的受限操作之后,接下来就可以分析渲染进程和浏览器内核各自都有哪些职责,如下图:


59.png


1)持久存储

  文件内容的读写都是在浏览器内核中完成的:

  (1)存储 Cookie 数据的读写。通常浏览器内核会维护一个存放所有 Cookie 的 Cookie 数据库,然后当渲染进程通过 JavaScript 来读取 Cookie 时,渲染进程会通过 IPC 将读取 Cookie 的信息发送给浏览器内核,浏览器内核读取 Cookie 之后再将内容返回给渲染进程。

  (2)一些缓存文件的读写也是由浏览器内核实现的,比如网络文件缓存的读取。

2)网络访问

  同样有了安全沙箱的保护,在渲染进程内部也是不能直接访问网络的,如果要访问网络,则需要通过浏览器内核。

  不过浏览器内核在处理 URL 请求之前,会检查渲染进程是否有权限请求该 URL,比如检查 XMLHttpRequest 或者 Fetch 是否是跨站点请求,或者检测 HTTPS 的站点中是否包含了 HTTP 的请求。

3)用户交互

  由于渲染进程不能直接访问窗口句柄,所以渲染进程需要完成以下两点大的改变。

  (1)第一点,渲染进程需要渲染出位图。为了向用户显示渲染进程渲染出来的位图,渲染进程需要将生成好的位图发送到浏览器内核,然后浏览器内核将位图复制到屏幕上。

  (2)第二点,操作系统没有将用户输入事件直接传递给渲染进程,而是将这些事件传递给浏览器内核。然后浏览器内核再根据当前浏览器界面的状态来判断如何调度这些事件,如果当前焦点位于浏览器地址栏中,则输入事件会在浏览器内核内部处理;如果当前焦点在页面的区域内,则浏览器内核会将输入事件转发给渲染进程。

  之所以这样设计,就是为了限制渲染进程有监控到用户输入事件的能力,所以所有的键盘鼠标事件都是由浏览器内核来接收的,然后浏览器内核再通过 IPC 将这些事件发送给渲染进程。

十、Chrome性能工具

1)Audits

  性能指标的分数是由六项指标决定的,它们分别是:

  (1)首次绘制 (First Paint)。

  如果 FP 时间过久,那就是页面的 HTML 文件可能由于网络原因导致加载时间过久,可利用网络面板做性能分析。

  (2)首次有效绘制 (First Meaningfull Paint),由于 FMP 计算复杂,所以现在不建议使用该指标了;

  (3)首屏时间 (Speed Index),即 LCP;

  如果 FMP 和 LCP 消耗时间过久,那么有可能是加载关键资源花的时间过久,也有可能是 JavaScript 执行过程中所花的时间过久,所以可以针对具体的情况来具体分析。

  (4)首次 CPU 空闲时间 (First CPU Idle),也称为 First Interactive,对大部分用户输入做出响应即可;

  要缩短首次 CPU 空闲时长,就需要尽可能快地加载完关键资源,尽可能快地渲染出来首屏内容。

  (5)完全可交互时间 (Time to Interactive),简称 TTI,页面的内容已经完全显示出来了,所有的 JavaScript 事件已经注册完成,页面能够对用户的交互做出快速响应,通常满足响应速度在 50 毫秒以内。

  如果要解决 TTI 时间过久的问题,可以推迟执行一些和生成页面无关的 JavaScript 工作。

  (6)最大估计输入延时 (Max Potential First Input Delay),估计 Web 页面在加载最繁忙的阶段,窗口中响应用户输入所需的时间。

  为了改善该指标,可以使用 Web Worker 来执行一些计算,从而释放主线程。另一个有用的措施是重构 CSS 选择器,以确保它们执行较少的计算。


60.png


  在渲染进程确认要渲染当前的请求后,渲染进程会创建一个空白页面,把创建空白页面的这个时间点称为 First Paint,简称 FP。

  上图中,bundle.js 是关键资源,因此需要完成加载之后,渲染进程才能执行该脚本,然后脚本会修改 DOM,引发重绘和重排等一系列操作,当页面中绘制了第一个像素时,把这个时间点称为 First Content Paint,简称 FCP。

  接下来继续执行 JavaScript 脚本,当首屏内容完全绘制完成时,把这个时间点称为 Largest Content Paint,简称 LCP。

  接下来 JavaScript 脚本执行结束,渲染进程判断该页面的 DOM 生成完毕,于是触发 DOMContentLoad 事件。等所有资源都加载结束之后,再触发 onload 事件。

2)Performance

  Performance 可以记录站点在运行过程中的性能数据,有了这些性能数据,就可以回放整个页面的执行过程,这样就方便定位和诊断每个时间段内页面的运行情况

  下图区域 1,设置该区域中的“Network”来限制网络加载速度,设置“CPU”来限制 CPU 的运算速度。区域 2 和区域 3有两个按钮,黑色按钮是用来记录交互阶段性能数据的,带箭头的圆圈形按钮用来记录加载阶段的性能数据。


61.png


  无论采用哪种方式录制,最终所生成的报告页都是一样的。


62.png


  (1)概览面板

  引入了时间线,Performance 就会将几个关键指标,诸如页面帧速 (FPS)、CPU 资源消耗、网络请求流量、V8 内存使用量 (堆内存) 等,按照时间顺序做成图表的形式展现出来。

  除了以上指标以外,概览面板还展示加载过程中的几个关键时间节点,如 FP、LCP、DOMContentLoaded、Onload 等事件产生的时间点。这些关键时间点体现在了几条不同颜色的竖线上。

  (2)性能面板

  记录了非常多的性能指标项,比如 Main 指标记录渲染主线程的任务执行过程,Compositor 指标记录了合成线程的任务执行过程,GPU 指标记录了 GPU 进程主线程的任务执行过程。

  通过概览面板来定位问题的时间节点,然后再使用性能面板分析该时间节点内的性能数据。比如概览面板中的 FPS 图表中出现了红色块,那么点击该红色块,性能面板就定位到该红色块的时间节点内。


63.png


  (3)解读性能面板的各项指标

  先看最为重要的 Main 指标,它记录了渲染进程的主线程的任务执行记录。


64.png


  光栅化线程池 (Raster),用来让 GPU 执行光栅化的任务。因为光栅化线程池和 GPU 进程中的任务执行也会影响到页面的性能,所以性能面板也添加了这两个指标,分别是 Raster 指标和 GPU 指标。

  渲染进程中除了有主线程、合成线程、光栅化线程池之外,还维护了一个 IO 线程。

  除此之外,性能面板还添加了其他一些比较重要的性能指标。

  a、第一个是 Network 指标,网络记录展示了页面中的每个网络请求所消耗的时长,并以瀑布流的形式展现。

  b、第二个是 Timings 指标,用来记录一些关键的时间节点在何时产生的数据信息,诸如 FP、FCP、LCP 等。

  c、第三个是 Frames 指标,帧记录就是用来记录渲染进程生成所有帧信息,包括了渲染出每帧的时长、每帧的图层构造等信息。

  d、第四个是 Interactions 指标,用来记录用户交互操作,比如点击鼠标、输入文字等交互信息。

  (4)详情面板

  通过上面的图形只能得到一个大致的信息,如果想要查看这些记录的详细信息,就需要引入详情面板了。

相关实践学习
部署Stable Diffusion玩转AI绘画(GPU云服务器)
本实验通过在ECS上从零开始部署Stable Diffusion来进行AI绘画创作,开启AIGC盲盒。
相关文章
|
1月前
|
存储 缓存 网络协议
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点,GET、POST的区别,Cookie与Session
计算机网络常见面试题(二):浏览器中输入URL返回页面过程、HTTP协议特点、状态码、报文格式,GET、POST的区别,DNS的解析过程、数字证书、Cookie与Session,对称加密和非对称加密
|
6月前
|
前端开发 安全 UED
【项目实战】从终端到浏览器:实现 ANSI 字体在前端页面的彩色展示
在学习和工作中,我们经常需要使用日志来记录程序的运行状态和调试信息。而为了更好地区分不同的日志等级,我们可以使用不同的颜色来呈现,使其更加醒目和易于阅读。 在下图运行结果中,我们使用了 colorlog 库来实现彩色日志输出。通过定义不同日志等级对应的颜色,我们可以在控制台中以彩色的方式显示日志信息。例如,DEBUG 级别的日志使用白色,INFO 级别的日志使用绿色,WARNING 级别的日志使用黄色,ERROR 级别的日志使用红色,CRITICAL 级别的日志使用蓝色。
|
1月前
|
域名解析 缓存 网络协议
浏览器中输入URL返回页面过程(超级详细)、DNS域名解析服务,TCP三次握手、四次挥手
浏览器中输入URL返回页面过程(超级详细)、DNS域名解析服务,TCP三次握手、四次挥手
|
6月前
|
Web App开发 XML 开发框架
技术心得记录:在IE浏览器中的奇怪页面表现
技术心得记录:在IE浏览器中的奇怪页面表现
70 0
|
2月前
|
前端开发 JavaScript 异构计算
简述浏览器的渲染原理
浏览器渲染原理主要包括以下步骤:1)解析HTML文档生成DOM树;2)解析CSS生成CSSOM树;3)结合DOM与CSSOM生成渲染树;4)布局计算(回流)确定元素大小和位置;5)绘制(Paint)将节点转为图形内容;6)合成(Composite)多层图像。整个过程从文档解析到最终输出完整网页,并通过优化技术提升性能。
|
7月前
|
Web App开发 JavaScript 前端开发
浏览器与Node.js事件循环:异同点及工作原理
浏览器与Node.js事件循环:异同点及工作原理
|
4月前
|
网络协议 前端开发 JavaScript
浏览器加载网页的幕后之旅:从URL到页面展示详解
【8月更文挑战第31天】当在浏览器地址栏输入URL并回车后,一系列复杂过程随即启动,包括DNS解析、TCP连接建立、HTTP请求发送、服务器请求处理及响应返回,最后是浏览器页面渲染。这一流程涉及网络通信、服务器处理和客户端渲染等多个环节。通过示例代码,本文详细解释了每个步骤,帮助读者深入理解Web应用程序的工作机制,从而在开发过程中作出更优决策。
77 5
|
6月前
|
JavaScript 前端开发 网络协议
浏览器的工作原理
主要分为导航、获取数据、HTML解析、css解析、执行javaScript、渲染树几个步骤。
77 1
|
5月前
|
Web App开发 编解码
软件开发常见流程之兼容性和手机屏页面设计,PC端和移动端常见浏览器,国内的UC都是根据Webkit修改过来的内核,开发重点关注尺寸,常见移动端尺寸汇总,移动端,理想视口根据你设别的样式进行修改
软件开发常见流程之兼容性和手机屏页面设计,PC端和移动端常见浏览器,国内的UC都是根据Webkit修改过来的内核,开发重点关注尺寸,常见移动端尺寸汇总,移动端,理想视口根据你设别的样式进行修改
|
5月前
|
缓存 JavaScript 前端开发
前端 JS 经典:浏览器中 ESModule 的工作原理
前端 JS 经典:浏览器中 ESModule 的工作原理
56 0